diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 34edacfa967..addf36f4339 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,7 +3,7 @@ name: Bug report about: Create a report to help us improve --- - + diff --git a/.github/calendar.yml b/.github/calendar.yml index 9b86376e5a0..307c3db379a 100644 --- a/.github/calendar.yml +++ b/.github/calendar.yml @@ -29,5 +29,7 @@ '2018-08-27 18:00, US/Pacific': 'endgame', '2018-09-05 12:00, US/Pacific': 'development', # 1.27.0 released '2018-09-24 18:00, US/Pacific': 'endgame', -# '2018-10-03 12:00, US/Pacific': 'development', # 1.28.0 released + '2018-10-08 09:00, US/Pacific': 'development', # 1.28.0 released + '2018-10-29 18:00, US/Pacific': 'endgame', + # '2018-11-07 09:00, US/Pacific': 'development', # 1.29.0 released } diff --git a/.github/classifier.yml b/.github/classifier.yml index eba4910f445..07b85720764 100644 --- a/.github/classifier.yml +++ b/.github/classifier.yml @@ -15,7 +15,7 @@ css-less-scss: [ aeschli ], debug-console: [], debug: { - assignees: [ isidorn ], + assignees: [ weinand ], assignLabel: false }, diff-editor: [], @@ -78,10 +78,10 @@ formatting: [], git: [ joaomoreno ], grammar: [], - hot-exit: [ Tyriar ], + hot-exit: [], html: [ aeschli ], install-update: [], - integrated-terminal: [ Tyriar ], + integrated-terminal: [], integration-test: [], intellisense-config: [], issue-reporter: [ RMacfarlane ], diff --git a/.github/commands.yml b/.github/commands.yml index ba6ab0e86e4..95a3df06c5b 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -46,6 +46,7 @@ { type: 'comment', name: 'duplicate', + allowUsers: ['cleidigh', 'usernamehw'], action: 'updateLabels', addLabel: '*duplicate' }, @@ -59,18 +60,21 @@ { type: 'comment', name: 'confirm', + allowUsers: ['cleidigh', 'usernamehw'], action: 'updateLabels', addLabel: 'confirmed' }, { type: 'comment', name: 'findDuplicates', + allowUsers: ['cleidigh', 'usernamehw'], action: 'comment', comment: "Potential duplicates:\n${potentialDuplicates}" }, { type: 'comment', name: 'needsMoreInfo', + allowUsers: ['cleidigh', 'usernamehw'], action: 'updateLabels', addLabel: 'needs more info', comment: "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!" diff --git a/.vscode/launch.json b/.vscode/launch.json index 73b99e84fbb..a117e3ed45d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -147,7 +147,7 @@ }, "urlFilter": "*workbench.html*", "runtimeArgs": [ - "--inspect=5875" + "--inspect=5875", "--no-cached-data" ], "smartStep": true, "skipFiles": [ @@ -155,6 +155,19 @@ ], "webRoot": "${workspaceFolder}" }, + { + "type": "node", + "request": "launch", + "name": "Launch VS Code (Main Process)", + "runtimeExecutable": "${workspaceFolder}/scripts/code.sh", + "runtimeArgs": [ + "--no-cached-data" + ], + "smartStep": true, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ] + }, { "type": "node", "request": "launch", diff --git a/.vscode/settings.json b/.vscode/settings.json index e8df189e775..3edd3534df9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,7 +21,8 @@ "out-vscode/**": true, "i18n/**": true, "extensions/**/out/**": true, - "test/smoke/out/**": true + "test/smoke/out/**": true, + "src/vs/base/test/node/uri.test.data.txt": true }, "tslint.enable": true, "lcov.path": [ @@ -42,5 +43,4 @@ "emmet.excludeLanguages": [], "typescript.preferences.importModuleSpecifier": "non-relative", "typescript.preferences.quoteStyle": "single" - } \ No newline at end of file diff --git a/.vscode/shared.code-snippets b/.vscode/shared.code-snippets index 7ea5b3c14dc..6d6690a0282 100644 --- a/.vscode/shared.code-snippets +++ b/.vscode/shared.code-snippets @@ -5,8 +5,8 @@ // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. // Placeholders with the same ids are connected. // Example: - "JavaScript, TypeScript Copyright Header": { - "scope": "javascript,typescript", + "MSFT Copyright Header": { + "scope": "javascript,typescript,css", "prefix": [ "header", "stub", @@ -18,8 +18,6 @@ " * Licensed under the MIT License. See License.txt in the project root for license information.", " *--------------------------------------------------------------------------------------------*/", "", - "'use strict';", - "", "$0" ], "description": "Insert Copyright Statement" @@ -33,25 +31,8 @@ "prefix": "emitter", "description": "Add emitter and event properties", "body": [ - "private _onDid$1 = new Emitter<$2>();", + "private readonly _onDid$1 = new Emitter<$2>();", "readonly onDid$1: Event<$2> = this._onDid$1.event;" ], - }, - "CSS Copyright Header": { - "scope": "css", - "prefix": [ - "header", - "stub", - "copyright" - ], - "body": [ - "/*---------------------------------------------------------------------------------------------", - " * Copyright (c) Microsoft Corporation. All rights reserved.", - " * Licensed under the MIT License. See License.txt in the project root for license information.", - " *--------------------------------------------------------------------------------------------*/", - "", - "$0" - ], - "description": "Insert Copyright Statement" } } diff --git a/.yarnrc b/.yarnrc index 55749a8a0a7..8e1ac6b6cac 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "2.0.11" +target "2.0.12" runtime "electron" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b1650a08e2e..7775753e8bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,4 +101,3 @@ please see [How to Contribute](https://github.com/Microsoft/vscode/wiki/How-to-C # Thank You! Your contributions to open source, large or small, make great projects like this possible. Thank you for taking the time to contribute. - diff --git a/OSSREADME.json b/OSSREADME.json index c59395867eb..0f988151200 100644 --- a/OSSREADME.json +++ b/OSSREADME.json @@ -51,7 +51,7 @@ }, { "name": "electron", - "version": "2.0.11", + "version": "2.0.12", "license": "MIT", "repositoryURL": "https://github.com/electron/electron", "isProd": true diff --git a/README.md b/README.md index f9b64ff7a21..a7294e71b9b 100644 --- a/README.md +++ b/README.md @@ -5,17 +5,16 @@ [![Bugs](https://img.shields.io/github/issues/Microsoft/vscode/bug.svg)](https://github.com/Microsoft/vscode/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Abug) [![Gitter](https://img.shields.io/badge/chat-on%20gitter-yellow.svg)](https://gitter.im/Microsoft/vscode) -[VS Code](https://code.visualstudio.com) is a new type of tool that combines the simplicity of -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](https://code.visualstudio.com) is a type of tool that combines the simplicity of +a code editor with what developers need for their core edit-build-debug cycle. It 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, macOS, and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases every day, 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 every day, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated daily at the very least.

VS Code in action

-The [`vscode`](https://github.com/microsoft/vscode) repository is where we do development and there are many ways you can participate in the project, for example: +The [`vscode`](https://github.com/microsoft/vscode) repository is where VS Code is developed and there are many ways you can participate in the project, for example: * [Submit bugs and feature requests](https://github.com/microsoft/vscode/issues) and help us verify as they are checked in. * Review [source code changes](https://github.com/microsoft/vscode/pulls). @@ -38,15 +37,20 @@ Please also see our [Code of Conduct](CODE_OF_CONDUCT.md). * Ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/vscode). * Request a new feature on [GitHub](CONTRIBUTING.md). -* Vote for [popular feature requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc). +* Vote for [Popular Feature Requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc). * File a bug in [GitHub Issues](https://github.com/Microsoft/vscode/issues). -* [Tweet](https://twitter.com/code) us with other feedback. +* [Tweet](https://twitter.com/code) us with any other feedback. ## Related Projects -Many of the core components and extensions to Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug). +Many of the core components and extensions to Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. -For a complete list, please see the [Related Projects](https://github.com/Microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/Microsoft/vscode/wiki). +For a complete list, please visit the [Related Projects](https://github.com/Microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/Microsoft/vscode/wiki). + +## Bundled Extensions + +Code ships with a set of extensions. These extensions are located in the [extensions](extensions) folder. +These extensions include grammars and snippets for several languages. Extensions that provide rich language support (code completion, go to definition) for a language have the suffix 'language-features'. For example, the 'json' extension provides coloring for JSON and the 'json-language-features' provides rich language support for JSON. ## License diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index ed7e5acda34..757d3800253 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -60,13 +60,12 @@ This project incorporates components from the projects listed below. The origina 53. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) 54. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) 55. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) -56. textmate/toml.tmbundle (https://github.com/textmate/toml.tmbundle) -57. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) -58. TypeScript-TmLanguage version 0.1.8 (https://github.com/Microsoft/TypeScript-TmLanguage) -59. Unicode () -60. vscode-logfile-highlighter version 1.2.0 (https://github.com/emilast/vscode-logfile-highlighter) -61. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) -62. Web Background Synchronization (https://github.com/WICG/BackgroundSync) +56. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) +57. TypeScript-TmLanguage version 0.1.8 (https://github.com/Microsoft/TypeScript-TmLanguage) +58. Unicode () +59. vscode-logfile-highlighter version 1.2.0 (https://github.com/emilast/vscode-logfile-highlighter) +60. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +61. Web Background Synchronization (https://github.com/WICG/BackgroundSync) %% atom/language-c NOTICES AND INFORMATION BEGIN HERE @@ -1282,7 +1281,49 @@ END OF MagicStack/MagicPython NOTICES AND INFORMATION %% marked NOTICES AND INFORMATION BEGIN HERE ========================================= -Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License) +information + +## Contribution License Agreement + +If you contribute code to this project, you are implicitly allowing your code +to be distributed under the MIT license. You are also implicitly verifying that +all code is your original work. `` + +## Marked + +Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## Markdown + +Copyright © 2004, John Gruber +http://daringfireball.net/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name "Markdown" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. ========================================= END OF marked NOTICES AND INFORMATION @@ -2180,20 +2221,6 @@ to the base-name name of the original file, and an extension of txt, html, or si ========================================= END OF textmate/ruby.tmbundle NOTICES AND INFORMATION -%% textmate/toml.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= -Copyright (c) https://github.com/infininight and https://github.com/mojombo -Permission to copy, use, modify, sell and distribute this -software is granted. This software is provided "as is" without -express or implied warranty, and with no claim as to its -suitability for any purpose - -An exception is made for files in readable text which contain their own license information, or files where an accompanying -file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension . -of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/toml.tmbundle NOTICES AND INFORMATION - %% textmate/yaml.tmbundle NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) 2015 FichteFoll diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 0426994695a..e2cb847b634 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.28.2", + "version": "1.29.2", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", @@ -16,7 +16,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.28.3", + "version": "1.29.5", "repo": "https://github.com/Microsoft/vscode-node-debug2", "metadata": { "id": "36d19e17-7569-4841-a001-947eb18602b2", @@ -28,5 +28,20 @@ }, "publisherDisplayName": "Microsoft" } + }, + { + "name": "ms-vscode.references-view", + "version": "0.0.8", + "repo": "https://github.com/Microsoft/vscode-reference-view", + "metadata": { + "id": "36d19e17-7569-4841-a001-947eb18602b2", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } } -] \ No newline at end of file +] diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 349a2b979e2..50258a98fb3 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -118,12 +118,12 @@ function prepareRpmPackage(arch) { const desktopUrlHandler = gulp.src('resources/linux/code-url-handler.desktop', { base: '.' }) .pipe(rename('BUILD/usr/share/applications/' + product.applicationName + '-url-handler.desktop')); - const desktops = es.merge(desktop, desktopUrlHandler) - .pipe(replace('@@NAME_LONG@@', product.nameLong)) - .pipe(replace('@@NAME_SHORT@@', product.nameShort)) - .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@ICON@@', product.applicationName)) - .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); + const desktops = es.merge(desktop, desktopUrlHandler) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME_SHORT@@', product.nameShort)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@ICON@@', product.applicationName)) + .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' }) .pipe(replace('@@NAME_LONG@@', product.nameLong)) @@ -172,6 +172,7 @@ function buildRpmPackage(arch) { 'cp "' + rpmOut + '/$(ls ' + rpmOut + ')" ' + destination + '/' ]); } + function getSnapBuildPath(arch) { return `.build/linux/snap/${arch}/${product.applicationName}-${arch}`; } @@ -192,17 +193,21 @@ function prepareSnapPackage(arch) { .pipe(rename(`usr/share/pixmaps/${product.applicationName}.png`)); const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) - .pipe(rename(function (p) { p.dirname = 'usr/share/' + product.applicationName + '/' + p.dirname; })); + .pipe(rename(function (p) { p.dirname = `usr/share/${product.applicationName}/${p.dirname}`; })); const snapcraft = gulp.src('resources/linux/snap/snapcraft.yaml', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@VERSION@@', packageJson.version)) + .pipe(replace('@@VERSION@@', `${packageJson.version}-${linuxPackageRevision}`)) .pipe(rename('snap/snapcraft.yaml')); + const snapUpdate = gulp.src('resources/linux/snap/snapUpdate.sh', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename(`usr/share/${product.applicationName}/snapUpdate.sh`)); + const electronLaunch = gulp.src('resources/linux/snap/electron-launch', { base: '.' }) .pipe(rename('electron-launch')); - const all = es.merge(desktop, icon, code, snapcraft, electronLaunch); + const all = es.merge(desktop, icon, code, snapcraft, electronLaunch, snapUpdate); return all.pipe(vfs.dest(destination)); }; @@ -210,11 +215,7 @@ function prepareSnapPackage(arch) { function buildSnapPackage(arch) { const snapBuildPath = getSnapBuildPath(arch); - const snapFilename = `${product.applicationName}-${packageJson.version}-${linuxPackageRevision}-${arch}.snap`; - return shell.task([ - `chmod +x ${snapBuildPath}/electron-launch`, - `cd ${snapBuildPath} && snapcraft snap --output ../${snapFilename}` - ]); + return shell.task(`cd ${snapBuildPath} && snapcraft build`); } gulp.task('clean-vscode-linux-ia32-deb', util.rimraf('.build/linux/deb/i386')); diff --git a/build/lib/compilation.js b/build/lib/compilation.js index fb311e14a01..9cc9846336a 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -11,7 +11,6 @@ const bom = require("gulp-bom"); const sourcemaps = require("gulp-sourcemaps"); const tsb = require("gulp-tsb"); const path = require("path"); -const ts = require("typescript"); const _ = require("underscore"); const monacodts = require("../monaco/api"); const nls = require("./nls"); @@ -89,7 +88,6 @@ function compileTask(src, out, build) { .pipe(generator.stream) .pipe(compile()) .pipe(gulp.dest(out)); - // .pipe(src !== 'src' ? es.through() : monacodtsTask(out, false)); }; } exports.compileTask = compileTask; @@ -110,87 +108,61 @@ exports.watchTask = watchTask; const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); class MonacoGenerator { constructor(isWatch) { + this._executeSoonTimer = null; this._isWatch = isWatch; this.stream = es.through(); - this._inputFiles = monacodts.getIncludesInRecipe().map((moduleId) => { - if (/\.d\.ts$/.test(moduleId)) { - // This source file is already in .d.ts form - return path.join(REPO_SRC_FOLDER, moduleId); - } - else { - return path.join(REPO_SRC_FOLDER, `${moduleId}.ts`); - } - }); - // Install watchers this._watchers = []; - if (this._isWatch) { - this._inputFiles.forEach((filePath) => { - const watcher = fs.watch(filePath); - watcher.addListener('change', () => { - this._inputFileChanged[filePath] = true; - // Avoid hitting empty files... :/ - setTimeout(() => this.execute(), 10); - }); - this._watchers.push(watcher); + this._watchedFiles = {}; + let onWillReadFile = (moduleId, filePath) => { + if (!this._isWatch) { + return; + } + if (this._watchedFiles[filePath]) { + return; + } + this._watchedFiles[filePath] = true; + const watcher = fs.watch(filePath); + watcher.addListener('change', () => { + this._declarationResolver.invalidateCache(moduleId); + this._executeSoon(); }); + this._watchers.push(watcher); + }; + this._fsProvider = new class extends monacodts.FSProvider { + readFileSync(moduleId, filePath) { + onWillReadFile(moduleId, filePath); + return super.readFileSync(moduleId, filePath); + } + }; + this._declarationResolver = new monacodts.DeclarationResolver(this._fsProvider); + if (this._isWatch) { const recipeWatcher = fs.watch(monacodts.RECIPE_PATH); recipeWatcher.addListener('change', () => { - this._recipeFileChanged = true; - // Avoid hitting empty files... :/ - setTimeout(() => this.execute(), 10); + this._executeSoon(); }); this._watchers.push(recipeWatcher); } - this._inputFileChanged = {}; - this._inputFiles.forEach(file => this._inputFileChanged[file] = true); - this._recipeFileChanged = true; - this._dtsFilesContents = {}; - this._dtsFilesContents2 = {}; + } + _executeSoon() { + if (this._executeSoonTimer !== null) { + clearTimeout(this._executeSoonTimer); + this._executeSoonTimer = null; + } + this._executeSoonTimer = setTimeout(() => { + this._executeSoonTimer = null; + this.execute(); + }, 20); } dispose() { this._watchers.forEach(watcher => watcher.close()); } _run() { - let somethingChanged = false; - const setDTSFileContent = (file, contents) => { - if (this._dtsFilesContents[file] === contents) { - return; - } - this._dtsFilesContents[file] = contents; - this._dtsFilesContents2[file] = ts.createSourceFile(file, contents, ts.ScriptTarget.ES5); - somethingChanged = true; - }; - const fileMap = {}; - this._inputFiles.forEach((inputFile) => { - if (!this._inputFileChanged[inputFile]) { - return; - } - this._inputFileChanged[inputFile] = false; - const inputFileContents = fs.readFileSync(inputFile).toString(); - if (/\.d\.ts$/.test(inputFile)) { - // This is a .d.ts file - setDTSFileContent(inputFile, inputFileContents); - return; - } - fileMap[inputFile] = inputFileContents; - }); - if (Object.keys(fileMap).length > 0) { - const service = ts.createLanguageService(new monacodts.TypeScriptLanguageServiceHost({}, fileMap, {})); - Object.keys(fileMap).forEach((fileName) => { - const output = service.getEmitOutput(fileName, true).outputFiles[0].text; - const destFileName = fileName.replace(/\.ts$/, '.d.ts'); - setDTSFileContent(destFileName, output); - }); + let r = monacodts.run3(this._declarationResolver); + if (!r && !this._isWatch) { + // The build must always be able to generate the monaco.d.ts + throw new Error(`monaco.d.ts generation error - Cannot continue`); } - if (this._recipeFileChanged) { - this._recipeFileChanged = false; - somethingChanged = true; - } - if (!somethingChanged) { - // Nothing changed - return null; - } - return monacodts.run2('src', this._dtsFilesContents2); + return r; } _log(message, ...rest) { util2.log(util2.colors.cyan('[monaco.d.ts]'), message, ...rest); @@ -203,10 +175,10 @@ class MonacoGenerator { return; } if (result.isTheSame) { - this._log(`monaco.d.ts is unchanged - total time took ${Date.now() - startTime} ms`); return; } fs.writeFileSync(result.filePath, result.content); + fs.writeFileSync(path.join(REPO_SRC_FOLDER, 'vs/editor/common/standalone/standaloneEnums.ts'), result.enums); this._log(`monaco.d.ts is changed - total time took ${Date.now() - startTime} ms`); if (!this._isWatch) { this.stream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 30d03c069cb..393bc56bd16 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -12,7 +12,6 @@ import * as bom from 'gulp-bom'; import * as sourcemaps from 'gulp-sourcemaps'; import * as tsb from 'gulp-tsb'; import * as path from 'path'; -import * as ts from 'typescript'; import * as _ from 'underscore'; import * as monacodts from '../monaco/api'; import * as nls from './nls'; @@ -106,7 +105,6 @@ export function compileTask(src: string, out: string, build: boolean): () => Nod .pipe(generator.stream) .pipe(compile()) .pipe(gulp.dest(out)); - // .pipe(src !== 'src' ? es.through() : monacodtsTask(out, false)); }; } @@ -136,57 +134,60 @@ const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); class MonacoGenerator { private readonly _isWatch: boolean; public readonly stream: NodeJS.ReadWriteStream; - /** - * This list is never changed for the lifetime of this object. - */ - private readonly _inputFiles: string[]; + private readonly _watchers: fs.FSWatcher[]; - - private _inputFileChanged: { [filePath: string]: boolean; }; - private _recipeFileChanged: boolean; - - private _dtsFilesContents: { [filePath: string]: string; }; - private _dtsFilesContents2: { [filePath: string]: ts.SourceFile; }; + private readonly _watchedFiles: { [filePath: string]: boolean; }; + private readonly _fsProvider: monacodts.FSProvider; + private readonly _declarationResolver: monacodts.DeclarationResolver; constructor(isWatch: boolean) { this._isWatch = isWatch; this.stream = es.through(); - this._inputFiles = monacodts.getIncludesInRecipe().map((moduleId) => { - if (/\.d\.ts$/.test(moduleId)) { - // This source file is already in .d.ts form - return path.join(REPO_SRC_FOLDER, moduleId); - } else { - return path.join(REPO_SRC_FOLDER, `${moduleId}.ts`); - } - }); - - // Install watchers this._watchers = []; - if (this._isWatch) { - this._inputFiles.forEach((filePath) => { - const watcher = fs.watch(filePath); - watcher.addListener('change', () => { - this._inputFileChanged[filePath] = true; - // Avoid hitting empty files... :/ - setTimeout(() => this.execute(), 10); - }); - this._watchers.push(watcher); - }); + this._watchedFiles = {}; + let onWillReadFile = (moduleId: string, filePath: string) => { + if (!this._isWatch) { + return; + } + if (this._watchedFiles[filePath]) { + return; + } + this._watchedFiles[filePath] = true; + const watcher = fs.watch(filePath); + watcher.addListener('change', () => { + this._declarationResolver.invalidateCache(moduleId); + this._executeSoon(); + }); + this._watchers.push(watcher); + }; + this._fsProvider = new class extends monacodts.FSProvider { + public readFileSync(moduleId: string, filePath: string): Buffer { + onWillReadFile(moduleId, filePath); + return super.readFileSync(moduleId, filePath); + } + }; + this._declarationResolver = new monacodts.DeclarationResolver(this._fsProvider); + + if (this._isWatch) { const recipeWatcher = fs.watch(monacodts.RECIPE_PATH); recipeWatcher.addListener('change', () => { - this._recipeFileChanged = true; - // Avoid hitting empty files... :/ - setTimeout(() => this.execute(), 10); + this._executeSoon(); }); this._watchers.push(recipeWatcher); } + } - this._inputFileChanged = {}; - this._inputFiles.forEach(file => this._inputFileChanged[file] = true); - this._recipeFileChanged = true; - this._dtsFilesContents = {}; - this._dtsFilesContents2 = {}; + private _executeSoonTimer: NodeJS.Timer | null = null; + private _executeSoon(): void { + if (this._executeSoonTimer !== null) { + clearTimeout(this._executeSoonTimer); + this._executeSoonTimer = null; + } + this._executeSoonTimer = setTimeout(() => { + this._executeSoonTimer = null; + this.execute(); + }, 20); } public dispose(): void { @@ -194,56 +195,12 @@ class MonacoGenerator { } private _run(): monacodts.IMonacoDeclarationResult | null { - let somethingChanged = false; - - const setDTSFileContent = (file: string, contents: string): void => { - if (this._dtsFilesContents[file] === contents) { - return; - } - this._dtsFilesContents[file] = contents; - this._dtsFilesContents2[file] = ts.createSourceFile(file, contents, ts.ScriptTarget.ES5); - somethingChanged = true; - }; - - const fileMap: { [fileName: string]: string; } = {}; - - this._inputFiles.forEach((inputFile) => { - if (!this._inputFileChanged[inputFile]) { - return; - } - this._inputFileChanged[inputFile] = false; - - const inputFileContents = fs.readFileSync(inputFile).toString(); - if (/\.d\.ts$/.test(inputFile)) { - // This is a .d.ts file - setDTSFileContent(inputFile, inputFileContents); - return; - } - - fileMap[inputFile] = inputFileContents; - }); - - if (Object.keys(fileMap).length > 0) { - const service = ts.createLanguageService(new monacodts.TypeScriptLanguageServiceHost({}, fileMap, {})); - - Object.keys(fileMap).forEach((fileName) => { - const output = service.getEmitOutput(fileName, true).outputFiles[0].text; - const destFileName = fileName.replace(/\.ts$/, '.d.ts'); - setDTSFileContent(destFileName, output); - }); + let r = monacodts.run3(this._declarationResolver); + if (!r && !this._isWatch) { + // The build must always be able to generate the monaco.d.ts + throw new Error(`monaco.d.ts generation error - Cannot continue`); } - - if (this._recipeFileChanged) { - this._recipeFileChanged = false; - somethingChanged = true; - } - - if (!somethingChanged) { - // Nothing changed - return null; - } - - return monacodts.run2('src', this._dtsFilesContents2); + return r; } private _log(message: any, ...rest: any[]): void { @@ -258,11 +215,11 @@ class MonacoGenerator { return; } if (result.isTheSame) { - this._log(`monaco.d.ts is unchanged - total time took ${Date.now() - startTime} ms`); return; } fs.writeFileSync(result.filePath, result.content); + fs.writeFileSync(path.join(REPO_SRC_FOLDER, 'vs/editor/common/standalone/standaloneEnums.ts'), result.enums); this._log(`monaco.d.ts is changed - total time took ${Date.now() - startTime} ms`); if (!this._isWatch) { this.stream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); diff --git a/build/lib/extensions.js b/build/lib/extensions.js index 293c1f0c2ef..335b6b71671 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -36,10 +36,12 @@ function fromLocalWebpack(extensionPath, sourceMappingURLBase) { const result = es.through(); const packagedDependencies = []; const packageJsonConfig = require(path.join(extensionPath, 'package.json')); - const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); - for (const key in webpackRootConfig.externals) { - if (key in packageJsonConfig.dependencies) { - packagedDependencies.push(key); + if (Array.isArray(packageJsonConfig.dependencies)) { + const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); + for (const key in webpackRootConfig.externals) { + if (key in packageJsonConfig.dependencies) { + packagedDependencies.push(key); + } } } vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn, packagedDependencies }).then(fileNames => { @@ -68,8 +70,10 @@ function fromLocalWebpack(extensionPath, sourceMappingURLBase) { .pipe(packageJsonFilter) .pipe(buffer()) .pipe(json((data) => { - // hardcoded entry point directory! - data.main = data.main.replace('/out/', /dist/); + if (data.main) { + // hardcoded entry point directory! + data.main = data.main.replace('/out/', /dist/); + } return data; })) .pipe(packageJsonFilter.restore); diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 7d3bc6630d1..07db5eac1c6 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -39,14 +39,15 @@ function fromLocalWebpack(extensionPath: string, sourceMappingURLBase: string | const packagedDependencies: string[] = []; const packageJsonConfig = require(path.join(extensionPath, 'package.json')); - const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); - for (const key in webpackRootConfig.externals) { - if (key in packageJsonConfig.dependencies) { - packagedDependencies.push(key); + if (Array.isArray(packageJsonConfig.dependencies)) { + const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); + for (const key in webpackRootConfig.externals) { + if (key in packageJsonConfig.dependencies) { + packagedDependencies.push(key); + } } } - vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn, packagedDependencies }).then(fileNames => { const files = fileNames .map(fileName => path.join(extensionPath, fileName)) @@ -80,8 +81,10 @@ function fromLocalWebpack(extensionPath: string, sourceMappingURLBase: string | .pipe(packageJsonFilter) .pipe(buffer()) .pipe(json((data: any) => { - // hardcoded entry point directory! - data.main = data.main.replace('/out/', /dist/); + if (data.main) { + // hardcoded entry point directory! + data.main = data.main.replace('/out/', /dist/); + } return data; })) .pipe(packageJsonFilter.restore); diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 204ea6beea6..40d686c08cf 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -166,6 +166,10 @@ "name": "vs/workbench/services/bulkEdit", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/commands", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/configuration", "project": "vscode-workbench" diff --git a/build/lib/snapshotLoader.js b/build/lib/snapshotLoader.js index 9a047e0f694..ee626a0f7f1 100644 --- a/build/lib/snapshotLoader.js +++ b/build/lib/snapshotLoader.js @@ -24,6 +24,7 @@ var snaps; case 'linux': loaderFilepath = `VSCode-${process.platform}-${arch}/resources/app/out/vs/loader.js`; startupBlobFilepath = `VSCode-${process.platform}-${arch}/snapshot_blob.bin`; + break; default: throw new Error('Unknown platform'); } diff --git a/build/lib/snapshotLoader.ts b/build/lib/snapshotLoader.ts index 3bc379697c8..40b06d67d05 100644 --- a/build/lib/snapshotLoader.ts +++ b/build/lib/snapshotLoader.ts @@ -30,6 +30,7 @@ namespace snaps { case 'linux': loaderFilepath = `VSCode-${process.platform}-${arch}/resources/app/out/vs/loader.js`; startupBlobFilepath = `VSCode-${process.platform}-${arch}/snapshot_blob.bin`; + break; default: throw new Error('Unknown platform'); diff --git a/build/lib/standalone.js b/build/lib/standalone.js index c01e0f461f4..bc5f6d9decc 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -28,12 +28,22 @@ function writeFile(filePath, contents) { } function extractEditor(options) { const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); - tsConfig.compilerOptions.noUnusedLocals = false; - tsConfig.compilerOptions.preserveConstEnums = false; - tsConfig.compilerOptions.declaration = false; - delete tsConfig.compilerOptions.types; + let compilerOptions; + if (tsConfig.extends) { + compilerOptions = Object.assign({}, require(path.join(options.sourcesRoot, tsConfig.extends)).compilerOptions, tsConfig.compilerOptions); + } + else { + compilerOptions = tsConfig.compilerOptions; + } + tsConfig.compilerOptions = compilerOptions; + compilerOptions.noUnusedLocals = false; + compilerOptions.preserveConstEnums = false; + compilerOptions.declaration = false; + compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic; + delete compilerOptions.types; + delete tsConfig.extends; tsConfig.exclude = []; - options.compilerOptions = tsConfig.compilerOptions; + options.compilerOptions = compilerOptions; let result = tss.shake(options); for (let fileName in result) { if (result.hasOwnProperty(fileName)) { diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index aba118ab529..c4b128542e7 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -32,13 +32,24 @@ function writeFile(filePath: string, contents: Buffer | string): void { export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string }): void { const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); - tsConfig.compilerOptions.noUnusedLocals = false; - tsConfig.compilerOptions.preserveConstEnums = false; - tsConfig.compilerOptions.declaration = false; - delete tsConfig.compilerOptions.types; + let compilerOptions: { [key: string]: any }; + if (tsConfig.extends) { + compilerOptions = Object.assign({}, require(path.join(options.sourcesRoot, tsConfig.extends)).compilerOptions, tsConfig.compilerOptions); + } else { + compilerOptions = tsConfig.compilerOptions; + } + tsConfig.compilerOptions = compilerOptions; + + compilerOptions.noUnusedLocals = false; + compilerOptions.preserveConstEnums = false; + compilerOptions.declaration = false; + compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic; + + delete compilerOptions.types; + delete tsConfig.extends; tsConfig.exclude = []; - options.compilerOptions = tsConfig.compilerOptions; + options.compilerOptions = compilerOptions; let result = tss.shake(options); for (let fileName in result) { diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index b8045415698..61cda44028a 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -70,7 +70,8 @@ function createTypeScriptLanguageService(options) { const filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); RESOLVED_LIBS[`defaultLib:${filename}`] = fs.readFileSync(filepath).toString(); }); - const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, ts.convertCompilerOptionsFromJson(options.compilerOptions, ``).options); + const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options; + const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, compilerOptions); return ts.createLanguageService(host); } /** diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index 3439b1437ac..e84aa7effeb 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -126,7 +126,9 @@ function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.Langu RESOLVED_LIBS[`defaultLib:${filename}`] = fs.readFileSync(filepath).toString(); }); - const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, ts.convertCompilerOptionsFromJson(options.compilerOptions, ``).options); + const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options; + + const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, compilerOptions); return ts.createLanguageService(host); } diff --git a/build/lib/tslint/noNewBufferRule.js b/build/lib/tslint/noNewBufferRule.js new file mode 100644 index 00000000000..ae9b02d457e --- /dev/null +++ b/build/lib/tslint/noNewBufferRule.js @@ -0,0 +1,22 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const ts = require("typescript"); +const Lint = require("tslint"); +class Rule extends Lint.Rules.AbstractRule { + apply(sourceFile) { + return this.applyWithWalker(new NewBufferRuleWalker(sourceFile, this.getOptions())); + } +} +exports.Rule = Rule; +class NewBufferRuleWalker extends Lint.RuleWalker { + visitNewExpression(node) { + if (node.expression.kind === ts.SyntaxKind.Identifier && node.expression && node.expression.text === 'Buffer') { + this.addFailureAtNode(node, '`new Buffer` is deprecated. Consider Buffer.From or Buffer.alloc instead.'); + } + super.visitNewExpression(node); + } +} diff --git a/build/lib/tslint/noNewBufferRule.ts b/build/lib/tslint/noNewBufferRule.ts new file mode 100644 index 00000000000..7b0dd43e538 --- /dev/null +++ b/build/lib/tslint/noNewBufferRule.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; + +export class Rule extends Lint.Rules.AbstractRule { + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithWalker(new NewBufferRuleWalker(sourceFile, this.getOptions())); + } +} + +class NewBufferRuleWalker extends Lint.RuleWalker { + visitNewExpression(node: ts.NewExpression) { + if (node.expression.kind === ts.SyntaxKind.Identifier && node.expression && (node.expression as ts.Identifier).text === 'Buffer') { + this.addFailureAtNode(node, '`new Buffer` is deprecated. Consider Buffer.From or Buffer.alloc instead.'); + } + + super.visitNewExpression(node); + } +} \ No newline at end of file diff --git a/build/lib/tslint/noUnexternalizedStringsRule.js b/build/lib/tslint/noUnexternalizedStringsRule.js index 7cb407c8f19..f79421effc1 100644 --- a/build/lib/tslint/noUnexternalizedStringsRule.js +++ b/build/lib/tslint/noUnexternalizedStringsRule.js @@ -52,6 +52,12 @@ class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker { visitSourceFile(node) { super.visitSourceFile(node); Object.keys(this.usedKeys).forEach(key => { + // Keys are quoted. + let identifier = key.substr(1, key.length - 2); + if (!NoUnexternalizedStringsRuleWalker.IDENTIFIER.test(identifier)) { + let occurrence = this.usedKeys[key][0]; + this.addFailure(this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `The key ${occurrence.key.getText()} doesn't conform to a valid localize identifier`)); + } const occurrences = this.usedKeys[key]; if (occurrences.length > 1) { occurrences.forEach(occurrence => { @@ -172,3 +178,4 @@ class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker { } NoUnexternalizedStringsRuleWalker.ImportFailureMessage = 'Do not use double quotes for imports.'; NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE = '"'; +NoUnexternalizedStringsRuleWalker.IDENTIFIER = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/; diff --git a/build/lib/tslint/noUnexternalizedStringsRule.ts b/build/lib/tslint/noUnexternalizedStringsRule.ts index 91112582ff1..e8e8d001a00 100644 --- a/build/lib/tslint/noUnexternalizedStringsRule.ts +++ b/build/lib/tslint/noUnexternalizedStringsRule.ts @@ -81,9 +81,16 @@ class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker { } } + private static IDENTIFIER = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/; protected visitSourceFile(node: ts.SourceFile): void { super.visitSourceFile(node); Object.keys(this.usedKeys).forEach(key => { + // Keys are quoted. + let identifier = key.substr(1, key.length - 2); + if (!NoUnexternalizedStringsRuleWalker.IDENTIFIER.test(identifier)) { + let occurrence = this.usedKeys[key][0]; + this.addFailure(this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `The key ${occurrence.key.getText()} doesn't conform to a valid localize identifier`)); + } const occurrences = this.usedKeys[key]; if (occurrences.length > 1) { occurrences.forEach(occurrence => { diff --git a/build/lib/util.ts b/build/lib/util.ts index f5dc8cd84f2..4617cc83f2f 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -91,7 +91,7 @@ export function fixWin32DirectoryPermissions(): NodeJS.ReadWriteStream { }); } -export function setExecutableBit(pattern: string | string[]): NodeJS.ReadWriteStream { +export function setExecutableBit(pattern?: string | string[]): NodeJS.ReadWriteStream { const setBit = es.mapSync(f => { f.stat.mode = /* 100755 */ 33261; return f; diff --git a/build/monaco/api.js b/build/monaco/api.js index 8b131969081..716d1f56655 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -8,24 +8,13 @@ const fs = require("fs"); const ts = require("typescript"); const path = require("path"); const util = require("gulp-util"); +const dtsv = '2'; const tsfmt = require('../../tsfmt.json'); -function log(message, ...rest) { - util.log(util.colors.cyan('[monaco.d.ts]'), message, ...rest); -} const SRC = path.join(__dirname, '../../src'); -const OUT_ROOT = path.join(__dirname, '../../'); exports.RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); -var CURRENT_PROCESSING_RULE = ''; function logErr(message, ...rest) { - util.log(util.colors.red('[monaco.d.ts]'), 'WHILE HANDLING RULE: ', CURRENT_PROCESSING_RULE); - util.log(util.colors.red('[monaco.d.ts]'), message, ...rest); -} -function moduleIdToPath(out, moduleId) { - if (/\.d\.ts/.test(moduleId)) { - return path.join(SRC, moduleId); - } - return path.join(OUT_ROOT, out, moduleId) + '.d.ts'; + util.log(util.colors.yellow(`[monaco.d.ts]`), message, ...rest); } function isDeclaration(a) { return (a.kind === ts.SyntaxKind.InterfaceDeclaration @@ -120,7 +109,7 @@ function isDefaultExport(declaration) { return (hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword) && hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword)); } -function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage) { +function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums) { let result = getNodeText(sourceFile, declaration); if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { let interfaceDeclaration = declaration; @@ -160,15 +149,115 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, } result = result.replace(/export default/g, 'export'); result = result.replace(/export declare/g, 'export'); + if (declaration.kind === ts.SyntaxKind.EnumDeclaration) { + result = result.replace(/const enum/, 'enum'); + enums.push(result); + } return result; } -function format(text) { +function format(text, endl) { + const REALLY_FORMAT = false; + text = preformat(text, endl); + if (!REALLY_FORMAT) { + return text; + } // Parse the source text let sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); // Get the formatting edits on the input sources let edits = ts.formatting.formatDocument(sourceFile, getRuleProvider(tsfmt), tsfmt); // Apply the edits on the input code return applyEdits(text, edits); + function countParensCurly(text) { + let cnt = 0; + for (let i = 0; i < text.length; i++) { + if (text.charAt(i) === '(' || text.charAt(i) === '{') { + cnt++; + } + if (text.charAt(i) === ')' || text.charAt(i) === '}') { + cnt--; + } + } + return cnt; + } + function repeatStr(s, cnt) { + let r = ''; + for (let i = 0; i < cnt; i++) { + r += s; + } + return r; + } + function preformat(text, endl) { + let lines = text.split(endl); + let inComment = false; + let inCommentDeltaIndent = 0; + let indent = 0; + for (let i = 0; i < lines.length; i++) { + let line = lines[i].replace(/\s$/, ''); + let repeat = false; + let lineIndent = 0; + do { + repeat = false; + if (line.substring(0, 4) === ' ') { + line = line.substring(4); + lineIndent++; + repeat = true; + } + if (line.charAt(0) === '\t') { + line = line.substring(1); + lineIndent++; + repeat = true; + } + } while (repeat); + if (line.length === 0) { + continue; + } + if (inComment) { + if (/\*\//.test(line)) { + inComment = false; + } + lines[i] = repeatStr('\t', lineIndent + inCommentDeltaIndent) + line; + continue; + } + if (/\/\*/.test(line)) { + inComment = true; + inCommentDeltaIndent = indent - lineIndent; + lines[i] = repeatStr('\t', indent) + line; + continue; + } + const cnt = countParensCurly(line); + let shouldUnindentAfter = false; + let shouldUnindentBefore = false; + if (cnt < 0) { + if (/[({]/.test(line)) { + shouldUnindentAfter = true; + } + else { + shouldUnindentBefore = true; + } + } + else if (cnt === 0) { + shouldUnindentBefore = /^\}/.test(line); + } + let shouldIndentAfter = false; + if (cnt > 0) { + shouldIndentAfter = true; + } + else if (cnt === 0) { + shouldIndentAfter = /{$/.test(line); + } + if (shouldUnindentBefore) { + indent--; + } + lines[i] = repeatStr('\t', indent) + line; + if (shouldUnindentAfter) { + indent--; + } + if (shouldIndentAfter) { + indent++; + } + } + return lines.join(endl); + } function getRuleProvider(options) { // Share this between multiple formatters using the same options. // This represents the bulk of the space the formatter uses. @@ -215,6 +304,7 @@ function generateDeclarationFile(recipe, sourceFileGetter) { let usageCounter = 0; let usageImports = []; let usage = []; + let failed = false; usage.push(`var a;`); usage.push(`var b;`); const generateUsageImport = (moduleId) => { @@ -222,13 +312,24 @@ function generateDeclarationFile(recipe, sourceFileGetter) { usageImports.push(`import * as ${importName} from './${moduleId.replace(/\.d\.ts$/, '')}';`); return importName; }; + let enums = []; + let version = null; lines.forEach(line => { + if (failed) { + return; + } + let m0 = line.match(/^\/\/dtsv=(\d+)$/); + if (m0) { + version = m0[1]; + } let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m1) { - CURRENT_PROCESSING_RULE = line; let moduleId = m1[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { + logErr(`While handling ${line}`); + logErr(`Cannot find ${moduleId}`); + failed = true; return; } const importName = generateUsageImport(moduleId); @@ -241,19 +342,23 @@ function generateDeclarationFile(recipe, sourceFileGetter) { } let declaration = getTopLevelDeclaration(sourceFile, typeName); if (!declaration) { - logErr('Cannot find type ' + typeName); + logErr(`While handling ${line}`); + logErr(`Cannot find ${typeName}`); + failed = true; return; } - result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage))); + result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums))); }); return; } let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m2) { - CURRENT_PROCESSING_RULE = line; let moduleId = m2[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { + logErr(`While handling ${line}`); + logErr(`Cannot find ${moduleId}`); + failed = true; return; } const importName = generateUsageImport(moduleId); @@ -284,92 +389,120 @@ function generateDeclarationFile(recipe, sourceFileGetter) { } } } - result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage))); + result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums))); }); return; } result.push(line); }); + if (failed) { + return null; + } + if (version !== dtsv) { + if (!version) { + logErr(`gulp watch restart required. 'monaco.d.ts.recipe' is written before versioning was introduced.`); + } + else { + logErr(`gulp watch restart required. 'monaco.d.ts.recipe' v${version} does not match runtime v${dtsv}.`); + } + return null; + } let resultTxt = result.join(endl); resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri'); resultTxt = resultTxt.replace(/\bEvent { - let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m1) { - let moduleId = m1[1]; - result.push(moduleId); - return; - } - let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m2) { - let moduleId = m2[1]; - result.push(moduleId); - return; - } - }); - return result; -} -exports.getIncludesInRecipe = getIncludesInRecipe; -function getFilesToWatch(out) { - return getIncludesInRecipe().map((moduleId) => moduleIdToPath(out, moduleId)); -} -exports.getFilesToWatch = getFilesToWatch; function _run(sourceFileGetter) { - log('Starting monaco.d.ts generation'); - let recipe = fs.readFileSync(exports.RECIPE_PATH).toString(); - let [result, usageContent] = generateDeclarationFile(recipe, sourceFileGetter); - let currentContent = fs.readFileSync(DECLARATION_PATH).toString(); - log('Finished monaco.d.ts generation'); + const recipe = fs.readFileSync(exports.RECIPE_PATH).toString(); + const t = generateDeclarationFile(recipe, sourceFileGetter); + if (!t) { + return null; + } + const result = t.result; + const usageContent = t.usageContent; + const enums = t.enums; + const currentContent = fs.readFileSync(DECLARATION_PATH).toString(); const one = currentContent.replace(/\r\n/gm, '\n'); const other = result.replace(/\r\n/gm, '\n'); const isTheSame = (one === other); return { content: result, usageContent: usageContent, + enums: enums, filePath: DECLARATION_PATH, isTheSame }; } -function run(out, inputFiles) { - let SOURCE_FILE_MAP = {}; - const sourceFileGetter = (moduleId) => { - if (!SOURCE_FILE_MAP[moduleId]) { - let filePath = path.normalize(moduleIdToPath(out, moduleId)); - if (!inputFiles.hasOwnProperty(filePath)) { - logErr('CANNOT FIND FILE ' + filePath + '. YOU MIGHT NEED TO RESTART gulp'); +class FSProvider { + existsSync(filePath) { + return fs.existsSync(filePath); + } + readFileSync(_moduleId, filePath) { + return fs.readFileSync(filePath); + } +} +exports.FSProvider = FSProvider; +class DeclarationResolver { + constructor(_fsProvider) { + this._fsProvider = _fsProvider; + this._sourceFileCache = Object.create(null); + } + invalidateCache(moduleId) { + this._sourceFileCache[moduleId] = null; + } + getDeclarationSourceFile(moduleId) { + if (!this._sourceFileCache[moduleId]) { + this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId); + } + return this._sourceFileCache[moduleId]; + } + _getDeclarationSourceFile(moduleId) { + if (/\.d\.ts$/.test(moduleId)) { + const fileName = path.join(SRC, moduleId); + if (!this._fsProvider.existsSync(fileName)) { return null; } - let fileContents = inputFiles[filePath]; - let sourceFile = ts.createSourceFile(filePath, fileContents, ts.ScriptTarget.ES5); - SOURCE_FILE_MAP[moduleId] = sourceFile; + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + return ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5); } - return SOURCE_FILE_MAP[moduleId]; - }; + const fileName = path.join(SRC, `${moduleId}.ts`); + if (!this._fsProvider.existsSync(fileName)) { + return null; + } + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + const fileMap = { + 'file.ts': fileContents + }; + const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {})); + const text = service.getEmitOutput('file.ts', true).outputFiles[0].text; + return ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5); + } +} +exports.DeclarationResolver = DeclarationResolver; +function run3(resolver) { + const sourceFileGetter = (moduleId) => resolver.getDeclarationSourceFile(moduleId); return _run(sourceFileGetter); } -exports.run = run; -function run2(out, sourceFileMap) { - const sourceFileGetter = (moduleId) => { - let filePath = path.normalize(moduleIdToPath(out, moduleId)); - return sourceFileMap[filePath]; - }; - return _run(sourceFileGetter); -} -exports.run2 = run2; -function complainErrors() { - logErr('Not running monaco.d.ts generation due to compile errors'); -} -exports.complainErrors = complainErrors; +exports.run3 = run3; class TypeScriptLanguageServiceHost { constructor(libs, files, compilerOptions) { this._libs = libs; @@ -415,28 +548,11 @@ class TypeScriptLanguageServiceHost { return fileName === this.getDefaultLibFileName(this._compilerOptions); } } -exports.TypeScriptLanguageServiceHost = TypeScriptLanguageServiceHost; function execute() { - const OUTPUT_FILES = {}; - const SRC_FILES = {}; - const SRC_FILE_TO_EXPECTED_NAME = {}; - getIncludesInRecipe().forEach((moduleId) => { - if (/\.d\.ts$/.test(moduleId)) { - let fileName = path.join(SRC, moduleId); - OUTPUT_FILES[moduleIdToPath('src', moduleId)] = fs.readFileSync(fileName).toString(); - return; - } - let fileName = path.join(SRC, moduleId) + '.ts'; - SRC_FILES[fileName] = fs.readFileSync(fileName).toString(); - SRC_FILE_TO_EXPECTED_NAME[fileName] = moduleIdToPath('src', moduleId); - }); - const languageService = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, SRC_FILES, {})); - var t1 = Date.now(); - Object.keys(SRC_FILES).forEach((fileName) => { - const emitOutput = languageService.getEmitOutput(fileName, true); - OUTPUT_FILES[SRC_FILE_TO_EXPECTED_NAME[fileName]] = emitOutput.outputFiles[0].text; - }); - console.log(`Generating .d.ts took ${Date.now() - t1} ms`); - return run('src', OUTPUT_FILES); + let r = run3(new DeclarationResolver(new FSProvider())); + if (!r) { + throw new Error(`monaco.d.ts generation error - Cannot continue`); + } + return r; } exports.execute = execute; diff --git a/build/monaco/api.ts b/build/monaco/api.ts index 5b3fa8eb56f..d12b77787a0 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -8,34 +8,19 @@ import * as ts from 'typescript'; import * as path from 'path'; import * as util from 'gulp-util'; +const dtsv = '2'; + const tsfmt = require('../../tsfmt.json'); -function log(message: any, ...rest: any[]): void { - util.log(util.colors.cyan('[monaco.d.ts]'), message, ...rest); -} - const SRC = path.join(__dirname, '../../src'); -const OUT_ROOT = path.join(__dirname, '../../'); export const RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); -var CURRENT_PROCESSING_RULE = ''; function logErr(message: any, ...rest: any[]): void { - util.log(util.colors.red('[monaco.d.ts]'), 'WHILE HANDLING RULE: ', CURRENT_PROCESSING_RULE); - util.log(util.colors.red('[monaco.d.ts]'), message, ...rest); + util.log(util.colors.yellow(`[monaco.d.ts]`), message, ...rest); } -function moduleIdToPath(out: string, moduleId: string): string { - if (/\.d\.ts/.test(moduleId)) { - return path.join(SRC, moduleId); - } - return path.join(OUT_ROOT, out, moduleId) + '.d.ts'; -} - -export interface ISourceFileMap { - [moduleId: string]: ts.SourceFile; -} -export type SourceFileGetter = (moduleId: string) => ts.SourceFile | null; +type SourceFileGetter = (moduleId: string) => ts.SourceFile | null; type TSTopLevelDeclaration = ts.InterfaceDeclaration | ts.EnumDeclaration | ts.ClassDeclaration | ts.TypeAliasDeclaration | ts.FunctionDeclaration | ts.ModuleDeclaration; type TSTopLevelDeclare = TSTopLevelDeclaration | ts.VariableStatement; @@ -152,7 +137,7 @@ function isDefaultExport(declaration: ts.InterfaceDeclaration | ts.ClassDeclarat ); } -function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[]): string { +function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[], enums: string[]): string { let result = getNodeText(sourceFile, declaration); if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { let interfaceDeclaration = declaration; @@ -194,10 +179,22 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati } result = result.replace(/export default/g, 'export'); result = result.replace(/export declare/g, 'export'); + + if (declaration.kind === ts.SyntaxKind.EnumDeclaration) { + result = result.replace(/const enum/, 'enum'); + enums.push(result); + } + return result; } -function format(text: string): string { +function format(text: string, endl: string): string { + const REALLY_FORMAT = false; + + text = preformat(text, endl); + if (!REALLY_FORMAT) { + return text; + } // Parse the source text let sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); @@ -208,6 +205,104 @@ function format(text: string): string { // Apply the edits on the input code return applyEdits(text, edits); + function countParensCurly(text: string): number { + let cnt = 0; + for (let i = 0; i < text.length; i++) { + if (text.charAt(i) === '(' || text.charAt(i) === '{') { + cnt++; + } + if (text.charAt(i) === ')' || text.charAt(i) === '}') { + cnt--; + } + } + return cnt; + } + + function repeatStr(s: string, cnt: number): string { + let r = ''; + for (let i = 0; i < cnt; i++) { + r += s; + } + return r; + } + + function preformat(text: string, endl: string): string { + let lines = text.split(endl); + let inComment = false; + let inCommentDeltaIndent = 0; + let indent = 0; + for (let i = 0; i < lines.length; i++) { + let line = lines[i].replace(/\s$/, ''); + let repeat = false; + let lineIndent = 0; + do { + repeat = false; + if (line.substring(0, 4) === ' ') { + line = line.substring(4); + lineIndent++; + repeat = true; + } + if (line.charAt(0) === '\t') { + line = line.substring(1); + lineIndent++; + repeat = true; + } + } while (repeat); + + if (line.length === 0) { + continue; + } + + if (inComment) { + if (/\*\//.test(line)) { + inComment = false; + } + lines[i] = repeatStr('\t', lineIndent + inCommentDeltaIndent) + line; + continue; + } + + if (/\/\*/.test(line)) { + inComment = true; + inCommentDeltaIndent = indent - lineIndent; + lines[i] = repeatStr('\t', indent) + line; + continue; + } + + const cnt = countParensCurly(line); + let shouldUnindentAfter = false; + let shouldUnindentBefore = false; + if (cnt < 0) { + if (/[({]/.test(line)) { + shouldUnindentAfter = true; + } else { + shouldUnindentBefore = true; + } + } else if (cnt === 0) { + shouldUnindentBefore = /^\}/.test(line); + } + let shouldIndentAfter = false; + if (cnt > 0) { + shouldIndentAfter = true; + } else if (cnt === 0) { + shouldIndentAfter = /{$/.test(line); + } + + if (shouldUnindentBefore) { + indent--; + } + + lines[i] = repeatStr('\t', indent) + line; + + if (shouldUnindentAfter) { + indent--; + } + if (shouldIndentAfter) { + indent++; + } + } + return lines.join(endl); + } + function getRuleProvider(options: ts.FormatCodeSettings) { // Share this between multiple formatters using the same options. // This represents the bulk of the space the formatter uses. @@ -252,7 +347,13 @@ function createReplacer(data: string): (str: string) => string { }; } -function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGetter): [string, string] { +interface ITempResult { + result: string; + usageContent: string; + enums: string; +} + +function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGetter): ITempResult | null { const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; let lines = recipe.split(endl); @@ -262,6 +363,8 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet let usageImports: string[] = []; let usage: string[] = []; + let failed = false; + usage.push(`var a;`); usage.push(`var b;`); @@ -271,14 +374,28 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet return importName; }; + let enums: string[] = []; + let version: string | null = null; + lines.forEach(line => { + if (failed) { + return; + } + + let m0 = line.match(/^\/\/dtsv=(\d+)$/); + if (m0) { + version = m0[1]; + } + let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m1) { - CURRENT_PROCESSING_RULE = line; let moduleId = m1[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { + logErr(`While handling ${line}`); + logErr(`Cannot find ${moduleId}`); + failed = true; return; } @@ -294,20 +411,24 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet } let declaration = getTopLevelDeclaration(sourceFile, typeName); if (!declaration) { - logErr('Cannot find type ' + typeName); + logErr(`While handling ${line}`); + logErr(`Cannot find ${typeName}`); + failed = true; return; } - result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage))); + result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums))); }); return; } let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m2) { - CURRENT_PROCESSING_RULE = line; let moduleId = m2[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { + logErr(`While handling ${line}`); + logErr(`Cannot find ${moduleId}`); + failed = true; return; } @@ -341,7 +462,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet } } } - result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage))); + result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums))); }); return; } @@ -349,63 +470,66 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet result.push(line); }); + if (failed) { + return null; + } + + if (version !== dtsv) { + if (!version) { + logErr(`gulp watch restart required. 'monaco.d.ts.recipe' is written before versioning was introduced.`); + } else { + logErr(`gulp watch restart required. 'monaco.d.ts.recipe' v${version} does not match runtime v${dtsv}.`); + } + return null; + } + let resultTxt = result.join(endl); resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri'); resultTxt = resultTxt.replace(/\bEvent { - - let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m1) { - let moduleId = m1[1]; - result.push(moduleId); - return; - } - - let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m2) { - let moduleId = m2[1]; - result.push(moduleId); - return; - } - }); - - return result; -} - -export function getFilesToWatch(out: string): string[] { - return getIncludesInRecipe().map((moduleId) => moduleIdToPath(out, moduleId)); + return { + result: resultTxt, + usageContent: `${usageImports.join('\n')}\n\n${usage.join('\n')}`, + enums: resultEnums + }; } export interface IMonacoDeclarationResult { content: string; usageContent: string; + enums: string; filePath: string; isTheSame: boolean; } -function _run(sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult { - log('Starting monaco.d.ts generation'); +function _run(sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult | null { + const recipe = fs.readFileSync(RECIPE_PATH).toString(); + const t = generateDeclarationFile(recipe, sourceFileGetter); + if (!t) { + return null; + } - let recipe = fs.readFileSync(RECIPE_PATH).toString(); - let [result, usageContent] = generateDeclarationFile(recipe, sourceFileGetter); - - let currentContent = fs.readFileSync(DECLARATION_PATH).toString(); - log('Finished monaco.d.ts generation'); + const result = t.result; + const usageContent = t.usageContent; + const enums = t.enums; + const currentContent = fs.readFileSync(DECLARATION_PATH).toString(); const one = currentContent.replace(/\r\n/gm, '\n'); const other = result.replace(/\r\n/gm, '\n'); const isTheSame = (one === other); @@ -413,53 +537,75 @@ function _run(sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult { return { content: result, usageContent: usageContent, + enums: enums, filePath: DECLARATION_PATH, isTheSame }; } -export function run(out: string, inputFiles: { [file: string]: string; }): IMonacoDeclarationResult { +export class FSProvider { + public existsSync(filePath: string): boolean { + return fs.existsSync(filePath); + } + public readFileSync(_moduleId: string, filePath: string): Buffer { + return fs.readFileSync(filePath); + } +} - let SOURCE_FILE_MAP: { [moduleId: string]: ts.SourceFile; } = {}; - const sourceFileGetter = (moduleId: string): ts.SourceFile | null => { - if (!SOURCE_FILE_MAP[moduleId]) { - let filePath = path.normalize(moduleIdToPath(out, moduleId)); +export class DeclarationResolver { - if (!inputFiles.hasOwnProperty(filePath)) { - logErr('CANNOT FIND FILE ' + filePath + '. YOU MIGHT NEED TO RESTART gulp'); + private _sourceFileCache: { [moduleId: string]: ts.SourceFile | null; }; + + constructor(private readonly _fsProvider: FSProvider) { + this._sourceFileCache = Object.create(null); + } + + public invalidateCache(moduleId: string): void { + this._sourceFileCache[moduleId] = null; + } + + public getDeclarationSourceFile(moduleId: string): ts.SourceFile | null { + if (!this._sourceFileCache[moduleId]) { + this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId); + } + return this._sourceFileCache[moduleId]; + } + + private _getDeclarationSourceFile(moduleId: string): ts.SourceFile | null { + if (/\.d\.ts$/.test(moduleId)) { + const fileName = path.join(SRC, moduleId); + if (!this._fsProvider.existsSync(fileName)) { return null; } - - let fileContents = inputFiles[filePath]; - let sourceFile = ts.createSourceFile(filePath, fileContents, ts.ScriptTarget.ES5); - - SOURCE_FILE_MAP[moduleId] = sourceFile; + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + return ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5); } - return SOURCE_FILE_MAP[moduleId]; - }; + const fileName = path.join(SRC, `${moduleId}.ts`); + if (!this._fsProvider.existsSync(fileName)) { + return null; + } + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + const fileMap: IFileMap = { + 'file.ts': fileContents + }; + const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {})); + const text = service.getEmitOutput('file.ts', true).outputFiles[0].text; + return ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5); + } +} +export function run3(resolver: DeclarationResolver): IMonacoDeclarationResult | null { + const sourceFileGetter = (moduleId: string) => resolver.getDeclarationSourceFile(moduleId); return _run(sourceFileGetter); } -export function run2(out: string, sourceFileMap: ISourceFileMap): IMonacoDeclarationResult { - const sourceFileGetter = (moduleId: string): ts.SourceFile | null => { - let filePath = path.normalize(moduleIdToPath(out, moduleId)); - return sourceFileMap[filePath]; - }; - - return _run(sourceFileGetter); -} - -export function complainErrors() { - logErr('Not running monaco.d.ts generation due to compile errors'); -} interface ILibMap { [libName: string]: string; } interface IFileMap { [fileName: string]: string; } -export class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { +class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { private readonly _libs: ILibMap; private readonly _files: IFileMap; @@ -513,30 +659,9 @@ export class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { } export function execute(): IMonacoDeclarationResult { - - const OUTPUT_FILES: { [file: string]: string; } = {}; - const SRC_FILES: IFileMap = {}; - const SRC_FILE_TO_EXPECTED_NAME: { [filename: string]: string; } = {}; - getIncludesInRecipe().forEach((moduleId) => { - if (/\.d\.ts$/.test(moduleId)) { - let fileName = path.join(SRC, moduleId); - OUTPUT_FILES[moduleIdToPath('src', moduleId)] = fs.readFileSync(fileName).toString(); - return; - } - - let fileName = path.join(SRC, moduleId) + '.ts'; - SRC_FILES[fileName] = fs.readFileSync(fileName).toString(); - SRC_FILE_TO_EXPECTED_NAME[fileName] = moduleIdToPath('src', moduleId); - }); - - const languageService = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, SRC_FILES, {})); - - var t1 = Date.now(); - Object.keys(SRC_FILES).forEach((fileName) => { - const emitOutput = languageService.getEmitOutput(fileName, true); - OUTPUT_FILES[SRC_FILE_TO_EXPECTED_NAME[fileName]] = emitOutput.outputFiles[0].text; - }); - console.log(`Generating .d.ts took ${Date.now() - t1} ms`); - - return run('src', OUTPUT_FILES); + let r = run3(new DeclarationResolver(new FSProvider())); + if (!r) { + throw new Error(`monaco.d.ts generation error - Cannot continue`); + } + return r; } diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index d1b4e5d873a..356ef3ff046 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -25,21 +25,12 @@ declare namespace monaco { dispose(): void; } - export enum MarkerTag { - Unnecessary = 1, - } - - export enum MarkerSeverity { - Hint = 1, - Info = 2, - Warning = 4, - Error = 8, - } - +#include(vs/platform/markers/common/markers): MarkerTag, MarkerSeverity #include(vs/base/common/winjs.base.d.ts): Promise #include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken #include(vs/base/common/uri): URI, UriComponents -#include(vs/editor/common/standalone/standaloneBase): KeyCode, KeyMod +#include(vs/base/common/keyCodes): KeyCode +#include(vs/editor/common/standalone/standaloneBase): KeyMod #include(vs/base/common/htmlContent): IMarkdownString #include(vs/base/browser/keyboardEvent): IKeyboardEvent #include(vs/base/browser/mouseEvent): IMouseEvent @@ -58,7 +49,7 @@ declare namespace monaco.editor { #include(vs/editor/common/services/webWorker): MonacoWebWorker, IWebWorkerOptions #include(vs/editor/standalone/browser/standaloneCodeEditor): IActionDescriptor, IEditorConstructionOptions, IDiffEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor export interface ICommandHandler { - (...args:any[]): void; + (...args: any[]): void; } #include(vs/platform/contextkey/common/contextkey): IContextKey #include(vs/editor/standalone/browser/standaloneServices): IEditorOverrideServices @@ -94,3 +85,5 @@ declare namespace monaco.worker { #includeAll(vs/editor/common/services/editorSimpleWorker;): } + +//dtsv=2 \ No newline at end of file diff --git a/build/npm/update-all-grammars.js b/build/npm/update-all-grammars.js index 3ff7254c3e2..9ca9f85616c 100644 --- a/build/npm/update-all-grammars.js +++ b/build/npm/update-all-grammars.js @@ -39,7 +39,7 @@ extensions.forEach(extension => updateGrammar(`extensions/${extension}`)); // run integration tests if (process.platform === 'win32') { - cp.spawn('.\scripts\test-integration.bat', [], { env: process.env, stdio: 'inherit' }); + cp.spawn('.\\scripts\\test-integration.bat', [], { env: process.env, stdio: 'inherit' }); } else { cp.spawn('/bin/bash', ['./scripts/test-integration.sh'], { env: process.env, stdio: 'inherit' }); } diff --git a/build/npm/update-grammar.js b/build/npm/update-grammar.js index 2ccd808e152..c009e36285c 100644 --- a/build/npm/update-grammar.js +++ b/build/npm/update-grammar.js @@ -119,7 +119,7 @@ exports.update = function (repoId, repoPath, dest, modifyGrammar, version = 'mas } try { - fs.writeFileSync(dest, JSON.stringify(result, null, '\t')); + fs.writeFileSync(dest, JSON.stringify(result, null, '\t').replace(/\n/g, '\r\n')); if (info) { console.log('Updated ' + path.basename(dest) + ' to ' + repoId + '@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); } else { diff --git a/build/package.json b/build/package.json index 9ad3ffbe133..7e096ee9bab 100644 --- a/build/package.json +++ b/build/package.json @@ -16,6 +16,8 @@ "@types/gulp-util": "^3.0.34", "@types/mime": "0.0.29", "@types/minimatch": "^3.0.3", + "@types/minimist": "^1.2.0", + "@types/mocha": "2.2.39", "@types/node": "8.0.33", "@types/pump": "^1.0.1", "@types/request": "^2.47.0", @@ -25,15 +27,19 @@ "@types/uglify-es": "^3.0.0", "@types/underscore": "^1.8.9", "@types/xml2js": "0.0.33", + "applicationinsights": "1.0.6", "azure-storage": "^2.1.0", "documentdb": "1.13.0", "github-releases": "^0.4.1", "gulp-bom": "^1.0.0", "gulp-sourcemaps": "^1.11.0", + "iconv-lite": "0.4.23", "mime": "^1.3.4", "minimist": "^1.2.0", "request": "^2.85.0", - "typescript": "3.1.1", + "tslint": "^5.9.1", + "typescript": "3.1.4", + "vsce": "1.48.0", "xml2js": "^0.4.17" }, "scripts": { diff --git a/build/tfs/common/publish.ts b/build/tfs/common/publish.ts index 7a74dc6106e..2095c2d2532 100644 --- a/build/tfs/common/publish.ts +++ b/build/tfs/common/publish.ts @@ -265,6 +265,11 @@ async function publish(commit: string, quality: string, platform: string, type: } function main(): void { + if (process.env['VSCODE_BUILD_SKIP_PUBLISH']) { + console.warn('Skipping publish due to VSCODE_BUILD_SKIP_PUBLISH'); + return; + } + const opts = minimist(process.argv.slice(2), { boolean: ['upload-only'] }); diff --git a/build/tfs/darwin/continuous-build-darwin.yml b/build/tfs/darwin/continuous-build-darwin.yml index 28e2a6079ab..99e17e940a6 100644 --- a/build/tfs/darwin/continuous-build-darwin.yml +++ b/build/tfs/darwin/continuous-build-darwin.yml @@ -32,17 +32,6 @@ steps: - script: | ./scripts/test-integration.sh --tfs "Integration Tests" displayName: Run Integration Tests -- script: | - yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)/artifacts" --log "$(Build.ArtifactStagingDirectory)/artifacts/smoketest.log" - displayName: Run Smoke Tests - continueOnError: true -- task: PublishBuildArtifacts@1 - displayName: Publish Smoketest Artifacts - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - ArtifactName: build-artifacts-darwin - publishLocation: Container - condition: eq(variables['System.PullRequest.IsFork'], 'False') - task: PublishTestResults@2 displayName: Publish Tests Results inputs: diff --git a/build/tfs/darwin/enqueue.js b/build/tfs/darwin/enqueue.js deleted file mode 100644 index d2243ad6ae1..00000000000 --- a/build/tfs/darwin/enqueue.js +++ /dev/null @@ -1,29 +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'; -Object.defineProperty(exports, "__esModule", { value: true }); -const child_process_1 = require("child_process"); -const azure = require("azure-storage"); -function queueSigningRequest(quality, commit) { - const retryOperations = new azure.ExponentialRetryPolicyFilter(); - const queueSvc = azure - .createQueueService(process.env['AZURE_STORAGE_ACCOUNT_2'], process.env['AZURE_STORAGE_ACCESS_KEY_2']) - .withFilter(retryOperations); - queueSvc.messageEncoder = new azure.QueueMessageEncoder.TextBase64QueueMessageEncoder(); - const message = `${quality}/${commit}`; - return new Promise((c, e) => queueSvc.createMessage('sign-darwin', message, err => err ? e(err) : c())); -} -async function main(quality) { - const commit = child_process_1.execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); - console.log(`Queueing signing request for '${quality}/${commit}'...`); - await queueSigningRequest(quality, commit); - // console.log('Waiting on signed build...'); - // await waitForSignedBuild(quality, commit); - // console.log('Found signed build!'); -} -main(process.argv[2]).catch(err => { - console.error(err); - process.exit(1); -}); diff --git a/build/tfs/darwin/enqueue.ts b/build/tfs/darwin/enqueue.ts deleted file mode 100644 index 2da255b0b97..00000000000 --- a/build/tfs/darwin/enqueue.ts +++ /dev/null @@ -1,41 +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 { execSync } from 'child_process'; -import * as azure from 'azure-storage'; - - -function queueSigningRequest(quality: string, commit: string): Promise { - const retryOperations = new azure.ExponentialRetryPolicyFilter(); - const queueSvc = azure - .createQueueService(process.env['AZURE_STORAGE_ACCOUNT_2']!, process.env['AZURE_STORAGE_ACCESS_KEY_2']!) - .withFilter(retryOperations); - - queueSvc.messageEncoder = new azure.QueueMessageEncoder.TextBase64QueueMessageEncoder(); - - const message = `${quality}/${commit}`; - - return new Promise((c, e) => queueSvc.createMessage('sign-darwin', message, err => err ? e(err) : c())); -} - - -async function main(quality: string): Promise { - const commit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); - - console.log(`Queueing signing request for '${quality}/${commit}'...`); - await queueSigningRequest(quality, commit); - - // console.log('Waiting on signed build...'); - // await waitForSignedBuild(quality, commit); - - // console.log('Found signed build!'); -} - -main(process.argv[2]).catch(err => { - console.error(err); - process.exit(1); -}); \ No newline at end of file diff --git a/build/tfs/darwin/product-build-darwin.yml b/build/tfs/darwin/product-build-darwin.yml index f5520d06036..e5fa44ac50d 100644 --- a/build/tfs/darwin/product-build-darwin.yml +++ b/build/tfs/darwin/product-build-darwin.yml @@ -84,4 +84,7 @@ steps: # upload configuration AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ yarn gulp -- upload-vscode-configuration - displayName: Publish \ No newline at end of file + displayName: Publish + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' diff --git a/build/tfs/linux/.gitignore b/build/tfs/linux/.gitignore index 5ca5f22fc5a..0f46fa7086a 100644 --- a/build/tfs/linux/.gitignore +++ b/build/tfs/linux/.gitignore @@ -1,2 +1 @@ -pat -*.js \ No newline at end of file +pat \ No newline at end of file diff --git a/build/tfs/linux/frozen-check.js b/build/tfs/linux/frozen-check.js new file mode 100644 index 00000000000..281632424b7 --- /dev/null +++ b/build/tfs/linux/frozen-check.js @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +const documentdb_1 = require("documentdb"); +function createDefaultConfig(quality) { + return { + id: quality, + frozen: false + }; +} +function getConfig(quality) { + const client = new documentdb_1.DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT'], { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const collection = 'dbs/builds/colls/config'; + const query = { + query: `SELECT TOP 1 * FROM c WHERE c.id = @quality`, + parameters: [ + { name: '@quality', value: quality } + ] + }; + return new Promise((c, e) => { + client.queryDocuments(collection, query).toArray((err, results) => { + if (err && err.code !== 409) { + return e(err); + } + c(!results || results.length === 0 ? createDefaultConfig(quality) : results[0]); + }); + }); +} +getConfig(process.argv[2]) + .then(config => { + console.log(config.frozen); + process.exit(0); +}) + .catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/build/tfs/linux/frozen-check.ts b/build/tfs/linux/frozen-check.ts deleted file mode 100644 index 09bc36a2406..00000000000 --- a/build/tfs/linux/frozen-check.ts +++ /dev/null @@ -1,49 +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 { DocumentClient } from 'documentdb'; - -interface Config { - id: string; - frozen: boolean; -} - -function createDefaultConfig(quality: string): Config { - return { - id: quality, - frozen: false - }; -} - -function getConfig(quality: string): Promise { - const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); - const collection = 'dbs/builds/colls/config'; - const query = { - query: `SELECT TOP 1 * FROM c WHERE c.id = @quality`, - parameters: [ - { name: '@quality', value: quality } - ] - }; - - return new Promise((c, e) => { - client.queryDocuments(collection, query).toArray((err, results) => { - if (err && err.code !== 409) { return e(err); } - - c(!results || results.length === 0 ? createDefaultConfig(quality) : results[0] as any as Config); - }); - }); -} - -getConfig(process.argv[2]) - .then(config => { - console.log(config.frozen); - process.exit(0); - }) - .catch(err => { - console.error(err); - process.exit(1); - }); \ No newline at end of file diff --git a/build/tfs/linux/ia32/Dockerfile b/build/tfs/linux/ia32/Dockerfile deleted file mode 100644 index 25d621d99fb..00000000000 --- a/build/tfs/linux/ia32/Dockerfile +++ /dev/null @@ -1,53 +0,0 @@ -FROM microsoft/vsts-agent:ubuntu-14.04-standard -MAINTAINER Joao Moreno - -ARG DEBIAN_FRONTEND=noninteractive -RUN dpkg --add-architecture i386 -RUN apt-get update - -# Dependencies -RUN apt-get install -y build-essential -RUN apt-get install -y gcc-multilib g++-multilib -RUN apt-get install -y git -RUN apt-get install -y zip -RUN apt-get install -y rpm -RUN apt-get install -y createrepo -RUN apt-get install -y python-gtk2 -RUN apt-get install -y jq -RUN apt-get install -y xvfb -RUN apt-get install -y fakeroot -RUN apt-get install -y libgtk2.0-0:i386 -RUN apt-get install -y libgconf-2-4:i386 -RUN apt-get install -y libnss3:i386 -RUN apt-get install -y libasound2:i386 -RUN apt-get install -y libxtst6:i386 -RUN apt-get install -y libfuse2 -RUN apt-get install -y libnotify-bin -RUN apt-get install -y libnotify4:i386 -RUN apt-get install -y libx11-dev:i386 -RUN apt-get install -y libxkbfile-dev:i386 -RUN apt-get install -y libxss1:i386 -RUN apt-get install -y libx11-xcb-dev:i386 -RUN apt-get install -y libgl1-mesa-glx:i386 libgl1-mesa-dri:i386 -RUN apt-get install -y libxkbfile-dev -RUN apt-get install -y bc bsdmainutils -RUN apt-get install -y libgirepository-1.0-1:i386 gir1.2-glib-2.0:i386 gir1.2-secret-1:i386 libsecret-1-dev:i386 -RUN apt-get install -y dpkg-dev:i386 - -# Xvfb -# Thanks https://medium.com/@griggheo/running-headless-selenium-webdriver-tests-in-docker-containers-342fdbabf756 -ADD xvfb.init /etc/init.d/xvfb -RUN chmod +x /etc/init.d/xvfb -RUN update-rc.d xvfb defaults - -# dbus -RUN ln -sf /bin/dbus-daemon /usr/bin/dbus-daemon - -# nvm -ENV NVM_DIR /usr/local/nvm -RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash - -# for libsecret -ENV PKG_CONFIG_PATH /usr/lib/i386-linux-gnu/pkgconfig - -CMD (service xvfb start; service dbus start; export DISPLAY=:10; ./start.sh) \ No newline at end of file diff --git a/build/tfs/linux/ia32/run-agent.sh b/build/tfs/linux/ia32/run-agent.sh deleted file mode 100755 index bcf9017f3cf..00000000000 --- a/build/tfs/linux/ia32/run-agent.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -if [ ! -f pat ]; then - echo "Error: file pat not found" - exit 1 -fi - -docker run \ - -e VSTS_ACCOUNT="monacotools" \ - -e VSTS_TOKEN="$(cat pat)" \ - -e VSTS_AGENT="tb-lnx-ia32-local" \ - -e VSTS_POOL="linux-ia32" \ - -e VSTS_WORK="/var/vsts/work" \ - --name "tb-lnx-ia32-local" \ - -it joaomoreno/vscode-vso-agent-ia32:latest \ No newline at end of file diff --git a/build/tfs/linux/ia32/xvfb.init b/build/tfs/linux/ia32/xvfb.init deleted file mode 100644 index 4d77d253a26..00000000000 --- a/build/tfs/linux/ia32/xvfb.init +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash -# -# /etc/rc.d/init.d/xvfbd -# -# chkconfig: 345 95 28 -# description: Starts/Stops X Virtual Framebuffer server -# processname: Xvfb -# -### BEGIN INIT INFO -# Provides: xvfb -# Required-Start: $remote_fs $syslog -# Required-Stop: $remote_fs $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Start xvfb at boot time -# Description: Enable xvfb provided by daemon. -### END INIT INFO - -[ "${NETWORKING}" = "no" ] && exit 0 - -PROG="/usr/bin/Xvfb" -PROG_OPTIONS=":10 -ac" -PROG_OUTPUT="/tmp/Xvfb.out" - -case "$1" in - start) - echo "Starting : X Virtual Frame Buffer " - $PROG $PROG_OPTIONS>>$PROG_OUTPUT 2>&1 & - disown -ar - ;; - stop) - echo "Shutting down : X Virtual Frame Buffer" - killproc $PROG - RETVAL=$? - [ $RETVAL -eq 0 ] && /bin/rm -f /var/lock/subsys/Xvfb - /var/run/Xvfb.pid - echo - ;; - restart|reload) - $0 stop - $0 start - RETVAL=$? - ;; - status) - status Xvfb - RETVAL=$? - ;; - *) - echo $"Usage: $0 (start|stop|restart|reload|status)" - exit 1 -esac - -exit $RETVAL \ No newline at end of file diff --git a/build/tfs/linux/new_package.json.template b/build/tfs/linux/new_package.json.template deleted file mode 100644 index 77e2ada928e..00000000000 --- a/build/tfs/linux/new_package.json.template +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "PACKAGENAME", - "version": "PACKAGEVERSION", - "repositoryId": "REPOSITORYID", - "sourceUrl": "PACKAGEURL" -} \ No newline at end of file diff --git a/build/tfs/linux/product-build-linux.yml b/build/tfs/linux/product-build-linux.yml index 7a8c637ef84..12269a80cfb 100644 --- a/build/tfs/linux/product-build-linux.yml +++ b/build/tfs/linux/product-build-linux.yml @@ -92,5 +92,20 @@ steps: MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ node build/tfs/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_RPM" package "$RPM_FILENAME" "$VERSION" true "$RPM_PATH" - # SNAP_FILENAME="$(ls $REPO/.build/linux/snap/$ARCH/ | grep .snap)" - # SNAP_PATH="$REPO/.build/linux/snap/$ARCH/$SNAP_FILENAME" + # Publish Snap + npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + + # Pack snap tarball artifact, in order to preserve file perms + mkdir -p $REPO/.build/linux/snap-tarball + SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$(VSCODE_ARCH).tar.gz" + rm -rf $SNAP_TARBALL_PATH + (cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + +- task: PublishPipelineArtifact@0 + displayName: 'Publish Pipeline Artifact' + inputs: + artifactName: snap-$(VSCODE_ARCH) + targetPath: .build/linux/snap-tarball diff --git a/build/tfs/linux/repoapi_client.sh b/build/tfs/linux/repoapi_client.sh deleted file mode 100755 index b700aceff03..00000000000 --- a/build/tfs/linux/repoapi_client.sh +++ /dev/null @@ -1,365 +0,0 @@ -#!/bin/bash -e -# This is a VERY basic script for Create/Delete operations on repos and packages -# -cmd=$1 -docDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # chrmarti: Changed to script's directory. -packageJsonTemplate=$docDir/new_package.json.template -repoJsonTemplate=$docDir/new_repo.json.template - -function Bail -{ - echo "ERROR: $@" - exit 1 -} - -function BailIfFileMissing { - file="$1" - if [ ! -f "$file" ]; then - Bail "File $file does not exist" - fi -} - -function Usage { - echo "USAGE: Manage repos and packages in an apt repository" - echo "$0 -config FILENAME -listrepos | -listpkgs | -addrepo FILENAME | -addpkg FILENAME |" - echo "-addpkgs FILENAME | -check ID | -delrepo REPOID | -delpkg PKGID" - echo -e "\t-config FILENAME : JSON file containing API server name and creds" - echo -e "Package Operations:" - echo -e "\t-listpkgs [REGEX] : List packages, optionally filter by REGEX" - echo -e "\t-addpkg FILENAME : Add package to repo using the specified JSON file" - echo -e "\t-addpkgs FILENAME : Add packages to repo using urls contained in FILENAME" - echo -e "\t-check ID : Check upload operation by ID" - echo -e "\t-delpkg PKGID : Delete the specified package by ID" - echo -e "File Operations:" - echo -e "\t-uploadfile FILENAME: Upload FILENAME (does not publish) " - echo -e "\t-addfile FILENAME : Upload FILENAME AND publish to the repo" - echo -e "\t-listfiles : List uploaded files" - echo -e "\t-delfile FILEID : Delete uploaded file by ID" - echo -e "Repository Operations:" - echo -e "\t-listrepos : List repositories" - echo -e "\t-addrepo FILENAME : Create a new repo using the specified JSON file" - echo -e "\t-delrepo REPOID : Delete the specified repo by ID" - exit 1 -} - -function ParseFromJson { - if [ -z "$secretContents" ]; then - Bail "Unable to parse value because no JSON contents were specified" - elif [ -z "$1" ]; then - Bail "Unable to parse value from JSON because no key was specified" - fi - # Write value directly to stdout to be used by caller - echo $secretContents | jq "$1" | tr -d '"' -} - -function ParseConfigFile { - configFile="$1" - if [ -z "$configFile" ]; then - echo "Must specify -config option" - Usage - fi - BailIfFileMissing "$configFile" - secretContents=$(cat "$configFile") - - server=$(ParseFromJson .server) - protocol=$(ParseFromJson .protocol) - port=$(ParseFromJson .port) - repositoryId=$(ParseFromJson .repositoryId) - user=$(ParseFromJson .username) - pass=$(ParseFromJson .password) - baseurl="$protocol://$user:$pass@$server:$port" -} - -# List Repositories -function ListRepositories -{ - echo "Fetching repo list from $server..." - curl -k "$baseurl/v1/repositories" | sed 's/,/,\n/g' | sed 's/^"/\t"/g' - echo "" -} - -# List packages, using $1 as a regex to filter results -function ListPackages -{ - echo "Fetching package list from $server" - curl -k "$baseurl/v1/packages" | sed 's/{/\n{/g' | egrep "$1" | sed 's/,/,\n/g' | sed 's/^"/\t"/g' - echo "" -} - -# Create a new Repo using the specified JSON file -function AddRepo -{ - repoFile=$1 - if [ -z $repoFile ]; then - Bail "Error: Must specify a JSON-formatted file. Reference $repoJsonTemplate" - fi - if [ ! -f $repoFile ]; then - Bail "Error: Cannot create repo - $repoFile does not exist" - fi - packageUrl=$(grep "url" $repoFile | head -n 1 | awk '{print $2}' | tr -d ',') - echo "Creating new repo on $server [$packageUrl]" - curl -i -k "$baseurl/v1/repositories" --data @$repoFile -H "Content-Type: application/json" - echo "" -} - -# Upload AND publish the file -function AddFile -{ - packageFile=$1 - # Validity checks are performed by UploadFile - echo "Uploading package to $server [$packageFile]" - response=$(UploadFile $packageFile "true") - id=$(echo $response | jq -r ".id") - - # Parse package metadata first to confirm it's a valid deb/rpm - # Needs to be performed in this function so we can use it to publish the package - jsonFile=$(WritePackageInfoToFile $packageFile) - - sed -i "s/REPOSITORYID/$repositoryId/g" $jsonFile - # Replace the url field with fileId - sed -i "s/PACKAGEURL/$id/g" $jsonFile - sed -i "s/sourceUrl/fileId/g" $jsonFile - - AddPackage $jsonFile - rm -f $jsonFile - echo "" -} - -# Upload a file -function UploadFile -{ - packageFile=$1 - quick=$2 - if [ -z $packageFile ]; then - Bail "Error: Must specify the path to a file to upload " - fi - if [ ! -f $packageFile ]; then - Bail "Error: Cannot upload - $packageFile does not exist" - fi - - # Additional validation and output if quick mode isn't enabled - # Basically, if this is part of a publish operation, these steps are handled elsewhere - if [ "$quick" != "true" ]; then - # Parse package metadata first to confirm it's a valid deb/rpm - jsonFile=$(WritePackageInfoToFile $packageFile) - rm -f $jsonFile - - echo "Uploading package to $server [$packageFile]" - fi - curl -s -k -X POST -F file=@$packageFile "$baseurl/v1/files" - echo "" -} - -function ListFiles -{ - curl -s -k "$baseurl/v1/files" | jq -} - -function DeleteFile -{ - fileId=$1 - if [ -z "$fileId" ]; then - Bail "Error: Must specify an ID to delete" - fi - curl -s -X DELETE "$baseurl/v1/files/$fileId" -} - -# Upload a single package using the specified JSON file -function AddPackage -{ - packageFile=$1 - if [ -z $packageFile ]; then - Bail "Error: Must specify a JSON-formatted file. Reference $packageJsonTemplate" - fi - if [ ! -f $packageFile ]; then - Bail "Error: Cannot add package - $packageFile does not exist" - fi - packageUrl=$(grep "sourceUrl" $packageFile | head -n 1 | awk '{print $2}') - echo "Adding package to $server [$packageUrl]" - curl -i -k "$baseurl/v1/packages" --data @$packageFile -H "Content-Type: application/json" - echo "" -} - -# Gets the package name and version and writes it to a file -function WritePackageInfoToFile -{ - packageFile=$1 - tmpOut=$(mktemp) - if [ -z "$packageFile" ]; then - Bail "Error: Must specify path to a deb/rpm package" - elif [ ! -f "$packageFile" ]; then - Bail "Error: Specified file $packageFile does not exist" - fi - if dpkg -I $packageFile > $tmpOut 2> /dev/null; then - >&2 echo "File is deb format" - pkgName=$(grep "^\s*Package:" $tmpOut | awk '{print $2}') - pkgVer=$(grep "^\s*Version:" $tmpOut | awk '{print $2}') - elif rpm -qpi $packageFile > $tmpOut 2> /dev/null; then - >&2 echo "File is rpm format" - pkgName=$(egrep "^Name" $tmpOut | tr -d ':' | awk '{print $2}') - pkgVer=$(egrep "^Version" $tmpOut | tr -d ':' | awk '{print $2}') - else - rm -f $tmpOut - Bail "File is not a valid deb/rpm package $url" - fi - - rm -f $tmpOut - if [ -z "$pkgName" ]; then - Bail "Unable to parse package name for $url" - elif [ -z "$pkgVer" ]; then - Bail "Unable to parse package version number for $url" - fi - - # Create Package .json file - outJson=$(mktemp) - escapedUrl=$(echo "$url" | sed 's/\//\\\//g' | sed 's/\&/\\\&/g') - cp $packageJsonTemplate $outJson - sed -i "s/PACKAGENAME/$pkgName/g" $outJson - sed -i "s/PACKAGEVERSION/$pkgVer/g" $outJson - - # Return path to json file - echo $outJson -} - -# Upload a single package by dynamically creating a JSON file using a provided URL -function AddPackageByUrl -{ - url=$(echo "$1") - if [ -z "$url" ]; then - Bail "Unable to publish package because no URL was specified" - fi - tmpFile=$(mktemp) - if ! wget -q "$url" -O $tmpFile; then - rm -f $tmpFile - Bail "Unable to download URL $url" - fi - - jsonFile=$(WritePackageInfoToFile $tmpFile) - # Create Package .json file - escapedUrl=$(echo "$url" | sed 's/\//\\\//g' | sed 's/\&/\\\&/g') - sed -i "s/PACKAGEURL/$escapedUrl/g" $jsonFile - sed -i "s/REPOSITORYID/$repositoryId/g" $jsonFile - # Perform Upload - AddPackage $jsonFile - # Cleanup - rm -f $jsonFile -} - -# Upload multiple packages by reading urls line-by-line from the specified file -function AddPackages -{ - urlFile=$1 - if [ -z $urlFile ]; then - Bail "Must specify a flat text file containing one or more URLs" - fi - if [ ! -f $urlFile ]; then - Bail "Cannot add packages. File $urlFile does not exist" - fi - for url in $(cat $urlFile); do - if [ -n "$url" ]; then - AddPackageByUrl "$url" - fi - sleep 5 - done -} - -# Check upload by ID -function CheckUpload { - id=$1 - if [ -z "$id" ]; then - Bail "Must specify an ID" - fi - curl -s -k $baseurl/v1/packages/queue/$id | jq - echo "" -} - -# Delete the specified repo -function DeleteRepo -{ - repoId=$1 - if [ -z $repoId ]; then - Bail "Please specify repository ID. Run -listrepos for a list of IDs" - fi - curl -I -k -X DELETE "$baseurl/v1/repositories/$repoId" -} - -# Delete the specified package -function DeletePackage -{ - packageId=$1 - if [ -z $packageId ]; then - Bail "Please specify package ID. Run -listpkgs for a list of IDs" - fi - echo Removing pkgId $packageId from repo $repositoryId - curl -I -k -X DELETE "$baseurl/v1/packages/$packageId" -} - -# Parse params -# Not using getopts because this uses multi-char flags -operation= -while (( "$#" )); do - if [[ "$1" == "-config" ]]; then - shift - configFile="$1" - elif [[ "$1" == "-listrepos" ]]; then - operation=ListRepositories - elif [[ "$1" == "-listpkgs" ]]; then - operation=ListPackages - if [ -n "$2" ]; then - shift - operand="$1" - fi - elif [[ "$1" == "-addrepo" ]]; then - operation=AddRepo - shift - operand="$1" - elif [[ "$1" == "-addpkg" ]]; then - operation=AddPackage - shift - operand="$1" - elif [[ "$1" == "-addpkgs" ]]; then - operation=AddPackages - shift - operand="$1" - elif [[ "$1" == "-addfile" ]]; then - operation=AddFile - shift - operand="$1" - elif [[ "$1" == "-uploadfile" ]]; then - operation=UploadFile - shift - operand="$1" - elif [[ "$1" == "-listfiles" ]]; then - operation=ListFiles - elif [[ "$1" == "-delfile" ]]; then - operation=DeleteFile - shift - operand="$1" - elif [[ "$1" == "-check" ]]; then - operation=CheckUpload - shift - operand="$1" - elif [[ "$1" == "-delrepo" ]]; then - operation=DeleteRepo - shift - operand="$1" - elif [[ "$1" == "-delpkg" ]]; then - operation=DeletePackage - shift - operand="$1" - else - Usage - fi - shift -done - -echo "Performing $operation $operand" -# Parse config file -ParseConfigFile "$configFile" - -# Exit if no operation was specified -if [ -z "operation" ]; then - Usage -fi - -$operation "$operand" diff --git a/build/tfs/linux/snap-build-linux.yml b/build/tfs/linux/snap-build-linux.yml new file mode 100644 index 00000000000..d38659aa189 --- /dev/null +++ b/build/tfs/linux/snap-build-linux.yml @@ -0,0 +1,42 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "8.12.0" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" + +- task: DownloadPipelineArtifact@0 + displayName: 'Download Pipeline Artifact' + inputs: + artifactName: snap-$(VSCODE_ARCH) + targetPath: .build/linux/snap-tarball + +- script: | + set -e + + REPO="$(pwd)" + ARCH="$(VSCODE_ARCH)" + SNAP_ROOT="$REPO/.build/linux/snap/$ARCH" + + # Install build dependencies + (cd build && yarn) + + # Unpack snap tarball artifact, in order to preserve file perms + SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$ARCH.tar.gz" + (cd .build/linux && tar -xzf $SNAP_TARBALL_PATH) + + # Create snap package + BUILD_VERSION="$(date +%s)" + SNAP_FILENAME="code-$VSCODE_QUALITY-$BUILD_VERSION.snap" + PACKAGEJSON="$(ls $SNAP_ROOT/code*/usr/share/code*/resources/app/package.json)" + VERSION=$(node -p "require(\"$PACKAGEJSON\").version") + SNAP_PATH="$SNAP_ROOT/$SNAP_FILENAME" + (cd $SNAP_ROOT/code-* && snapcraft snap --output "$SNAP_PATH") + + # Publish snap package + AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ + MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ + node build/tfs/common/publish.js "$VSCODE_QUALITY" "linux-snap-$ARCH" package "$SNAP_FILENAME" "$VERSION" true "$SNAP_PATH" \ No newline at end of file diff --git a/build/tfs/linux/x64/Dockerfile b/build/tfs/linux/x64/Dockerfile deleted file mode 100644 index ae9190c29db..00000000000 --- a/build/tfs/linux/x64/Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -FROM microsoft/vsts-agent:ubuntu-14.04-standard -MAINTAINER Joao Moreno - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update - -# Dependencies -RUN apt-get install -y build-essential -RUN apt-get install -y gcc-multilib g++-multilib -RUN apt-get install -y git -RUN apt-get install -y dpkg-dev -RUN apt-get install -y zip -RUN apt-get install -y rpm -RUN apt-get install -y createrepo -RUN apt-get install -y python-gtk2 -RUN apt-get install -y jq -RUN apt-get install -y xvfb -RUN apt-get install -y fakeroot -RUN apt-get install -y libgtk2.0-0 -RUN apt-get install -y libgconf-2-4 -RUN apt-get install -y libnss3 -RUN apt-get install -y libasound2 -RUN apt-get install -y libxtst6 -RUN apt-get install -y libfuse2 -RUN apt-get install -y libnotify-bin -RUN apt-get install -y libx11-dev -RUN apt-get install -y libxss1 -RUN apt-get install -y libx11-xcb-dev -RUN apt-get install -y libxkbfile-dev -RUN apt-get install -y bc bsdmainutils -RUN apt-get install -y libsecret-1-dev - -# Xvfb -# Thanks https://medium.com/@griggheo/running-headless-selenium-webdriver-tests-in-docker-containers-342fdbabf756 -ADD xvfb.init /etc/init.d/xvfb -RUN chmod +x /etc/init.d/xvfb -RUN update-rc.d xvfb defaults - -# dbus -RUN ln -sf /bin/dbus-daemon /usr/bin/dbus-daemon - -# nvm -ENV NVM_DIR /usr/local/nvm -RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash - -CMD (service xvfb start; service dbus start; export DISPLAY=:10; ./start.sh) \ No newline at end of file diff --git a/build/tfs/linux/x64/run-agent.sh b/build/tfs/linux/x64/run-agent.sh deleted file mode 100755 index 76978ce2b02..00000000000 --- a/build/tfs/linux/x64/run-agent.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -if [ ! -f pat ]; then - echo "Error: file pat not found" - exit 1 -fi - -docker run \ - -e VSTS_ACCOUNT="monacotools" \ - -e VSTS_TOKEN="$(cat pat)" \ - -e VSTS_AGENT="tb-lnx-x64-local" \ - -e VSTS_POOL="linux-x64" \ - -e VSTS_WORK="/var/vsts/work" \ - --name "tb-lnx-x64-local" \ - -it joaomoreno/vscode-vso-agent-x64:latest \ No newline at end of file diff --git a/build/tfs/linux/x64/xvfb.init b/build/tfs/linux/x64/xvfb.init deleted file mode 100644 index 4d77d253a26..00000000000 --- a/build/tfs/linux/x64/xvfb.init +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash -# -# /etc/rc.d/init.d/xvfbd -# -# chkconfig: 345 95 28 -# description: Starts/Stops X Virtual Framebuffer server -# processname: Xvfb -# -### BEGIN INIT INFO -# Provides: xvfb -# Required-Start: $remote_fs $syslog -# Required-Stop: $remote_fs $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Start xvfb at boot time -# Description: Enable xvfb provided by daemon. -### END INIT INFO - -[ "${NETWORKING}" = "no" ] && exit 0 - -PROG="/usr/bin/Xvfb" -PROG_OPTIONS=":10 -ac" -PROG_OUTPUT="/tmp/Xvfb.out" - -case "$1" in - start) - echo "Starting : X Virtual Frame Buffer " - $PROG $PROG_OPTIONS>>$PROG_OUTPUT 2>&1 & - disown -ar - ;; - stop) - echo "Shutting down : X Virtual Frame Buffer" - killproc $PROG - RETVAL=$? - [ $RETVAL -eq 0 ] && /bin/rm -f /var/lock/subsys/Xvfb - /var/run/Xvfb.pid - echo - ;; - restart|reload) - $0 stop - $0 start - RETVAL=$? - ;; - status) - status Xvfb - RETVAL=$? - ;; - *) - echo $"Usage: $0 (start|stop|restart|reload|status)" - exit 1 -esac - -exit $RETVAL \ No newline at end of file diff --git a/build/tfs/product-build.yml b/build/tfs/product-build.yml index 417ae4f781c..afebc973ccc 100644 --- a/build/tfs/product-build.yml +++ b/build/tfs/product-build.yml @@ -4,6 +4,8 @@ resources: image: joaomoreno/vscode-linux-build-agent:x64 - container: vscode-ia32 image: joaomoreno/vscode-linux-build-agent:ia32 + - container: snapcraft + image: snapcore/snapcraft jobs: - job: Windows @@ -34,6 +36,17 @@ jobs: steps: - template: linux/product-build-linux.yml +- job: LinuxSnap + condition: eq(variables['VSCODE_BUILD_LINUX'], 'true') + pool: + vmImage: 'Ubuntu-16.04' + variables: + VSCODE_ARCH: x64 + container: snapcraft + dependsOn: Linux + steps: + - template: linux/snap-build-linux.yml + - job: Linux32 condition: eq(variables['VSCODE_BUILD_LINUX_32BIT'], 'true') pool: diff --git a/build/tfs/win32/continuous-build-win32.yml b/build/tfs/win32/continuous-build-win32.yml index 0df397a0a6a..7145e67e2ad 100644 --- a/build/tfs/win32/continuous-build-win32.yml +++ b/build/tfs/win32/continuous-build-win32.yml @@ -36,17 +36,6 @@ steps: - powershell: | .\scripts\test-integration.bat --tfs "Integration Tests" displayName: Run Integration Tests -- powershell: | - yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)\artifacts" --log "$(Build.ArtifactStagingDirectory)\artifacts\smoketest.log" - displayName: Run Smoke Tests - continueOnError: true -- task: PublishBuildArtifacts@1 - displayName: Publish Smoketest Artifacts - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - ArtifactName: build-artifacts-win32 - publishLocation: Container - condition: eq(variables['System.PullRequest.IsFork'], 'False') - task: PublishTestResults@2 displayName: Publish Tests Results inputs: diff --git a/build/tfs/win32/product-build-win32.yml b/build/tfs/win32/product-build-win32.yml index 9dd2d4c6516..d0b653d14f3 100644 --- a/build/tfs/win32/product-build-win32.yml +++ b/build/tfs/win32/product-build-win32.yml @@ -170,3 +170,6 @@ steps: # publish hockeyapp symbols $hockeyAppId = if ("$(VSCODE_ARCH)" -eq "ia32") { "$(VSCODE_HOCKEYAPP_ID_WIN32)" } else { "$(VSCODE_HOCKEYAPP_ID_WIN64)" } exec { node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" $hockeyAppId } + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' diff --git a/build/yarn.lock b/build/yarn.lock index 9ec0f9fe922..45a09657223 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -151,6 +151,16 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/minimist@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" + integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= + +"@types/mocha@2.2.39": + version "2.2.39" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.39.tgz#f68d63db8b69c38e9558b4073525cf96c4f7a829" + integrity sha1-9o1j24tpw46VWLQHNSXPlsT3qCk= + "@types/node@*": version "8.0.51" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" @@ -318,6 +328,22 @@ ansi-wrap@0.1.0: resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= +applicationinsights@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" + integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== + dependencies: + diagnostic-channel "0.2.0" + diagnostic-channel-publishers "0.2.1" + zone.js "0.7.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + array-differ@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" @@ -390,6 +416,15 @@ azure-storage@^2.1.0: xml2js "0.2.7" xmlbuilder "0.4.3" +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -412,6 +447,11 @@ binary-search-bounds@2.0.3: resolved "https://registry.yarnpkg.com/binary-search-bounds/-/binary-search-bounds-2.0.3.tgz#5ff8616d6dd2ca5388bc85b2d6266e2b9da502dc" integrity sha1-X/hhbW3SylOIvIWy1iZuK52lAtw= +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" @@ -446,12 +486,22 @@ browserify-mime@~1.2.9: resolved "https://registry.yarnpkg.com/browserify-mime/-/browserify-mime-1.2.9.tgz#aeb1af28de6c0d7a6a2ce40adb68ff18422af31f" integrity sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8= +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^1.0.0: +chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -462,7 +512,7 @@ chalk@^1.0.0: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.2.0: +chalk@^2.2.0, chalk@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== @@ -471,6 +521,18 @@ chalk@^2.2.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +cheerio@^1.0.0-rc.1: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" + integrity sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" @@ -522,6 +584,11 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" +commander@^2.12.1, commander@^2.8.1: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -553,6 +620,21 @@ cryptiles@3.x.x: dependencies: boom "5.x.x" +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" + integrity sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ== + css@2.X: version "2.2.4" resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" @@ -601,11 +683,33 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +denodeify@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" + integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= + detect-newline@2.X: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= +diagnostic-channel-publishers@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" + integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM= + +diagnostic-channel@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17" + integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc= + dependencies: + semver "^5.3.0" + +diff@^3.2.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + documentdb@1.13.0: version "1.13.0" resolved "https://registry.yarnpkg.com/documentdb/-/documentdb-1.13.0.tgz#bba6f03150b2f42498cec4261bc439d834a33f8b" @@ -616,6 +720,52 @@ documentdb@1.13.0: semaphore "1.0.5" underscore "1.8.3" +dom-serializer@0, dom-serializer@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + integrity sha1-BzxpdUbOB4DOI75KKOKT5AvDDII= + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domelementtype@1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.2.1.tgz#578558ef23befac043a1abb0db07635509393479" + integrity sha512-SQVCLFS2E7G5CRCMdn6K9bIhRj1bS6QBWZfF0TUPh4V/BbqrQ619IdSS3/izn0FZ+9l+uODzaZjb08fjOfablA== + +domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + integrity sha1-sXrtguirWeUt2cGbF1bg/BhyBMI= + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + integrity sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs= + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + duplexer2@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" @@ -630,11 +780,26 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" +entities@^1.1.1, entities@~1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + extend@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/extend/-/extend-1.2.1.tgz#a0f5fd6cfc83a5fe49ef698d60ec8a624dd4576c" @@ -669,6 +834,13 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -692,6 +864,11 @@ form-data@~2.3.1: combined-stream "1.0.6" mime-types "^2.1.12" +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -709,6 +886,18 @@ github-releases@^0.4.1: prettyjson "1.2.1" request "2.81.0" +glob@^7.0.6, glob@^7.1.1: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glogg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810" @@ -860,6 +1049,18 @@ hoek@4.x.x: resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA== +htmlparser2@^3.9.1: + version "3.10.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.0.tgz#5f5e422dcf6119c0d983ed36260ce9ded0bee464" + integrity sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ== + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.0.6" + http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -878,7 +1079,22 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +iconv-lite@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= @@ -908,6 +1124,19 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@^3.7.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -967,6 +1196,13 @@ lazy-debug-legacy@0.0.X: resolved "https://registry.yarnpkg.com/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz#537716c0776e4cf79e3ed1b621f7658c2911b1b1" integrity sha1-U3cWwHduTPeePtG2IfdljCkRsbE= +linkify-it@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f" + integrity sha1-2UpGSPmxwXnWT6lykSaL22zpQ08= + dependencies: + uc.micro "^1.0.1" + lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" @@ -1066,6 +1302,22 @@ lodash.templatesettings@^3.0.0: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" +lodash@^4.15.0, lodash@^4.17.10: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== + +markdown-it@^8.3.1: + version "8.4.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" + integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== + dependencies: + argparse "^1.0.7" + entities "~1.1.1" + linkify-it "^2.0.0" + mdurl "^1.0.1" + uc.micro "^1.0.5" + md5.js@1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" @@ -1074,6 +1326,11 @@ md5.js@1.3.4: hash-base "^3.0.0" inherits "^2.0.1" +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" @@ -1103,7 +1360,7 @@ mime@^1.3.4: resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== -minimatch@3.0.4: +minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -1132,6 +1389,11 @@ multipipe@^0.1.2: dependencies: duplexer2 "0.0.2" +mute-stream@~0.0.4: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + normalize-path@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -1139,6 +1401,13 @@ normalize-path@^2.0.1: dependencies: remove-trailing-separator "^1.0.1" +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -1154,6 +1423,13 @@ object-assign@^3.0.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + optimist@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -1162,6 +1438,53 @@ optimist@0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +parse-semver@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/parse-semver/-/parse-semver-1.1.1.tgz#9a4afd6df063dc4826f93fba4a99cf223f666cb8" + integrity sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg= + dependencies: + semver "^5.1.0" + +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== + dependencies: + "@types/node" "*" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-parse@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" @@ -1200,6 +1523,11 @@ punycode@^1.4.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= +q@^1.0.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" @@ -1210,6 +1538,13 @@ qs@~6.5.1: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== +read@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= + dependencies: + mute-stream "~0.0.4" + readable-stream@^2.1.5: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -1223,6 +1558,15 @@ readable-stream@^2.1.5: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.0.6.tgz#351302e4c68b5abd6a2ed55376a7f9a25be3057a" + integrity sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -1316,6 +1660,13 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve@^1.3.2: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== + dependencies: + path-parse "^1.0.5" + safe-buffer@^5.0.1, safe-buffer@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -1326,6 +1677,11 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + sax@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.2.tgz#735ffaa39a1cff8ffb9598f0223abdb03a9fb2ea" @@ -1341,6 +1697,11 @@ semaphore@1.0.5: resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.0.5.tgz#b492576e66af193db95d65e25ec53f5f19798d60" integrity sha1-tJJXbmavGT25XWXiXsU/Xxl5jWA= +semver@^5.1.0, semver@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" + integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== + sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" @@ -1381,6 +1742,11 @@ sparkles@^1.0.0: resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + sshpk@^1.7.0: version "1.13.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" @@ -1396,18 +1762,18 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -string_decoder@~1.1.1: +string_decoder@^1.1.1, string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + stringstream@~0.0.4, stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -1452,6 +1818,13 @@ time-stamp@^1.0.0: resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= +tmp@0.0.29: + version "0.0.29" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0" + integrity sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA= + dependencies: + os-tmpdir "~1.0.1" + tough-cookie@~2.3.0: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" @@ -1466,6 +1839,36 @@ tough-cookie@~2.3.3: dependencies: punycode "^1.4.1" +tslib@^1.8.0, tslib@^1.8.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== + +tslint@^5.9.1: + version "5.11.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed" + integrity sha1-mPMMAurjzecAYgHkwzywi0hYHu0= + dependencies: + babel-code-frame "^6.22.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^3.2.0" + glob "^7.1.1" + js-yaml "^3.7.0" + minimatch "^3.0.4" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.27.2" + +tsutils@^2.27.2: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -1473,27 +1876,55 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.4.tgz#2d3785a158c174c9a16dc2c046ec5fc5f1742213" + integrity sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM= + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -typescript@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.1.tgz#3362ba9dd1e482ebb2355b02dfe8bcd19a2c7c96" - integrity sha512-Veu0w4dTc/9wlWNf2jeRInNodKlcdLgemvPsrNpfu5Pq39sgfFjvIIgTsvUHCoLBnMhPoUA+tFxsXjU6VexVRQ== +typed-rest-client@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-0.9.0.tgz#f768cc0dc3f4e950f06e04825c36b3e7834aa1f2" + integrity sha1-92jMDcP06VDwbgSCXDaz54NKofI= + dependencies: + tunnel "0.0.4" + underscore "1.8.3" + +typescript@3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.4.tgz#c74ef7b3c2da65beff548b903022cb8c3cd997ed" + integrity sha512-JZHJtA6ZL15+Q3Dqkbh8iCUmvxD3iJ7ujXS+fVkKnwIVAdHc5BJTDNM0aTrnr2luKulFjU7W+SRhDZvi66Ru7Q== + +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" + integrity sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg== underscore@1.8.3, underscore@~1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= +underscore@^1.8.3: + version "1.9.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" + integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== + urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -util-deprecate@~1.0.1: +url-join@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78" + integrity sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg= + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -1540,11 +1971,49 @@ vinyl@^0.5.0: clone-stats "^0.0.1" replace-ext "0.0.1" +vsce@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/vsce/-/vsce-1.48.0.tgz#31c1a4c6909c3b8bdc48b3d32cc8c8e94c7113a2" + integrity sha512-1qJn6QLRTu26FIvvMbK/gzHLLdxJVTg9CUTSnCjJHObCCF5CQ0F3FUv7t+5cT7i0J5v5YljrsRY09u7dPBcEnA== + dependencies: + cheerio "^1.0.0-rc.1" + commander "^2.8.1" + denodeify "^1.2.1" + glob "^7.0.6" + lodash "^4.17.10" + markdown-it "^8.3.1" + mime "^1.3.4" + minimatch "^3.0.3" + osenv "^0.1.3" + parse-semver "^1.1.1" + read "^1.0.7" + semver "^5.1.0" + tmp "0.0.29" + url-join "^1.1.0" + vso-node-api "6.1.2-preview" + yauzl "^2.3.1" + yazl "^2.2.2" + +vso-node-api@6.1.2-preview: + version "6.1.2-preview" + resolved "https://registry.yarnpkg.com/vso-node-api/-/vso-node-api-6.1.2-preview.tgz#aab3546df2451ecd894e071bb99b5df19c5fa78f" + integrity sha1-qrNUbfJFHs2JTgcbuZtd8Zxfp48= + dependencies: + q "^1.0.1" + tunnel "0.0.4" + typed-rest-client "^0.9.0" + underscore "^1.8.3" + wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + xml2js@0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.2.7.tgz#1838518bb01741cae0878bab4915e494c32306af" @@ -1574,3 +2043,23 @@ xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= + +yauzl@^2.3.1: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yazl@^2.2.2: + version "2.4.3" + resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071" + integrity sha1-7CblzIfVYBud+EMtvdPNLlFzoHE= + dependencies: + buffer-crc32 "~0.2.3" + +zone.js@0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" + integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk= diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index 358d1b76ce8..31fb9c6b818 100644 --- a/extensions/csharp/syntaxes/csharp.tmLanguage.json +++ b/extensions/csharp/syntaxes/csharp.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/dotnet/csharp-tmLanguage/commit/822c147f65d9b009096d7163f7d624379812cd63", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/b95e4044ff1ac52e03f622de76f459dc5388954c", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -2607,10 +2607,14 @@ }, "patterns": [ { - "include": "#string-character-escape" + "include": "#char-character-escape" } ] }, + "char-character-escape": { + "name": "constant.character.escape.cs", + "match": "\\\\(['\"\\\\0abfnrtv]|x[0-9a-fA-F]{1,4}|u[0-9a-fA-F]{4})" + }, "string-literal": { "name": "string.quoted.double.cs", "begin": "(? -/// \ No newline at end of file diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index c779f246284..3e5f4cf62e5 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -14,7 +14,6 @@ "onLanguage:scss", "onCommand:_css.applyCodeAction" ], - "enableProposedApi": true, "main": "./client/out/cssMain", "scripts": { "compile": "gulp compile-extension:css-language-features-client compile-extension:css-language-features-server", @@ -143,7 +142,7 @@ "error" ], "default": "warning", - "description": "%css.lint.fontFaceProperties.desc%" + "markdownDescription": "%css.lint.fontFaceProperties.desc%" }, "css.lint.hexColorLength": { "type": "string", @@ -178,6 +177,16 @@ "default": "warning", "description": "%css.lint.unknownProperties.desc%" }, + "css.lint.validProperties": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + }, + "scope": "resource", + "default": [], + "description": "%css.lint.validProperties.desc%" + }, "css.lint.ieHack": { "type": "string", "scope": "resource", @@ -220,7 +229,7 @@ "error" ], "default": "ignore", - "description": "%css.lint.important.desc%" + "markdownDescription": "%css.lint.important.desc%" }, "css.lint.float": { "type": "string", @@ -418,6 +427,16 @@ "default": "warning", "description": "%scss.lint.unknownProperties.desc%" }, + "scss.lint.validProperties": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + }, + "scope": "resource", + "default": [], + "description": "%scss.lint.validProperties.desc%" + }, "scss.lint.ieHack": { "type": "string", "scope": "resource", @@ -637,6 +656,16 @@ "default": "warning", "description": "%less.lint.unknownProperties.desc%" }, + "less.lint.validProperties": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + }, + "scope": "resource", + "default": [], + "description": "%less.lint.validProperties.desc%" + }, "less.lint.ieHack": { "type": "string", "scope": "resource", @@ -679,7 +708,7 @@ "error" ], "default": "ignore", - "description": "%less.lint.important.desc%" + "markdownDescription": "%less.lint.important.desc%" }, "less.lint.float": { "type": "string", diff --git a/extensions/css-language-features/package.nls.json b/extensions/css-language-features/package.nls.json index f62c6fb669c..56eeb384643 100644 --- a/extensions/css-language-features/package.nls.json +++ b/extensions/css-language-features/package.nls.json @@ -18,6 +18,7 @@ "css.lint.universalSelector.desc": "The universal selector (`*`) is known to be slow.", "css.lint.unknownAtRules.desc": "Unknown at-rule.", "css.lint.unknownProperties.desc": "Unknown property.", + "css.lint.validProperties.desc": "A list of properties that are not validated against the `unknownProperties` rule.", "css.lint.unknownVendorSpecificProperties.desc": "Unknown vendor specific property.", "css.lint.vendorPrefix.desc": "When using a vendor-specific prefix, also include the standard property.", "css.lint.zeroUnits.desc": "No unit for zero needed.", @@ -35,11 +36,12 @@ "less.lint.hexColorLength.desc": "Hex colors must consist of three or six hex numbers.", "less.lint.idSelector.desc": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML.", "less.lint.ieHack.desc": "IE hacks are only necessary when supporting IE7 and older.", - "less.lint.important.desc": "Avoid using !important. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", + "less.lint.important.desc": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", "less.lint.importStatement.desc": "Import statements do not load in parallel.", "less.lint.propertyIgnoredDueToDisplay.desc": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.", "less.lint.universalSelector.desc": "The universal selector (`*`) is known to be slow.", "less.lint.unknownProperties.desc": "Unknown property.", + "less.lint.validProperties.desc": "A list of properties that are not validated against the `unknownProperties` rule.", "less.lint.unknownVendorSpecificProperties.desc": "Unknown vendor specific property.", "less.lint.vendorPrefix.desc": "When using a vendor-specific prefix, also include the standard property.", "less.lint.zeroUnits.desc": "No unit for zero needed.", @@ -56,11 +58,12 @@ "scss.lint.hexColorLength.desc": "Hex colors must consist of three or six hex numbers.", "scss.lint.idSelector.desc": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML.", "scss.lint.ieHack.desc": "IE hacks are only necessary when supporting IE7 and older.", - "scss.lint.important.desc": "Avoid using !important. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", + "scss.lint.important.desc": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", "scss.lint.importStatement.desc": "Import statements do not load in parallel.", "scss.lint.propertyIgnoredDueToDisplay.desc": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.", "scss.lint.universalSelector.desc": "The universal selector (`*`) is known to be slow.", "scss.lint.unknownProperties.desc": "Unknown property.", + "scss.lint.validProperties.desc": "A list of properties that are not validated against the `unknownProperties` rule.", "scss.lint.unknownVendorSpecificProperties.desc": "Unknown vendor specific property.", "scss.lint.vendorPrefix.desc": "When using a vendor-specific prefix, also include the standard property.", "scss.lint.zeroUnits.desc": "No unit for zero needed.", diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index c4a430eb067..28f60c966d7 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,7 +9,7 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.12-next.1", + "vscode-css-languageservice": "^3.0.13-next.3", "vscode-languageserver": "^5.1.0" }, "devDependencies": { diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 133486a978e..30c2fb6c62f 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -229,10 +229,10 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.12-next.1: - version "3.0.12-next.1" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.12-next.1.tgz#1bc76d04f68b6d3d9b25cf01592ba46cea91c26c" - integrity sha512-Be1pfmRlcRsKMl1O/5rci8lu8RlE5vwT8LOjUEfHZkz5eHL2n9rTLo3dzmbVGtSL7+T1XEArjqUks9MzzDUhcw== +vscode-css-languageservice@^3.0.13-next.3: + version "3.0.13-next.3" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.3.tgz#b9e6f253cace52fbb749d2fe194ae4b196f3543e" + integrity sha512-7+7JddZRt8zFRLbqygxJw+GHtbQ5o2YvgEFvi4ixvbUAX6KlY3Yw6CgUUbg9dmBla7h+GbtoDjwiLFV6Oy+SBQ== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 23cb3185c25..f6634d35c9c 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -614,7 +614,7 @@ function expandAbbr(input: ExpandAbbreviationInput): string { } // If wrapping with a block element, insert newline in the text to wrap. - if (wrappingNode && inlineElements.indexOf(wrappingNode.name) === -1) { + if (wrappingNode && inlineElements.indexOf(wrappingNode.name) === -1 && (expandOptions['profile'].hasOwnProperty('format') ? expandOptions['profile'].format : true)) { wrappingNode.value = '\n\t' + wrappingNode.value + '\n'; } } diff --git a/extensions/emmet/src/test/wrapWithAbbreviation.test.ts b/extensions/emmet/src/test/wrapWithAbbreviation.test.ts index 6e2bf273f92..9fc924508e2 100644 --- a/extensions/emmet/src/test/wrapWithAbbreviation.test.ts +++ b/extensions/emmet/src/test/wrapWithAbbreviation.test.ts @@ -5,7 +5,7 @@ import 'mocha'; import * as assert from 'assert'; -import { Selection } from 'vscode'; +import { Selection, workspace, ConfigurationTarget } from 'vscode'; import { withRandomFileEditor, closeAllEditors } from './testUtils'; import { wrapWithAbbreviation, wrapIndividualLinesWithAbbreviation } from '../abbreviationActions'; @@ -56,6 +56,13 @@ const wrapMultiLineAbbrExpected = ` `; +const wrapInlineElementExpectedFormatFalse = ` + +`; + suite('Tests for Wrap with Abbreviations', () => { teardown(closeAllEditors); @@ -63,6 +70,7 @@ suite('Tests for Wrap with Abbreviations', () => { const multiCursorsWithSelection = [new Selection(2, 2, 2, 28), new Selection(3, 2, 3, 33)]; const multiCursorsWithFullLineSelection = [new Selection(2, 0, 2, 28), new Selection(3, 0, 4, 0)]; + const oldValueForSyntaxProfiles = workspace.getConfiguration('emmet').inspect('syntaxProfile'); test('Wrap with block element using multi cursor', () => { return testWrapWithAbbreviation(multiCursors, 'div', wrapBlockElementExpected); @@ -340,11 +348,47 @@ suite('Tests for Wrap with Abbreviations', () => { }); }); }); + + test('Wrap with abbreviation and format set to false', () => { + return workspace.getConfiguration('emmet').update('syntaxProfiles',{ 'html' : { 'format': false } } , ConfigurationTarget.Global).then(() => { + return testWrapWithAbbreviation(multiCursors,'h1',wrapInlineElementExpectedFormatFalse).then(() => { + return workspace.getConfiguration('emmet').update('syntaxProfiles',oldValueForSyntaxProfiles ? oldValueForSyntaxProfiles.globalValue : undefined, ConfigurationTarget.Global); + }); + }); + }); + + test('Wrap multi line selections with abbreviation', () => { + const htmlContentsForWrapMultiLineTests = ` + + `; + + const wrapMultiLineExpected = ` + + `; + + return testWrapWithAbbreviation([new Selection(2, 4, 3, 9), new Selection(5, 4, 6, 9)], 'div', wrapMultiLineExpected, htmlContentsForWrapMultiLineTests); + }); }); -function testWrapWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string): Thenable { - return withRandomFileEditor(htmlContentsForWrapTests, 'html', (editor, _) => { +function testWrapWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string = htmlContentsForWrapTests): Thenable { + return withRandomFileEditor(input, 'html', (editor, _) => { editor.selections = selections; const promise = wrapWithAbbreviation({ abbreviation }); if (!promise) { diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index 6b2b4265e54..067389b1650 100644 --- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json +++ b/extensions/fsharp/syntaxes/fsharp.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/ionide/ionide-fsgrammar/commit/eb210da2bfea65a7dff46d22c97a0e9e410a9469", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/24c1588529af144d205f66fbcec6889500f9aaa9", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ @@ -48,10 +48,13 @@ "include": "#record_declaration" }, { - "include": "#keywords" + "include": "#records" }, { - "include": "#records" + "include": "#strp_inlined" + }, + { + "include": "#keywords" }, { "include": "#cexprs" @@ -61,10 +64,173 @@ } ], "repository": { + "strp_inlined_body": { + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#anonymous_functions" + }, + { + "match": "(\\^[[:alpha:]0-9'._]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "name": "keyword.fsharp", + "match": "\\b(and|when|or)\\b" + }, + { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "include": "#strp_inlined_body" + } + ] + }, + { + "match": "(static member|member)\\s*([[:alpha:]0-9'`<>^._]+|``[[:alpha:]0-9' <>^._]+``)\\s*(:)", + "captures": { + "1": { + "name": "keyword.fsharp" + }, + "2": { + "name": "variable.fsharp" + }, + "3": { + "name": "keyword.symbol.fsharp" + } + } + }, + { + "include": "#constants" + }, + { + "include": "#strings" + }, + { + "include": "#chars" + }, + { + "include": "#double_tick" + }, + { + "include": "#keywords" + }, + { + "include": "#text" + } + ] + }, + "strp_inlined": { + "patterns": [ + { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "include": "#strp_inlined_body" + } + ] + } + ] + }, "generic_declaration": { "patterns": [ { - "match": "([^<>,*()-])", + "comments": "SRTP syntax support", + "begin": "(:)\\s*(\\()\\s*(static member|member)", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "keyword.symbol.fsharp" + }, + "3": { + "name": "keyword.fsharp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "include": "#member_declaration" + } + ] + }, + { + "match": "(('|\\^)[[:alpha:]0-9'._]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "include": "#variables" + }, + { + "include": "#keywords" + } + ] + }, + { + "name": "keyword.fsharp", + "match": "\\b(private|to|public|internal|function|yield!|yield|class|exception|match|delegate|of|new|in|as|if|then|else|elif|for|begin|end|inherit|do|let\\!|return\\!|return|interface|with|abstract|property|union|enum|member|try|finally|and|when|use|use\\!|struct|while|mutable)(?!')\\b" + }, + { + "name": "keyword.fsharp", + "match": ":" + }, + { + "include": "#constants" + }, + { + "match": "(('|\\^)[[:alpha:]0-9'._]+)", "captures": { "1": { "name": "entity.name.type.fsharp" @@ -86,7 +252,7 @@ }, "patterns": [ { - "match": "([^<>,*()-])", + "match": "(('|\\^)[[:alpha:]0-9'._]+)", "captures": { "1": { "name": "entity.name.type.fsharp" @@ -101,6 +267,24 @@ } ] }, + { + "match": "(?!when|and|or\\b)\\b([\\w0-9'`^._]+)", + "comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "match": "(\\|)", + "comments": "Prevent captures of `|>` as a keyword when defining custom operator like `<|>`", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + } + } + }, { "include": "#keywords" } @@ -217,18 +401,23 @@ "include": "#comments" }, { - "include": "#member_declaration" - }, - { - "match": "(:)(\\s*([?[:alpha:]0-9'`<>^._ ]+))*", - "captures": { + "begin": "(\\()", + "end": "\\s*(?=(->))", + "beginCaptures": { "1": { "name": "keyword.symbol.fsharp" - }, - "2": { - "name": "entity.name.type.fsharp" } - } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "include": "#member_declaration" + } + ] }, { "include": "#variables" @@ -364,15 +553,18 @@ "include": "#common_declaration" }, { - "match": "\\?{0,1}([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([?[:alpha:]0-9'`^._ ]+)){0,1}", + "match": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([[:alpha:]0-9'`^._ ]+)){0,1}", "captures": { "1": { - "name": "variable.parameter.fsharp" - }, - "2": { "name": "keyword.symbol.fsharp" }, + "2": { + "name": "variable.parameter.fsharp" + }, "3": { + "name": "keyword.symbol.fsharp" + }, + "4": { "name": "entity.name.type.fsharp" } } @@ -391,11 +583,232 @@ } ] }, + "common_binding_definition": { + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#attributes" + }, + { + "comments": "SRTP syntax support", + "begin": "(:)\\s*(\\()\\s*(static member|member)", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "keyword.symbol.fsharp" + }, + "3": { + "name": "keyword.fsharp" + } + }, + "end": "(\\))\\s*((?=,)|(?=\\=))", + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "match": "(\\^[[:alpha:]0-9'._]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "include": "#variables" + }, + { + "include": "#keywords" + } + ] + }, + { + "begin": "(:)\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "keyword.symbol.fsharp" + } + }, + "end": "(\\)\\s*(([?[:alpha:]0-9'`^._ ]*)))", + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "entity.name.type.fsharp" + } + }, + "patterns": [ + { + "include": "#tuple_signature" + } + ] + }, + { + "begin": "(:)\\s*(\\^[[:alpha:]0-9'._]+)\\s*(when)", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "entity.name.type.fsharp" + }, + "3": { + "name": "keyword.fsharp" + } + }, + "end": "(?=:)", + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "name": "keyword.fsharp", + "match": "\\b(and|when|or)\\b" + }, + { + "comment": "Because we first capture the keywords, we can capture what looks like a word and assume it's an entity definition", + "match": "([[:alpha:]0-9'^._]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "name": "keyword.symbol.fsharp", + "match": "(\\(|\\))" + } + ] + }, + { + "match": "(:)\\s*([?[:alpha:]0-9'`^._ ]+)", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "match": "(->)\\s*(\\()?\\s*([?[:alpha:]0-9'`^._ ]+)*", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "keyword.symbol.fsharp" + }, + "3": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "begin": "(\\*)\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "keyword.symbol.fsharp" + } + }, + "end": "(\\)\\s*(([?[:alpha:]0-9'`^._ ]+))+)", + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "entity.name.type.fsharp" + } + }, + "patterns": [ + { + "include": "#tuple_signature" + } + ] + }, + { + "match": "(\\*)(\\s*([?[:alpha:]0-9'`^._ ]+))*", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "begin": "(<(?![[:space:]]*\\)))", + "beginComment": "The group (?![[:space:]]*\\) is for protection against overload operator. static member (<)", + "end": "((?)", + "endComment": "The group (? when using SRTP synthax", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "include": "#generic_declaration" + } + ] + }, + { + "begin": "({)", + "end": "(})", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "include": "#record_signature" + } + ] + }, + { + "include": "#definition" + }, + { + "include": "#variables" + }, + { + "include": "#keywords" + } + ] + }, "definition": { "patterns": [ { "name": "binding.fsharp", - "begin": "\\b(val mutable|val|let mutable|let inline|let|member val|member|static member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", + "begin": "\\b(let mutable|let inline|let|member val|static member inline|static member|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", "end": "\\s*(with\\b|=|\\n+=|(?<=\\=))", "beginCaptures": { "1": { @@ -421,140 +834,34 @@ }, "patterns": [ { - "include": "#comments" + "include": "#common_binding_definition" + } + ] + }, + { + "name": "binding.fsharp", + "begin": "\\b(static val mutable|val mutable|val)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", + "end": "\\n$", + "beginCaptures": { + "1": { + "name": "keyword.fsharp" }, - { - "include": "#attributes" + "2": { + "name": "keyword.fsharp" }, - { - "begin": "(:)\\s*(\\()", - "beginCaptures": { - "1": { - "name": "keyword.symbol.fsharp" - }, - "2": { - "name": "keyword.symbol.fsharp" - } - }, - "end": "(\\)\\s*(([?[:alpha:]0-9'`^._ ]+))+)", - "endCaptures": { - "1": { - "name": "keyword.symbol.fsharp" - }, - "2": { - "name": "entity.name.type.fsharp" - } - }, - "patterns": [ - { - "include": "#tuple_signature" - } - ] + "3": { + "name": "support.function.attribute.fsharp" }, - { - "match": "(:)\\s*([?[:alpha:]0-9'`^._ ]+)*", - "captures": { - "1": { - "name": "keyword.symbol.fsharp" - }, - "2": { - "name": "entity.name.type.fsharp" - } - } + "4": { + "name": "keyword.fsharp" }, + "5": { + "name": "variable.fsharp" + } + }, + "patterns": [ { - "match": "(->)\\s*(\\()?\\s*([?[:alpha:]0-9'`^._ ]+)*", - "captures": { - "1": { - "name": "keyword.symbol.fsharp" - }, - "2": { - "name": "keyword.symbol.fsharp" - }, - "3": { - "name": "entity.name.type.fsharp" - } - } - }, - { - "begin": "(\\*)\\s*(\\()", - "beginCaptures": { - "1": { - "name": "keyword.symbol.fsharp" - }, - "2": { - "name": "keyword.symbol.fsharp" - } - }, - "end": "(\\)\\s*(([?[:alpha:]0-9'`^._ ]+))+)", - "endCaptures": { - "1": { - "name": "keyword.symbol.fsharp" - }, - "2": { - "name": "entity.name.type.fsharp" - } - }, - "patterns": [ - { - "include": "#tuple_signature" - } - ] - }, - { - "match": "(\\*)(\\s*([?[:alpha:]0-9'`^._ ]+))*", - "captures": { - "1": { - "name": "keyword.symbol.fsharp" - }, - "2": { - "name": "entity.name.type.fsharp" - } - } - }, - { - "begin": "(<(?![[:space:]]*\\)))", - "end": "(>)", - "beginCaptures": { - "1": { - "name": "keyword.symbol.fsharp" - } - }, - "endCaptures": { - "1": { - "name": "keyword.symbol.fsharp" - } - }, - "patterns": [ - { - "include": "#generic_declaration" - } - ] - }, - { - "begin": "({)", - "end": "(})", - "beginCaptures": { - "1": { - "name": "keyword.symbol.fsharp" - } - }, - "endCaptures": { - "1": { - "name": "keyword.symbol.fsharp" - } - }, - "patterns": [ - { - "include": "#record_signature" - } - ] - }, - { - "include": "#variables" - }, - { - "include": "#keywords" + "include": "#common_binding_definition" } ] } @@ -613,7 +920,7 @@ "patterns": [ { "name": "keyword.fsharp", - "match": "\\b(private|to|public|internal|function|yield!|yield|class|exception|match|delegate|of|new|in|as|if|then|else|elif|for|begin|end|inherit|do|let\\!|return\\!|return|interface|with|abstract|property|union|enum|member|try|finally|and|when|use|use\\!|struct|while|mutable)(?!')\\b" + "match": "\\b(private|to|public|internal|function|yield!|yield|class|exception|match|delegate|of|new|in|as|if|then|else|elif|for|begin|end|inherit|do|let\\!|return\\!|return|interface|with|abstract|property|union|enum|member|try|finally|and|when|or|use|use\\!|struct|while|mutable)(?!')\\b" }, { "name": "keyword.symbol.fsharp", @@ -817,8 +1124,15 @@ "match": "\\(\\)" }, { - "name": "variable.parameter.fsharp", - "match": "``[[:alpha:]0-9'`^:,._ ]+``|[[:alpha:]0-9'`<>^._ ]\\w*" + "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|[[:alpha:]0-9'`<>^._ ]\\w*)", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "variable.parameter.fsharp" + } + } } ] }, @@ -869,19 +1183,22 @@ } }, { - "begin": "\\?{0,1}([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([?[:alpha:]0-9'`^._ ]+)(<))", + "begin": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([?[:alpha:]0-9'`^._ ]+)(<))", "end": "(>)", "beginCaptures": { "1": { - "name": "variable.parameter.fsharp" + "name": "keyword.symbol.fsharp" }, "2": { - "name": "keyword.symbol.fsharp" + "name": "variable.parameter.fsharp" }, "3": { "name": "keyword.symbol.fsharp" }, "4": { + "name": "keyword.symbol.fsharp" + }, + "5": { "name": "entity.name.type.fsharp" } }, @@ -909,13 +1226,26 @@ "member_declaration": { "patterns": [ { - "begin": "(\\()", - "end": "(\\))", + "include": "#comments" + }, + { + "include": "#common_declaration" + }, + { + "comments": "SRTP syntax support", + "begin": "(:)\\s*(\\()\\s*(static member|member)", "beginCaptures": { "1": { "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "keyword.symbol.fsharp" + }, + "3": { + "name": "keyword.fsharp" } }, + "end": "(\\))\\s*((?=,)|(?=\\=))", "endCaptures": { "1": { "name": "keyword.symbol.fsharp" @@ -923,29 +1253,75 @@ }, "patterns": [ { - "include": "#comments" + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "include": "#member_declaration" + } + ] }, { - "include": "#common_declaration" - }, - { - "match": "\\?{0,1}([[:alpha:]0-9'`^._ ]+)\\s*(:{0,1})(\\s*([?[:alpha:]0-9'`<>^._ ]+)){0,1}", + "match": "(\\^[[:alpha:]0-9'._]+)", "captures": { "1": { - "name": "variable.parameter.fsharp" - }, - "2": { - "name": "keyword.symbol.fsharp" - }, - "3": { "name": "entity.name.type.fsharp" } } }, + { + "include": "#variables" + }, { "include": "#keywords" } ] + }, + { + "match": "(\\^[[:alpha:]0-9'._]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "name": "keyword.fsharp", + "match": "\\b(and|when|or)\\b" + }, + { + "name": "keyword.symbol.fsharp", + "match": "(\\(|\\))" + }, + { + "match": "(\\?{0,1})([[:alpha:]0-9'`^._]+|``[[:alpha:]0-9'`^:,._ ]+``)\\s*(:{0,1})(\\s*([?[:alpha:]0-9'`<>._ ]+)){0,1}", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "variable.parameter.fsharp" + }, + "3": { + "name": "keyword.symbol.fsharp" + }, + "4": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "include": "#keywords" } ] }, @@ -973,16 +1349,13 @@ { "name": "record.fsharp", "begin": "\\b(type)[\\s]+(private|internal|public)?\\s*", - "end": "\\s*((with)|((as)\\s*([[:alpha:]0-9']+))|(=)|[\\n=]|(\\(\\)))", + "end": "\\s*((with)|((as)\\s+([[:alpha:]0-9']+))|(=)|[\\n=]|(\\(\\)))", "beginCaptures": { "1": { "name": "keyword.fsharp" }, "2": { "name": "keyword.fsharp" - }, - "3": { - "name": "support.function.attribute.fsharp" } }, "endCaptures": { @@ -1013,18 +1386,82 @@ "include": "#attributes" }, { - "match": "([[:alpha:]0-9'`^:,._]+|``[[:alpha:]0-9'`^:,._ ]+``)(<)", + "match": "([[:alpha:]0-9'^._]+|``[[:alpha:]0-9'`^:,._ ]+``)", "captures": { "1": { "name": "entity.name.type.fsharp" - }, - "2": { - "name": "keyword.symbol.fsharp" } } }, { - "match": "\\s*(>)\\s*(private|internal|public)?", + "begin": "(<)", + "end": "((?)", + "beginCaptures": { + "1": { + "name": "keyword.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.fsharp" + } + }, + "patterns": [ + { + "match": "(('|\\^)``[[:alpha:]0-9`^:,._ ]+``|('|\\^)[[:alpha:]0-9`^:._]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "name": "keyword.fsharp", + "match": "\\b(interface|with|abstract|and|when|or|not|struct|equality|comparison|unmanaged|delegate|enum)\\b" + }, + { + "begin": "(\\()", + "end": "(\\))", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "match": "(static member|member|new)", + "captures": { + "1": { + "name": "keyword.fsharp" + } + } + }, + { + "include": "#common_binding_definition" + } + ] + }, + { + "match": "([\\w0-9'`^._]+)", + "comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "include": "#keywords" + } + ] + }, + { + "match": "\\s*(private|internal|public)", "captures": { "1": { "name": "keyword.symbol.fsharp" @@ -1035,15 +1472,23 @@ } }, { - "match": "([[:alpha:]0-9'`^._ ]+)", - "captures": { + "begin": "(\\()", + "end": "\\s*(?=(=)|[\\n=]|(\\(\\))|(as))", + "beginCaptures": { "1": { - "name": "entity.name.type.fsharp" + "name": "keyword.symbol.fsharp" } - } - }, - { - "include": "#member_declaration" + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "include": "#member_declaration" + } + ] }, { "include": "#keywords" @@ -1099,9 +1544,6 @@ } ] }, - { - "include": "#chars" - }, { "include": "#compiler_directives" }, diff --git a/extensions/fsharp/test/colorize-results/test_fs.json b/extensions/fsharp/test/colorize-results/test_fs.json index 328557e57b3..37c0b61c14a 100644 --- a/extensions/fsharp/test/colorize-results/test_fs.json +++ b/extensions/fsharp/test/colorize-results/test_fs.json @@ -110,7 +110,18 @@ } }, { - "c": " age", + "c": " ", + "t": "source.fsharp record.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "age", "t": "source.fsharp record.fsharp variable.parameter.fsharp", "r": { "dark_plus": "variable: #9CDCFE", diff --git a/extensions/git/README.md b/extensions/git/README.md index bd7a8ef26c2..a20f3207534 100644 --- a/extensions/git/README.md +++ b/extensions/git/README.md @@ -4,4 +4,17 @@ ## Features -See [Git support in VS Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support) to learn about the features of this extension. \ No newline at end of file +See [Git support in VS Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support) to learn about the features of this extension. + +## API + +The Git extension exposes an API, reachable by any other extension. + +1. Copy `src/api/git.d.ts` to your extension's sources; +2. Include `git.d.ts` in your extension's compilation. +3. Get a hold of the API with the following snippet: + + ```ts + const gitExtension = vscode.extensions.getExtension('vscode.git').exports; + const git = gitExtension.getAPI(1); + ``` \ No newline at end of file diff --git a/extensions/git/package.json b/extensions/git/package.json index 81734a94d09..02ec66df32e 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1339,7 +1339,7 @@ "dependencies": { "byline": "^5.0.0", "file-type": "^7.2.0", - "iconv-lite": "0.4.19", + "iconv-lite": "^0.4.24", "jschardet": "^1.6.0", "vscode-extension-telemetry": "0.1.0", "vscode-nls": "^4.0.0", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 4d73891f818..071c07a2cf0 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -66,7 +66,7 @@ "config.autoRepositoryDetection.subFolders": "Scan for subfolders of the currently opened folder.", "config.autoRepositoryDetection.openEditors": "Scan for parent folders of open files.", "config.autorefresh": "Whether auto refreshing is enabled.", - "config.autofetch": "Whether auto fetching is enabled.", + "config.autofetch": "When enabled, commits will automatically be fetched from the default remote of the current Git repository.", "config.confirmSync": "Confirm before synchronizing git repositories.", "config.countBadge": "Controls the git badge counter.", "config.countBadge.all": "Count all changes.", diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 783fdf857ad..68622e7ac91 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -5,7 +5,7 @@ import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; -import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState } from './git'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status } from './git'; import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode'; import { mapEvent } from '../util'; @@ -17,7 +17,12 @@ class ApiInputBox implements InputBox { export class ApiChange implements Change { - constructor(_resource: Resource) { } + get uri(): Uri { return this.resource.resourceUri; } + get originalUri(): Uri { return this.resource.original; } + get renameUri(): Uri | undefined { return this.resource.renameResourceUri; } + get status(): Status { return this.resource.type; } + + constructor(private readonly resource: Resource) { } } export class ApiRepositoryState implements RepositoryState { @@ -67,6 +72,18 @@ export class ApiRepository implements Repository { return this._repository.setConfig(key, value); } + getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number; }> { + return this._repository.getObjectDetails(treeish, path); + } + + detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }> { + return this._repository.detectObjectType(object); + } + + buffer(ref: string, filePath: string): Promise { + return this._repository.buffer(ref, filePath); + } + show(ref: string, path: string): Promise { return this._repository.show(ref, path); } @@ -75,8 +92,8 @@ export class ApiRepository implements Repository { return this._repository.getCommit(ref); } - getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number; }> { - return this._repository.getObjectDetails(treeish, path); + clean(paths: string[]) { + return this._repository.clean(paths.map(p => Uri.file(p))); } diffWithHEAD(path: string): Promise { @@ -111,8 +128,8 @@ export class ApiRepository implements Repository { return this._repository.branch(name, checkout, ref); } - deleteBranch(name: string): Promise { - return this._repository.deleteBranch(name); + deleteBranch(name: string, force?: boolean): Promise { + return this._repository.deleteBranch(name, force); } getBranch(name: string): Promise { @@ -150,6 +167,10 @@ export class ApiRepository implements Repository { pull(): Promise { return this._repository.pull(); } + + push(remoteName?: string, branchName?: string, setUpstream: boolean = false): Promise { + return this._repository.pushTo(remoteName, branchName, setUpstream); + } } export class ApiGit implements Git { diff --git a/extensions/git/src/api/extension.ts b/extensions/git/src/api/extension.ts index 1ca1892a7bd..8d7dc611e62 100644 --- a/extensions/git/src/api/extension.ts +++ b/extensions/git/src/api/extension.ts @@ -6,6 +6,8 @@ import { Model } from '../model'; import { GitExtension, Repository, API } from './git'; import { ApiRepository, ApiImpl } from './api1'; +import { Event, EventEmitter } from 'vscode'; +import { latchEvent } from '../util'; export function deprecated(_target: any, key: string, descriptor: any): void { if (typeof descriptor.value !== 'function') { @@ -19,50 +21,56 @@ export function deprecated(_target: any, key: string, descriptor: any): void { }; } -class NoModelGitExtension implements GitExtension { +export class GitExtensionImpl implements GitExtension { + + enabled: boolean = false; + + private _onDidChangeEnablement = new EventEmitter(); + readonly onDidChangeEnablement: Event = latchEvent(this._onDidChangeEnablement.event); + + private _model: Model | undefined = undefined; + + set model(model: Model | undefined) { + this._model = model; + + this.enabled = !!model; + this._onDidChangeEnablement.fire(this.enabled); + } + + constructor(model?: Model) { + if (model) { + this.enabled = true; + this._model = model; + } + } @deprecated async getGitPath(): Promise { - throw new Error('Git model not found'); - } + if (!this._model) { + throw new Error('Git model not found'); + } - @deprecated - async getRepositories(): Promise { - throw new Error('Git model not found'); - } - - getAPI(): API { - throw new Error('Git model not found'); - } -} - -class GitExtensionImpl implements GitExtension { - - constructor(private _model: Model) { } - - @deprecated - async getGitPath(): Promise { return this._model.git.path; } @deprecated async getRepositories(): Promise { + if (!this._model) { + throw new Error('Git model not found'); + } + return this._model.repositories.map(repository => new ApiRepository(repository)); } getAPI(version: number): API { + if (!this._model) { + throw new Error('Git model not found'); + } + if (version !== 1) { throw new Error(`No API version ${version} found.`); } return new ApiImpl(this._model); } -} - -export function createGitExtension(model?: Model): GitExtension { - if (!model) { - return new NoModelGitExtension(); - } - - return new GitExtensionImpl(model); -} +} \ No newline at end of file diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index a20df351ea5..99ded276f33 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -61,8 +61,38 @@ export interface Remote { readonly isReadOnly: boolean; } +export const enum Status { + INDEX_MODIFIED, + INDEX_ADDED, + INDEX_DELETED, + INDEX_RENAMED, + INDEX_COPIED, + + MODIFIED, + DELETED, + UNTRACKED, + IGNORED, + + ADDED_BY_US, + ADDED_BY_THEM, + DELETED_BY_US, + DELETED_BY_THEM, + BOTH_ADDED, + BOTH_DELETED, + BOTH_MODIFIED +} + export interface Change { - // TODO + + /** + * Returns either `originalUri` or `renameUri`, depending + * on whether this change is a rename change. When + * in doubt always use `uri` over the other two alternatives. + */ + readonly uri: Uri; + readonly originalUri: Uri; + readonly renameUri: Uri | undefined; + readonly status: Status; } export interface RepositoryState { @@ -95,9 +125,13 @@ export interface Repository { getConfig(key: string): Promise; setConfig(key: string, value: string): Promise; + getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }>; + detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }>; + buffer(ref: string, path: string): Promise; show(ref: string, path: string): Promise; getCommit(ref: string): Promise; - getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }>; + + clean(paths: string[]): Promise; diffWithHEAD(path: string): Promise; diffWith(ref: string, path: string): Promise; @@ -109,7 +143,7 @@ export interface Repository { hashObject(data: string): Promise; createBranch(name: string, checkout: boolean, ref?: string): Promise; - deleteBranch(name: string): Promise; + deleteBranch(name: string, force?: boolean): Promise; getBranch(name: string): Promise; setBranchUpstream(name: string, upstream: string): Promise; @@ -123,6 +157,7 @@ export interface Repository { fetch(remote?: string, ref?: string): Promise; pull(): Promise; + push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise; } export interface API { @@ -134,9 +169,16 @@ export interface API { export interface GitExtension { + readonly enabled: boolean; + readonly onDidChangeEnablement: Event; + /** * Returns a specific API version. * + * Throws error if git extension is disabled. You can listed to the + * [GitExtension.onDidChangeEnablement](#GitExtension.onDidChangeEnablement) event + * to know when the extension becomes enabled/disabled. + * * @param version Version number. * @returns API instance */ diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index f92f6951b8e..92a33b1ab71 100755 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -5,7 +5,7 @@ import { Uri, commands, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation, TextEditor, MessageOptions, WorkspaceFolder } from 'vscode'; import { Git, CommitOptions, Stash, ForcePushMode } from './git'; -import { Repository, Resource, Status, ResourceGroupType } from './repository'; +import { Repository, Resource, ResourceGroupType } from './repository'; import { Model } from './model'; import { toGitUri, fromGitUri } from './uri'; import { grep, isDescendant, pathEquals } from './util'; @@ -15,7 +15,7 @@ import { lstat, Stats } from 'fs'; import * as os from 'os'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; -import { Ref, RefType, Branch, GitErrorCodes } from './api/git'; +import { Ref, RefType, Branch, GitErrorCodes, Status } from './api/git'; const localize = nls.loadMessageBundle(); @@ -655,6 +655,7 @@ export class CommandCenter { @command('git.openHEADFile') async openHEADFile(arg?: Resource | Uri): Promise { let resource: Resource | undefined = undefined; + const preview = !(arg instanceof Resource); if (arg instanceof Resource) { resource = arg; @@ -675,12 +676,18 @@ export class CommandCenter { return; } - return await commands.executeCommand('vscode.open', HEAD); + const opts: TextDocumentShowOptions = { + preview + }; + + return await commands.executeCommand('vscode.open', HEAD, opts); } @command('git.openChange') async openChange(arg?: Resource | Uri, ...resourceStates: SourceControlResourceState[]): Promise { const preserveFocus = arg instanceof Resource; + const preview = !(arg instanceof Resource); + const preserveSelection = arg instanceof Uri || !arg; let resources: Resource[] | undefined = undefined; @@ -707,7 +714,6 @@ export class CommandCenter { return; } - const preview = resources.length === 1 ? undefined : false; for (const resource of resources) { await this._openResource(resource, preview, preserveFocus, preserveSelection); } diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index dcfbf569ec9..0c27365bc51 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -5,11 +5,11 @@ import { window, workspace, Uri, Disposable, Event, EventEmitter, DecorationData, DecorationProvider, ThemeColor } from 'vscode'; import * as path from 'path'; -import { Repository, GitResourceGroup, Status } from './repository'; +import { Repository, GitResourceGroup } from './repository'; import { Model } from './model'; import { debounce } from './decorators'; import { filterEvent, dispose, anyEvent, fireEvent } from './util'; -import { GitErrorCodes } from './api/git'; +import { GitErrorCodes, Status } from './api/git'; type Callback = { resolve: (status: boolean) => void, reject: (err: any) => void }; diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 1ff683150a5..52b7c3c106a 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -17,7 +17,7 @@ import { toDisposable, filterEvent, eventToPromise } from './util'; import TelemetryReporter from 'vscode-extension-telemetry'; import { GitExtension } from './api/git'; import { GitProtocolHandler } from './protocolHandler'; -import { createGitExtension } from './api/extension'; +import { GitExtensionImpl } from './api/extension'; import * as path from 'path'; import * as fs from 'fs'; @@ -137,12 +137,15 @@ export async function activate(context: ExtensionContext): Promise if (!enabled) { const onConfigChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git')); const onEnabled = filterEvent(onConfigChange, () => workspace.getConfiguration('git', null).get('enabled') === true); - await eventToPromise(onEnabled); + const result = new GitExtensionImpl(); + + eventToPromise(onEnabled).then(async () => result.model = await createModel(context, outputChannel, telemetryReporter, disposables)); + return result; } try { const model = await createModel(context, outputChannel, telemetryReporter, disposables); - return createGitExtension(model); + return new GitExtensionImpl(model); } catch (err) { if (!/Git installation not found/.test(err.message || '')) { throw err; @@ -151,9 +154,9 @@ export async function activate(context: ExtensionContext): Promise console.warn(err.message); outputChannel.appendLine(err.message); - await warnAboutMissingGit(); + warnAboutMissingGit(); - return createGitExtension(); + return new GitExtensionImpl(); } } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index cff2a80a899..287552aef80 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -13,7 +13,7 @@ import * as path from 'path'; import * as nls from 'vscode-nls'; import * as fs from 'fs'; import { StatusBarCommands } from './statusbar'; -import { Branch, Ref, Remote, RefType, GitErrorCodes, TrackingShip } from './api/git'; +import { Branch, Ref, Remote, RefType, GitErrorCodes, Status, TrackingShip } from './api/git'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -29,27 +29,6 @@ export const enum RepositoryState { Disposed } -export const enum Status { - INDEX_MODIFIED, - INDEX_ADDED, - INDEX_DELETED, - INDEX_RENAMED, - INDEX_COPIED, - - MODIFIED, - DELETED, - UNTRACKED, - IGNORED, - - ADDED_BY_US, - ADDED_BY_THEM, - DELETED_BY_US, - DELETED_BY_THEM, - BOTH_ADDED, - BOTH_DELETED, - BOTH_MODIFIED -} - export const enum ResourceGroupType { Merge, Index, @@ -122,6 +101,7 @@ export class Resource implements SourceControlResourceState { case Status.DELETED_BY_US: return Resource.Icons[theme].Conflict; case Status.BOTH_ADDED: return Resource.Icons[theme].Conflict; case Status.BOTH_MODIFIED: return Resource.Icons[theme].Conflict; + default: throw new Error('Unknown git status: ' + this.type); } } @@ -207,6 +187,8 @@ export class Resource implements SourceControlResourceState { case Status.BOTH_ADDED: case Status.BOTH_MODIFIED: return 'C'; + default: + throw new Error('Unknown git status: ' + this.type); } } @@ -234,6 +216,8 @@ export class Resource implements SourceControlResourceState { case Status.BOTH_ADDED: case Status.BOTH_MODIFIED: return new ThemeColor('gitDecoration.conflictingResourceForeground'); + default: + throw new Error('Unknown git status: ' + this.type); } } @@ -846,7 +830,7 @@ export class Repository implements Disposable { } async branch(name: string, _checkout: boolean, _ref?: string): Promise { - await this.run(Operation.Branch, () => this.repository.branch(name, true)); + await this.run(Operation.Branch, () => this.repository.branch(name, _checkout, _ref)); } async deleteBranch(name: string, force?: boolean): Promise { diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index ea1ddb97abb..820397f02da 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -44,6 +44,18 @@ export function filterEvent(event: Event, filter: (e: T) => boolean): Even return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); } +export function latchEvent(event: Event): Event { + let firstCall = true; + let cache: T; + + return filterEvent(event, value => { + let shouldEmit = firstCall || value !== cache; + firstCall = false; + cache = value; + return shouldEmit; + }); +} + export function anyEvent(...events: Event[]): Event { return (listener, thisArgs = null, disposables?) => { const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i)))); diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 4a18c906ecb..616d42caddf 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -151,10 +151,12 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -iconv-lite@0.4.19: - version "0.4.19" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" - integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" inflight@^1.0.4: version "1.0.6" @@ -294,6 +296,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + semver@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" diff --git a/extensions/grunt/src/main.ts b/extensions/grunt/src/main.ts index e90277cfe9e..38589e8a596 100644 --- a/extensions/grunt/src/main.ts +++ b/extensions/grunt/src/main.ts @@ -81,7 +81,7 @@ class FolderDetector { } public start(): void { - let pattern = path.join(this._workspaceFolder.uri.fsPath, '[Gg]runtfile.js'); + let pattern = path.join(this._workspaceFolder.uri.fsPath, '{node_modules,[Gg]runtfile.js}'); this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); this.fileWatcher.onDidChange(() => this.promise = undefined); this.fileWatcher.onDidCreate(() => this.promise = undefined); diff --git a/extensions/grunt/src/typings/refs.d.ts b/extensions/grunt/src/typings/refs.d.ts index 954bab971e3..bc057c55878 100644 --- a/extensions/grunt/src/typings/refs.d.ts +++ b/extensions/grunt/src/typings/refs.d.ts @@ -4,5 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// -/// /// diff --git a/extensions/gulp/src/main.ts b/extensions/gulp/src/main.ts index c5adf07d1b3..b8612d511c2 100644 --- a/extensions/gulp/src/main.ts +++ b/extensions/gulp/src/main.ts @@ -82,7 +82,7 @@ class FolderDetector { } public start(): void { - let pattern = path.join(this._workspaceFolder.uri.fsPath, 'gulpfile{.babel.js,.js,.ts}'); + let pattern = path.join(this._workspaceFolder.uri.fsPath, '{node_modules,gulpfile{.babel.js,.js,.ts}}'); this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); this.fileWatcher.onDidChange(() => this.promise = undefined); this.fileWatcher.onDidCreate(() => this.promise = undefined); diff --git a/extensions/gulp/src/typings/refs.d.ts b/extensions/gulp/src/typings/refs.d.ts index 954bab971e3..bc057c55878 100644 --- a/extensions/gulp/src/typings/refs.d.ts +++ b/extensions/gulp/src/typings/refs.d.ts @@ -4,5 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// -/// /// diff --git a/extensions/html-language-features/client/src/typings/ref.d.ts b/extensions/html-language-features/client/src/typings/ref.d.ts index 2b90c6587fe..9c1a5df18ed 100644 --- a/extensions/html-language-features/client/src/typings/ref.d.ts +++ b/extensions/html-language-features/client/src/typings/ref.d.ts @@ -4,4 +4,3 @@ *--------------------------------------------------------------------------------------------*/ /// -/// \ No newline at end of file diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 1bbfa36ead5..19ba1cb08dd 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -14,7 +14,6 @@ "onLanguage:handlebars", "onLanguage:razor" ], - "enableProposedApi": true, "main": "./client/out/htmlMain", "scripts": { "compile": "gulp compile-extension:html-language-features-client compile-extension:html-language-features-server", diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index c46f30cf5bc..93882a7ec94 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,8 +9,8 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.12-next.1", - "vscode-html-languageservice": "^2.1.9", + "vscode-css-languageservice": "^3.0.12", + "vscode-html-languageservice": "^2.1.10", "vscode-languageserver": "^5.1.0", "vscode-languageserver-types": "^3.13.0", "vscode-nls": "^4.0.0", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 48ff5aef257..6530713c465 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -229,18 +229,18 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.12-next.1: - version "3.0.12-next.1" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.12-next.1.tgz#1bc76d04f68b6d3d9b25cf01592ba46cea91c26c" - integrity sha512-Be1pfmRlcRsKMl1O/5rci8lu8RlE5vwT8LOjUEfHZkz5eHL2n9rTLo3dzmbVGtSL7+T1XEArjqUks9MzzDUhcw== +vscode-css-languageservice@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.12.tgz#fb4aac5ae3c5b761b1db1d7224b78ff824284dc3" + integrity sha512-+FLQ9LcukIhnxaGTjDOqb3Nb1hesz9BLXf5yeoZxUsuK7joADPLPdxLwlZugFcMAvgmtnaFIGnzkQhGOVqf5yw== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" -vscode-html-languageservice@^2.1.9: - version "2.1.9" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.9.tgz#906f2f894b023d3a464739ebc5b4eb695ef1cf58" - integrity sha512-zHb6zqt55THIkHjywsjBqGwBr9vCOmBDh6mGyyawGi/8XH2Y6yIAH7KXTxN4Ov9A2M0CT2mwSA3tl+IKtIJtjg== +vscode-html-languageservice@^2.1.10: + version "2.1.10" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.10.tgz#3433fd53e188cb25d5ea190b61a148fe9965ff91" + integrity sha512-nuzLd7a3J+Ttvk/9Pg2H0vS7rV2oZRfsQYPRheHnUNJNqivkcieSI8ZCGvZjmr3NDBG2QQaRFambnCtceYAj3A== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" diff --git a/extensions/jake/src/main.ts b/extensions/jake/src/main.ts index c4e20edf10c..938ca51bec9 100644 --- a/extensions/jake/src/main.ts +++ b/extensions/jake/src/main.ts @@ -81,7 +81,7 @@ class FolderDetector { } public start(): void { - let pattern = path.join(this._workspaceFolder.uri.fsPath, '{Jakefile,Jakefile.js}'); + let pattern = path.join(this._workspaceFolder.uri.fsPath, '{node_modules,Jakefile,Jakefile.js}'); this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); this.fileWatcher.onDidChange(() => this.promise = undefined); this.fileWatcher.onDidCreate(() => this.promise = undefined); diff --git a/extensions/jake/src/typings/refs.d.ts b/extensions/jake/src/typings/refs.d.ts index 954bab971e3..bc057c55878 100644 --- a/extensions/jake/src/typings/refs.d.ts +++ b/extensions/jake/src/typings/refs.d.ts @@ -4,5 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// -/// /// diff --git a/extensions/java/syntaxes/java.tmLanguage.json b/extensions/java/syntaxes/java.tmLanguage.json index b0ecaec639f..2ccdc0733de 100644 --- a/extensions/java/syntaxes/java.tmLanguage.json +++ b/extensions/java/syntaxes/java.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/atom/language-java/commit/295af4375e4a5da4a4352fa08a8bb3e17145ec47", + "version": "https://github.com/atom/language-java/commit/95ebcd0b15c369666ecc4d1593495466132dd5bf", "name": "Java", "scopeName": "source.java", "patterns": [ @@ -373,7 +373,7 @@ "include": "#static-initializer" }, { - "include": "#methods" + "include": "#class-fields-and-methods" }, { "include": "#annotations" @@ -389,6 +389,22 @@ } ] }, + "class-fields-and-methods": { + "patterns": [ + { + "begin": "(?=\\=)", + "end": "(?=;)", + "patterns": [ + { + "include": "#code" + } + ] + }, + { + "include": "#methods" + } + ] + }, "code": { "patterns": [ { @@ -442,6 +458,9 @@ { "include": "#variables" }, + { + "include": "#variables-local" + }, { "include": "#objects" }, @@ -479,9 +498,6 @@ "match": "/\\*\\*/", "name": "comment.block.empty.java" }, - { - "include": "text.html.javadoc" - }, { "include": "#comments-inline" } @@ -911,7 +927,7 @@ "include": "#parens" }, { - "include": "#comments-inline" + "include": "#comments" } ] }, @@ -928,6 +944,9 @@ }, { "include": "#parens" + }, + { + "include": "#comments" } ] }, @@ -1496,12 +1515,12 @@ ] }, "variables": { - "begin": "(?x)\n(?=\n (\n \\b(void|boolean|byte|char|short|int|float|long|double)\\b\n |\n (?>(\\w+\\.)*[A-Z]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|;)\n)", - "end": "(?=\\=|;)", + "begin": "(?x)\n(?=\n (\n \\b(void|boolean|byte|char|short|int|float|long|double)\\b\n |\n (?>(\\w+\\.)*[A-Z]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|:|;)\n)", + "end": "(?=\\=|:|;)", "name": "meta.definition.variable.java", "patterns": [ { - "match": "([A-Za-z$_][\\w$]*)(?=\\s*(\\[\\])*\\s*(;|=|,))", + "match": "([A-Za-z$_][\\w$]*)(?=\\s*(\\[\\])*\\s*(;|:|=|,))", "captures": { "1": { "name": "variable.other.definition.java" @@ -1515,6 +1534,28 @@ "include": "#code" } ] + }, + "variables-local": { + "begin": "(?=\\b(var)\\b\\s+[A-Za-z_$][\\w$]*\\s*(=|:|;))", + "end": "(?=\\=|:|;)", + "name": "meta.definition.variable.local.java", + "patterns": [ + { + "match": "\\bvar\\b", + "name": "storage.type.local.java" + }, + { + "match": "([A-Za-z$_][\\w$]*)(?=\\s*(\\[\\])*\\s*(=|:|;))", + "captures": { + "1": { + "name": "variable.other.definition.java" + } + } + }, + { + "include": "#code" + } + ] } } } \ No newline at end of file diff --git a/extensions/javascript/snippets/javascript.json b/extensions/javascript/snippets/javascript.json index 5579e4e0c16..5da4ebe0c18 100644 --- a/extensions/javascript/snippets/javascript.json +++ b/extensions/javascript/snippets/javascript.json @@ -140,6 +140,15 @@ ], "description": "Set Timeout Function" }, + "Set Interval Function": { + "prefix": "setinterval", + "body": [ + "setInterval(() => {", + "\t$0", + "}, ${1:interval});" + ], + "description": "Set Interval Function" + }, "Import external module.": { "prefix": "import statement", "body": [ diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index ab11abeb0a6..50fa8fca327 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.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/Microsoft/TypeScript-TmLanguage/commit/6e8a3830c29b6f29c06d2de091240e1a880f21aa", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/3133e3d914db9a2bb8812119f9273727a305f16b", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -1153,7 +1153,7 @@ "name": "meta.definition.function.js entity.name.function.js" } }, - "end": "(?=$|^|;)|(?<=\\})", + "end": "(?=;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|(?<=\\})", "patterns": [ { "include": "#function-name" @@ -1413,6 +1413,9 @@ }, { "include": "#arrow-return-type" + }, + { + "include": "#possibly-arrow-return-type" } ] }, @@ -1424,8 +1427,11 @@ "name": "storage.type.function.arrow.js" } }, - "end": "(?<=\\}|\\S)(?)|((?!\\{)(?=\\S))", + "end": "((?<=\\}|\\S)(?)|((?!\\{)(?=\\S)))(?!\\/[\\/\\*])", "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, { "include": "#decl-block" }, @@ -2623,13 +2629,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2676,7 +2682,7 @@ "name": "keyword.operator.new.js" } }, - "end": "(?<=\\))|(?=[;),}\\]:]|\\|\\||\\&\\&|$|((?\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.js meta.return.type.arrow.js keyword.operator.type.annotation.js" @@ -3587,6 +3593,14 @@ } }, "patterns": [ + { + "match": "(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { @@ -3937,7 +3948,7 @@ "include": "#typeof-operator" }, { - "begin": "([&|\\*])(?=\\s*\\{)", + "begin": "([&|])(?=\\s*\\{)", "beginCaptures": { "0": { "name": "keyword.operator.type.js" @@ -3951,7 +3962,7 @@ ] }, { - "begin": "[&|\\*]", + "begin": "[&|]", "beginCaptures": { "0": { "name": "keyword.operator.type.js" @@ -4089,7 +4100,7 @@ "patterns": [ { "name": "string.template.js", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js" diff --git a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json index 85dd338686c..38aff66512d 100644 --- a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScriptReact.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/Microsoft/TypeScript-TmLanguage/commit/6e8a3830c29b6f29c06d2de091240e1a880f21aa", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/3133e3d914db9a2bb8812119f9273727a305f16b", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -1153,7 +1153,7 @@ "name": "meta.definition.function.js.jsx entity.name.function.js.jsx" } }, - "end": "(?=$|^|;)|(?<=\\})", + "end": "(?=;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|(?<=\\})", "patterns": [ { "include": "#function-name" @@ -1413,6 +1413,9 @@ }, { "include": "#arrow-return-type" + }, + { + "include": "#possibly-arrow-return-type" } ] }, @@ -1424,8 +1427,11 @@ "name": "storage.type.function.arrow.js.jsx" } }, - "end": "(?<=\\}|\\S)(?)|((?!\\{)(?=\\S))", + "end": "((?<=\\}|\\S)(?)|((?!\\{)(?=\\S)))(?!\\/[\\/\\*])", "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, { "include": "#decl-block" }, @@ -2623,13 +2629,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js.jsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2676,7 +2682,7 @@ "name": "keyword.operator.new.js.jsx" } }, - "end": "(?<=\\))|(?=[;),}\\]:]|\\|\\||\\&\\&|$|((?\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.js.jsx meta.return.type.arrow.js.jsx keyword.operator.type.annotation.js.jsx" @@ -3587,6 +3593,14 @@ } }, "patterns": [ + { + "match": "(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { @@ -3937,7 +3948,7 @@ "include": "#typeof-operator" }, { - "begin": "([&|\\*])(?=\\s*\\{)", + "begin": "([&|])(?=\\s*\\{)", "beginCaptures": { "0": { "name": "keyword.operator.type.js.jsx" @@ -3951,7 +3962,7 @@ ] }, { - "begin": "[&|\\*]", + "begin": "[&|]", "beginCaptures": { "0": { "name": "keyword.operator.type.js.jsx" @@ -4089,7 +4100,7 @@ "patterns": [ { "name": "string.template.js.jsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js.jsx" diff --git a/extensions/json-language-features/.vscode/tasks.json b/extensions/json-language-features/.vscode/tasks.json index 9e5593ade83..390a93a3a7f 100644 --- a/extensions/json-language-features/.vscode/tasks.json +++ b/extensions/json-language-features/.vscode/tasks.json @@ -1,30 +1,11 @@ -// Available variables which can be used inside of strings. -// ${workspaceFolder}: the root folder of the team -// ${file}: the current opened file -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls a custom npm script that compiles the extension. { - "version": "0.1.0", - - // we want to run npm + "version": "2.0.0", "command": "npm", - - // the command is a shell script - "isShellCommand": true, - - // show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // we run the custom script "compile" as defined in package.json + "type": "shell", + "presentation": { + "reveal": "silent" + }, "args": ["run", "compile"], - - // The tsc compiler is started in watching mode - "isWatching": true, - - // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "isBackground": true, "problemMatcher": "$tsc-watch" -} \ No newline at end of file +} diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index 8da28460f36..759ef0381cd 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -8,8 +8,8 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration } from 'vscode'; -import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient'; +import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, Diagnostic, StatusBarAlignment, TextEditor } from 'vscode'; +import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature } from 'vscode-languageclient'; import TelemetryReporter from 'vscode-extension-telemetry'; import { hash } from './utils/hash'; @@ -22,6 +22,10 @@ namespace SchemaContentChangeNotification { export const type: NotificationType = new NotificationType('json/schemaContent'); } +namespace ForceValidateRequest { + export const type: RequestType = new RequestType('json/validate'); +} + export interface ISchemaAssociations { [pattern: string]: string[]; } @@ -77,6 +81,14 @@ export function activate(context: ExtensionContext) { let documentSelector = ['json', 'jsonc']; + let schemaResolutionErrorStatusBarItem = window.createStatusBarItem(StatusBarAlignment.Right, 0); + schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema'; + schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema.') + ' ' + localize('json.clickToRetry', 'Click to retry.'); + schemaResolutionErrorStatusBarItem.text = '$(alert)'; + toDispose.push(schemaResolutionErrorStatusBarItem); + + let fileSchemaErrors = new Map(); + // Options to control the language client let clientOptions: LanguageClientOptions = { // Register the server for json documents @@ -89,6 +101,23 @@ export function activate(context: ExtensionContext) { middleware: { workspace: { didChangeConfiguration: () => client.sendNotification(DidChangeConfigurationNotification.type, { settings: getSettings() }) + }, + handleDiagnostics: (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => { + const schemaErrorIndex = diagnostics.findIndex(candidate => candidate.code === /* SchemaResolveError */ 0x300); + + if (schemaErrorIndex === -1) { + fileSchemaErrors.delete(uri.toString()); + return next(uri, diagnostics); + } + + const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; + fileSchemaErrors.set(uri.toString(), schemaResolveDiagnostic.message); + + if (window.activeTextEditor && window.activeTextEditor.document.uri.toString() === uri.toString()) { + schemaResolutionErrorStatusBarItem.show(); + } + + next(uri, diagnostics); } } }; @@ -121,8 +150,47 @@ export function activate(context: ExtensionContext) { client.sendNotification(SchemaContentChangeNotification.type, uri.toString()); } }; + + let handleActiveEditorChange = (activeEditor?: TextEditor) => { + if (!activeEditor) { + return; + } + + const activeDocUri = activeEditor.document.uri.toString(); + + if (activeDocUri && fileSchemaErrors.has(activeDocUri)) { + schemaResolutionErrorStatusBarItem.show(); + } else { + schemaResolutionErrorStatusBarItem.hide(); + } + }; + toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri))); - toDispose.push(workspace.onDidCloseTextDocument(d => handleContentChange(d.uri))); + toDispose.push(workspace.onDidCloseTextDocument(d => { + handleContentChange(d.uri); + fileSchemaErrors.delete(d.uri.toString()); + })); + toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); + + let handleRetryResolveSchemaCommand = () => { + if (window.activeTextEditor) { + schemaResolutionErrorStatusBarItem.text = '$(watch)'; + const activeDocUri = window.activeTextEditor.document.uri.toString(); + client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => { + const schemaErrorIndex = diagnostics.findIndex(candidate => candidate.code === /* SchemaResolveError */ 0x300); + if (schemaErrorIndex !== -1) { + // Show schema resolution errors in status bar only; ref: #51032 + const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; + fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message); + } else { + schemaResolutionErrorStatusBarItem.hide(); + } + schemaResolutionErrorStatusBarItem.text = '$(alert)'; + }); + } + }; + + toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); }); diff --git a/extensions/json-language-features/client/src/typings/ref.d.ts b/extensions/json-language-features/client/src/typings/ref.d.ts index 2b90c6587fe..9c1a5df18ed 100644 --- a/extensions/json-language-features/client/src/typings/ref.d.ts +++ b/extensions/json-language-features/client/src/typings/ref.d.ts @@ -4,4 +4,3 @@ *--------------------------------------------------------------------------------------------*/ /// -/// \ No newline at end of file diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 0e4b57cf994..a1a5204c8b5 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -13,10 +13,10 @@ "onLanguage:json", "onLanguage:jsonc" ], - "enableProposedApi": true, "main": "./client/out/jsonMain", "scripts": { - "compile": "gulp compile-extension:json-language-features-client && gulp compile-extension:json-language-features-server", + "compile": "gulp compile-extension:json-language-features-client compile-extension:json-language-features-server", + "watch": "gulp watch-extension:json-language-features-client watch-extension:json-language-features-server", "postinstall": "cd server && yarn install", "install-client-next": "yarn add vscode-languageclient@next" }, diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index 943de414e15..c61e7b70e8f 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -9,5 +9,7 @@ "json.format.enable.desc": "Enable/disable default JSON formatter", "json.tracing.desc": "Traces the communication between VS Code and the JSON language server.", "json.colorDecorators.enable.desc": "Enables or disables color decorators", - "json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`." -} \ No newline at end of file + "json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.", + "json.schemaResolutionErrorMessage": "Unable to resolve schema.", + "json.clickToRetry": "Click to retry." +} diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 5e7de8ed719..db0182fa088 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -14,7 +14,7 @@ "dependencies": { "jsonc-parser": "^2.0.2", "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.2.0", + "vscode-json-languageservice": "^3.2.1", "vscode-languageserver": "^5.1.0", "vscode-nls": "^4.0.0", "vscode-uri": "^1.0.6" diff --git a/extensions/json-language-features/server/src/jsonServerMain.ts b/extensions/json-language-features/server/src/jsonServerMain.ts index 6eadf2403b8..8a8ce642331 100644 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/jsonServerMain.ts @@ -6,7 +6,7 @@ import { createConnection, IConnection, TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType, - DocumentRangeFormattingRequest, Disposable, ServerCapabilities + DocumentRangeFormattingRequest, Disposable, ServerCapabilities, Diagnostic } from 'vscode-languageserver'; import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; @@ -34,6 +34,10 @@ namespace SchemaContentChangeNotification { export const type: NotificationType = new NotificationType('json/schemaContent'); } +namespace ForceValidateRequest { + export const type: RequestType = new RequestType('json/validate'); +} + // Create a connection for the server const connection: IConnection = createConnection(); @@ -207,6 +211,21 @@ connection.onNotification(SchemaContentChangeNotification.type, uri => { languageService.resetSchema(uri); }); +// Retry schema validation on all open documents +connection.onRequest(ForceValidateRequest.type, uri => { + return new Promise(resolve => { + const document = documents.get(uri); + if (document) { + updateConfiguration(); + validateTextDocument(document, diagnostics => { + resolve(diagnostics); + }); + } else { + resolve([]); + } + }); +}); + function updateConfiguration() { const languageSettings = { validate: true, @@ -271,10 +290,15 @@ function triggerValidation(textDocument: TextDocument): void { }, validationDelayMs); } -function validateTextDocument(textDocument: TextDocument): void { +function validateTextDocument(textDocument: TextDocument, callback?: (diagnostics: Diagnostic[]) => void): void { + const respond = (diagnostics: Diagnostic[]) => { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + if (callback) { + callback(diagnostics); + } + }; if (textDocument.getText().length === 0) { - // ignore empty documents - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: [] }); + respond([]); // ignore empty documents return; } const jsonDocument = getJSONDocument(textDocument); @@ -285,8 +309,7 @@ function validateTextDocument(textDocument: TextDocument): void { setTimeout(() => { const currDocument = documents.get(textDocument.uri); if (currDocument && currDocument.version === version) { - // Send the computed diagnostics to VSCode. - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + respond(diagnostics); // Send the computed diagnostics to VSCode. } }, 100); }, error => { @@ -405,4 +428,4 @@ connection.onFoldingRanges((params, token) => { }); // Listen on the connection -connection.listen(); \ No newline at end of file +connection.listen(); diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index e4c2fee15a6..012e346360a 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -73,10 +73,10 @@ request-light@^0.2.4: https-proxy-agent "^2.2.1" vscode-nls "^4.0.0" -vscode-json-languageservice@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.2.0.tgz#fe796c2ddbda966d87905442f9636f139e00f341" - integrity sha512-tLAv9/D01fLAvnYnZ1OLy03HSHhVFjaSkUidEjfrwytHrxVDgqXLkHAJg+F6Q3mPYfpnPQvN2jTjiJ1yInuNVg== +vscode-json-languageservice@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.2.1.tgz#991d51128ebd81c5525d0578cabfa5b03e3cba2a" + integrity sha512-ee9MJ70/xR55ywvm0bZsDLhA800HCRE27AYgMNTU14RSg20Y+ngHdQnUt6OmiTXrQDI/7sne6QUOtHIN0hPQYA== dependencies: jsonc-parser "^2.0.2" vscode-languageserver-types "^3.13.0" diff --git a/extensions/log/syntaxes/log.tmLanguage.json b/extensions/log/syntaxes/log.tmLanguage.json index 15cb646dadb..e2e5af634b1 100644 --- a/extensions/log/syntaxes/log.tmLanguage.json +++ b/extensions/log/syntaxes/log.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/emilast/vscode-logfile-highlighter/commit/03cb9582024f477f93f3d27c46f7c084b2b14afe", + "version": "https://github.com/emilast/vscode-logfile-highlighter/commit/30a19d24b9a0070b0fab5eef45f89c92e7cd71ea", "name": "Log file", "scopeName": "text.log", "patterns": [ @@ -49,7 +49,7 @@ "name": "comment log.date" }, { - "match": "\\d{2}:\\d{2}(:\\d{2}([.,]\\d{3,})?)?(Z| ?[+-]\\d{2}:\\d{2})?\\b", + "match": "\\d{1,2}:\\d{2}(:\\d{2}([.,]\\d{1,})?)?(Z| ?[+-]\\d{1,2}:\\d{2})?\\b", "name": "comment log.date" }, { @@ -69,7 +69,7 @@ "name": "string log.string" }, { - "match": "(^|[^\\w])'[^']*'", + "match": "(??) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in quotes…\n | ((\").+?(\")) # or in parens.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", + "match": "(?x)\n \\s* # Leading whitespace\n (\\[)([^]]+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in quotes…\n | ((\").+?(\")) # or in parens.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", "name": "meta.link.reference.def.markdown" }, "list_paragraph": { diff --git a/extensions/markdown-basics/test/colorize-results/test_md.json b/extensions/markdown-basics/test/colorize-results/test_md.json index faa18bc663b..9bc9aa7595f 100644 --- a/extensions/markdown-basics/test/colorize-results/test_md.json +++ b/extensions/markdown-basics/test/colorize-results/test_md.json @@ -2520,7 +2520,7 @@ }, { "c": "]", - "t": "text.html.markdown meta.paragraph.markdown meta.link.reference.markdown", + "t": "text.html.markdown meta.paragraph.markdown meta.link.reference.markdown punctuation.definition.string.end.markdown", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 99e63083556..25df5db9f4e 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -825,11 +825,11 @@ const getCodeLineElements = (() => { let elements; return () => { if (!elements) { - elements = Array.prototype.map.call(document.getElementsByClassName('code-line'), (element) => { + elements = ([{ element: document.body, line: 0 }]).concat(Array.prototype.map.call(document.getElementsByClassName('code-line'), (element) => { const line = +element.getAttribute('data-line'); return { element, line }; }) - .filter((x) => !isNaN(x.line)); + .filter((x) => !isNaN(x.line))); } return elements; }; @@ -887,22 +887,30 @@ exports.getLineElementsAtPageOffset = getLineElementsAtPageOffset; * Attempt to reveal the element for a source line in the editor. */ function scrollToRevealSourceLine(line) { - const { previous, next } = getElementsForSourceLine(line); - if (previous && settings_1.getSettings().scrollPreviewWithEditor) { - let scrollTo = 0; - const rect = previous.element.getBoundingClientRect(); - const previousTop = rect.top; - if (next && next.line !== previous.line) { - // Between two elements. Go to percentage offset between them. - const betweenProgress = (line - previous.line) / (next.line - previous.line); - const elementOffset = next.element.getBoundingClientRect().top - previousTop; - scrollTo = previousTop + betweenProgress * elementOffset; - } - else { - scrollTo = previousTop; - } - window.scroll(0, Math.max(1, window.scrollY + scrollTo)); + if (!settings_1.getSettings().scrollPreviewWithEditor) { + return; } + if (line <= 0) { + window.scroll(window.scrollX, 0); + return; + } + const { previous, next } = getElementsForSourceLine(line); + if (!previous) { + return; + } + let scrollTo = 0; + const rect = previous.element.getBoundingClientRect(); + const previousTop = rect.top; + if (next && next.line !== previous.line) { + // Between two elements. Go to percentage offset between them. + const betweenProgress = (line - previous.line) / (next.line - previous.line); + const elementOffset = next.element.getBoundingClientRect().top - previousTop; + scrollTo = previousTop + betweenProgress * elementOffset; + } + else { + scrollTo = previousTop; + } + window.scroll(window.scrollX, Math.max(1, window.scrollY + scrollTo)); } exports.scrollToRevealSourceLine = scrollToRevealSourceLine; function getEditorLineNumberForPageOffset(offset) { @@ -970,4 +978,4 @@ exports.getSettings = getSettings; /***/ }) /******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsT0FBTztBQUNsQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLDhDQUE4QyxrQkFBa0I7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsb0JBQW9CO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsRUFBRTtBQUNiLGFBQWEsUUFBUTtBQUNyQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxFQUFFO0FBQ2IsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOzs7Ozs7Ozs7Ozs7O0FDdGJBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNENBQTRDOztBQUU1Qzs7Ozs7Ozs7Ozs7Ozs7O0FDbkJBOzs7Z0dBR2dHO0FBQ2hHLCtGQUF5RDtBQUV6RDtJQUdDLDhCQUE4QixDQUFDLElBQVk7UUFDMUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLHNDQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxJQUFJLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRUQsT0FBTyxDQUFDLE1BQStCO1FBQ3RDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQ3hCLENBQUM7SUFFRCxvQkFBb0IsQ0FBQyxPQUFnQztRQUNwRCxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDZCxNQUFNLENBQUM7UUFDUixDQUFDO1FBQ0QsT0FBTyxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUM1RSxDQUFDO0lBRUQsa0JBQWtCLENBQUMsT0FBZ0M7UUFDbEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ2QsTUFBTSxDQUFDO1FBQ1IsQ0FBQztRQUNELE9BQU8sQ0FBQyxTQUFTLElBQUksbUJBQW1CLENBQUM7SUFDMUMsQ0FBQztDQUNEO0FBM0JELDRDQTJCQzs7Ozs7Ozs7Ozs7Ozs7QUNqQ0Q7OztnR0FHZ0c7O0FBRWhHLDRCQUFtQyxDQUFhO0lBQy9DLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEtBQUssU0FBUyxJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssZUFBZSxDQUFDLENBQUMsQ0FBQztRQUNsRixRQUFRLENBQUMsZ0JBQWdCLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxFQUFFLENBQUM7SUFDTCxDQUFDO0FBQ0YsQ0FBQztBQU5ELGdEQU1DOzs7Ozs7Ozs7Ozs7OztBQ1hEOzs7Z0dBR2dHOztBQUVoRyw4R0FBc0Q7QUFDdEQsZ0ZBQThDO0FBQzlDLHlGQUFvRDtBQUNwRCwrRkFBMkY7QUFDM0Ysc0ZBQWtEO0FBQ2xELHVHQUE2QztBQUk3QyxJQUFJLGNBQWMsR0FBRyxJQUFJLENBQUM7QUFDMUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxtQ0FBZ0IsRUFBRSxDQUFDO0FBQ3RDLE1BQU0sUUFBUSxHQUFHLHNCQUFXLEVBQUUsQ0FBQztBQUUvQixNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsRUFBRSxDQUFDO0FBRWxDLG9CQUFvQjtBQUNwQixNQUFNLEtBQUssR0FBRyxrQkFBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO0FBQ3BDLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7QUFFdkIsTUFBTSxTQUFTLEdBQUcsaUNBQXFCLENBQUMsTUFBTSxDQUFDLENBQUM7QUFFaEQsTUFBTSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDdkMsTUFBTSxDQUFDLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUVoRCxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsRUFBRTtJQUNwQixnQkFBZ0IsRUFBRSxDQUFDO0FBQ3BCLENBQUMsQ0FBQztBQUVGLDJCQUFrQixDQUFDLEdBQUcsRUFBRTtJQUN2QixFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDZixNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7WUFDbkMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUN6QixjQUFjLEdBQUcsSUFBSSxDQUFDO2dCQUN0QixzQ0FBd0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUN2QyxDQUFDO1FBQ0YsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztBQUNGLENBQUMsQ0FBQyxDQUFDO0FBRUgsTUFBTSxZQUFZLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDMUIsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLENBQUMsSUFBWSxFQUFFLEVBQUU7UUFDMUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUN0QixzQ0FBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNoQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFUCxNQUFNLENBQUMsQ0FBQyxJQUFZLEVBQUUsUUFBYSxFQUFFLEVBQUU7UUFDdEMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2xCLFFBQVEsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1lBQ3JCLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNoQixDQUFDO0lBQ0YsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMLElBQUksZ0JBQWdCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRTtJQUNwQyxNQUFNLFNBQVMsR0FBb0QsRUFBRSxDQUFDO0lBQ3RFLElBQUksTUFBTSxHQUFHLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNsRCxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ1osSUFBSSxDQUFDLENBQUM7UUFDTixHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDcEMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRXRCLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDdkMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDakMsQ0FBQztZQUVELFNBQVMsQ0FBQyxJQUFJLENBQUM7Z0JBQ2QsRUFBRSxFQUFFLEdBQUcsQ0FBQyxFQUFFO2dCQUNWLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTTtnQkFDbEIsS0FBSyxFQUFFLEdBQUcsQ0FBQyxLQUFLO2FBQ2hCLENBQUMsQ0FBQztRQUNKLENBQUM7UUFFRCxTQUFTLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQ3JELENBQUM7QUFDRixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFFUCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUN0QyxjQUFjLEdBQUcsSUFBSSxDQUFDO0lBQ3RCLGdCQUFnQixFQUFFLENBQUM7QUFDcEIsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBRVQsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsRUFBRTtJQUMxQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUMzQyxNQUFNLENBQUM7SUFDUixDQUFDO0lBRUQsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3pCLEtBQUssZ0NBQWdDO1lBQ3BDLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZELEtBQUssQ0FBQztRQUVQLEtBQUssWUFBWTtZQUNoQixZQUFZLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDeEMsS0FBSyxDQUFDO0lBQ1IsQ0FBQztBQUNGLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztBQUVWLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLEVBQUU7SUFDN0MsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsMkJBQTJCLENBQUMsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCx5QkFBeUI7SUFDekIsR0FBRyxDQUFDLENBQUMsSUFBSSxJQUFJLEdBQUcsS0FBSyxDQUFDLE1BQXFCLEVBQUUsSUFBSSxFQUFFLElBQUksR0FBRyxJQUFJLENBQUMsVUFBeUIsRUFBRSxDQUFDO1FBQzFGLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUMxQixNQUFNLENBQUM7UUFDUixDQUFDO0lBQ0YsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUM7SUFDM0IsTUFBTSxJQUFJLEdBQUcsOENBQWdDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdEQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM5QyxTQUFTLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUMvRCxDQUFDO0FBQ0YsQ0FBQyxDQUFDLENBQUM7QUFFSCxRQUFRLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxFQUFFO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNaLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCxJQUFJLElBQUksR0FBUSxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQzdCLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDYixFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3ZELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDL0MsS0FBSyxDQUFDO1lBQ1AsQ0FBQztZQUNELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNqRixNQUFNLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGdDQUFnQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDNUYsU0FBUyxDQUFDLFdBQVcsQ0FBQyxXQUFXLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDdkQsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUN2QixLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7Z0JBQ3hCLEtBQUssQ0FBQztZQUNQLENBQUM7WUFDRCxLQUFLLENBQUM7UUFDUCxDQUFDO1FBQ0QsSUFBSSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDeEIsQ0FBQztBQUNGLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUVULEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLENBQUM7SUFDdEMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQy9DLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7WUFDcEIsY0FBYyxHQUFHLEtBQUssQ0FBQztRQUN4QixDQUFDO1FBQUMsSUFBSSxDQUFDLENBQUM7WUFDUCxNQUFNLElBQUksR0FBRyw4Q0FBZ0MsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDOUQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDOUMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxZQUFZLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQy9DLENBQUM7UUFDRixDQUFDO0lBQ0YsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDVCxDQUFDOzs7Ozs7Ozs7Ozs7OztBQzdKRDs7O2dHQUdnRzs7QUFFaEcsc0ZBQXlDO0FBUzVCLDZCQUFxQixHQUFHLENBQUMsTUFBVyxFQUFFLEVBQUU7SUFDcEQsTUFBTSxDQUFDLElBQUk7UUFDVixXQUFXLENBQUMsSUFBWSxFQUFFLElBQVk7WUFDckMsTUFBTSxDQUFDLFdBQVcsQ0FBQztnQkFDbEIsSUFBSTtnQkFDSixNQUFNLEVBQUUsc0JBQVcsRUFBRSxDQUFDLE1BQU07Z0JBQzVCLElBQUk7YUFDSixDQUFDLENBQUM7UUFDSixDQUFDO0tBQ0QsQ0FBQztBQUNILENBQUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUN4QkY7OztnR0FHZ0c7O0FBRWhHLHNGQUF5QztBQUd6QyxlQUFlLEdBQVcsRUFBRSxHQUFXLEVBQUUsS0FBYTtJQUNyRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBRUQsbUJBQW1CLElBQVk7SUFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsc0JBQVcsRUFBRSxDQUFDLFNBQVMsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQVFELE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDakMsSUFBSSxRQUEyQixDQUFDO0lBQ2hDLE1BQU0sQ0FBQyxHQUFHLEVBQUU7UUFDWCxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDZixRQUFRLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUNsQyxRQUFRLENBQUMsc0JBQXNCLENBQUMsV0FBVyxDQUFDLEVBQzVDLENBQUMsT0FBWSxFQUFFLEVBQUU7Z0JBQ2hCLE1BQU0sSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDaEQsTUFBTSxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDO1lBQzFCLENBQUMsQ0FBQztpQkFDRCxNQUFNLENBQUMsQ0FBQyxDQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLENBQUM7UUFDRCxNQUFNLENBQUMsUUFBUSxDQUFDO0lBQ2pCLENBQUMsQ0FBQztBQUNILENBQUMsQ0FBQyxFQUFFLENBQUM7QUFFTDs7Ozs7R0FLRztBQUNILGtDQUF5QyxVQUFrQjtJQUMxRCxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQzFDLE1BQU0sS0FBSyxHQUFHLG1CQUFtQixFQUFFLENBQUM7SUFDcEMsSUFBSSxRQUFRLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQztJQUNoQyxHQUFHLENBQUMsQ0FBQyxNQUFNLEtBQUssSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBQzNCLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssVUFBVSxDQUFDLENBQUMsQ0FBQztZQUMvQixNQUFNLENBQUMsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQztRQUM3QyxDQUFDO1FBQ0QsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLEdBQUcsVUFBVSxDQUFDLENBQUMsQ0FBQztZQUNsQyxNQUFNLENBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxDQUFDO1FBQ2xDLENBQUM7UUFDRCxRQUFRLEdBQUcsS0FBSyxDQUFDO0lBQ2xCLENBQUM7SUFDRCxNQUFNLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQztBQUNyQixDQUFDO0FBZEQsNERBY0M7QUFFRDs7R0FFRztBQUNILHFDQUE0QyxNQUFjO0lBQ3pELE1BQU0sS0FBSyxHQUFHLG1CQUFtQixFQUFFLENBQUM7SUFDcEMsTUFBTSxRQUFRLEdBQUcsTUFBTSxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUM7SUFDekMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDWixJQUFJLEVBQUUsR0FBRyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUMxQixPQUFPLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUM7UUFDcEIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUN0QyxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDMUQsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsR0FBRyxNQUFNLENBQUMsTUFBTSxJQUFJLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDNUMsRUFBRSxHQUFHLEdBQUcsQ0FBQztRQUNWLENBQUM7UUFDRCxJQUFJLENBQUMsQ0FBQztZQUNMLEVBQUUsR0FBRyxHQUFHLENBQUM7UUFDVixDQUFDO0lBQ0YsQ0FBQztJQUNELE1BQU0sU0FBUyxHQUFHLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM1QixNQUFNLFFBQVEsR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUM7SUFDM0QsRUFBRSxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsSUFBSSxRQUFRLENBQUMsR0FBRyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDeEMsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzVCLE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO0lBQ2pELENBQUM7SUFDRCxNQUFNLENBQUMsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFDaEMsQ0FBQztBQXRCRCxrRUFzQkM7QUFFRDs7R0FFRztBQUNILGtDQUF5QyxJQUFZO0lBQ3BELE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEdBQUcsd0JBQXdCLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDMUQsRUFBRSxDQUFDLENBQUMsUUFBUSxJQUFJLHNCQUFXLEVBQUUsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLENBQUM7UUFDdkQsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO1FBQ2pCLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUN0RCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO1FBQzdCLEVBQUUsQ0FBQyxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3pDLDhEQUE4RDtZQUM5RCxNQUFNLGVBQWUsR0FBRyxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUM3RSxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLFdBQVcsQ0FBQztZQUM3RSxRQUFRLEdBQUcsV0FBVyxHQUFHLGVBQWUsR0FBRyxhQUFhLENBQUM7UUFDMUQsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsUUFBUSxHQUFHLFdBQVcsQ0FBQztRQUN4QixDQUFDO1FBQ0QsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBQzFELENBQUM7QUFDRixDQUFDO0FBakJELDREQWlCQztBQUVELDBDQUFpRCxNQUFjO0lBQzlELE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEdBQUcsMkJBQTJCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDL0QsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUNkLE1BQU0sY0FBYyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUNoRSxNQUFNLGtCQUFrQixHQUFHLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzFFLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDVixNQUFNLHVCQUF1QixHQUFHLGtCQUFrQixHQUFHLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDLEdBQUcsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDckgsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksR0FBRyx1QkFBdUIsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ25GLE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsTUFBTSxxQkFBcUIsR0FBRyxrQkFBa0IsR0FBRyxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUMzRSxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxHQUFHLHFCQUFxQixDQUFDO1lBQ25ELE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztJQUNGLENBQUM7SUFDRCxNQUFNLENBQUMsSUFBSSxDQUFDO0FBQ2IsQ0FBQztBQWpCRCw0RUFpQkM7Ozs7Ozs7Ozs7Ozs7O0FDOUhEOzs7Z0dBR2dHOztBQVloRyxJQUFJLGNBQWMsR0FBZ0MsU0FBUyxDQUFDO0FBRTVELGlCQUF3QixHQUFXO0lBQ2xDLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUN4RSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ2IsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ1YsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekIsQ0FBQztJQUNGLENBQUM7SUFFRCxNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixHQUFHLEVBQUUsQ0FBQyxDQUFDO0FBQ25ELENBQUM7QUFWRCwwQkFVQztBQUVEO0lBQ0MsRUFBRSxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQztRQUNwQixNQUFNLENBQUMsY0FBYyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxjQUFjLEdBQUcsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7UUFDcEIsTUFBTSxDQUFDLGNBQWMsQ0FBQztJQUN2QixDQUFDO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO0FBQzVDLENBQUM7QUFYRCxrQ0FXQyIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwge1xuIFx0XHRcdFx0Y29uZmlndXJhYmxlOiBmYWxzZSxcbiBcdFx0XHRcdGVudW1lcmFibGU6IHRydWUsXG4gXHRcdFx0XHRnZXQ6IGdldHRlclxuIFx0XHRcdH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3ByZXZpZXctc3JjL2luZGV4LnRzXCIpO1xuIiwiLyoqXG4gKiBsb2Rhc2ggKEN1c3RvbSBCdWlsZCkgPGh0dHBzOi8vbG9kYXNoLmNvbS8+XG4gKiBCdWlsZDogYGxvZGFzaCBtb2R1bGFyaXplIGV4cG9ydHM9XCJucG1cIiAtbyAuL2BcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzIDxodHRwczovL2pxdWVyeS5vcmcvPlxuICogUmVsZWFzZWQgdW5kZXIgTUlUIGxpY2Vuc2UgPGh0dHBzOi8vbG9kYXNoLmNvbS9saWNlbnNlPlxuICogQmFzZWQgb24gVW5kZXJzY29yZS5qcyAxLjguMyA8aHR0cDovL3VuZGVyc2NvcmVqcy5vcmcvTElDRU5TRT5cbiAqIENvcHlyaWdodCBKZXJlbXkgQXNoa2VuYXMsIERvY3VtZW50Q2xvdWQgYW5kIEludmVzdGlnYXRpdmUgUmVwb3J0ZXJzICYgRWRpdG9yc1xuICovXG5cbi8qKiBVc2VkIGFzIHRoZSBgVHlwZUVycm9yYCBtZXNzYWdlIGZvciBcIkZ1bmN0aW9uc1wiIG1ldGhvZHMuICovXG52YXIgRlVOQ19FUlJPUl9URVhUID0gJ0V4cGVjdGVkIGEgZnVuY3Rpb24nO1xuXG4vKiogVXNlZCBhcyByZWZlcmVuY2VzIGZvciB2YXJpb3VzIGBOdW1iZXJgIGNvbnN0YW50cy4gKi9cbnZhciBOQU4gPSAwIC8gMDtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIHN5bWJvbFRhZyA9ICdbb2JqZWN0IFN5bWJvbF0nO1xuXG4vKiogVXNlZCB0byBtYXRjaCBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLiAqL1xudmFyIHJlVHJpbSA9IC9eXFxzK3xcXHMrJC9nO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmFkIHNpZ25lZCBoZXhhZGVjaW1hbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCYWRIZXggPSAvXlstK10weFswLTlhLWZdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJpbmFyeSBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCaW5hcnkgPSAvXjBiWzAxXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBvY3RhbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNPY3RhbCA9IC9eMG9bMC03XSskL2k7XG5cbi8qKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyB3aXRob3V0IGEgZGVwZW5kZW5jeSBvbiBgcm9vdGAuICovXG52YXIgZnJlZVBhcnNlSW50ID0gcGFyc2VJbnQ7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgZ2xvYmFsYCBmcm9tIE5vZGUuanMuICovXG52YXIgZnJlZUdsb2JhbCA9IHR5cGVvZiBnbG9iYWwgPT0gJ29iamVjdCcgJiYgZ2xvYmFsICYmIGdsb2JhbC5PYmplY3QgPT09IE9iamVjdCAmJiBnbG9iYWw7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgc2VsZmAuICovXG52YXIgZnJlZVNlbGYgPSB0eXBlb2Ygc2VsZiA9PSAnb2JqZWN0JyAmJiBzZWxmICYmIHNlbGYuT2JqZWN0ID09PSBPYmplY3QgJiYgc2VsZjtcblxuLyoqIFVzZWQgYXMgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvYmplY3QuICovXG52YXIgcm9vdCA9IGZyZWVHbG9iYWwgfHwgZnJlZVNlbGYgfHwgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblxuLyoqIFVzZWQgZm9yIGJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlXG4gKiBbYHRvU3RyaW5nVGFnYF0oaHR0cDovL2VjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9iamVjdFRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heCxcbiAgICBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqXG4gKiBHZXRzIHRoZSB0aW1lc3RhbXAgb2YgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdGhhdCBoYXZlIGVsYXBzZWQgc2luY2VcbiAqIHRoZSBVbml4IGVwb2NoICgxIEphbnVhcnkgMTk3MCAwMDowMDowMCBVVEMpLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMi40LjBcbiAqIEBjYXRlZ29yeSBEYXRlXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSB0aW1lc3RhbXAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uZGVmZXIoZnVuY3Rpb24oc3RhbXApIHtcbiAqICAgY29uc29sZS5sb2coXy5ub3coKSAtIHN0YW1wKTtcbiAqIH0sIF8ubm93KCkpO1xuICogLy8gPT4gTG9ncyB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBpdCB0b29rIGZvciB0aGUgZGVmZXJyZWQgaW52b2NhdGlvbi5cbiAqL1xudmFyIG5vdyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gcm9vdC5EYXRlLm5vdygpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVib3VuY2VkIGZ1bmN0aW9uIHRoYXQgZGVsYXlzIGludm9raW5nIGBmdW5jYCB1bnRpbCBhZnRlciBgd2FpdGBcbiAqIG1pbGxpc2Vjb25kcyBoYXZlIGVsYXBzZWQgc2luY2UgdGhlIGxhc3QgdGltZSB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHdhc1xuICogaW52b2tlZC4gVGhlIGRlYm91bmNlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGAgbWV0aG9kIHRvIGNhbmNlbFxuICogZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG8gaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uXG4gKiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYCBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGVcbiAqIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YCB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWRcbiAqIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnRcbiAqIGNhbGxzIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gcmV0dXJuIHRoZSByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgXG4gKiBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLmRlYm91bmNlYCBhbmQgYF8udGhyb3R0bGVgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gZGVib3VuY2UuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gZGVsYXkuXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz1mYWxzZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMubWF4V2FpdF1cbiAqICBUaGUgbWF4aW11bSB0aW1lIGBmdW5jYCBpcyBhbGxvd2VkIHRvIGJlIGRlbGF5ZWQgYmVmb3JlIGl0J3MgaW52b2tlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZGVib3VuY2VkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBjb3N0bHkgY2FsY3VsYXRpb25zIHdoaWxlIHRoZSB3aW5kb3cgc2l6ZSBpcyBpbiBmbHV4LlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Jlc2l6ZScsIF8uZGVib3VuY2UoY2FsY3VsYXRlTGF5b3V0LCAxNTApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHNlbmRNYWlsYCB3aGVuIGNsaWNrZWQsIGRlYm91bmNpbmcgc3Vic2VxdWVudCBjYWxscy5cbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCBfLmRlYm91bmNlKHNlbmRNYWlsLCAzMDAsIHtcbiAqICAgJ2xlYWRpbmcnOiB0cnVlLFxuICogICAndHJhaWxpbmcnOiBmYWxzZVxuICogfSkpO1xuICpcbiAqIC8vIEVuc3VyZSBgYmF0Y2hMb2dgIGlzIGludm9rZWQgb25jZSBhZnRlciAxIHNlY29uZCBvZiBkZWJvdW5jZWQgY2FsbHMuXG4gKiB2YXIgZGVib3VuY2VkID0gXy5kZWJvdW5jZShiYXRjaExvZywgMjUwLCB7ICdtYXhXYWl0JzogMTAwMCB9KTtcbiAqIHZhciBzb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2UoJy9zdHJlYW0nKTtcbiAqIGpRdWVyeShzb3VyY2UpLm9uKCdtZXNzYWdlJywgZGVib3VuY2VkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIGRlYm91bmNlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgZGVib3VuY2VkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIGRlYm91bmNlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxhc3RBcmdzLFxuICAgICAgbGFzdFRoaXMsXG4gICAgICBtYXhXYWl0LFxuICAgICAgcmVzdWx0LFxuICAgICAgdGltZXJJZCxcbiAgICAgIGxhc3RDYWxsVGltZSxcbiAgICAgIGxhc3RJbnZva2VUaW1lID0gMCxcbiAgICAgIGxlYWRpbmcgPSBmYWxzZSxcbiAgICAgIG1heGluZyA9IGZhbHNlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIHdhaXQgPSB0b051bWJlcih3YWl0KSB8fCAwO1xuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gISFvcHRpb25zLmxlYWRpbmc7XG4gICAgbWF4aW5nID0gJ21heFdhaXQnIGluIG9wdGlvbnM7XG4gICAgbWF4V2FpdCA9IG1heGluZyA/IG5hdGl2ZU1heCh0b051bWJlcihvcHRpb25zLm1heFdhaXQpIHx8IDAsIHdhaXQpIDogbWF4V2FpdDtcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG5cbiAgZnVuY3Rpb24gaW52b2tlRnVuYyh0aW1lKSB7XG4gICAgdmFyIGFyZ3MgPSBsYXN0QXJncyxcbiAgICAgICAgdGhpc0FyZyA9IGxhc3RUaGlzO1xuXG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgcmVzdWx0ID0gZnVuYy5hcHBseSh0aGlzQXJnLCBhcmdzKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbGVhZGluZ0VkZ2UodGltZSkge1xuICAgIC8vIFJlc2V0IGFueSBgbWF4V2FpdGAgdGltZXIuXG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIC8vIFN0YXJ0IHRoZSB0aW1lciBmb3IgdGhlIHRyYWlsaW5nIGVkZ2UuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAvLyBJbnZva2UgdGhlIGxlYWRpbmcgZWRnZS5cbiAgICByZXR1cm4gbGVhZGluZyA/IGludm9rZUZ1bmModGltZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiByZW1haW5pbmdXYWl0KHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lLFxuICAgICAgICByZXN1bHQgPSB3YWl0IC0gdGltZVNpbmNlTGFzdENhbGw7XG5cbiAgICByZXR1cm4gbWF4aW5nID8gbmF0aXZlTWluKHJlc3VsdCwgbWF4V2FpdCAtIHRpbWVTaW5jZUxhc3RJbnZva2UpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gc2hvdWxkSW52b2tlKHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lO1xuXG4gICAgLy8gRWl0aGVyIHRoaXMgaXMgdGhlIGZpcnN0IGNhbGwsIGFjdGl2aXR5IGhhcyBzdG9wcGVkIGFuZCB3ZSdyZSBhdCB0aGVcbiAgICAvLyB0cmFpbGluZyBlZGdlLCB0aGUgc3lzdGVtIHRpbWUgaGFzIGdvbmUgYmFja3dhcmRzIGFuZCB3ZSdyZSB0cmVhdGluZ1xuICAgIC8vIGl0IGFzIHRoZSB0cmFpbGluZyBlZGdlLCBvciB3ZSd2ZSBoaXQgdGhlIGBtYXhXYWl0YCBsaW1pdC5cbiAgICByZXR1cm4gKGxhc3RDYWxsVGltZSA9PT0gdW5kZWZpbmVkIHx8ICh0aW1lU2luY2VMYXN0Q2FsbCA+PSB3YWl0KSB8fFxuICAgICAgKHRpbWVTaW5jZUxhc3RDYWxsIDwgMCkgfHwgKG1heGluZyAmJiB0aW1lU2luY2VMYXN0SW52b2tlID49IG1heFdhaXQpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRpbWVyRXhwaXJlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpO1xuICAgIGlmIChzaG91bGRJbnZva2UodGltZSkpIHtcbiAgICAgIHJldHVybiB0cmFpbGluZ0VkZ2UodGltZSk7XG4gICAgfVxuICAgIC8vIFJlc3RhcnQgdGhlIHRpbWVyLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgcmVtYWluaW5nV2FpdCh0aW1lKSk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFpbGluZ0VkZ2UodGltZSkge1xuICAgIHRpbWVySWQgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBPbmx5IGludm9rZSBpZiB3ZSBoYXZlIGBsYXN0QXJnc2Agd2hpY2ggbWVhbnMgYGZ1bmNgIGhhcyBiZWVuXG4gICAgLy8gZGVib3VuY2VkIGF0IGxlYXN0IG9uY2UuXG4gICAgaWYgKHRyYWlsaW5nICYmIGxhc3RBcmdzKSB7XG4gICAgICByZXR1cm4gaW52b2tlRnVuYyh0aW1lKTtcbiAgICB9XG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuY2VsKCkge1xuICAgIGlmICh0aW1lcklkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcklkKTtcbiAgICB9XG4gICAgbGFzdEludm9rZVRpbWUgPSAwO1xuICAgIGxhc3RBcmdzID0gbGFzdENhbGxUaW1lID0gbGFzdFRoaXMgPSB0aW1lcklkID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgZnVuY3Rpb24gZmx1c2goKSB7XG4gICAgcmV0dXJuIHRpbWVySWQgPT09IHVuZGVmaW5lZCA/IHJlc3VsdCA6IHRyYWlsaW5nRWRnZShub3coKSk7XG4gIH1cblxuICBmdW5jdGlvbiBkZWJvdW5jZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKSxcbiAgICAgICAgaXNJbnZva2luZyA9IHNob3VsZEludm9rZSh0aW1lKTtcblxuICAgIGxhc3RBcmdzID0gYXJndW1lbnRzO1xuICAgIGxhc3RUaGlzID0gdGhpcztcbiAgICBsYXN0Q2FsbFRpbWUgPSB0aW1lO1xuXG4gICAgaWYgKGlzSW52b2tpbmcpIHtcbiAgICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGxlYWRpbmdFZGdlKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgICBpZiAobWF4aW5nKSB7XG4gICAgICAgIC8vIEhhbmRsZSBpbnZvY2F0aW9ucyBpbiBhIHRpZ2h0IGxvb3AuXG4gICAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgICAgIHJldHVybiBpbnZva2VGdW5jKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgZGVib3VuY2VkLmNhbmNlbCA9IGNhbmNlbDtcbiAgZGVib3VuY2VkLmZsdXNoID0gZmx1c2g7XG4gIHJldHVybiBkZWJvdW5jZWQ7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHRocm90dGxlZCBmdW5jdGlvbiB0aGF0IG9ubHkgaW52b2tlcyBgZnVuY2AgYXQgbW9zdCBvbmNlIHBlclxuICogZXZlcnkgYHdhaXRgIG1pbGxpc2Vjb25kcy4gVGhlIHRocm90dGxlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGBcbiAqIG1ldGhvZCB0byBjYW5jZWwgZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG9cbiAqIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYFxuICogc2hvdWxkIGJlIGludm9rZWQgb24gdGhlIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YFxuICogdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZVxuICogdGhyb3R0bGVkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50IGNhbGxzIHRvIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gcmV0dXJuIHRoZVxuICogcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYCBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLnRocm90dGxlYCBhbmQgYF8uZGVib3VuY2VgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gdGhyb3R0bGUuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gdGhyb3R0bGUgaW52b2NhdGlvbnMgdG8uXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgdGhyb3R0bGVkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBleGNlc3NpdmVseSB1cGRhdGluZyB0aGUgcG9zaXRpb24gd2hpbGUgc2Nyb2xsaW5nLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Njcm9sbCcsIF8udGhyb3R0bGUodXBkYXRlUG9zaXRpb24sIDEwMCkpO1xuICpcbiAqIC8vIEludm9rZSBgcmVuZXdUb2tlbmAgd2hlbiB0aGUgY2xpY2sgZXZlbnQgaXMgZmlyZWQsIGJ1dCBub3QgbW9yZSB0aGFuIG9uY2UgZXZlcnkgNSBtaW51dGVzLlxuICogdmFyIHRocm90dGxlZCA9IF8udGhyb3R0bGUocmVuZXdUb2tlbiwgMzAwMDAwLCB7ICd0cmFpbGluZyc6IGZhbHNlIH0pO1xuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIHRocm90dGxlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyB0aHJvdHRsZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIHRocm90dGxlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiB0aHJvdHRsZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsZWFkaW5nID0gdHJ1ZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gJ2xlYWRpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMubGVhZGluZyA6IGxlYWRpbmc7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuICByZXR1cm4gZGVib3VuY2UoZnVuYywgd2FpdCwge1xuICAgICdsZWFkaW5nJzogbGVhZGluZyxcbiAgICAnbWF4V2FpdCc6IHdhaXQsXG4gICAgJ3RyYWlsaW5nJzogdHJhaWxpbmdcbiAgfSk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgdGhlXG4gKiBbbGFuZ3VhZ2UgdHlwZV0oaHR0cDovL3d3dy5lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLWVjbWFzY3JpcHQtbGFuZ3VhZ2UtdHlwZXMpXG4gKiBvZiBgT2JqZWN0YC4gKGUuZy4gYXJyYXlzLCBmdW5jdGlvbnMsIG9iamVjdHMsIHJlZ2V4ZXMsIGBuZXcgTnVtYmVyKDApYCwgYW5kIGBuZXcgU3RyaW5nKCcnKWApXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYW4gb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3Qoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KF8ubm9vcCk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0KHZhbHVlKSB7XG4gIHZhciB0eXBlID0gdHlwZW9mIHZhbHVlO1xuICByZXR1cm4gISF2YWx1ZSAmJiAodHlwZSA9PSAnb2JqZWN0JyB8fCB0eXBlID09ICdmdW5jdGlvbicpO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLiBBIHZhbHVlIGlzIG9iamVjdC1saWtlIGlmIGl0J3Mgbm90IGBudWxsYFxuICogYW5kIGhhcyBhIGB0eXBlb2ZgIHJlc3VsdCBvZiBcIm9iamVjdFwiLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKF8ubm9vcCk7XG4gKiAvLyA9PiBmYWxzZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3RMaWtlKHZhbHVlKSB7XG4gIHJldHVybiAhIXZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PSAnb2JqZWN0Jztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBjbGFzc2lmaWVkIGFzIGEgYFN5bWJvbGAgcHJpbWl0aXZlIG9yIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIHN5bWJvbCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzU3ltYm9sKFN5bWJvbC5pdGVyYXRvcik7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc1N5bWJvbCgnYWJjJyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1N5bWJvbCh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09ICdzeW1ib2wnIHx8XG4gICAgKGlzT2JqZWN0TGlrZSh2YWx1ZSkgJiYgb2JqZWN0VG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gc3ltYm9sVGFnKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBgdmFsdWVgIHRvIGEgbnVtYmVyLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBwcm9jZXNzLlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgbnVtYmVyLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLnRvTnVtYmVyKDMuMik7XG4gKiAvLyA9PiAzLjJcbiAqXG4gKiBfLnRvTnVtYmVyKE51bWJlci5NSU5fVkFMVUUpO1xuICogLy8gPT4gNWUtMzI0XG4gKlxuICogXy50b051bWJlcihJbmZpbml0eSk7XG4gKiAvLyA9PiBJbmZpbml0eVxuICpcbiAqIF8udG9OdW1iZXIoJzMuMicpO1xuICogLy8gPT4gMy4yXG4gKi9cbmZ1bmN0aW9uIHRvTnVtYmVyKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gJ251bWJlcicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgaWYgKGlzU3ltYm9sKHZhbHVlKSkge1xuICAgIHJldHVybiBOQU47XG4gIH1cbiAgaWYgKGlzT2JqZWN0KHZhbHVlKSkge1xuICAgIHZhciBvdGhlciA9IHR5cGVvZiB2YWx1ZS52YWx1ZU9mID09ICdmdW5jdGlvbicgPyB2YWx1ZS52YWx1ZU9mKCkgOiB2YWx1ZTtcbiAgICB2YWx1ZSA9IGlzT2JqZWN0KG90aGVyKSA/IChvdGhlciArICcnKSA6IG90aGVyO1xuICB9XG4gIGlmICh0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWUgPT09IDAgPyB2YWx1ZSA6ICt2YWx1ZTtcbiAgfVxuICB2YWx1ZSA9IHZhbHVlLnJlcGxhY2UocmVUcmltLCAnJyk7XG4gIHZhciBpc0JpbmFyeSA9IHJlSXNCaW5hcnkudGVzdCh2YWx1ZSk7XG4gIHJldHVybiAoaXNCaW5hcnkgfHwgcmVJc09jdGFsLnRlc3QodmFsdWUpKVxuICAgID8gZnJlZVBhcnNlSW50KHZhbHVlLnNsaWNlKDIpLCBpc0JpbmFyeSA/IDIgOiA4KVxuICAgIDogKHJlSXNCYWRIZXgudGVzdCh2YWx1ZSkgPyBOQU4gOiArdmFsdWUpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHRocm90dGxlO1xuIiwidmFyIGc7XHJcblxyXG4vLyBUaGlzIHdvcmtzIGluIG5vbi1zdHJpY3QgbW9kZVxyXG5nID0gKGZ1bmN0aW9uKCkge1xyXG5cdHJldHVybiB0aGlzO1xyXG59KSgpO1xyXG5cclxudHJ5IHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIGV2YWwgaXMgYWxsb3dlZCAoc2VlIENTUClcclxuXHRnID0gZyB8fCBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCkgfHwgKDEsIGV2YWwpKFwidGhpc1wiKTtcclxufSBjYXRjaCAoZSkge1xyXG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXHJcblx0aWYgKHR5cGVvZiB3aW5kb3cgPT09IFwib2JqZWN0XCIpIGcgPSB3aW5kb3c7XHJcbn1cclxuXHJcbi8vIGcgY2FuIHN0aWxsIGJlIHVuZGVmaW5lZCwgYnV0IG5vdGhpbmcgdG8gZG8gYWJvdXQgaXQuLi5cclxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3NcclxuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxyXG5cclxubW9kdWxlLmV4cG9ydHMgPSBnO1xyXG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmltcG9ydCB7IGdldEVsZW1lbnRzRm9yU291cmNlTGluZSB9IGZyb20gJy4vc2Nyb2xsLXN5bmMnO1xuXG5leHBvcnQgY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG5cdHByaXZhdGUgX2N1cnJlbnQ6IGFueTtcblxuXHRvbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24obGluZTogbnVtYmVyKSB7XG5cdFx0Y29uc3QgeyBwcmV2aW91cyB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRcdHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcblx0fVxuXG5cdF91cGRhdGUoYmVmb3JlOiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdHRoaXMuX3VubWFya0FjdGl2ZUVsZW1lbnQodGhpcy5fY3VycmVudCk7XG5cdFx0dGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcblx0XHR0aGlzLl9jdXJyZW50ID0gYmVmb3JlO1xuXHR9XG5cblx0X3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgPSBlbGVtZW50LmNsYXNzTmFtZS5yZXBsYWNlKC9cXGJjb2RlLWFjdGl2ZS1saW5lXFxiL2csICcnKTtcblx0fVxuXG5cdF9tYXJrQWN0aXZlRWxlbWVudChlbGVtZW50OiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdGlmICghZWxlbWVudCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuXHR9XG59IiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBmdW5jdGlvbiBvbmNlRG9jdW1lbnRMb2FkZWQoZjogKCkgPT4gdm9pZCkge1xuXHRpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuXHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBmKTtcblx0fSBlbHNlIHtcblx0XHRmKCk7XG5cdH1cbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgQWN0aXZlTGluZU1hcmtlciB9IGZyb20gJy4vYWN0aXZlTGluZU1hcmtlcic7XG5pbXBvcnQgeyBvbmNlRG9jdW1lbnRMb2FkZWQgfSBmcm9tICcuL2V2ZW50cyc7XG5pbXBvcnQgeyBjcmVhdGVQb3N0ZXJGb3JWc0NvZGUgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5pbXBvcnQgeyBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCwgc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lIH0gZnJvbSAnLi9zY3JvbGwtc3luYyc7XG5pbXBvcnQgeyBnZXRTZXR0aW5ncywgZ2V0RGF0YSB9IGZyb20gJy4vc2V0dGluZ3MnO1xuaW1wb3J0IHRocm90dGxlID0gcmVxdWlyZSgnbG9kYXNoLnRocm90dGxlJyk7XG5cbmRlY2xhcmUgdmFyIGFjcXVpcmVWc0NvZGVBcGk6IGFueTtcblxudmFyIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbmNvbnN0IG1hcmtlciA9IG5ldyBBY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IGdldFNldHRpbmdzKCk7XG5cbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcblxuLy8gU2V0IFZTIENvZGUgc3RhdGVcbmNvbnN0IHN0YXRlID0gZ2V0RGF0YSgnZGF0YS1zdGF0ZScpO1xudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblxuY29uc3QgbWVzc2FnaW5nID0gY3JlYXRlUG9zdGVyRm9yVnNDb2RlKHZzY29kZSk7XG5cbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG5cdHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5cbm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG5cdGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuXHRcdHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdGlmICghaXNOYU4oaW5pdGlhbExpbmUpKSB7XG5cdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdH1cblx0XHR9LCAwKTtcblx0fVxufSk7XG5cbmNvbnN0IG9uVXBkYXRlVmlldyA9ICgoKSA9PiB7XG5cdGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmU6IG51bWJlcikgPT4ge1xuXHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG5cdH0sIDUwKTtcblxuXHRyZXR1cm4gKGxpbmU6IG51bWJlciwgc2V0dGluZ3M6IGFueSkgPT4ge1xuXHRcdGlmICghaXNOYU4obGluZSkpIHtcblx0XHRcdHNldHRpbmdzLmxpbmUgPSBsaW5lO1xuXHRcdFx0ZG9TY3JvbGwobGluZSk7XG5cdFx0fVxuXHR9O1xufSkoKTtcblxubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG5cdGNvbnN0IGltYWdlSW5mbzogeyBpZDogc3RyaW5nLCBoZWlnaHQ6IG51bWJlciwgd2lkdGg6IG51bWJlciB9W10gPSBbXTtcblx0bGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcblx0aWYgKGltYWdlcykge1xuXHRcdGxldCBpO1xuXHRcdGZvciAoaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyBpKyspIHtcblx0XHRcdGNvbnN0IGltZyA9IGltYWdlc1tpXTtcblxuXHRcdFx0aWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuXHRcdFx0XHRpbWcuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHRpbWFnZUluZm8ucHVzaCh7XG5cdFx0XHRcdGlkOiBpbWcuaWQsXG5cdFx0XHRcdGhlaWdodDogaW1nLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IGltZy53aWR0aFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuXHR9XG59LCA1MCk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG5cdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZXZlbnQgPT4ge1xuXHRpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG5cdFx0Y2FzZSAnb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uJzpcblx0XHRcdG1hcmtlci5vbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24oZXZlbnQuZGF0YS5saW5lKTtcblx0XHRcdGJyZWFrO1xuXG5cdFx0Y2FzZSAndXBkYXRlVmlldyc6XG5cdFx0XHRvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG5cdFx0XHRicmVhaztcblx0fVxufSwgZmFsc2UpO1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcblx0aWYgKCFzZXR0aW5ncy5kb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHQvLyBJZ25vcmUgY2xpY2tzIG9uIGxpbmtzXG5cdGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lID09PSAnQScpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdH1cblxuXHRjb25zdCBvZmZzZXQgPSBldmVudC5wYWdlWTtcblx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcblx0fVxufSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIWV2ZW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0bGV0IG5vZGU6IGFueSA9IGV2ZW50LnRhcmdldDtcblx0d2hpbGUgKG5vZGUpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuXHRcdFx0aWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKG5vZGUuaHJlZi5zdGFydHNXaXRoKCdmaWxlOi8vJykgfHwgbm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ3ZzY29kZS1yZXNvdXJjZTonKSkge1xuXHRcdFx0XHRjb25zdCBbcGF0aCwgZnJhZ21lbnRdID0gbm9kZS5ocmVmLnJlcGxhY2UoL14oZmlsZTpcXC9cXC98dnNjb2RlLXJlc291cmNlOikvaSwgJycpLnNwbGl0KCcjJyk7XG5cdFx0XHRcdG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnY2xpY2tMaW5rJywgeyBwYXRoLCBmcmFnbWVudCB9KTtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0ZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXHRcdG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG5cdH1cbn0sIHRydWUpO1xuXG5pZiAoc2V0dGluZ3Muc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXcpIHtcblx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcblx0XHRpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcblx0XHRcdHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IGxpbmUgPSBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG5cdFx0XHRpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LCA1MCkpO1xufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE1lc3NhZ2VQb3N0ZXIge1xuXHQvKipcblx0ICogUG9zdCBhIG1lc3NhZ2UgdG8gdGhlIG1hcmtkb3duIGV4dGVuc2lvblxuXHQgKi9cblx0cG9zdE1lc3NhZ2UodHlwZTogc3RyaW5nLCBib2R5OiBvYmplY3QpOiB2b2lkO1xufVxuXG5leHBvcnQgY29uc3QgY3JlYXRlUG9zdGVyRm9yVnNDb2RlID0gKHZzY29kZTogYW55KSA9PiB7XG5cdHJldHVybiBuZXcgY2xhc3MgaW1wbGVtZW50cyBNZXNzYWdlUG9zdGVyIHtcblx0XHRwb3N0TWVzc2FnZSh0eXBlOiBzdHJpbmcsIGJvZHk6IG9iamVjdCk6IHZvaWQge1xuXHRcdFx0dnNjb2RlLnBvc3RNZXNzYWdlKHtcblx0XHRcdFx0dHlwZSxcblx0XHRcdFx0c291cmNlOiBnZXRTZXR0aW5ncygpLnNvdXJjZSxcblx0XHRcdFx0Ym9keVxuXHRcdFx0fSk7XG5cdFx0fVxuXHR9O1xufTtcblxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IGdldFNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5cblxuZnVuY3Rpb24gY2xhbXAobWluOiBudW1iZXIsIG1heDogbnVtYmVyLCB2YWx1ZTogbnVtYmVyKSB7XG5cdHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgdmFsdWUpKTtcbn1cblxuZnVuY3Rpb24gY2xhbXBMaW5lKGxpbmU6IG51bWJlcikge1xuXHRyZXR1cm4gY2xhbXAoMCwgZ2V0U2V0dGluZ3MoKS5saW5lQ291bnQgLSAxLCBsaW5lKTtcbn1cblxuXG5leHBvcnQgaW50ZXJmYWNlIENvZGVMaW5lRWxlbWVudCB7XG5cdGVsZW1lbnQ6IEhUTUxFbGVtZW50O1xuXHRsaW5lOiBudW1iZXI7XG59XG5cbmNvbnN0IGdldENvZGVMaW5lRWxlbWVudHMgPSAoKCkgPT4ge1xuXHRsZXQgZWxlbWVudHM6IENvZGVMaW5lRWxlbWVudFtdO1xuXHRyZXR1cm4gKCkgPT4ge1xuXHRcdGlmICghZWxlbWVudHMpIHtcblx0XHRcdGVsZW1lbnRzID0gQXJyYXkucHJvdG90eXBlLm1hcC5jYWxsKFxuXHRcdFx0XHRkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCdjb2RlLWxpbmUnKSxcblx0XHRcdFx0KGVsZW1lbnQ6IGFueSkgPT4ge1xuXHRcdFx0XHRcdGNvbnN0IGxpbmUgPSArZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbGluZScpO1xuXHRcdFx0XHRcdHJldHVybiB7IGVsZW1lbnQsIGxpbmUgfTtcblx0XHRcdFx0fSlcblx0XHRcdFx0LmZpbHRlcigoeDogYW55KSA9PiAhaXNOYU4oeC5saW5lKSk7XG5cdFx0fVxuXHRcdHJldHVybiBlbGVtZW50cztcblx0fTtcbn0pKCk7XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKHRhcmdldExpbmU6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuXHRjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcblx0bGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcblx0Zm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuXHRcdGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG5cdFx0XHRyZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuXHRcdH1cblx0XHRlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuXHRcdFx0cmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG5cdFx0fVxuXHRcdHByZXZpb3VzID0gZW50cnk7XG5cdH1cblx0cmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG5cdGNvbnN0IHBvc2l0aW9uID0gb2Zmc2V0IC0gd2luZG93LnNjcm9sbFk7XG5cdGxldCBsbyA9IC0xO1xuXHRsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuXHR3aGlsZSAobG8gKyAxIDwgaGkpIHtcblx0XHRjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuXHRcdGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcblx0XHRcdGhpID0gbWlkO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGxvID0gbWlkO1xuXHRcdH1cblx0fVxuXHRjb25zdCBoaUVsZW1lbnQgPSBsaW5lc1toaV07XG5cdGNvbnN0IGhpQm91bmRzID0gaGlFbGVtZW50LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG5cdFx0Y29uc3QgbG9FbGVtZW50ID0gbGluZXNbbG9dO1xuXHRcdHJldHVybiB7IHByZXZpb3VzOiBsb0VsZW1lbnQsIG5leHQ6IGhpRWxlbWVudCB9O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmU6IG51bWJlcikge1xuXHRjb25zdCB7IHByZXZpb3VzLCBuZXh0IH0gPSBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG5cdGlmIChwcmV2aW91cyAmJiBnZXRTZXR0aW5ncygpLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG5cdFx0bGV0IHNjcm9sbFRvID0gMDtcblx0XHRjb25zdCByZWN0ID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuXHRcdGlmIChuZXh0ICYmIG5leHQubGluZSAhPT0gcHJldmlvdXMubGluZSkge1xuXHRcdFx0Ly8gQmV0d2VlbiB0d28gZWxlbWVudHMuIEdvIHRvIHBlcmNlbnRhZ2Ugb2Zmc2V0IGJldHdlZW4gdGhlbS5cblx0XHRcdGNvbnN0IGJldHdlZW5Qcm9ncmVzcyA9IChsaW5lIC0gcHJldmlvdXMubGluZSkgLyAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0XHRjb25zdCBlbGVtZW50T2Zmc2V0ID0gbmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzVG9wO1xuXHRcdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIGJldHdlZW5Qcm9ncmVzcyAqIGVsZW1lbnRPZmZzZXQ7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcDtcblx0XHR9XG5cdFx0d2luZG93LnNjcm9sbCgwLCBNYXRoLm1heCgxLCB3aW5kb3cuc2Nyb2xsWSArIHNjcm9sbFRvKSk7XG5cdH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKSB7XG5cdGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpO1xuXHRpZiAocHJldmlvdXMpIHtcblx0XHRjb25zdCBwcmV2aW91c0JvdW5kcyA9IHByZXZpb3VzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdFx0Y29uc3Qgb2Zmc2V0RnJvbVByZXZpb3VzID0gKG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcblx0XHRpZiAobmV4dCkge1xuXHRcdFx0Y29uc3QgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgPSBvZmZzZXRGcm9tUHJldmlvdXMgLyAobmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG5cdFx0XHRjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzICogKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuXHRcdFx0cmV0dXJuIGNsYW1wTGluZShsaW5lKTtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRjb25zdCBwcm9ncmVzc1dpdGhpbkVsZW1lbnQgPSBvZmZzZXRGcm9tUHJldmlvdXMgLyAocHJldmlvdXNCb3VuZHMuaGVpZ2h0KTtcblx0XHRcdGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50O1xuXHRcdFx0cmV0dXJuIGNsYW1wTGluZShsaW5lKTtcblx0XHR9XG5cdH1cblx0cmV0dXJuIG51bGw7XG59XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuZXhwb3J0IGludGVyZmFjZSBQcmV2aWV3U2V0dGluZ3Mge1xuXHRzb3VyY2U6IHN0cmluZztcblx0bGluZTogbnVtYmVyO1xuXHRsaW5lQ291bnQ6IG51bWJlcjtcblx0c2Nyb2xsUHJldmlld1dpdGhFZGl0b3I/OiBib29sZWFuO1xuXHRzY3JvbGxFZGl0b3JXaXRoUHJldmlldzogYm9vbGVhbjtcblx0ZGlzYWJsZVNlY3VyaXR5V2FybmluZ3M6IGJvb2xlYW47XG5cdGRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcjogYm9vbGVhbjtcbn1cblxubGV0IGNhY2hlZFNldHRpbmdzOiBQcmV2aWV3U2V0dGluZ3MgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXREYXRhKGtleTogc3RyaW5nKTogUHJldmlld1NldHRpbmdzIHtcblx0Y29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG5cdGlmIChlbGVtZW50KSB7XG5cdFx0Y29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG5cdFx0aWYgKGRhdGEpIHtcblx0XHRcdHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuXHRcdH1cblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGxvYWQgZGF0YSBmb3IgJHtrZXl9YCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTZXR0aW5ncygpOiBQcmV2aWV3U2V0dGluZ3Mge1xuXHRpZiAoY2FjaGVkU2V0dGluZ3MpIHtcblx0XHRyZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG5cdH1cblxuXHRjYWNoZWRTZXR0aW5ncyA9IGdldERhdGEoJ2RhdGEtc2V0dGluZ3MnKTtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0dGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzZXR0aW5ncycpO1xufVxuIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsT0FBTztBQUNsQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLDhDQUE4QyxrQkFBa0I7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsb0JBQW9CO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsRUFBRTtBQUNiLGFBQWEsUUFBUTtBQUNyQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxFQUFFO0FBQ2IsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOzs7Ozs7Ozs7Ozs7O0FDdGJBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNENBQTRDOztBQUU1Qzs7Ozs7Ozs7Ozs7Ozs7O0FDbkJBOzs7Z0dBR2dHO0FBQ2hHLCtGQUF5RDtBQUV6RDtJQUdDLDhCQUE4QixDQUFDLElBQVk7UUFDMUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLHNDQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxJQUFJLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRUQsT0FBTyxDQUFDLE1BQStCO1FBQ3RDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQ3hCLENBQUM7SUFFRCxvQkFBb0IsQ0FBQyxPQUFnQztRQUNwRCxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDZCxNQUFNLENBQUM7UUFDUixDQUFDO1FBQ0QsT0FBTyxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUM1RSxDQUFDO0lBRUQsa0JBQWtCLENBQUMsT0FBZ0M7UUFDbEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ2QsTUFBTSxDQUFDO1FBQ1IsQ0FBQztRQUNELE9BQU8sQ0FBQyxTQUFTLElBQUksbUJBQW1CLENBQUM7SUFDMUMsQ0FBQztDQUNEO0FBM0JELDRDQTJCQzs7Ozs7Ozs7Ozs7Ozs7QUNqQ0Q7OztnR0FHZ0c7O0FBRWhHLDRCQUFtQyxDQUFhO0lBQy9DLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEtBQUssU0FBUyxJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssZUFBZSxDQUFDLENBQUMsQ0FBQztRQUNsRixRQUFRLENBQUMsZ0JBQWdCLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxFQUFFLENBQUM7SUFDTCxDQUFDO0FBQ0YsQ0FBQztBQU5ELGdEQU1DOzs7Ozs7Ozs7Ozs7OztBQ1hEOzs7Z0dBR2dHOztBQUVoRyw4R0FBc0Q7QUFDdEQsZ0ZBQThDO0FBQzlDLHlGQUFvRDtBQUNwRCwrRkFBMkY7QUFDM0Ysc0ZBQWtEO0FBQ2xELHVHQUE2QztBQUk3QyxJQUFJLGNBQWMsR0FBRyxJQUFJLENBQUM7QUFDMUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxtQ0FBZ0IsRUFBRSxDQUFDO0FBQ3RDLE1BQU0sUUFBUSxHQUFHLHNCQUFXLEVBQUUsQ0FBQztBQUUvQixNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsRUFBRSxDQUFDO0FBRWxDLG9CQUFvQjtBQUNwQixNQUFNLEtBQUssR0FBRyxrQkFBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO0FBQ3BDLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7QUFFdkIsTUFBTSxTQUFTLEdBQUcsaUNBQXFCLENBQUMsTUFBTSxDQUFDLENBQUM7QUFFaEQsTUFBTSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDdkMsTUFBTSxDQUFDLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUVoRCxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsRUFBRTtJQUNwQixnQkFBZ0IsRUFBRSxDQUFDO0FBQ3BCLENBQUMsQ0FBQztBQUVGLDJCQUFrQixDQUFDLEdBQUcsRUFBRTtJQUN2QixFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDZixNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7WUFDbkMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUN6QixjQUFjLEdBQUcsSUFBSSxDQUFDO2dCQUN0QixzQ0FBd0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUN2QyxDQUFDO1FBQ0YsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztBQUNGLENBQUMsQ0FBQyxDQUFDO0FBRUgsTUFBTSxZQUFZLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDMUIsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLENBQUMsSUFBWSxFQUFFLEVBQUU7UUFDMUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUN0QixzQ0FBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNoQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFUCxNQUFNLENBQUMsQ0FBQyxJQUFZLEVBQUUsUUFBYSxFQUFFLEVBQUU7UUFDdEMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2xCLFFBQVEsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1lBQ3JCLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNoQixDQUFDO0lBQ0YsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMLElBQUksZ0JBQWdCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRTtJQUNwQyxNQUFNLFNBQVMsR0FBb0QsRUFBRSxDQUFDO0lBQ3RFLElBQUksTUFBTSxHQUFHLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNsRCxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ1osSUFBSSxDQUFDLENBQUM7UUFDTixHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDcEMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRXRCLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDdkMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDakMsQ0FBQztZQUVELFNBQVMsQ0FBQyxJQUFJLENBQUM7Z0JBQ2QsRUFBRSxFQUFFLEdBQUcsQ0FBQyxFQUFFO2dCQUNWLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTTtnQkFDbEIsS0FBSyxFQUFFLEdBQUcsQ0FBQyxLQUFLO2FBQ2hCLENBQUMsQ0FBQztRQUNKLENBQUM7UUFFRCxTQUFTLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQ3JELENBQUM7QUFDRixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFFUCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUN0QyxjQUFjLEdBQUcsSUFBSSxDQUFDO0lBQ3RCLGdCQUFnQixFQUFFLENBQUM7QUFDcEIsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBRVQsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsRUFBRTtJQUMxQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUMzQyxNQUFNLENBQUM7SUFDUixDQUFDO0lBRUQsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3pCLEtBQUssZ0NBQWdDO1lBQ3BDLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZELEtBQUssQ0FBQztRQUVQLEtBQUssWUFBWTtZQUNoQixZQUFZLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDeEMsS0FBSyxDQUFDO0lBQ1IsQ0FBQztBQUNGLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztBQUVWLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLEVBQUU7SUFDN0MsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsMkJBQTJCLENBQUMsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCx5QkFBeUI7SUFDekIsR0FBRyxDQUFDLENBQUMsSUFBSSxJQUFJLEdBQUcsS0FBSyxDQUFDLE1BQXFCLEVBQUUsSUFBSSxFQUFFLElBQUksR0FBRyxJQUFJLENBQUMsVUFBeUIsRUFBRSxDQUFDO1FBQzFGLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUMxQixNQUFNLENBQUM7UUFDUixDQUFDO0lBQ0YsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUM7SUFDM0IsTUFBTSxJQUFJLEdBQUcsOENBQWdDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdEQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM5QyxTQUFTLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUMvRCxDQUFDO0FBQ0YsQ0FBQyxDQUFDLENBQUM7QUFFSCxRQUFRLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxFQUFFO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNaLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCxJQUFJLElBQUksR0FBUSxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQzdCLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDYixFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3ZELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDL0MsS0FBSyxDQUFDO1lBQ1AsQ0FBQztZQUNELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNqRixNQUFNLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGdDQUFnQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDNUYsU0FBUyxDQUFDLFdBQVcsQ0FBQyxXQUFXLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDdkQsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUN2QixLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7Z0JBQ3hCLEtBQUssQ0FBQztZQUNQLENBQUM7WUFDRCxLQUFLLENBQUM7UUFDUCxDQUFDO1FBQ0QsSUFBSSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDeEIsQ0FBQztBQUNGLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUVULEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLENBQUM7SUFDdEMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQy9DLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7WUFDcEIsY0FBYyxHQUFHLEtBQUssQ0FBQztRQUN4QixDQUFDO1FBQUMsSUFBSSxDQUFDLENBQUM7WUFDUCxNQUFNLElBQUksR0FBRyw4Q0FBZ0MsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDOUQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDOUMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxZQUFZLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQy9DLENBQUM7UUFDRixDQUFDO0lBQ0YsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDVCxDQUFDOzs7Ozs7Ozs7Ozs7OztBQzdKRDs7O2dHQUdnRzs7QUFFaEcsc0ZBQXlDO0FBUzVCLDZCQUFxQixHQUFHLENBQUMsTUFBVyxFQUFFLEVBQUU7SUFDcEQsTUFBTSxDQUFDLElBQUk7UUFDVixXQUFXLENBQUMsSUFBWSxFQUFFLElBQVk7WUFDckMsTUFBTSxDQUFDLFdBQVcsQ0FBQztnQkFDbEIsSUFBSTtnQkFDSixNQUFNLEVBQUUsc0JBQVcsRUFBRSxDQUFDLE1BQU07Z0JBQzVCLElBQUk7YUFDSixDQUFDLENBQUM7UUFDSixDQUFDO0tBQ0QsQ0FBQztBQUNILENBQUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUN4QkY7OztnR0FHZ0c7O0FBRWhHLHNGQUF5QztBQUd6QyxlQUFlLEdBQVcsRUFBRSxHQUFXLEVBQUUsS0FBYTtJQUNyRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBRUQsbUJBQW1CLElBQVk7SUFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsc0JBQVcsRUFBRSxDQUFDLFNBQVMsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQVFELE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDakMsSUFBSSxRQUEyQixDQUFDO0lBQ2hDLE1BQU0sQ0FBQyxHQUFHLEVBQUU7UUFDWCxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDZixRQUFRLEdBQUcsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLFFBQVEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUNqRixRQUFRLENBQUMsc0JBQXNCLENBQUMsV0FBVyxDQUFDLEVBQzVDLENBQUMsT0FBWSxFQUFFLEVBQUU7Z0JBQ2hCLE1BQU0sSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDaEQsTUFBTSxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDO1lBQzFCLENBQUMsQ0FBQztpQkFDRCxNQUFNLENBQUMsQ0FBQyxDQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdkMsQ0FBQztRQUNELE1BQU0sQ0FBQyxRQUFRLENBQUM7SUFDakIsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMOzs7OztHQUtHO0FBQ0gsa0NBQXlDLFVBQWtCO0lBQzFELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDMUMsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sS0FBSyxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDM0IsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBQy9CLE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO1FBQzdDLENBQUM7UUFBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBQ3BDLE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFDbEMsQ0FBQztRQUNELFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDbEIsQ0FBQztJQUNELE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDO0FBQ3JCLENBQUM7QUFiRCw0REFhQztBQUVEOztHQUVHO0FBQ0gscUNBQTRDLE1BQWM7SUFDekQsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxNQUFNLFFBQVEsR0FBRyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQztJQUN6QyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNaLElBQUksRUFBRSxHQUFHLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQzFCLE9BQU8sRUFBRSxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQztRQUNwQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUMxRCxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxHQUFHLE1BQU0sQ0FBQyxNQUFNLElBQUksUUFBUSxDQUFDLENBQUMsQ0FBQztZQUM1QyxFQUFFLEdBQUcsR0FBRyxDQUFDO1FBQ1YsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsRUFBRSxHQUFHLEdBQUcsQ0FBQztRQUNWLENBQUM7SUFDRixDQUFDO0lBQ0QsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzVCLE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUMzRCxFQUFFLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxJQUFJLFFBQVEsQ0FBQyxHQUFHLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUN4QyxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDNUIsTUFBTSxDQUFDLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7SUFDakQsQ0FBQztJQUNELE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBdEJELGtFQXNCQztBQUVEOztHQUVHO0FBQ0gsa0NBQXlDLElBQVk7SUFDcEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxzQkFBVyxFQUFFLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCxFQUFFLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNmLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNqQyxNQUFNLENBQUM7SUFDUixDQUFDO0lBRUQsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsR0FBRyx3QkFBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMxRCxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDZixNQUFNLENBQUM7SUFDUixDQUFDO0lBQ0QsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO0lBQ2pCLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUN0RCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO0lBQzdCLEVBQUUsQ0FBQyxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3pDLDhEQUE4RDtRQUM5RCxNQUFNLGVBQWUsR0FBRyxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3RSxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLFdBQVcsQ0FBQztRQUM3RSxRQUFRLEdBQUcsV0FBVyxHQUFHLGVBQWUsR0FBRyxhQUFhLENBQUM7SUFDMUQsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ1AsUUFBUSxHQUFHLFdBQVcsQ0FBQztJQUN4QixDQUFDO0lBQ0QsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxPQUFPLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQztBQUN2RSxDQUFDO0FBMUJELDREQTBCQztBQUVELDBDQUFpRCxNQUFjO0lBQzlELE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEdBQUcsMkJBQTJCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDL0QsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUNkLE1BQU0sY0FBYyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUNoRSxNQUFNLGtCQUFrQixHQUFHLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzFFLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDVixNQUFNLHVCQUF1QixHQUFHLGtCQUFrQixHQUFHLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDLEdBQUcsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDckgsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksR0FBRyx1QkFBdUIsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ25GLE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsTUFBTSxxQkFBcUIsR0FBRyxrQkFBa0IsR0FBRyxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUMzRSxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxHQUFHLHFCQUFxQixDQUFDO1lBQ25ELE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztJQUNGLENBQUM7SUFDRCxNQUFNLENBQUMsSUFBSSxDQUFDO0FBQ2IsQ0FBQztBQWpCRCw0RUFpQkM7Ozs7Ozs7Ozs7Ozs7O0FDdElEOzs7Z0dBR2dHOztBQVloRyxJQUFJLGNBQWMsR0FBZ0MsU0FBUyxDQUFDO0FBRTVELGlCQUF3QixHQUFXO0lBQ2xDLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUN4RSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ2IsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ1YsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekIsQ0FBQztJQUNGLENBQUM7SUFFRCxNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixHQUFHLEVBQUUsQ0FBQyxDQUFDO0FBQ25ELENBQUM7QUFWRCwwQkFVQztBQUVEO0lBQ0MsRUFBRSxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQztRQUNwQixNQUFNLENBQUMsY0FBYyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxjQUFjLEdBQUcsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7UUFDcEIsTUFBTSxDQUFDLGNBQWMsQ0FBQztJQUN2QixDQUFDO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO0FBQzVDLENBQUM7QUFYRCxrQ0FXQyIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwge1xuIFx0XHRcdFx0Y29uZmlndXJhYmxlOiBmYWxzZSxcbiBcdFx0XHRcdGVudW1lcmFibGU6IHRydWUsXG4gXHRcdFx0XHRnZXQ6IGdldHRlclxuIFx0XHRcdH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3ByZXZpZXctc3JjL2luZGV4LnRzXCIpO1xuIiwiLyoqXG4gKiBsb2Rhc2ggKEN1c3RvbSBCdWlsZCkgPGh0dHBzOi8vbG9kYXNoLmNvbS8+XG4gKiBCdWlsZDogYGxvZGFzaCBtb2R1bGFyaXplIGV4cG9ydHM9XCJucG1cIiAtbyAuL2BcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzIDxodHRwczovL2pxdWVyeS5vcmcvPlxuICogUmVsZWFzZWQgdW5kZXIgTUlUIGxpY2Vuc2UgPGh0dHBzOi8vbG9kYXNoLmNvbS9saWNlbnNlPlxuICogQmFzZWQgb24gVW5kZXJzY29yZS5qcyAxLjguMyA8aHR0cDovL3VuZGVyc2NvcmVqcy5vcmcvTElDRU5TRT5cbiAqIENvcHlyaWdodCBKZXJlbXkgQXNoa2VuYXMsIERvY3VtZW50Q2xvdWQgYW5kIEludmVzdGlnYXRpdmUgUmVwb3J0ZXJzICYgRWRpdG9yc1xuICovXG5cbi8qKiBVc2VkIGFzIHRoZSBgVHlwZUVycm9yYCBtZXNzYWdlIGZvciBcIkZ1bmN0aW9uc1wiIG1ldGhvZHMuICovXG52YXIgRlVOQ19FUlJPUl9URVhUID0gJ0V4cGVjdGVkIGEgZnVuY3Rpb24nO1xuXG4vKiogVXNlZCBhcyByZWZlcmVuY2VzIGZvciB2YXJpb3VzIGBOdW1iZXJgIGNvbnN0YW50cy4gKi9cbnZhciBOQU4gPSAwIC8gMDtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIHN5bWJvbFRhZyA9ICdbb2JqZWN0IFN5bWJvbF0nO1xuXG4vKiogVXNlZCB0byBtYXRjaCBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLiAqL1xudmFyIHJlVHJpbSA9IC9eXFxzK3xcXHMrJC9nO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmFkIHNpZ25lZCBoZXhhZGVjaW1hbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCYWRIZXggPSAvXlstK10weFswLTlhLWZdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJpbmFyeSBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCaW5hcnkgPSAvXjBiWzAxXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBvY3RhbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNPY3RhbCA9IC9eMG9bMC03XSskL2k7XG5cbi8qKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyB3aXRob3V0IGEgZGVwZW5kZW5jeSBvbiBgcm9vdGAuICovXG52YXIgZnJlZVBhcnNlSW50ID0gcGFyc2VJbnQ7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgZ2xvYmFsYCBmcm9tIE5vZGUuanMuICovXG52YXIgZnJlZUdsb2JhbCA9IHR5cGVvZiBnbG9iYWwgPT0gJ29iamVjdCcgJiYgZ2xvYmFsICYmIGdsb2JhbC5PYmplY3QgPT09IE9iamVjdCAmJiBnbG9iYWw7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgc2VsZmAuICovXG52YXIgZnJlZVNlbGYgPSB0eXBlb2Ygc2VsZiA9PSAnb2JqZWN0JyAmJiBzZWxmICYmIHNlbGYuT2JqZWN0ID09PSBPYmplY3QgJiYgc2VsZjtcblxuLyoqIFVzZWQgYXMgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvYmplY3QuICovXG52YXIgcm9vdCA9IGZyZWVHbG9iYWwgfHwgZnJlZVNlbGYgfHwgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblxuLyoqIFVzZWQgZm9yIGJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlXG4gKiBbYHRvU3RyaW5nVGFnYF0oaHR0cDovL2VjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9iamVjdFRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heCxcbiAgICBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqXG4gKiBHZXRzIHRoZSB0aW1lc3RhbXAgb2YgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdGhhdCBoYXZlIGVsYXBzZWQgc2luY2VcbiAqIHRoZSBVbml4IGVwb2NoICgxIEphbnVhcnkgMTk3MCAwMDowMDowMCBVVEMpLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMi40LjBcbiAqIEBjYXRlZ29yeSBEYXRlXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSB0aW1lc3RhbXAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uZGVmZXIoZnVuY3Rpb24oc3RhbXApIHtcbiAqICAgY29uc29sZS5sb2coXy5ub3coKSAtIHN0YW1wKTtcbiAqIH0sIF8ubm93KCkpO1xuICogLy8gPT4gTG9ncyB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBpdCB0b29rIGZvciB0aGUgZGVmZXJyZWQgaW52b2NhdGlvbi5cbiAqL1xudmFyIG5vdyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gcm9vdC5EYXRlLm5vdygpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVib3VuY2VkIGZ1bmN0aW9uIHRoYXQgZGVsYXlzIGludm9raW5nIGBmdW5jYCB1bnRpbCBhZnRlciBgd2FpdGBcbiAqIG1pbGxpc2Vjb25kcyBoYXZlIGVsYXBzZWQgc2luY2UgdGhlIGxhc3QgdGltZSB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHdhc1xuICogaW52b2tlZC4gVGhlIGRlYm91bmNlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGAgbWV0aG9kIHRvIGNhbmNlbFxuICogZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG8gaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uXG4gKiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYCBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGVcbiAqIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YCB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWRcbiAqIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnRcbiAqIGNhbGxzIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gcmV0dXJuIHRoZSByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgXG4gKiBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLmRlYm91bmNlYCBhbmQgYF8udGhyb3R0bGVgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gZGVib3VuY2UuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gZGVsYXkuXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz1mYWxzZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMubWF4V2FpdF1cbiAqICBUaGUgbWF4aW11bSB0aW1lIGBmdW5jYCBpcyBhbGxvd2VkIHRvIGJlIGRlbGF5ZWQgYmVmb3JlIGl0J3MgaW52b2tlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZGVib3VuY2VkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBjb3N0bHkgY2FsY3VsYXRpb25zIHdoaWxlIHRoZSB3aW5kb3cgc2l6ZSBpcyBpbiBmbHV4LlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Jlc2l6ZScsIF8uZGVib3VuY2UoY2FsY3VsYXRlTGF5b3V0LCAxNTApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHNlbmRNYWlsYCB3aGVuIGNsaWNrZWQsIGRlYm91bmNpbmcgc3Vic2VxdWVudCBjYWxscy5cbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCBfLmRlYm91bmNlKHNlbmRNYWlsLCAzMDAsIHtcbiAqICAgJ2xlYWRpbmcnOiB0cnVlLFxuICogICAndHJhaWxpbmcnOiBmYWxzZVxuICogfSkpO1xuICpcbiAqIC8vIEVuc3VyZSBgYmF0Y2hMb2dgIGlzIGludm9rZWQgb25jZSBhZnRlciAxIHNlY29uZCBvZiBkZWJvdW5jZWQgY2FsbHMuXG4gKiB2YXIgZGVib3VuY2VkID0gXy5kZWJvdW5jZShiYXRjaExvZywgMjUwLCB7ICdtYXhXYWl0JzogMTAwMCB9KTtcbiAqIHZhciBzb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2UoJy9zdHJlYW0nKTtcbiAqIGpRdWVyeShzb3VyY2UpLm9uKCdtZXNzYWdlJywgZGVib3VuY2VkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIGRlYm91bmNlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgZGVib3VuY2VkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIGRlYm91bmNlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxhc3RBcmdzLFxuICAgICAgbGFzdFRoaXMsXG4gICAgICBtYXhXYWl0LFxuICAgICAgcmVzdWx0LFxuICAgICAgdGltZXJJZCxcbiAgICAgIGxhc3RDYWxsVGltZSxcbiAgICAgIGxhc3RJbnZva2VUaW1lID0gMCxcbiAgICAgIGxlYWRpbmcgPSBmYWxzZSxcbiAgICAgIG1heGluZyA9IGZhbHNlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIHdhaXQgPSB0b051bWJlcih3YWl0KSB8fCAwO1xuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gISFvcHRpb25zLmxlYWRpbmc7XG4gICAgbWF4aW5nID0gJ21heFdhaXQnIGluIG9wdGlvbnM7XG4gICAgbWF4V2FpdCA9IG1heGluZyA/IG5hdGl2ZU1heCh0b051bWJlcihvcHRpb25zLm1heFdhaXQpIHx8IDAsIHdhaXQpIDogbWF4V2FpdDtcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG5cbiAgZnVuY3Rpb24gaW52b2tlRnVuYyh0aW1lKSB7XG4gICAgdmFyIGFyZ3MgPSBsYXN0QXJncyxcbiAgICAgICAgdGhpc0FyZyA9IGxhc3RUaGlzO1xuXG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgcmVzdWx0ID0gZnVuYy5hcHBseSh0aGlzQXJnLCBhcmdzKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbGVhZGluZ0VkZ2UodGltZSkge1xuICAgIC8vIFJlc2V0IGFueSBgbWF4V2FpdGAgdGltZXIuXG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIC8vIFN0YXJ0IHRoZSB0aW1lciBmb3IgdGhlIHRyYWlsaW5nIGVkZ2UuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAvLyBJbnZva2UgdGhlIGxlYWRpbmcgZWRnZS5cbiAgICByZXR1cm4gbGVhZGluZyA/IGludm9rZUZ1bmModGltZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiByZW1haW5pbmdXYWl0KHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lLFxuICAgICAgICByZXN1bHQgPSB3YWl0IC0gdGltZVNpbmNlTGFzdENhbGw7XG5cbiAgICByZXR1cm4gbWF4aW5nID8gbmF0aXZlTWluKHJlc3VsdCwgbWF4V2FpdCAtIHRpbWVTaW5jZUxhc3RJbnZva2UpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gc2hvdWxkSW52b2tlKHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lO1xuXG4gICAgLy8gRWl0aGVyIHRoaXMgaXMgdGhlIGZpcnN0IGNhbGwsIGFjdGl2aXR5IGhhcyBzdG9wcGVkIGFuZCB3ZSdyZSBhdCB0aGVcbiAgICAvLyB0cmFpbGluZyBlZGdlLCB0aGUgc3lzdGVtIHRpbWUgaGFzIGdvbmUgYmFja3dhcmRzIGFuZCB3ZSdyZSB0cmVhdGluZ1xuICAgIC8vIGl0IGFzIHRoZSB0cmFpbGluZyBlZGdlLCBvciB3ZSd2ZSBoaXQgdGhlIGBtYXhXYWl0YCBsaW1pdC5cbiAgICByZXR1cm4gKGxhc3RDYWxsVGltZSA9PT0gdW5kZWZpbmVkIHx8ICh0aW1lU2luY2VMYXN0Q2FsbCA+PSB3YWl0KSB8fFxuICAgICAgKHRpbWVTaW5jZUxhc3RDYWxsIDwgMCkgfHwgKG1heGluZyAmJiB0aW1lU2luY2VMYXN0SW52b2tlID49IG1heFdhaXQpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRpbWVyRXhwaXJlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpO1xuICAgIGlmIChzaG91bGRJbnZva2UodGltZSkpIHtcbiAgICAgIHJldHVybiB0cmFpbGluZ0VkZ2UodGltZSk7XG4gICAgfVxuICAgIC8vIFJlc3RhcnQgdGhlIHRpbWVyLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgcmVtYWluaW5nV2FpdCh0aW1lKSk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFpbGluZ0VkZ2UodGltZSkge1xuICAgIHRpbWVySWQgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBPbmx5IGludm9rZSBpZiB3ZSBoYXZlIGBsYXN0QXJnc2Agd2hpY2ggbWVhbnMgYGZ1bmNgIGhhcyBiZWVuXG4gICAgLy8gZGVib3VuY2VkIGF0IGxlYXN0IG9uY2UuXG4gICAgaWYgKHRyYWlsaW5nICYmIGxhc3RBcmdzKSB7XG4gICAgICByZXR1cm4gaW52b2tlRnVuYyh0aW1lKTtcbiAgICB9XG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuY2VsKCkge1xuICAgIGlmICh0aW1lcklkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcklkKTtcbiAgICB9XG4gICAgbGFzdEludm9rZVRpbWUgPSAwO1xuICAgIGxhc3RBcmdzID0gbGFzdENhbGxUaW1lID0gbGFzdFRoaXMgPSB0aW1lcklkID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgZnVuY3Rpb24gZmx1c2goKSB7XG4gICAgcmV0dXJuIHRpbWVySWQgPT09IHVuZGVmaW5lZCA/IHJlc3VsdCA6IHRyYWlsaW5nRWRnZShub3coKSk7XG4gIH1cblxuICBmdW5jdGlvbiBkZWJvdW5jZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKSxcbiAgICAgICAgaXNJbnZva2luZyA9IHNob3VsZEludm9rZSh0aW1lKTtcblxuICAgIGxhc3RBcmdzID0gYXJndW1lbnRzO1xuICAgIGxhc3RUaGlzID0gdGhpcztcbiAgICBsYXN0Q2FsbFRpbWUgPSB0aW1lO1xuXG4gICAgaWYgKGlzSW52b2tpbmcpIHtcbiAgICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGxlYWRpbmdFZGdlKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgICBpZiAobWF4aW5nKSB7XG4gICAgICAgIC8vIEhhbmRsZSBpbnZvY2F0aW9ucyBpbiBhIHRpZ2h0IGxvb3AuXG4gICAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgICAgIHJldHVybiBpbnZva2VGdW5jKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgZGVib3VuY2VkLmNhbmNlbCA9IGNhbmNlbDtcbiAgZGVib3VuY2VkLmZsdXNoID0gZmx1c2g7XG4gIHJldHVybiBkZWJvdW5jZWQ7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHRocm90dGxlZCBmdW5jdGlvbiB0aGF0IG9ubHkgaW52b2tlcyBgZnVuY2AgYXQgbW9zdCBvbmNlIHBlclxuICogZXZlcnkgYHdhaXRgIG1pbGxpc2Vjb25kcy4gVGhlIHRocm90dGxlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGBcbiAqIG1ldGhvZCB0byBjYW5jZWwgZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG9cbiAqIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYFxuICogc2hvdWxkIGJlIGludm9rZWQgb24gdGhlIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YFxuICogdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZVxuICogdGhyb3R0bGVkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50IGNhbGxzIHRvIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gcmV0dXJuIHRoZVxuICogcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYCBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLnRocm90dGxlYCBhbmQgYF8uZGVib3VuY2VgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gdGhyb3R0bGUuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gdGhyb3R0bGUgaW52b2NhdGlvbnMgdG8uXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgdGhyb3R0bGVkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBleGNlc3NpdmVseSB1cGRhdGluZyB0aGUgcG9zaXRpb24gd2hpbGUgc2Nyb2xsaW5nLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Njcm9sbCcsIF8udGhyb3R0bGUodXBkYXRlUG9zaXRpb24sIDEwMCkpO1xuICpcbiAqIC8vIEludm9rZSBgcmVuZXdUb2tlbmAgd2hlbiB0aGUgY2xpY2sgZXZlbnQgaXMgZmlyZWQsIGJ1dCBub3QgbW9yZSB0aGFuIG9uY2UgZXZlcnkgNSBtaW51dGVzLlxuICogdmFyIHRocm90dGxlZCA9IF8udGhyb3R0bGUocmVuZXdUb2tlbiwgMzAwMDAwLCB7ICd0cmFpbGluZyc6IGZhbHNlIH0pO1xuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIHRocm90dGxlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyB0aHJvdHRsZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIHRocm90dGxlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiB0aHJvdHRsZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsZWFkaW5nID0gdHJ1ZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gJ2xlYWRpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMubGVhZGluZyA6IGxlYWRpbmc7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuICByZXR1cm4gZGVib3VuY2UoZnVuYywgd2FpdCwge1xuICAgICdsZWFkaW5nJzogbGVhZGluZyxcbiAgICAnbWF4V2FpdCc6IHdhaXQsXG4gICAgJ3RyYWlsaW5nJzogdHJhaWxpbmdcbiAgfSk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgdGhlXG4gKiBbbGFuZ3VhZ2UgdHlwZV0oaHR0cDovL3d3dy5lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLWVjbWFzY3JpcHQtbGFuZ3VhZ2UtdHlwZXMpXG4gKiBvZiBgT2JqZWN0YC4gKGUuZy4gYXJyYXlzLCBmdW5jdGlvbnMsIG9iamVjdHMsIHJlZ2V4ZXMsIGBuZXcgTnVtYmVyKDApYCwgYW5kIGBuZXcgU3RyaW5nKCcnKWApXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYW4gb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3Qoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KF8ubm9vcCk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0KHZhbHVlKSB7XG4gIHZhciB0eXBlID0gdHlwZW9mIHZhbHVlO1xuICByZXR1cm4gISF2YWx1ZSAmJiAodHlwZSA9PSAnb2JqZWN0JyB8fCB0eXBlID09ICdmdW5jdGlvbicpO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLiBBIHZhbHVlIGlzIG9iamVjdC1saWtlIGlmIGl0J3Mgbm90IGBudWxsYFxuICogYW5kIGhhcyBhIGB0eXBlb2ZgIHJlc3VsdCBvZiBcIm9iamVjdFwiLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKF8ubm9vcCk7XG4gKiAvLyA9PiBmYWxzZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3RMaWtlKHZhbHVlKSB7XG4gIHJldHVybiAhIXZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PSAnb2JqZWN0Jztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBjbGFzc2lmaWVkIGFzIGEgYFN5bWJvbGAgcHJpbWl0aXZlIG9yIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIHN5bWJvbCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzU3ltYm9sKFN5bWJvbC5pdGVyYXRvcik7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc1N5bWJvbCgnYWJjJyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1N5bWJvbCh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09ICdzeW1ib2wnIHx8XG4gICAgKGlzT2JqZWN0TGlrZSh2YWx1ZSkgJiYgb2JqZWN0VG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gc3ltYm9sVGFnKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBgdmFsdWVgIHRvIGEgbnVtYmVyLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBwcm9jZXNzLlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgbnVtYmVyLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLnRvTnVtYmVyKDMuMik7XG4gKiAvLyA9PiAzLjJcbiAqXG4gKiBfLnRvTnVtYmVyKE51bWJlci5NSU5fVkFMVUUpO1xuICogLy8gPT4gNWUtMzI0XG4gKlxuICogXy50b051bWJlcihJbmZpbml0eSk7XG4gKiAvLyA9PiBJbmZpbml0eVxuICpcbiAqIF8udG9OdW1iZXIoJzMuMicpO1xuICogLy8gPT4gMy4yXG4gKi9cbmZ1bmN0aW9uIHRvTnVtYmVyKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gJ251bWJlcicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgaWYgKGlzU3ltYm9sKHZhbHVlKSkge1xuICAgIHJldHVybiBOQU47XG4gIH1cbiAgaWYgKGlzT2JqZWN0KHZhbHVlKSkge1xuICAgIHZhciBvdGhlciA9IHR5cGVvZiB2YWx1ZS52YWx1ZU9mID09ICdmdW5jdGlvbicgPyB2YWx1ZS52YWx1ZU9mKCkgOiB2YWx1ZTtcbiAgICB2YWx1ZSA9IGlzT2JqZWN0KG90aGVyKSA/IChvdGhlciArICcnKSA6IG90aGVyO1xuICB9XG4gIGlmICh0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWUgPT09IDAgPyB2YWx1ZSA6ICt2YWx1ZTtcbiAgfVxuICB2YWx1ZSA9IHZhbHVlLnJlcGxhY2UocmVUcmltLCAnJyk7XG4gIHZhciBpc0JpbmFyeSA9IHJlSXNCaW5hcnkudGVzdCh2YWx1ZSk7XG4gIHJldHVybiAoaXNCaW5hcnkgfHwgcmVJc09jdGFsLnRlc3QodmFsdWUpKVxuICAgID8gZnJlZVBhcnNlSW50KHZhbHVlLnNsaWNlKDIpLCBpc0JpbmFyeSA/IDIgOiA4KVxuICAgIDogKHJlSXNCYWRIZXgudGVzdCh2YWx1ZSkgPyBOQU4gOiArdmFsdWUpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHRocm90dGxlO1xuIiwidmFyIGc7XHJcblxyXG4vLyBUaGlzIHdvcmtzIGluIG5vbi1zdHJpY3QgbW9kZVxyXG5nID0gKGZ1bmN0aW9uKCkge1xyXG5cdHJldHVybiB0aGlzO1xyXG59KSgpO1xyXG5cclxudHJ5IHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIGV2YWwgaXMgYWxsb3dlZCAoc2VlIENTUClcclxuXHRnID0gZyB8fCBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCkgfHwgKDEsIGV2YWwpKFwidGhpc1wiKTtcclxufSBjYXRjaCAoZSkge1xyXG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXHJcblx0aWYgKHR5cGVvZiB3aW5kb3cgPT09IFwib2JqZWN0XCIpIGcgPSB3aW5kb3c7XHJcbn1cclxuXHJcbi8vIGcgY2FuIHN0aWxsIGJlIHVuZGVmaW5lZCwgYnV0IG5vdGhpbmcgdG8gZG8gYWJvdXQgaXQuLi5cclxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3NcclxuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxyXG5cclxubW9kdWxlLmV4cG9ydHMgPSBnO1xyXG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmltcG9ydCB7IGdldEVsZW1lbnRzRm9yU291cmNlTGluZSB9IGZyb20gJy4vc2Nyb2xsLXN5bmMnO1xuXG5leHBvcnQgY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG5cdHByaXZhdGUgX2N1cnJlbnQ6IGFueTtcblxuXHRvbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24obGluZTogbnVtYmVyKSB7XG5cdFx0Y29uc3QgeyBwcmV2aW91cyB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRcdHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcblx0fVxuXG5cdF91cGRhdGUoYmVmb3JlOiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdHRoaXMuX3VubWFya0FjdGl2ZUVsZW1lbnQodGhpcy5fY3VycmVudCk7XG5cdFx0dGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcblx0XHR0aGlzLl9jdXJyZW50ID0gYmVmb3JlO1xuXHR9XG5cblx0X3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgPSBlbGVtZW50LmNsYXNzTmFtZS5yZXBsYWNlKC9cXGJjb2RlLWFjdGl2ZS1saW5lXFxiL2csICcnKTtcblx0fVxuXG5cdF9tYXJrQWN0aXZlRWxlbWVudChlbGVtZW50OiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdGlmICghZWxlbWVudCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuXHR9XG59IiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBmdW5jdGlvbiBvbmNlRG9jdW1lbnRMb2FkZWQoZjogKCkgPT4gdm9pZCkge1xuXHRpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuXHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBmKTtcblx0fSBlbHNlIHtcblx0XHRmKCk7XG5cdH1cbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgQWN0aXZlTGluZU1hcmtlciB9IGZyb20gJy4vYWN0aXZlTGluZU1hcmtlcic7XG5pbXBvcnQgeyBvbmNlRG9jdW1lbnRMb2FkZWQgfSBmcm9tICcuL2V2ZW50cyc7XG5pbXBvcnQgeyBjcmVhdGVQb3N0ZXJGb3JWc0NvZGUgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5pbXBvcnQgeyBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCwgc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lIH0gZnJvbSAnLi9zY3JvbGwtc3luYyc7XG5pbXBvcnQgeyBnZXRTZXR0aW5ncywgZ2V0RGF0YSB9IGZyb20gJy4vc2V0dGluZ3MnO1xuaW1wb3J0IHRocm90dGxlID0gcmVxdWlyZSgnbG9kYXNoLnRocm90dGxlJyk7XG5cbmRlY2xhcmUgdmFyIGFjcXVpcmVWc0NvZGVBcGk6IGFueTtcblxudmFyIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbmNvbnN0IG1hcmtlciA9IG5ldyBBY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IGdldFNldHRpbmdzKCk7XG5cbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcblxuLy8gU2V0IFZTIENvZGUgc3RhdGVcbmNvbnN0IHN0YXRlID0gZ2V0RGF0YSgnZGF0YS1zdGF0ZScpO1xudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblxuY29uc3QgbWVzc2FnaW5nID0gY3JlYXRlUG9zdGVyRm9yVnNDb2RlKHZzY29kZSk7XG5cbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG5cdHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5cbm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG5cdGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuXHRcdHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdGlmICghaXNOYU4oaW5pdGlhbExpbmUpKSB7XG5cdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdH1cblx0XHR9LCAwKTtcblx0fVxufSk7XG5cbmNvbnN0IG9uVXBkYXRlVmlldyA9ICgoKSA9PiB7XG5cdGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmU6IG51bWJlcikgPT4ge1xuXHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG5cdH0sIDUwKTtcblxuXHRyZXR1cm4gKGxpbmU6IG51bWJlciwgc2V0dGluZ3M6IGFueSkgPT4ge1xuXHRcdGlmICghaXNOYU4obGluZSkpIHtcblx0XHRcdHNldHRpbmdzLmxpbmUgPSBsaW5lO1xuXHRcdFx0ZG9TY3JvbGwobGluZSk7XG5cdFx0fVxuXHR9O1xufSkoKTtcblxubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG5cdGNvbnN0IGltYWdlSW5mbzogeyBpZDogc3RyaW5nLCBoZWlnaHQ6IG51bWJlciwgd2lkdGg6IG51bWJlciB9W10gPSBbXTtcblx0bGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcblx0aWYgKGltYWdlcykge1xuXHRcdGxldCBpO1xuXHRcdGZvciAoaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyBpKyspIHtcblx0XHRcdGNvbnN0IGltZyA9IGltYWdlc1tpXTtcblxuXHRcdFx0aWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuXHRcdFx0XHRpbWcuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHRpbWFnZUluZm8ucHVzaCh7XG5cdFx0XHRcdGlkOiBpbWcuaWQsXG5cdFx0XHRcdGhlaWdodDogaW1nLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IGltZy53aWR0aFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuXHR9XG59LCA1MCk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG5cdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZXZlbnQgPT4ge1xuXHRpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG5cdFx0Y2FzZSAnb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uJzpcblx0XHRcdG1hcmtlci5vbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24oZXZlbnQuZGF0YS5saW5lKTtcblx0XHRcdGJyZWFrO1xuXG5cdFx0Y2FzZSAndXBkYXRlVmlldyc6XG5cdFx0XHRvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG5cdFx0XHRicmVhaztcblx0fVxufSwgZmFsc2UpO1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcblx0aWYgKCFzZXR0aW5ncy5kb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHQvLyBJZ25vcmUgY2xpY2tzIG9uIGxpbmtzXG5cdGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lID09PSAnQScpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdH1cblxuXHRjb25zdCBvZmZzZXQgPSBldmVudC5wYWdlWTtcblx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcblx0fVxufSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIWV2ZW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0bGV0IG5vZGU6IGFueSA9IGV2ZW50LnRhcmdldDtcblx0d2hpbGUgKG5vZGUpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuXHRcdFx0aWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKG5vZGUuaHJlZi5zdGFydHNXaXRoKCdmaWxlOi8vJykgfHwgbm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ3ZzY29kZS1yZXNvdXJjZTonKSkge1xuXHRcdFx0XHRjb25zdCBbcGF0aCwgZnJhZ21lbnRdID0gbm9kZS5ocmVmLnJlcGxhY2UoL14oZmlsZTpcXC9cXC98dnNjb2RlLXJlc291cmNlOikvaSwgJycpLnNwbGl0KCcjJyk7XG5cdFx0XHRcdG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnY2xpY2tMaW5rJywgeyBwYXRoLCBmcmFnbWVudCB9KTtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0ZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXHRcdG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG5cdH1cbn0sIHRydWUpO1xuXG5pZiAoc2V0dGluZ3Muc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXcpIHtcblx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcblx0XHRpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcblx0XHRcdHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IGxpbmUgPSBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG5cdFx0XHRpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LCA1MCkpO1xufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE1lc3NhZ2VQb3N0ZXIge1xuXHQvKipcblx0ICogUG9zdCBhIG1lc3NhZ2UgdG8gdGhlIG1hcmtkb3duIGV4dGVuc2lvblxuXHQgKi9cblx0cG9zdE1lc3NhZ2UodHlwZTogc3RyaW5nLCBib2R5OiBvYmplY3QpOiB2b2lkO1xufVxuXG5leHBvcnQgY29uc3QgY3JlYXRlUG9zdGVyRm9yVnNDb2RlID0gKHZzY29kZTogYW55KSA9PiB7XG5cdHJldHVybiBuZXcgY2xhc3MgaW1wbGVtZW50cyBNZXNzYWdlUG9zdGVyIHtcblx0XHRwb3N0TWVzc2FnZSh0eXBlOiBzdHJpbmcsIGJvZHk6IG9iamVjdCk6IHZvaWQge1xuXHRcdFx0dnNjb2RlLnBvc3RNZXNzYWdlKHtcblx0XHRcdFx0dHlwZSxcblx0XHRcdFx0c291cmNlOiBnZXRTZXR0aW5ncygpLnNvdXJjZSxcblx0XHRcdFx0Ym9keVxuXHRcdFx0fSk7XG5cdFx0fVxuXHR9O1xufTtcblxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IGdldFNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5cblxuZnVuY3Rpb24gY2xhbXAobWluOiBudW1iZXIsIG1heDogbnVtYmVyLCB2YWx1ZTogbnVtYmVyKSB7XG5cdHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgdmFsdWUpKTtcbn1cblxuZnVuY3Rpb24gY2xhbXBMaW5lKGxpbmU6IG51bWJlcikge1xuXHRyZXR1cm4gY2xhbXAoMCwgZ2V0U2V0dGluZ3MoKS5saW5lQ291bnQgLSAxLCBsaW5lKTtcbn1cblxuXG5leHBvcnQgaW50ZXJmYWNlIENvZGVMaW5lRWxlbWVudCB7XG5cdGVsZW1lbnQ6IEhUTUxFbGVtZW50O1xuXHRsaW5lOiBudW1iZXI7XG59XG5cbmNvbnN0IGdldENvZGVMaW5lRWxlbWVudHMgPSAoKCkgPT4ge1xuXHRsZXQgZWxlbWVudHM6IENvZGVMaW5lRWxlbWVudFtdO1xuXHRyZXR1cm4gKCkgPT4ge1xuXHRcdGlmICghZWxlbWVudHMpIHtcblx0XHRcdGVsZW1lbnRzID0gKFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV0pLmNvbmNhdChBcnJheS5wcm90b3R5cGUubWFwLmNhbGwoXG5cdFx0XHRcdGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtbGluZScpLFxuXHRcdFx0XHQoZWxlbWVudDogYW55KSA9PiB7XG5cdFx0XHRcdFx0Y29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJyk7XG5cdFx0XHRcdFx0cmV0dXJuIHsgZWxlbWVudCwgbGluZSB9O1xuXHRcdFx0XHR9KVxuXHRcdFx0XHQuZmlsdGVyKCh4OiBhbnkpID0+ICFpc05hTih4LmxpbmUpKSk7XG5cdFx0fVxuXHRcdHJldHVybiBlbGVtZW50cztcblx0fTtcbn0pKCk7XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKHRhcmdldExpbmU6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuXHRjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcblx0bGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcblx0Zm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuXHRcdGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG5cdFx0XHRyZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuXHRcdH0gZWxzZSBpZiAoZW50cnkubGluZSA+IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzLCBuZXh0OiBlbnRyeSB9O1xuXHRcdH1cblx0XHRwcmV2aW91cyA9IGVudHJ5O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzIH07XG59XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IGFyZSBhdCBhIHNwZWNpZmljIHBpeGVsIG9mZnNldCBvbiB0aGUgcGFnZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQ6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRjb25zdCBwb3NpdGlvbiA9IG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZO1xuXHRsZXQgbG8gPSAtMTtcblx0bGV0IGhpID0gbGluZXMubGVuZ3RoIC0gMTtcblx0d2hpbGUgKGxvICsgMSA8IGhpKSB7XG5cdFx0Y29uc3QgbWlkID0gTWF0aC5mbG9vcigobG8gKyBoaSkgLyAyKTtcblx0XHRjb25zdCBib3VuZHMgPSBsaW5lc1ttaWRdLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdFx0aWYgKGJvdW5kcy50b3AgKyBib3VuZHMuaGVpZ2h0ID49IHBvc2l0aW9uKSB7XG5cdFx0XHRoaSA9IG1pZDtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRsbyA9IG1pZDtcblx0XHR9XG5cdH1cblx0Y29uc3QgaGlFbGVtZW50ID0gbGluZXNbaGldO1xuXHRjb25zdCBoaUJvdW5kcyA9IGhpRWxlbWVudC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRpZiAoaGkgPj0gMSAmJiBoaUJvdW5kcy50b3AgPiBwb3NpdGlvbikge1xuXHRcdGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcblx0XHRyZXR1cm4geyBwcmV2aW91czogbG9FbGVtZW50LCBuZXh0OiBoaUVsZW1lbnQgfTtcblx0fVxuXHRyZXR1cm4geyBwcmV2aW91czogaGlFbGVtZW50IH07XG59XG5cbi8qKlxuICogQXR0ZW1wdCB0byByZXZlYWwgdGhlIGVsZW1lbnQgZm9yIGEgc291cmNlIGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lOiBudW1iZXIpIHtcblx0aWYgKCFnZXRTZXR0aW5ncygpLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0aWYgKGxpbmUgPD0gMCkge1xuXHRcdHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIDApO1xuXHRcdHJldHVybjtcblx0fVxuXG5cdGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0aWYgKCFwcmV2aW91cykge1xuXHRcdHJldHVybjtcblx0fVxuXHRsZXQgc2Nyb2xsVG8gPSAwO1xuXHRjb25zdCByZWN0ID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0Y29uc3QgcHJldmlvdXNUb3AgPSByZWN0LnRvcDtcblx0aWYgKG5leHQgJiYgbmV4dC5saW5lICE9PSBwcmV2aW91cy5saW5lKSB7XG5cdFx0Ly8gQmV0d2VlbiB0d28gZWxlbWVudHMuIEdvIHRvIHBlcmNlbnRhZ2Ugb2Zmc2V0IGJldHdlZW4gdGhlbS5cblx0XHRjb25zdCBiZXR3ZWVuUHJvZ3Jlc3MgPSAobGluZSAtIHByZXZpb3VzLmxpbmUpIC8gKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuXHRcdGNvbnN0IGVsZW1lbnRPZmZzZXQgPSBuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNUb3A7XG5cdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIGJldHdlZW5Qcm9ncmVzcyAqIGVsZW1lbnRPZmZzZXQ7XG5cdH0gZWxzZSB7XG5cdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcDtcblx0fVxuXHR3aW5kb3cuc2Nyb2xsKHdpbmRvdy5zY3JvbGxYLCBNYXRoLm1heCgxLCB3aW5kb3cuc2Nyb2xsWSArIHNjcm9sbFRvKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldChvZmZzZXQ6IG51bWJlcikge1xuXHRjb25zdCB7IHByZXZpb3VzLCBuZXh0IH0gPSBnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQob2Zmc2V0KTtcblx0aWYgKHByZXZpb3VzKSB7XG5cdFx0Y29uc3QgcHJldmlvdXNCb3VuZHMgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRcdGNvbnN0IG9mZnNldEZyb21QcmV2aW91cyA9IChvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWSAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG5cdFx0aWYgKG5leHQpIHtcblx0XHRcdGNvbnN0IHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzID0gb2Zmc2V0RnJvbVByZXZpb3VzIC8gKG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuXHRcdFx0Y29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyAqIChuZXh0LmxpbmUgLSBwcmV2aW91cy5saW5lKTtcblx0XHRcdHJldHVybiBjbGFtcExpbmUobGluZSk7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0Y29uc3QgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50ID0gb2Zmc2V0RnJvbVByZXZpb3VzIC8gKHByZXZpb3VzQm91bmRzLmhlaWdodCk7XG5cdFx0XHRjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzV2l0aGluRWxlbWVudDtcblx0XHRcdHJldHVybiBjbGFtcExpbmUobGluZSk7XG5cdFx0fVxuXHR9XG5cdHJldHVybiBudWxsO1xufVxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJldmlld1NldHRpbmdzIHtcblx0c291cmNlOiBzdHJpbmc7XG5cdGxpbmU6IG51bWJlcjtcblx0bGluZUNvdW50OiBudW1iZXI7XG5cdHNjcm9sbFByZXZpZXdXaXRoRWRpdG9yPzogYm9vbGVhbjtcblx0c2Nyb2xsRWRpdG9yV2l0aFByZXZpZXc6IGJvb2xlYW47XG5cdGRpc2FibGVTZWN1cml0eVdhcm5pbmdzOiBib29sZWFuO1xuXHRkb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3I6IGJvb2xlYW47XG59XG5cbmxldCBjYWNoZWRTZXR0aW5nczogUHJldmlld1NldHRpbmdzIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGF0YShrZXk6IHN0cmluZyk6IFByZXZpZXdTZXR0aW5ncyB7XG5cdGNvbnN0IGVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuXHRpZiAoZWxlbWVudCkge1xuXHRcdGNvbnN0IGRhdGEgPSBlbGVtZW50LmdldEF0dHJpYnV0ZShrZXkpO1xuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcblx0XHR9XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKTogUHJldmlld1NldHRpbmdzIHtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0Y2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index d8860815e0e..54b289a7d23 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -6,7 +6,6 @@ "icon": "icon.png", "publisher": "vscode", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", - "enableProposedApi": true, "engines": { "vscode": "^1.20.0" }, @@ -325,4 +324,4 @@ "webpack": "^4.1.0", "webpack-cli": "^2.0.10" } -} \ No newline at end of file +} diff --git a/extensions/markdown-language-features/preview-src/scroll-sync.ts b/extensions/markdown-language-features/preview-src/scroll-sync.ts index dacc14a8676..3bd2939e029 100644 --- a/extensions/markdown-language-features/preview-src/scroll-sync.ts +++ b/extensions/markdown-language-features/preview-src/scroll-sync.ts @@ -24,13 +24,13 @@ const getCodeLineElements = (() => { let elements: CodeLineElement[]; return () => { if (!elements) { - elements = Array.prototype.map.call( + elements = ([{ element: document.body, line: 0 }]).concat(Array.prototype.map.call( document.getElementsByClassName('code-line'), (element: any) => { const line = +element.getAttribute('data-line'); return { element, line }; }) - .filter((x: any) => !isNaN(x.line)); + .filter((x: any) => !isNaN(x.line))); } return elements; }; @@ -49,8 +49,7 @@ export function getElementsForSourceLine(targetLine: number): { previous: CodeLi for (const entry of lines) { if (entry.line === lineNumber) { return { previous: entry, next: undefined }; - } - else if (entry.line > lineNumber) { + } else if (entry.line > lineNumber) { return { previous, next: entry }; } previous = entry; @@ -89,22 +88,31 @@ export function getLineElementsAtPageOffset(offset: number): { previous: CodeLin * Attempt to reveal the element for a source line in the editor. */ export function scrollToRevealSourceLine(line: number) { - const { previous, next } = getElementsForSourceLine(line); - if (previous && getSettings().scrollPreviewWithEditor) { - let scrollTo = 0; - const rect = previous.element.getBoundingClientRect(); - const previousTop = rect.top; - if (next && next.line !== previous.line) { - // Between two elements. Go to percentage offset between them. - const betweenProgress = (line - previous.line) / (next.line - previous.line); - const elementOffset = next.element.getBoundingClientRect().top - previousTop; - scrollTo = previousTop + betweenProgress * elementOffset; - } - else { - scrollTo = previousTop; - } - window.scroll(0, Math.max(1, window.scrollY + scrollTo)); + if (!getSettings().scrollPreviewWithEditor) { + return; } + + if (line <= 0) { + window.scroll(window.scrollX, 0); + return; + } + + const { previous, next } = getElementsForSourceLine(line); + if (!previous) { + return; + } + let scrollTo = 0; + const rect = previous.element.getBoundingClientRect(); + const previousTop = rect.top; + if (next && next.line !== previous.line) { + // Between two elements. Go to percentage offset between them. + const betweenProgress = (line - previous.line) / (next.line - previous.line); + const elementOffset = next.element.getBoundingClientRect().top - previousTop; + scrollTo = previousTop + betweenProgress * elementOffset; + } else { + scrollTo = previousTop; + } + window.scroll(window.scrollX, Math.max(1, window.scrollY + scrollTo)); } export function getEditorLineNumberForPageOffset(offset: number) { diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index a11bf3b1162..30767b88c5d 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -104,7 +104,7 @@ export class MarkdownContentProvider { // Use href if it is already an URL const hrefUri = vscode.Uri.parse(href); if (['http', 'https'].indexOf(hrefUri.scheme) >= 0) { - return hrefUri.toString(); + return hrefUri.toString(true); } // Use href as file URI if it is absolute @@ -131,7 +131,7 @@ export class MarkdownContentProvider { private computeCustomStyleSheetIncludes(resource: vscode.Uri, config: MarkdownPreviewConfiguration): string { if (Array.isArray(config.styles)) { return config.styles.map(style => { - return ``; + return ``; }).join('\n'); } return ''; diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 085b1a5e2eb..9bd90ead16a 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -46,6 +46,9 @@ export class MarkdownEngine { if (lang && lang.toLocaleLowerCase() === 'json5') { lang = 'json'; } + if (lang && lang.toLocaleLowerCase() === 'c#') { + lang = 'cs'; + } if (lang && hljs.getLanguage(lang)) { try { return `
${hljs.highlight(lang, str, true).value}
`; @@ -178,7 +181,8 @@ export class MarkdownEngine { try { const externalSchemeUri = getUriForLinkWithKnownExternalScheme(link); if (externalSchemeUri) { - return normalizeLink(externalSchemeUri.toString()); + // set true to skip encoding + return normalizeLink(externalSchemeUri.toString(true)); } diff --git a/extensions/markdown-language-features/src/typings/ref.d.ts b/extensions/markdown-language-features/src/typings/ref.d.ts index 954bab971e3..bc057c55878 100644 --- a/extensions/markdown-language-features/src/typings/ref.d.ts +++ b/extensions/markdown-language-features/src/typings/ref.d.ts @@ -4,5 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// -/// /// diff --git a/extensions/markdown-language-features/src/util/links.ts b/extensions/markdown-language-features/src/util/links.ts index e1fc274d092..1afcbff83bd 100644 --- a/extensions/markdown-language-features/src/util/links.ts +++ b/extensions/markdown-language-features/src/util/links.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; -const knownSchemes = ['http:', 'https:', 'file:', 'mailto:']; +const knownSchemes = ['http:', 'https:', 'file:', 'mailto:', 'data:', 'vscode-resource:']; export function getUriForLinkWithKnownExternalScheme( link: string, diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 6c8578ad784..b50486c6bb3 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -7,7 +7,6 @@ "engines": { "vscode": "0.10.x" }, - "enableProposedApi": true, "icon": "images/npm_icon.png", "categories": [ "Other" diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/main.ts index bc40b996a0d..5903d6ebb58 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/main.ts @@ -11,9 +11,11 @@ import { invalidateTasksCache, NpmTaskProvider } from './tasks'; import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover'; import { runSelectedScript } from './commands'; +let treeDataProvider: NpmScriptsTreeDataProvider | undefined; + export async function activate(context: vscode.ExtensionContext): Promise { registerTaskProvider(context); - const treeDataProvider = registerExplorer(context); + treeDataProvider = registerExplorer(context); registerHoverProvider(context); configureHttpRequest(); @@ -46,6 +48,9 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab function invalidateScriptCaches() { invalidateHoverScriptsCache(); invalidateTasksCache(); + if (treeDataProvider) { + treeDataProvider.refresh(); + } } if (vscode.workspace.workspaceFolders) { diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index 2a7fd295656..9bcd272a958 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -108,6 +108,13 @@ class NpmScript extends TreeItem { dark: context.asAbsolutePath(path.join('resources', 'dark', 'script.svg')) }; } + + let uri = getPackageJsonUriFromTask(task); + getScripts(uri!).then(scripts => { + if (scripts && scripts[task.definition['script']]) { + this.tooltip = scripts[task.definition['script']]; + } + }); } getFolder(): WorkspaceFolder { diff --git a/extensions/npm/src/typings/refs.d.ts b/extensions/npm/src/typings/refs.d.ts index 954bab971e3..bc057c55878 100644 --- a/extensions/npm/src/typings/refs.d.ts +++ b/extensions/npm/src/typings/refs.d.ts @@ -4,5 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// -/// /// diff --git a/extensions/package.json b/extensions/package.json index 10a06e899b4..9c80cfc7731 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": "3.1.3" + "typescript": "3.1.4" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/php/.vscode/tasks.json b/extensions/php/.vscode/tasks.json index 9e5593ade83..390a93a3a7f 100644 --- a/extensions/php/.vscode/tasks.json +++ b/extensions/php/.vscode/tasks.json @@ -1,30 +1,11 @@ -// Available variables which can be used inside of strings. -// ${workspaceFolder}: the root folder of the team -// ${file}: the current opened file -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls a custom npm script that compiles the extension. { - "version": "0.1.0", - - // we want to run npm + "version": "2.0.0", "command": "npm", - - // the command is a shell script - "isShellCommand": true, - - // show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // we run the custom script "compile" as defined in package.json + "type": "shell", + "presentation": { + "reveal": "silent" + }, "args": ["run", "compile"], - - // The tsc compiler is started in watching mode - "isWatching": true, - - // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "isBackground": true, "problemMatcher": "$tsc-watch" -} \ No newline at end of file +} diff --git a/extensions/php/syntaxes/html.tmLanguage.json b/extensions/php/syntaxes/html.tmLanguage.json index 59db289a2d5..809b1120530 100644 --- a/extensions/php/syntaxes/html.tmLanguage.json +++ b/extensions/php/syntaxes/html.tmLanguage.json @@ -4,10 +4,17 @@ "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/atom/language-php/commit/72bfa9592e689fdcb70562ff7d882ad5308e79f7", + "version": "https://github.com/atom/language-php/commit/b6c5e83016b52311cdc622c2579462861ee91587", "name": "PHP", "scopeName": "text.html.php", "injections": { + "text.html.php - (meta.embedded | meta.tag), L:((text.html.php meta.tag) - (meta.embedded.block.php | meta.embedded.line.php)), L:(source.js.embedded.html - (meta.embedded.block.php | meta.embedded.line.php))": { + "patterns": [ + { + "include": "#php-tag" + } + ] + }, "L:source.php string.quoted.single.sql.php source.sql.embedded.php": { "patterns": [ { @@ -108,13 +115,6 @@ "include": "source.php#interpolation_double_quoted" } ] - }, - "text.html.php - (meta.embedded | meta.tag), L:text.html.php meta.tag, L:text.html.php source.js": { - "patterns": [ - { - "include": "#php-tag" - } - ] } }, "patterns": [ diff --git a/extensions/php/test/colorize-results/issue-28354_php.json b/extensions/php/test/colorize-results/issue-28354_php.json index 12e439430fb..329c9d6f998 100644 --- a/extensions/php/test/colorize-results/issue-28354_php.json +++ b/extensions/php/test/colorize-results/issue-28354_php.json @@ -66,85 +66,8 @@ } }, { - "c": "", - "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php punctuation.section.embedded.end.php", + "c": "php", + "t": "text.html.php meta.embedded.block.html source.js variable.other.readwrite.js", "r": { - "dark_plus": "punctuation.section.embedded.end.php: #569CD6", - "light_plus": "punctuation.section.embedded.end.php: #800000", - "dark_vs": "punctuation.section.embedded.end.php: #569CD6", - "light_vs": "punctuation.section.embedded.end.php: #800000", - "hc_black": "punctuation.section.embedded: #569CD6" + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "variable: #9CDCFE" } }, { - "c": " ", + "c": " ", "t": "text.html.php meta.embedded.block.html source.js", "r": { "dark_plus": "meta.embedded: #D4D4D4", @@ -484,58 +110,300 @@ } }, { - "c": "...", - "t": "text.html.php meta.embedded.block.html source.js keyword.operator.spread.js", + "c": "foreach", + "t": "text.html.php meta.embedded.block.html source.js meta.function-call.js entity.name.function.js", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "entity.name.function: #DCDCAA" } }, { - "c": "<", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", + "c": "(", + "t": "text.html.php meta.embedded.block.html source.js meta.brace.round.js", "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" } }, { - "c": "/", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", + "c": "$actID", + "t": "text.html.php meta.embedded.block.html source.js variable.other.readwrite.js", "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "variable: #9CDCFE" } }, { - "c": "script", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", + "c": " ", + "t": "text.html.php meta.embedded.block.html source.js", "r": { - "dark_plus": "entity.name.tag: #569CD6", - "light_plus": "entity.name.tag: #800000", - "dark_vs": "entity.name.tag: #569CD6", - "light_vs": "entity.name.tag: #800000", - "hc_black": "entity.name.tag: #569CD6" + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" } }, { - "c": ">", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", + "c": "AS", + "t": "text.html.php meta.embedded.block.html source.js variable.other.constant.js", "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "text.html.php meta.embedded.block.html source.js", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "$act", + "t": "text.html.php meta.embedded.block.html source.js variable.other.readwrite.js", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "text.html.php meta.embedded.block.html source.js meta.brace.round.js", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.php meta.embedded.block.html source.js", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "{", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js punctuation.definition.block.js", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": " echo ", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "'", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js punctuation.definition.string.begin.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "divNames.push(", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\\'", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js constant.character.escape.js", + "r": { + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #FF0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6" + } + }, + { + "c": "[nid=", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js punctuation.definition.string.end.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ".$act.", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "r": { + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.object-literal.key: #9CDCFE" + } + }, + { + "c": "'", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js punctuation.definition.string.begin.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "]", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\\'", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js constant.character.escape.js", + "r": { + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #FF0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6" + } + }, + { + "c": ");", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js punctuation.definition.string.end.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ";", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "r": { + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.object-literal.key: #9CDCFE" + } + }, + { + "c": " }", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "r": { + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.object-literal.key: #9CDCFE" + } + }, + { + "c": " ?>", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "r": { + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.object-literal.key: #9CDCFE" + } + }, + { + "c": " ...", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "r": { + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.object-literal.key: #9CDCFE" + } + }, + { + "c": "", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "r": { + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.object-literal.key: #9CDCFE" } } ] \ No newline at end of file diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json index 44a995bc046..e51fcce1a85 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/b453f26ed856c9b16a053517c41207e3a72cc7d5", + "version": "https://github.com/MagicStack/MagicPython/commit/8ff35b3e5fcde471fae62a57ea1ae1c7cd34c9fc", "name": "MagicPython", "scopeName": "source.python", "patterns": [ @@ -97,7 +97,8 @@ }, "docstring-statement": { "begin": "^(?=\\s*[rR]?(\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\"))", - "end": "(?<=\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\")", + "comment": "the string either terminates correctly or by the beginning of a new line (this is for single line docstrings that aren't terminated) AND it's not followed by another docstring", + "end": "((?<=\\1)|^)(?!\\s*[rR]?(\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\"))", "patterns": [ { "include": "#docstring" @@ -164,7 +165,7 @@ { "name": "string.quoted.docstring.single.python", "begin": "(\\'|\\\")", - "end": "(\\1)|((?=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)? [bcdeEfFgGnosxX%]? )?\n })\n )\n", + "name": "meta.format.brace.python", + "match": "(?x)\n (\n {{ | }}\n | (?:\n {\n \\w* (\\.[[:alpha:]_]\\w* | \\[[^\\]'\"]+\\])*\n (![rsa])?\n ( : \\w? [<>=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)? [bcdeEfFgGnosxX%]? )?\n })\n )\n", "captures": { - "2": { - "name": "storage.type.format.python" + "1": { + "name": "constant.character.format.placeholder.other.python" }, "3": { "name": "storage.type.format.python" + }, + "4": { + "name": "storage.type.format.python" } } }, { - "name": "constant.character.format.placeholder.other.python", - "begin": "(?x)\n \\{\n \\w*? (\\.[[:alpha:]_]\\w*? | \\[[^\\]'\"]+\\])*?\n (![rsa])?\n (:)\n (?=[^'\"}\\n]*\\})\n", - "end": "\\}", - "beginCaptures": { - "2": { - "name": "storage.type.format.python" + "name": "meta.format.brace.python", + "match": "(?x)\n (\n {\n \\w* (\\.[[:alpha:]_]\\w* | \\[[^\\]'\"]+\\])*\n (![rsa])?\n (:)\n [^'\"{}\\n]* (?:\n \\{ [^'\"}\\n]*? \\} [^'\"{}\\n]*\n )*\n }\n )\n", + "captures": { + "1": { + "name": "constant.character.format.placeholder.other.python" }, "3": { "name": "storage.type.format.python" + }, + "4": { + "name": "storage.type.format.python" } - }, - "patterns": [ - { - "match": "(?x) \\{ [^'\"}\\n]*? \\} (?=.*?\\})\n" - } - ] + } } ] }, @@ -899,25 +915,43 @@ "match": "(}(?!}))" }, "import": { - "comment": "Import statements\n", + "comment": "Import statements used to correctly mark `from`, `import`, and `as`\n", "patterns": [ { - "match": "(?x)\n \\s* \\b(from)\\b \\s*(\\.+)\\s* (import)?\n", - "captures": { + "begin": "\\b(?)|((?!\\{)(?=\\S))", + "end": "((?<=\\}|\\S)(?)|((?!\\{)(?=\\S)))(?!\\/[\\/\\*])", "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, { "include": "#decl-block" }, @@ -2620,13 +2626,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.ts", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2673,7 +2679,7 @@ "name": "keyword.operator.new.ts" } }, - "end": "(?<=\\))|(?=[;),}\\]:]|\\|\\||\\&\\&|$|((?\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.ts meta.return.type.arrow.ts keyword.operator.type.annotation.ts" @@ -3621,6 +3627,14 @@ } }, "patterns": [ + { + "match": "(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { @@ -3971,7 +3982,7 @@ "include": "#typeof-operator" }, { - "begin": "([&|\\*])(?=\\s*\\{)", + "begin": "([&|])(?=\\s*\\{)", "beginCaptures": { "0": { "name": "keyword.operator.type.ts" @@ -3985,7 +3996,7 @@ ] }, { - "begin": "[&|\\*]", + "begin": "[&|]", "beginCaptures": { "0": { "name": "keyword.operator.type.ts" @@ -4123,7 +4134,7 @@ "patterns": [ { "name": "string.template.ts", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.ts" diff --git a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json index d96e10f5ead..6d39e0862c5 100644 --- a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScriptReact.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/Microsoft/TypeScript-TmLanguage/commit/6e8a3830c29b6f29c06d2de091240e1a880f21aa", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/3133e3d914db9a2bb8812119f9273727a305f16b", "name": "TypeScriptReact", "scopeName": "source.tsx", "patterns": [ @@ -1153,7 +1153,7 @@ "name": "meta.definition.function.tsx entity.name.function.tsx" } }, - "end": "(?=$|^|;)|(?<=\\})", + "end": "(?=;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|(?<=\\})", "patterns": [ { "include": "#function-name" @@ -1413,6 +1413,9 @@ }, { "include": "#arrow-return-type" + }, + { + "include": "#possibly-arrow-return-type" } ] }, @@ -1424,8 +1427,11 @@ "name": "storage.type.function.arrow.tsx" } }, - "end": "(?<=\\}|\\S)(?)|((?!\\{)(?=\\S))", + "end": "((?<=\\}|\\S)(?)|((?!\\{)(?=\\S)))(?!\\/[\\/\\*])", "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, { "include": "#decl-block" }, @@ -2623,13 +2629,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.tsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2676,7 +2682,7 @@ "name": "keyword.operator.new.tsx" } }, - "end": "(?<=\\))|(?=[;),}\\]:]|\\|\\||\\&\\&|$|((?\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.tsx meta.return.type.arrow.tsx keyword.operator.type.annotation.tsx" @@ -3587,6 +3593,14 @@ } }, "patterns": [ + { + "match": "(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { @@ -3937,7 +3948,7 @@ "include": "#typeof-operator" }, { - "begin": "([&|\\*])(?=\\s*\\{)", + "begin": "([&|])(?=\\s*\\{)", "beginCaptures": { "0": { "name": "keyword.operator.type.tsx" @@ -3951,7 +3962,7 @@ ] }, { - "begin": "[&|\\*]", + "begin": "[&|]", "beginCaptures": { "0": { "name": "keyword.operator.type.tsx" @@ -4089,7 +4100,7 @@ "patterns": [ { "name": "string.template.tsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.tsx" diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 048c8d669c4..92160253eba 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -42,6 +42,7 @@ "onCommand:typescript.goToProjectConfig", "onCommand:typescript.openTsServerLog", "onCommand:workbench.action.tasks.runTask", + "onCommand:_typescript.configurePlugin", "onLanguage:jsonc" ], "main": "./out/extension", diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 9308f771233..2cd347a012a 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -5,7 +5,7 @@ "configuration.typescript": "TypeScript", "configuration.suggest.completeFunctionCalls": "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.", + "typescript.disableAutomaticTypeAcquisition": "Disables automatic type acquisition. Automatic type acquisition fetches `@types` packages from npm to improve IntelliSense for external libraries.", "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.pluginPaths": "Additional paths to discover Typescript Language Service plugins. Requires using TypeScript 2.3.0 or newer in the workspace.", "typescript.tsserver.pluginPaths.item": "Either an absolute or relative path. Relative path will be resolved against workspace folder(s).", diff --git a/extensions/typescript-language-features/src/commands.ts b/extensions/typescript-language-features/src/commands.ts index 2c40c512e18..8a353e30807 100644 --- a/extensions/typescript-language-features/src/commands.ts +++ b/extensions/typescript-language-features/src/commands.ts @@ -10,6 +10,7 @@ import { Command } from './utils/commandManager'; import { Lazy } from './utils/lazy'; import { isImplicitProjectConfigFile, openOrCreateConfigFile } from './utils/tsconfig'; import { nulToken } from './utils/cancellation'; +import { PluginConfigProvider } from './typescriptServiceClient'; const localize = nls.loadMessageBundle(); @@ -105,6 +106,18 @@ export class JavaScriptGoToProjectConfigCommand implements Command { } } +export class ConfigurePluginCommand implements Command { + public readonly id = '_typescript.configurePlugin'; + + public constructor( + private readonly pluginConfigProvider: PluginConfigProvider, + ) { } + + public execute(pluginId: string, configuration: any) { + this.pluginConfigProvider.set(pluginId, configuration); + } +} + async function goToProjectConfig( clientHost: TypeScriptServiceClientHost, isTypeScriptProject: boolean, diff --git a/extensions/typescript-language-features/src/extension.ts b/extensions/typescript-language-features/src/extension.ts index 48415ef1f1f..cce5a94dda7 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -18,19 +18,36 @@ import ManagedFileContextManager from './utils/managedFileContext'; import { getContributedTypeScriptServerPlugins, TypeScriptServerPlugin } from './utils/plugins'; import * as ProjectStatus from './utils/projectStatus'; import { Surveyor } from './utils/surveyor'; +import { PluginConfigProvider } from './typescriptServiceClient'; +interface ApiV0 { + readonly onCompletionAccepted: vscode.Event; +} + + + +interface Api { + getAPI(version: 0): ApiV0 | undefined; +} + export function activate( context: vscode.ExtensionContext -): void { +): Api { const plugins = getContributedTypeScriptServerPlugins(); + const pluginConfigProvider = new PluginConfigProvider(); const commandManager = new CommandManager(); context.subscriptions.push(commandManager); - const lazyClientHost = createLazyClientHost(context, plugins, commandManager); + const onCompletionAccepted = new vscode.EventEmitter(); + context.subscriptions.push(onCompletionAccepted); - registerCommands(commandManager, lazyClientHost); + const lazyClientHost = createLazyClientHost(context, plugins, pluginConfigProvider, commandManager, item => { + onCompletionAccepted.fire(item); + }); + + registerCommands(commandManager, lazyClientHost, pluginConfigProvider); context.subscriptions.push(new TypeScriptTaskProviderManager(lazyClientHost.map(x => x.serviceClient))); context.subscriptions.push(new LanguageConfigurationManager()); @@ -62,12 +79,25 @@ export function activate( break; } } + + return { + getAPI(version) { + if (version === 0) { + return { + onCompletionAccepted: onCompletionAccepted.event + } as ApiV0; + } + return undefined; + } + } as Api; } function createLazyClientHost( context: vscode.ExtensionContext, plugins: TypeScriptServerPlugin[], - commandManager: CommandManager + pluginConfigProvider: PluginConfigProvider, + commandManager: CommandManager, + onCompletionAccepted: (item: vscode.CompletionItem) => void, ): Lazy { return lazy(() => { const logDirectoryProvider = new LogDirectoryProvider(context); @@ -76,8 +106,10 @@ function createLazyClientHost( standardLanguageDescriptions, context.workspaceState, plugins, + pluginConfigProvider, commandManager, - logDirectoryProvider); + logDirectoryProvider, + onCompletionAccepted); context.subscriptions.push(clientHost); @@ -99,7 +131,8 @@ function createLazyClientHost( function registerCommands( commandManager: CommandManager, - lazyClientHost: Lazy + lazyClientHost: Lazy, + pluginConfigProvider: PluginConfigProvider, ) { commandManager.register(new commands.ReloadTypeScriptProjectsCommand(lazyClientHost)); commandManager.register(new commands.ReloadJavaScriptProjectsCommand(lazyClientHost)); @@ -108,6 +141,7 @@ function registerCommands( commandManager.register(new commands.RestartTsServerCommand(lazyClientHost)); commandManager.register(new commands.TypeScriptGoToProjectConfigCommand(lazyClientHost)); commandManager.register(new commands.JavaScriptGoToProjectConfigCommand(lazyClientHost)); + commandManager.register(new commands.ConfigurePluginCommand(pluginConfigProvider)); } function isSupportedDocument( diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index 01f598b5b6b..1dfb2530981 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -76,15 +76,32 @@ class MyCompletionItem extends vscode.CompletionItem { } } - if (tsEntry.kindModifiers && tsEntry.kindModifiers.match(/\boptional\b/)) { - if (!this.insertText) { - this.insertText = this.label; + if (tsEntry.kindModifiers) { + const kindModifiers = new Set(tsEntry.kindModifiers.split(/\s+/g)); + + if (kindModifiers.has(PConst.KindModifiers.optional)) { + if (!this.insertText) { + this.insertText = this.label; + } + + if (!this.filterText) { + this.filterText = this.label; + } + this.label += '?'; } - if (!this.filterText) { - this.filterText = this.label; + if (tsEntry.kind === PConst.Kind.script) { + for (const extModifier of PConst.KindModifiers.fileExtensionKindModifiers) { + if (kindModifiers.has(extModifier)) { + if (tsEntry.name.toLowerCase().endsWith(extModifier)) { + this.detail = tsEntry.name; + } else { + this.detail = tsEntry.name + extModifier; + } + break; + } + } } - this.label += '?'; } this.resolveRange(line); } @@ -191,6 +208,31 @@ class MyCompletionItem extends vscode.CompletionItem { } } +class CompositeCommand implements Command { + public static readonly ID = '_typescript.composite'; + public readonly id = CompositeCommand.ID; + + public execute(...commands: vscode.Command[]) { + for (const command of commands) { + vscode.commands.executeCommand(command.command, ...(command.arguments || [])); + } + } +} + + +class CompletionAcceptedCommand implements Command { + public static readonly ID = '_typescript.onCompletionAccepted'; + public readonly id = CompletionAcceptedCommand.ID; + + public constructor( + private readonly onCompletionAccepted: (item: vscode.CompletionItem) => void, + ) { } + + public execute(item: vscode.CompletionItem) { + this.onCompletionAccepted(item); + } +} + class ApplyCompletionCodeActionCommand implements Command { public static readonly ID = '_typescript.applyCompletionCodeAction'; public readonly id = ApplyCompletionCodeActionCommand.ID; @@ -270,9 +312,12 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider private readonly modeId: string, private readonly typingsStatus: TypingsStatus, private readonly fileConfigurationManager: FileConfigurationManager, - commandManager: CommandManager + commandManager: CommandManager, + onCompletionAccepted: (item: vscode.CompletionItem) => void ) { commandManager.register(new ApplyCompletionCodeActionCommand(this.client)); + commandManager.register(new CompositeCommand()); + commandManager.register(new CompletionAcceptedCommand(onCompletionAccepted)); } public async provideCompletionItems( @@ -372,30 +417,46 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider ] }; - let details: Proto.CompletionEntryDetails[] | undefined; const response = await this.client.execute('completionEntryDetails', args, token); - if (response.type !== 'response') { + if (response.type !== 'response' || !response.body) { return item; } - const { body } = response; - details = body; - if (!details || !details.length || !details[0]) { - return item; + const detail = response.body[0]; + + if (!item.detail && detail.displayParts.length) { + item.detail = Previewer.plain(detail.displayParts); } - const detail = details[0]; - item.detail = detail.displayParts.length ? Previewer.plain(detail.displayParts) : undefined; item.documentation = this.getDocumentation(detail, item); - const { command, additionalTextEdits } = this.getCodeActions(detail, filepath); - item.command = command; - item.additionalTextEdits = additionalTextEdits; + const codeAction = this.getCodeActions(detail, filepath); + const commands: vscode.Command[] = [{ + command: CompletionAcceptedCommand.ID, + title: '', + arguments: [item] + }]; + if (codeAction.command) { + commands.push(codeAction.command); + } + item.additionalTextEdits = codeAction.additionalTextEdits; if (detail && item.useCodeSnippet) { const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, item.position, token); if (shouldCompleteFunction) { item.insertText = this.snippetForFunctionCall(item, detail); - item.command = { title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }; + commands.push({ title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }); + } + } + + if (commands.length) { + if (commands.length === 1) { + item.command = commands[0]; + } else { + item.command = { + command: CompositeCommand.ID, + title: '', + arguments: commands + }; } } @@ -456,14 +517,16 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider document: vscode.TextDocument, position: vscode.Position ): boolean { - // TODO: Workaround for https://github.com/Microsoft/TypeScript/issues/13456 - // Only enable dot completions when previous character is an identifier. - // Prevents incorrectly completing while typing spread operators. - if (position.character > 1) { - const preText = document.getText(new vscode.Range( - position.line, 0, - position.line, position.character)); - return preText.match(/(^|[a-z_$\(\)\[\]\{\}]|[^.]\.)\s*$/ig) !== null; + if (this.client.apiVersion.lt(API.v320)) { + // Workaround for https://github.com/Microsoft/TypeScript/issues/27742 + // Only enable dot completions when previous character not a dot preceeded by whitespace. + // Prevents incorrectly completing while typing spread operators. + if (position.character > 1) { + const preText = document.getText(new vscode.Range( + position.line, 0, + position.line, position.character)); + return preText.match(/(\s|^)\.$/ig) === null; + } } return true; @@ -628,9 +691,10 @@ export function register( typingsStatus: TypingsStatus, fileConfigurationManager: FileConfigurationManager, commandManager: CommandManager, + onCompletionAccepted: (item: vscode.CompletionItem) => void ) { return new ConfigurationDependentRegistration(modeId, 'suggest.enabled', () => vscode.languages.registerCompletionItemProvider(selector, - new TypeScriptCompletionItemProvider(client, modeId, typingsStatus, fileConfigurationManager, commandManager), + new TypeScriptCompletionItemProvider(client, modeId, typingsStatus, fileConfigurationManager, commandManager, onCompletionAccepted), ...TypeScriptCompletionItemProvider.triggerCharacters)); } diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index 93aab9d7ca8..3e49abaaef4 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -194,7 +194,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { return false; } - return rangeOrSelection instanceof vscode.Selection && !rangeOrSelection.isEmpty; + return rangeOrSelection instanceof vscode.Selection; } private static getKind(refactor: Proto.RefactorActionInfo) { diff --git a/extensions/typescript-language-features/src/features/signatureHelp.ts b/extensions/typescript-language-features/src/features/signatureHelp.ts index 001155c7e02..8eac3a9ca91 100644 --- a/extensions/typescript-language-features/src/features/signatureHelp.ts +++ b/extensions/typescript-language-features/src/features/signatureHelp.ts @@ -73,16 +73,20 @@ class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { function toTsTriggerReason(context: vscode.SignatureHelpContext): Proto.SignatureHelpTriggerReason { switch (context.triggerReason) { - case vscode.SignatureHelpTriggerReason.Retrigger: - return { kind: 'retrigger' }; - case vscode.SignatureHelpTriggerReason.TriggerCharacter: if (context.triggerCharacter) { - return { kind: 'characterTyped', triggerCharacter: context.triggerCharacter as any }; + if (context.isRetrigger) { + return { kind: 'retrigger', triggerCharacter: context.triggerCharacter as any }; + } else { + return { kind: 'characterTyped', triggerCharacter: context.triggerCharacter as any }; + } } else { return { kind: 'invoked' }; } + case vscode.SignatureHelpTriggerReason.ContentChange: + return context.isRetrigger ? { kind: 'retrigger' } : { kind: 'invoked' }; + case vscode.SignatureHelpTriggerReason.Invoke: default: return { kind: 'invoked' }; diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 25cb080ec61..c95fc1e70f4 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -29,7 +29,8 @@ export default class LanguageProvider extends Disposable { private readonly commandManager: CommandManager, private readonly telemetryReporter: TelemetryReporter, private readonly typingsStatus: TypingsStatus, - private readonly fileConfigurationManager: FileConfigurationManager + private readonly fileConfigurationManager: FileConfigurationManager, + private readonly onCompletionAccepted: (item: vscode.CompletionItem) => void, ) { super(); vscode.workspace.onDidChangeConfiguration(this.configurationChanged, this, this._disposables); @@ -57,7 +58,7 @@ export default class LanguageProvider extends Disposable { const cachedResponse = new CachedNavTreeResponse(); - this._register((await import('./features/completions')).register(selector, this.description.id, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager)); + this._register((await import('./features/completions')).register(selector, this.description.id, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager, this.onCompletionAccepted)); this._register((await import('./features/definitions')).register(selector, this.client)); this._register((await import('./features/directiveCommentCompletions')).register(selector, this.client)); this._register((await import('./features/documentHighlight')).register(selector, this.client)); diff --git a/extensions/typescript-language-features/src/protocol.const.ts b/extensions/typescript-language-features/src/protocol.const.ts index 241bffd24d5..d06820b106f 100644 --- a/extensions/typescript-language-features/src/protocol.const.ts +++ b/extensions/typescript-language-features/src/protocol.const.ts @@ -39,4 +39,24 @@ export class DiagnosticCategory { public static readonly error = 'error'; public static readonly warning = 'warning'; public static readonly suggestion = 'suggestion'; +} + +export class KindModifiers { + public static readonly optional = 'optional'; + + public static readonly dtsFile = '.d.ts'; + public static readonly tsFile = '.ts'; + public static readonly tsxFile = '.tsx'; + public static readonly jsFile = '.js'; + public static readonly jsxFile = '.jsx'; + public static readonly jsonFile = '.json'; + + public static readonly fileExtensionKindModifiers = [ + KindModifiers.dtsFile, + KindModifiers.tsFile, + KindModifiers.tsxFile, + KindModifiers.jsFile, + KindModifiers.jsxFile, + KindModifiers.jsonFile, + ]; } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/test/requestQueue.test.ts b/extensions/typescript-language-features/src/test/requestQueue.test.ts new file mode 100644 index 00000000000..c157ae244cf --- /dev/null +++ b/extensions/typescript-language-features/src/test/requestQueue.test.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import { RequestQueue, RequestQueueingType } from '../tsServer/requestQueue'; + +suite('RequestQueue', () => { + test('should be empty on creation', async () => { + const queue = new RequestQueue(); + assert.strictEqual(queue.length, 0); + assert.strictEqual(queue.shift(), undefined); + }); + + suite('RequestQueue.createRequest', () => { + test('should create items with increasing sequence numbers', async () => { + const queue = new RequestQueue(); + + for (let i = 0; i < 100; ++i) { + const command = `command-${i}`; + const request = queue.createRequest(command, i); + assert.strictEqual(request.seq, i); + assert.strictEqual(request.command, command); + assert.strictEqual(request.arguments, i); + } + }); + }); + + test('should queue normal requests in first in first out order', async () => { + const queue = new RequestQueue(); + assert.strictEqual(queue.length, 0); + + const request1 = queue.createRequest('a', 1); + queue.push({ request: request1, expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Normal }); + assert.strictEqual(queue.length, 1); + + const request2 = queue.createRequest('b', 2); + queue.push({ request: request2, expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Normal }); + assert.strictEqual(queue.length, 2); + + { + const item = queue.shift(); + assert.strictEqual(queue.length, 1); + assert.strictEqual(item!.request.command, 'a'); + } + { + const item = queue.shift(); + assert.strictEqual(queue.length, 0); + assert.strictEqual(item!.request.command, 'b'); + } + { + const item = queue.shift(); + assert.strictEqual(item, undefined); + assert.strictEqual(queue.length, 0); + } + }); + + test('should put normal requests in front of low priority requests', async () => { + const queue = new RequestQueue(); + assert.strictEqual(queue.length, 0); + + queue.push({ request: queue.createRequest('low-1', 1), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.LowPriority }); + queue.push({ request: queue.createRequest('low-2', 1), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.LowPriority }); + queue.push({ request: queue.createRequest('normal-1', 2), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Normal }); + queue.push({ request: queue.createRequest('normal-2', 2), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Normal }); + + { + const item = queue.shift(); + assert.strictEqual(queue.length, 3); + assert.strictEqual(item!.request.command, 'normal-1'); + } + { + const item = queue.shift(); + assert.strictEqual(queue.length, 2); + assert.strictEqual(item!.request.command, 'normal-2'); + } + { + const item = queue.shift(); + assert.strictEqual(queue.length, 1); + assert.strictEqual(item!.request.command, 'low-1'); + } + { + const item = queue.shift(); + assert.strictEqual(queue.length, 0); + assert.strictEqual(item!.request.command, 'low-2'); + } + }); + + test('should not push fence requests front of low priority requests', async () => { + const queue = new RequestQueue(); + assert.strictEqual(queue.length, 0); + + queue.push({ request: queue.createRequest('low-1', 0), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.LowPriority }); + queue.push({ request: queue.createRequest('fence', 0), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Fence }); + queue.push({ request: queue.createRequest('low-2', 0), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.LowPriority }); + queue.push({ request: queue.createRequest('normal', 0), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Normal }); + + { + const item = queue.shift(); + assert.strictEqual(queue.length, 3); + assert.strictEqual(item!.request.command, 'low-1'); + } + { + const item = queue.shift(); + assert.strictEqual(queue.length, 2); + assert.strictEqual(item!.request.command, 'fence'); + } + { + const item = queue.shift(); + assert.strictEqual(queue.length, 1); + assert.strictEqual(item!.request.command, 'normal'); + } + { + const item = queue.shift(); + assert.strictEqual(queue.length, 0); + assert.strictEqual(item!.request.command, 'low-2'); + } + }); +}); + diff --git a/extensions/typescript-language-features/src/tsServer/callbackMap.ts b/extensions/typescript-language-features/src/tsServer/callbackMap.ts new file mode 100644 index 00000000000..ec80c07ab02 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/callbackMap.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as Proto from '../protocol'; +import { CancelledResponse, ServerResponse } from '../typescriptService'; + +export interface CallbackItem { + readonly onSuccess: (value: R) => void; + readonly onError: (err: any) => void; + readonly startTime: number; + readonly isAsync: boolean; +} + +export class CallbackMap { + private readonly _callbacks = new Map | undefined>>(); + private readonly _asyncCallbacks = new Map | undefined>>(); + + public destroy(cause: string): void { + const cancellation = new CancelledResponse(cause); + for (const callback of this._callbacks.values()) { + callback.onSuccess(cancellation); + } + this._callbacks.clear(); + for (const callback of this._asyncCallbacks.values()) { + callback.onSuccess(cancellation); + } + this._asyncCallbacks.clear(); + } + + public add(seq: number, callback: CallbackItem | undefined>, isAsync: boolean) { + if (isAsync) { + this._asyncCallbacks.set(seq, callback); + } + else { + this._callbacks.set(seq, callback); + } + } + + public fetch(seq: number): CallbackItem | undefined> | undefined { + const callback = this._callbacks.get(seq) || this._asyncCallbacks.get(seq); + this.delete(seq); + return callback; + } + + private delete(seq: number) { + if (!this._callbacks.delete(seq)) { + this._asyncCallbacks.delete(seq); + } + } +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/tsServer/requestQueue.ts b/extensions/typescript-language-features/src/tsServer/requestQueue.ts new file mode 100644 index 00000000000..4ca1c44b072 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/requestQueue.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as Proto from '../protocol'; + +export enum RequestQueueingType { + /** + * Normal request that is executed in order. + */ + Normal = 1, + + /** + * Request that normal requests jump in front of in the queue. + */ + LowPriority = 2, + + /** + * A fence that blocks request reordering. + * + * Fences are not reordered but unlike a normal request, a fence will never jump in front of a low priority request + * in the request queue. + */ + Fence = 3, +} + +export interface RequestItem { + readonly request: Proto.Request; + readonly expectsResponse: boolean; + readonly isAsync: boolean; + readonly queueingType: RequestQueueingType; +} + +export class RequestQueue { + private readonly queue: RequestItem[] = []; + private sequenceNumber: number = 0; + + public get length(): number { + return this.queue.length; + } + + public push(item: RequestItem): void { + if (item.queueingType === RequestQueueingType.Normal) { + let index = this.queue.length - 1; + while (index >= 0) { + if (this.queue[index].queueingType !== RequestQueueingType.LowPriority) { + break; + } + --index; + } + this.queue.splice(index + 1, 0, item); + } else { + //if none is low priority just push to end + this.queue.push(item); + } + } + + public shift(): RequestItem | undefined { + return this.queue.shift(); + } + + public tryCancelPendingRequest(seq: number): boolean { + for (let i = 0; i < this.queue.length; i++) { + if (this.queue[i].request.seq === seq) { + this.queue.splice(i, 1); + return true; + } + } + return false; + } + + public createRequest(command: string, args: any): Proto.Request { + return { + seq: this.sequenceNumber++, + type: 'request', + command: command, + arguments: args + }; + } +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts similarity index 81% rename from extensions/typescript-language-features/src/server.ts rename to extensions/typescript-language-features/src/tsServer/server.ts index cd216ce6d63..a57c679e039 100644 --- a/extensions/typescript-language-features/src/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -7,108 +7,22 @@ import * as cp from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import * as Proto from './protocol'; -import { CancelledResponse, NoContentResponse, ServerResponse } from './typescriptService'; -import API from './utils/api'; -import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; -import { Disposable } from './utils/dispose'; -import * as electron from './utils/electron'; -import LogDirectoryProvider from './utils/logDirectoryProvider'; -import Logger from './utils/logger'; -import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider'; -import { TypeScriptServerPlugin } from './utils/plugins'; -import TelemetryReporter from './utils/telemetry'; -import Tracer from './utils/tracer'; -import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider'; -import { Reader } from './utils/wireProtocol'; - -interface CallbackItem { - readonly onSuccess: (value: R) => void; - readonly onError: (err: any) => void; - readonly startTime: number; - readonly isAsync: boolean; -} - -class CallbackMap { - private readonly _callbacks = new Map | undefined>>(); - private readonly _asyncCallbacks = new Map | undefined>>(); - - public destroy(cause: string): void { - const cancellation = new CancelledResponse(cause); - for (const callback of this._callbacks.values()) { - callback.onSuccess(cancellation); - } - this._callbacks.clear(); - - for (const callback of this._asyncCallbacks.values()) { - callback.onSuccess(cancellation); - } - this._asyncCallbacks.clear(); - } - - public add(seq: number, callback: CallbackItem | undefined>, isAsync: boolean) { - if (isAsync) { - this._asyncCallbacks.set(seq, callback); - } else { - this._callbacks.set(seq, callback); - } - } - - - public fetch(seq: number): CallbackItem | undefined> | undefined { - const callback = this._callbacks.get(seq) || this._asyncCallbacks.get(seq); - this.delete(seq); - return callback; - } - - private delete(seq: number) { - if (!this._callbacks.delete(seq)) { - this._asyncCallbacks.delete(seq); - } - } -} - -interface RequestItem { - readonly request: Proto.Request; - readonly expectsResponse: boolean; - readonly isAsync: boolean; -} - -class RequestQueue { - private readonly queue: RequestItem[] = []; - private sequenceNumber: number = 0; - - public get length(): number { - return this.queue.length; - } - - public push(item: RequestItem): void { - this.queue.push(item); - } - - public shift(): RequestItem | undefined { - return this.queue.shift(); - } - - public tryCancelPendingRequest(seq: number): boolean { - for (let i = 0; i < this.queue.length; i++) { - if (this.queue[i].request.seq === seq) { - this.queue.splice(i, 1); - return true; - } - } - return false; - } - - public createRequest(command: string, args: any): Proto.Request { - return { - seq: this.sequenceNumber++, - type: 'request', - command: command, - arguments: args - }; - } -} +import * as Proto from '../protocol'; +import { CancelledResponse, NoContentResponse } from '../typescriptService'; +import API from '../utils/api'; +import { TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; +import { Disposable } from '../utils/dispose'; +import * as electron from '../utils/electron'; +import LogDirectoryProvider from '../utils/logDirectoryProvider'; +import Logger from '../utils/logger'; +import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider'; +import { TypeScriptServerPlugin } from '../utils/plugins'; +import TelemetryReporter from '../utils/telemetry'; +import Tracer from '../utils/tracer'; +import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider'; +import { Reader } from '../utils/wireProtocol'; +import { CallbackMap } from './callbackMap'; +import { RequestQueue, RequestItem, RequestQueueingType } from './requestQueue'; export class TypeScriptServerSpawner { public constructor( @@ -381,12 +295,13 @@ export class TypeScriptServer extends Disposable { } } - public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean }): Promise { + public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise { const request = this._requestQueue.createRequest(command, args); const requestInfo: RequestItem = { request: request, expectsResponse: executeInfo.expectsResult, - isAsync: executeInfo.isAsync + isAsync: executeInfo.isAsync, + queueingType: getQueueingType(command, executeInfo.lowPriority) }; let result: Promise; if (executeInfo.expectsResult) { @@ -490,3 +405,15 @@ export class TypeScriptServer extends Disposable { } } +const fenceCommands = new Set(['change', 'close', 'open']); + +function getQueueingType( + command: string, + lowPriority?: boolean +): RequestQueueingType { + if (fenceCommands.has(command)) { + return RequestQueueingType.Fence; + } + return lowPriority ? RequestQueueingType.LowPriority : RequestQueueingType.Normal; +} + diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index df5613d849a..2fa36ce3959 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -14,7 +14,7 @@ import FileConfigurationManager from './features/fileConfigurationManager'; import LanguageProvider from './languageProvider'; import * as Proto from './protocol'; import * as PConst from './protocol.const'; -import TypeScriptServiceClient from './typescriptServiceClient'; +import TypeScriptServiceClient, { PluginConfigProvider } from './typescriptServiceClient'; import API from './utils/api'; import { CommandManager } from './utils/commandManager'; import { Disposable } from './utils/dispose'; @@ -49,8 +49,10 @@ export default class TypeScriptServiceClientHost extends Disposable { descriptions: LanguageDescription[], workspaceState: vscode.Memento, plugins: TypeScriptServerPlugin[], + pluginConfigProvider: PluginConfigProvider, private readonly commandManager: CommandManager, - logDirectoryProvider: LogDirectoryProvider + logDirectoryProvider: LogDirectoryProvider, + onCompletionAccepted: (item: vscode.CompletionItem) => void, ) { super(); const handleProjectCreateOrDelete = () => { @@ -72,6 +74,7 @@ export default class TypeScriptServiceClientHost extends Disposable { workspaceState, version => this.versionStatus.onDidChangeTypeScriptVersion(version), plugins, + pluginConfigProvider, logDirectoryProvider, allModeIds)); @@ -89,7 +92,7 @@ export default class TypeScriptServiceClientHost extends Disposable { this.fileConfigurationManager = this._register(new FileConfigurationManager(this.client)); for (const description of descriptions) { - const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager); + const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted); this.languages.push(manager); this._register(manager); this.languagePerId.set(description.id, manager); @@ -122,7 +125,7 @@ export default class TypeScriptServiceClientHost extends Disposable { diagnosticOwner: 'typescript', isExternal: true }; - const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager); + const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted); this.languages.push(manager); this._register(manager); this.languagePerId.set(description.id, manager); diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 8852e8b4929..d14201af795 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -95,7 +95,8 @@ export interface ITypeScriptServiceClient { execute( command: K, args: TypeScriptRequestTypes[K][0], - token: vscode.CancellationToken + token: vscode.CancellationToken, + lowPriority?: boolean ): Promise>; executeWithoutWaitingForResponse(command: 'open', args: Proto.OpenRequestArgs): void; diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index a70d25330e2..41d53385257 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -10,7 +10,7 @@ import * as nls from 'vscode-nls'; import BufferSyncSupport from './features/bufferSyncSupport'; import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics'; import * as Proto from './protocol'; -import { TypeScriptServer, TypeScriptServerSpawner } from './server'; +import { TypeScriptServer, TypeScriptServerSpawner } from './tsServer/server'; import { ITypeScriptServiceClient } from './typescriptService'; import API from './utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; @@ -29,6 +29,22 @@ import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionPro const localize = nls.loadMessageBundle(); +export class PluginConfigProvider extends Disposable { + private readonly _config = new Map(); + + private readonly _onDidUpdateConfig = this._register(new vscode.EventEmitter<{ pluginId: string, config: {} }>()); + public readonly onDidUpdateConfig = this._onDidUpdateConfig.event; + + public set(pluginId: string, config: {}) { + this._config.set(pluginId, config); + this._onDidUpdateConfig.fire({ pluginId, config }); + } + + public entries(): IterableIterator<[string, {}]> { + return this._config.entries(); + } +} + export interface TsDiagnostics { readonly kind: DiagnosticKind; readonly resource: vscode.Uri; @@ -75,6 +91,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType private readonly workspaceState: vscode.Memento, private readonly onDidChangeTypeScriptVersion: (version: TypeScriptVersion) => void, public readonly plugins: TypeScriptServerPlugin[], + private readonly pluginConfigProvider: PluginConfigProvider, private readonly logDirectoryProvider: LogDirectoryProvider, allModeIds: string[] ) { @@ -132,6 +149,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.telemetryReporter = this._register(new TelemetryReporter(() => this._tsserverVersion || this._apiVersion.versionString)); this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer); + + this._register(this.pluginConfigProvider.onDidUpdateConfig(update => { + this.configurePlugin(update.pluginId, update.config); + })); } public get configuration() { @@ -406,6 +427,11 @@ export default class TypeScriptServiceClient extends Disposable implements IType if (resendModels) { this._onResendModelsRequested.fire(); } + + // Reconfigure any plugins + for (const [config, pluginName] of this.pluginConfigProvider.entries()) { + this.configurePlugin(config, pluginName); + } } private setCompilerOptionsForInferredProjects(configuration: TypeScriptServiceConfiguration): void { @@ -560,11 +586,12 @@ export default class TypeScriptServiceClient extends Disposable implements IType return undefined; } - public execute(command: string, args: any, token: vscode.CancellationToken): Promise { + public execute(command: string, args: any, token: vscode.CancellationToken, lowPriority?: boolean): Promise { return this.executeImpl(command, args, { isAsync: false, token, - expectsResult: true + expectsResult: true, + lowPriority }); } @@ -584,7 +611,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType }); } - private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean }): Promise { + private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise { const server = this.service(); if (!server) { return Promise.reject(new Error('Could not load TS Server')); @@ -718,8 +745,13 @@ export default class TypeScriptServiceClient extends Disposable implements IType this._apiVersion = API.defaultVersion; this._tsserverVersion = undefined; } -} + private configurePlugin(pluginName: string, configuration: {}): any { + if (this._apiVersion.gte(API.v314)) { + this.executeWithoutWaitingForResponse('configurePlugin', { pluginName, configuration }); + } + } +} function getDignosticsKind(event: Proto.Event) { switch (event.event) { diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index 9bcab14cf59..0c7f5b9ff69 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -31,6 +31,7 @@ export default class API { public static readonly v292 = API.fromSimpleString('2.9.2'); public static readonly v300 = API.fromSimpleString('3.0.0'); public static readonly v310 = API.fromSimpleString('3.1.0'); + public static readonly v314 = API.fromSimpleString('3.1.4'); public static readonly v320 = API.fromSimpleString('3.2.0'); diff --git a/extensions/vscode-api-tests/.vscode/tasks.json b/extensions/vscode-api-tests/.vscode/tasks.json index e2a020d0645..390a93a3a7f 100644 --- a/extensions/vscode-api-tests/.vscode/tasks.json +++ b/extensions/vscode-api-tests/.vscode/tasks.json @@ -1,30 +1,11 @@ -// Available variables which can be used inside of strings. -// ${workspaceFolder}: the root folder of the team -// ${file}: the current opened file -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls a custom npm script that compiles the extension. { - "version": "0.1.0", - - // we want to run npm + "version": "2.0.0", "command": "npm", - - // the command is a shell script - "isShellCommand": true, - - // show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // we run the custom script "compile" as defined in package.json - "args": ["run", "compile", "--loglevel", "silent"], - - // The tsc compiler is started in watching mode - "isWatching": true, - - // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "type": "shell", + "presentation": { + "reveal": "silent" + }, + "args": ["run", "compile"], + "isBackground": true, "problemMatcher": "$tsc-watch" -} \ No newline at end of file +} diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index dce3b608da5..1c56454d0d5 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -384,6 +384,7 @@ suite('window namespace tests', () => { test('showQuickPick, accept first', async function () { const pick = window.showQuickPick(['eins', 'zwei', 'drei']); + await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); assert.equal(await pick, 'eins'); }); @@ -421,6 +422,7 @@ suite('window namespace tests', () => { canPickMany: true }); const first = new Promise(resolve => resolves.push(resolve)); + await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. await commands.executeCommand('workbench.action.quickOpenSelectNext'); assert.equal(await first, 'eins'); await commands.executeCommand('workbench.action.quickPickManyToggle'); @@ -442,6 +444,7 @@ suite('window namespace tests', () => { ], { canPickMany: true }); + await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); assert.deepStrictEqual((await picks)!.map(pick => pick.label), ['zwei', 'drei']); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 7af7b00b60c..8921b4aee46 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -520,8 +520,9 @@ suite('workspace-namespace', () => { }); assert.equal(results.length, 1); - assert(results[0].preview.text.indexOf('foo') >= 0); - assert.equal(vscode.workspace.asRelativePath(results[0].uri), '10linefile.ts'); + const match = results[0]; + assert(match.preview.text.indexOf('foo') >= 0); + assert.equal(vscode.workspace.asRelativePath(match.uri), '10linefile.ts'); }); test('findTextInFiles, cancellation', async () => { diff --git a/extensions/vscode-colorize-tests/.vscode/tasks.json b/extensions/vscode-colorize-tests/.vscode/tasks.json index 3b6c357da2d..390a93a3a7f 100644 --- a/extensions/vscode-colorize-tests/.vscode/tasks.json +++ b/extensions/vscode-colorize-tests/.vscode/tasks.json @@ -1,31 +1,11 @@ -// Available variables which can be used inside of strings. -// ${workspaceFolder}: the root folder of the team -// ${file}: the current opened file -// ${relativeFile}: the current opened file relative to cwd -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls a custom npm script that compiles the extension. { - "version": "0.1.0", - - // we want to run npm + "version": "2.0.0", "command": "npm", - - // the command is a shell script - "isShellCommand": true, - - // show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // we run the custom script "compile" as defined in package.json - "args": ["run", "compile", "--loglevel", "silent"], - - // The tsc compiler is started in watching mode - "isWatching": true, - - // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "type": "shell", + "presentation": { + "reveal": "silent" + }, + "args": ["run", "compile"], + "isBackground": true, "problemMatcher": "$tsc-watch" -} \ No newline at end of file +} diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 0630b4a19f8..e2dfd06e5bb 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.3.tgz#01b70247a6d3c2467f70c45795ef5ea18ce191d5" - integrity sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA== +typescript@3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.4.tgz#c74ef7b3c2da65beff548b903022cb8c3cd997ed" + integrity sha512-JZHJtA6ZL15+Q3Dqkbh8iCUmvxD3iJ7ujXS+fVkKnwIVAdHc5BJTDNM0aTrnr2luKulFjU7W+SRhDZvi66Ru7Q== diff --git a/package.json b/package.json index d362ad36c35..d15b3dfc915 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.29.0", - "distro": "eceef408e99ace55c0a17a2a90b88e46a8c9090a", + "version": "1.30.0", + "distro": "3e54b1cdfa3b0bb32253d0e81e2fe2b875b2a92a", "author": { "name": "Microsoft Corporation" }, @@ -46,13 +46,14 @@ "spdlog": "0.7.2", "sudo-prompt": "8.2.0", "v8-inspect-profiler": "^0.0.8", - "vscode-chokidar": "1.6.4", + "vscode-chokidar": "1.6.5", "vscode-debugprotocol": "1.32.0", "vscode-nsfw": "1.1.1", - "vscode-ripgrep": "^1.2.2", + "vscode-proxy-agent": "0.1.1", + "vscode-ripgrep": "^1.2.4", "vscode-sqlite3": "4.0.2", "vscode-textmate": "^4.0.1", - "vscode-xterm": "3.9.0-beta9", + "vscode-xterm": "3.9.0-beta13", "winreg": "^1.2.4", "yauzl": "^2.9.1", "yazl": "^2.4.3" @@ -124,7 +125,7 @@ "source-map": "^0.4.4", "ts-loader": "^4.4.2", "tslint": "^5.9.1", - "typescript": "3.1.1", + "typescript": "3.1.4", "typescript-formatter": "7.1.0", "uglify-es": "^3.0.18", "underscore": "^1.8.2", diff --git a/product.json b/product.json index e95e36c0ec8..eb6526c2d8f 100644 --- a/product.json +++ b/product.json @@ -17,5 +17,8 @@ "win32ShellNameShort": "C&ode - OSS", "darwinBundleIdentifier": "com.visualstudio.code.oss", "reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new", - "urlProtocol": "code-oss" + "urlProtocol": "code-oss", + "extensionAllowedProposedApi": [ + "ms-vscode.references-view" + ] } diff --git a/resources/linux/snap/electron-launch b/resources/linux/snap/electron-launch old mode 100644 new mode 100755 index 65c91d6fc1b..6fdd68a34cf --- a/resources/linux/snap/electron-launch +++ b/resources/linux/snap/electron-launch @@ -1,3 +1,6 @@ #!/bin/sh -exec "$@" --executed-from="$(pwd)" --pid=$$ +# Create $XDG_RUNTIME_DIR if it doesn't exist +[ -n "$XDG_RUNTIME_DIR" ] && mkdir -p $XDG_RUNTIME_DIR -m 700 + +exec "$@" diff --git a/resources/linux/snap/snapUpdate.sh b/resources/linux/snap/snapUpdate.sh new file mode 100755 index 00000000000..d9299ca4d58 --- /dev/null +++ b/resources/linux/snap/snapUpdate.sh @@ -0,0 +1,3 @@ +#!/bin/sh +sleep 2 +@@NAME@@ \ No newline at end of file diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index e8a5d48fdf1..e0772308684 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -15,6 +15,7 @@ parts: source: . stage-packages: - libasound2 + - libc++1 - libgconf2-4 - libnotify4 - libnspr4 @@ -23,20 +24,21 @@ parts: - libpulse0 - libxss1 - libxtst6 - # desktop-gtk2 deps below - libxkbcommon0 - - libgtk2.0-0 - # - unity-gtk2-module + - libgtk-3-0 + - libgdk-pixbuf2.0-0 + - libglib2.0-bin + - unity-gtk2-module - libappindicator1 + - xdg-user-dirs + - libsecret-1-0 + # TODO@joao @Tyriar check these deps + # - libatomic1 + # - libgtk2.0-bin prime: - -usr/share/dh-python - electron-launch: - plugin: dump - source: . - organize: - electron-launch: bin/electron-launch apps: @@NAME@@: - command: bin/electron-launch ${SNAP}/usr/share/@@NAME@@/bin/@@NAME@@ + command: electron-launch ${SNAP}/usr/share/@@NAME@@/bin/@@NAME@@ desktop: usr/share/applications/@@NAME@@.desktop \ No newline at end of file diff --git a/scripts/code.bat b/scripts/code.bat index 58d5ca2d6e0..6789e54fdda 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -17,7 +17,7 @@ set CODE=".build\electron\%NAMESHORT%" node build\lib\electron.js if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron -:: Manage build-in extensions +:: Manage built-in extensions if "%1"=="--builtin" goto builtin :: Sync built-in extensions @@ -49,4 +49,4 @@ goto end popd -endlocal \ No newline at end of file +endlocal diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index 4b8310ee04a..fc13a2d99b4 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -5,12 +5,16 @@ pushd %~dp0\.. set VSCODEUSERDATADIR=%TMP%\vscodeuserfolder-%RANDOM%-%TIME:~6,5% -:: Tests in the extension host -call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% +:: Integration & performance tests in AMD +call .\scripts\test.bat --runGlob **\*.integrationTest.js %* if %errorlevel% neq 0 exit /b %errorlevel% -call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% -if %errorlevel% neq 0 exit /b %errorlevel% +:: Tests in the extension host +REM call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% +REM if %errorlevel% neq 0 exit /b %errorlevel% + +REM call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% +REM if %errorlevel% neq 0 exit /b %errorlevel% call .\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% @@ -18,10 +22,6 @@ if %errorlevel% neq 0 exit /b %errorlevel% call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% . if %errorlevel% neq 0 exit /b %errorlevel% -:: Integration & performance tests in AMD -call .\scripts\test.bat --runGlob **\*.integrationTest.js %* -if %errorlevel% neq 0 exit /b %errorlevel% - # Tests in commonJS (HTML, CSS, JSON language server tests...) call .\scripts\node-electron.bat .\node_modules\mocha\bin\_mocha .\extensions\*\server\out\test\**\*.test.js if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index d2225c4475d..a51f3ee172c 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -12,6 +12,9 @@ fi cd $ROOT +# Integration tests in AMD +./scripts/test.sh --runGlob **/*.integrationTest.js "$@" + # Tests in the extension host ./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started ./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started @@ -22,9 +25,6 @@ mkdir $ROOT/extensions/emmet/test-fixtures ./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started . rm -r $ROOT/extensions/emmet/test-fixtures -# Integration tests in AMD -./scripts/test.sh --runGlob **/*.integrationTest.js "$@" - # Tests in commonJS cd $ROOT/extensions/css-language-features/server && $ROOT/scripts/node-electron.sh test/index.js cd $ROOT/extensions/html-language-features/server && $ROOT/scripts/node-electron.sh test/index.js diff --git a/src/main.js b/src/main.js index 92375bc68c0..a551417c8e8 100644 --- a/src/main.js +++ b/src/main.js @@ -81,17 +81,14 @@ function onReady() { nlsConfiguration = Promise.resolve(undefined); } - // We first need to test a user defined locale. If it fails we try the app locale. + // First, we need to test a user defined locale. If it fails we try the app locale. // If that fails we fall back to English. nlsConfiguration.then((nlsConfig) => { const startup = nlsConfig => { nlsConfig._languagePackSupport = true; process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig); - - if (cachedDataDir) { - process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir; - } + process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir || ''; // Load main in AMD require('./bootstrap-amd').load('vs/code/electron-main/main'); @@ -407,7 +404,7 @@ function rimraf(location) { }); } -// Language tags are case insensitve however an amd loader is case sensitive +// Language tags are case insensitive however an amd loader is case sensitive // To make this work on case preserving & insensitive FS we do the following: // the language bundles have lower case language tags and we always lower case // the locale we receive from the user or OS. @@ -608,4 +605,4 @@ function getNLSConfiguration(locale) { return defaultResult(locale); } } -//#endregion \ No newline at end of file +//#endregion diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 9e295f40659..1bf4732a4a0 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -1,5 +1,4 @@ { - "$schema": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json", "compilerOptions": { "noEmit": true, "module": "amd", diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 2d17fd87162..1bd3c7c6fd6 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -22,11 +22,22 @@ "./vs/base/browser/mouseEvent.ts", "./vs/base/browser/touch.ts", "./vs/base/browser/ui/aria/aria.ts", + "./vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts", "./vs/base/browser/ui/button/button.ts", + "./vs/base/browser/ui/centered/centeredViewLayout.ts", "./vs/base/browser/ui/contextview/contextview.ts", "./vs/base/browser/ui/countBadge/countBadge.ts", + "./vs/base/browser/ui/grid/grid.ts", + "./vs/base/browser/ui/grid/gridview.ts", + "./vs/base/browser/ui/highlightedlabel/highlightedLabel.ts", + "./vs/base/browser/ui/iconLabel/iconLabel.ts", "./vs/base/browser/ui/keybindingLabel/keybindingLabel.ts", "./vs/base/browser/ui/list/list.ts", + "./vs/base/browser/ui/list/listPaging.ts", + "./vs/base/browser/ui/list/listView.ts", + "./vs/base/browser/ui/list/listWidget.ts", + "./vs/base/browser/ui/list/rangeMap.ts", + "./vs/base/browser/ui/list/rowCache.ts", "./vs/base/browser/ui/list/splice.ts", "./vs/base/browser/ui/octiconLabel/octiconLabel.mock.ts", "./vs/base/browser/ui/octiconLabel/octiconLabel.ts", @@ -40,31 +51,76 @@ "./vs/base/browser/ui/scrollbar/scrollbarState.ts", "./vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts", "./vs/base/browser/ui/scrollbar/verticalScrollbar.ts", + "./vs/base/browser/ui/splitview/panelview.ts", "./vs/base/browser/ui/splitview/splitview.ts", + "./vs/base/browser/ui/tree/abstractTree.ts", + "./vs/base/browser/ui/tree/dataTree.ts", + "./vs/base/browser/ui/tree/indexTree.ts", + "./vs/base/browser/ui/tree/indexTreeModel.ts", + "./vs/base/browser/ui/tree/objectTree.ts", + "./vs/base/browser/ui/tree/objectTreeModel.ts", "./vs/base/browser/ui/tree/tree.ts", "./vs/base/browser/ui/widget.ts", + "./vs/base/common/json.ts", + "./vs/base/common/jsonEdit.ts", + "./vs/base/common/jsonFormatter.ts", + "./vs/base/common/paths.ts", + "./vs/base/common/uriIpc.ts", + "./vs/base/node/console.ts", + "./vs/base/node/crypto.ts", "./vs/base/node/decoder.ts", + "./vs/base/node/encoding.ts", + "./vs/base/node/extfs.ts", + "./vs/base/node/flow.ts", "./vs/base/node/id.ts", "./vs/base/node/paths.ts", + "./vs/base/node/pfs.ts", "./vs/base/node/ports.ts", + "./vs/base/node/processes.ts", + "./vs/base/node/proxy.ts", "./vs/base/node/request.ts", + "./vs/base/node/stats.ts", + "./vs/base/node/storage.ts", + "./vs/base/node/stream.ts", "./vs/base/parts/contextmenu/common/contextmenu.ts", "./vs/base/parts/contextmenu/electron-browser/contextmenu.ts", "./vs/base/parts/contextmenu/electron-main/contextmenu.ts", + "./vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts", + "./vs/base/parts/ipc/electron-main/ipc.electron-main.ts", + "./vs/base/parts/ipc/node/ipc.cp.ts", + "./vs/base/parts/ipc/node/ipc.electron.ts", + "./vs/base/parts/ipc/node/ipc.net.ts", "./vs/base/parts/ipc/node/ipc.ts", + "./vs/base/parts/ipc/test/node/testApp.ts", "./vs/base/parts/ipc/test/node/testService.ts", "./vs/base/parts/quickopen/common/quickOpen.ts", + "./vs/base/test/browser/ui/grid/util.ts", + "./vs/base/test/common/json.test.ts", + "./vs/base/test/common/jsonEdit.test.ts", + "./vs/base/test/common/jsonFormatter.test.ts", + "./vs/base/test/common/paths.test.ts", "./vs/base/test/common/utils.ts", + "./vs/base/test/node/processes/fixtures/fork.ts", + "./vs/base/test/node/processes/fixtures/fork_large.ts", "./vs/base/test/node/uri.test.perf.ts", + "./vs/base/test/node/utils.ts", "./vs/base/worker/defaultWorkerFactory.ts", "./vs/base/worker/workerMain.ts", "./vs/code/code.main.ts", "./vs/code/electron-browser/issue/issueReporterModel.ts", "./vs/code/electron-browser/issue/issueReporterPage.ts", "./vs/code/electron-browser/issue/issueReporterUtil.ts", + "./vs/code/electron-browser/sharedProcess/contrib/contributions.ts", + "./vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts", + "./vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts", + "./vs/code/electron-main/auth.ts", "./vs/code/electron-main/keyboard.ts", + "./vs/code/electron-main/sharedProcess.ts", "./vs/code/electron-main/theme.ts", + "./vs/code/node/paths.ts", "./vs/code/node/shellEnv.ts", + "./vs/code/node/wait.ts", + "./vs/code/node/windowsFinder.ts", "./vs/editor/browser/config/charWidthReader.ts", "./vs/editor/browser/config/configuration.ts", "./vs/editor/browser/config/elementSizeObserver.ts", @@ -199,6 +255,7 @@ "./vs/editor/common/services/resourceConfigurationImpl.ts", "./vs/editor/common/services/webWorker.ts", "./vs/editor/common/standalone/standaloneBase.ts", + "./vs/editor/common/standalone/standaloneEnums.ts", "./vs/editor/common/view/editorColorRegistry.ts", "./vs/editor/common/view/minimapCharRenderer.ts", "./vs/editor/common/view/overviewZoneManager.ts", @@ -226,13 +283,18 @@ "./vs/editor/contrib/caretOperations/transpose.ts", "./vs/editor/contrib/clipboard/clipboard.ts", "./vs/editor/contrib/codeAction/codeAction.ts", + "./vs/editor/contrib/codeAction/codeActionModel.ts", "./vs/editor/contrib/codeAction/codeActionTrigger.ts", + "./vs/editor/contrib/codeAction/lightBulbWidget.ts", + "./vs/editor/contrib/codelens/codelens.ts", "./vs/editor/contrib/colorPicker/color.ts", + "./vs/editor/contrib/colorPicker/colorDetector.ts", "./vs/editor/contrib/colorPicker/colorPickerModel.ts", "./vs/editor/contrib/comment/blockCommentCommand.ts", "./vs/editor/contrib/comment/comment.ts", "./vs/editor/contrib/comment/lineCommentCommand.ts", "./vs/editor/contrib/cursorUndo/cursorUndo.ts", + "./vs/editor/contrib/dnd/dnd.ts", "./vs/editor/contrib/dnd/dragAndDropCommand.ts", "./vs/editor/contrib/find/findDecorations.ts", "./vs/editor/contrib/find/findModel.ts", @@ -256,20 +318,27 @@ "./vs/editor/contrib/fontZoom/fontZoom.ts", "./vs/editor/contrib/goToDefinition/clickLinkGesture.ts", "./vs/editor/contrib/goToDefinition/goToDefinition.ts", + "./vs/editor/contrib/gotoError/gotoErrorWidget.ts", "./vs/editor/contrib/hover/getHover.ts", "./vs/editor/contrib/hover/hoverOperation.ts", "./vs/editor/contrib/hover/hoverWidgets.ts", + "./vs/editor/contrib/hover/modesGlyphHover.ts", "./vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts", "./vs/editor/contrib/indentation/indentUtils.ts", "./vs/editor/contrib/linesOperations/copyLinesCommand.ts", "./vs/editor/contrib/linesOperations/deleteLinesCommand.ts", + "./vs/editor/contrib/linesOperations/moveLinesCommand.ts", "./vs/editor/contrib/linesOperations/sortLinesCommand.ts", "./vs/editor/contrib/links/getLinks.ts", "./vs/editor/contrib/links/links.ts", + "./vs/editor/contrib/markdown/markdownRenderer.ts", "./vs/editor/contrib/message/messageController.ts", "./vs/editor/contrib/parameterHints/provideSignatureHelp.ts", "./vs/editor/contrib/quickOpen/quickOpen.ts", "./vs/editor/contrib/referenceSearch/referencesModel.ts", + "./vs/editor/contrib/rename/rename.ts", + "./vs/editor/contrib/rename/renameInputField.ts", + "./vs/editor/contrib/smartSelect/tokenSelectionSupport.ts", "./vs/editor/contrib/smartSelect/tokenTree.ts", "./vs/editor/contrib/snippet/snippetParser.ts", "./vs/editor/contrib/suggest/suggest.ts", @@ -277,8 +346,10 @@ "./vs/editor/contrib/suggest/wordDistance.ts", "./vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts", "./vs/editor/contrib/wordHighlighter/wordHighlighter.ts", + "./vs/editor/contrib/wordOperations/test/wordTestUtils.ts", "./vs/editor/contrib/wordOperations/wordOperations.ts", "./vs/editor/contrib/wordPartOperations/wordPartOperations.ts", + "./vs/editor/contrib/zoneWidget/zoneWidget.ts", "./vs/editor/editor.worker.ts", "./vs/editor/standalone/browser/colorizer.ts", "./vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts", @@ -293,6 +364,8 @@ "./vs/editor/standalone/common/themes.ts", "./vs/editor/test/browser/controller/imeTester.ts", "./vs/editor/test/browser/editorTestServices.ts", + "./vs/editor/test/browser/testCodeEditor.ts", + "./vs/editor/test/browser/testCommand.ts", "./vs/editor/test/browser/view/minimapFontCreator.ts", "./vs/editor/test/common/commentMode.ts", "./vs/editor/test/common/core/viewLineToken.ts", @@ -300,7 +373,12 @@ "./vs/editor/test/common/mocks/mockMode.ts", "./vs/editor/test/common/mocks/testConfiguration.ts", "./vs/editor/test/common/model/benchmark/benchmarkUtils.ts", + "./vs/editor/test/common/model/benchmark/entry.ts", + "./vs/editor/test/common/model/benchmark/modelbuilder.benchmark.ts", + "./vs/editor/test/common/model/benchmark/operations.benchmark.ts", + "./vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts", "./vs/editor/test/common/model/editableTextModelTestUtils.ts", + "./vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts", "./vs/editor/test/common/modes/supports/javascriptOnEnterRules.ts", "./vs/editor/test/common/modesTestUtils.ts", "./vs/editor/test/common/view/minimapCharRendererFactory.ts", @@ -309,16 +387,24 @@ "./vs/nls.d.ts", "./vs/nls.mock.ts", "./vs/platform/actions/common/actions.ts", + "./vs/platform/actions/common/menu.ts", + "./vs/platform/actions/common/menuService.ts", "./vs/platform/backup/common/backup.ts", "./vs/platform/broadcast/electron-browser/broadcastService.ts", "./vs/platform/clipboard/common/clipboardService.ts", "./vs/platform/clipboard/electron-browser/clipboardService.ts", "./vs/platform/commands/common/commands.ts", "./vs/platform/configuration/common/configuration.ts", + "./vs/platform/configuration/common/configurationModels.ts", "./vs/platform/configuration/common/configurationRegistry.ts", "./vs/platform/contextkey/browser/contextKeyService.ts", "./vs/platform/contextkey/common/contextkey.ts", + "./vs/platform/dialogs/common/dialogs.ts", + "./vs/platform/dialogs/node/dialogIpc.ts", + "./vs/platform/dialogs/node/dialogService.ts", "./vs/platform/download/common/download.ts", + "./vs/platform/download/node/downloadService.ts", + "./vs/platform/driver/node/driver.ts", "./vs/platform/editor/common/editor.ts", "./vs/platform/environment/common/environment.ts", "./vs/platform/environment/node/argv.ts", @@ -329,11 +415,14 @@ "./vs/platform/extensionManagement/node/extensionLifecycle.ts", "./vs/platform/extensionManagement/node/extensionManagementIpc.ts", "./vs/platform/extensionManagement/node/extensionManagementUtil.ts", + "./vs/platform/extensionManagement/node/extensionsManifestCache.ts", "./vs/platform/extensions/common/extensionHost.ts", "./vs/platform/extensions/common/extensions.ts", "./vs/platform/extensions/node/extensionValidator.ts", "./vs/platform/files/common/files.ts", "./vs/platform/files/node/files.ts", + "./vs/platform/history/common/history.ts", + "./vs/platform/history/electron-main/historyMainService.ts", "./vs/platform/instantiation/common/descriptors.ts", "./vs/platform/instantiation/common/extensions.ts", "./vs/platform/instantiation/common/graph.ts", @@ -344,6 +433,7 @@ "./vs/platform/integrity/common/integrity.ts", "./vs/platform/integrity/node/integrityServiceImpl.ts", "./vs/platform/issue/common/issue.ts", + "./vs/platform/issue/node/issueIpc.ts", "./vs/platform/jsonschemas/common/jsonContributionRegistry.ts", "./vs/platform/keybinding/common/abstractKeybindingService.ts", "./vs/platform/keybinding/common/keybinding.ts", @@ -355,12 +445,18 @@ "./vs/platform/label/common/label.ts", "./vs/platform/label/electron-browser/label.contribution.ts", "./vs/platform/lifecycle/common/lifecycle.ts", + "./vs/platform/lifecycle/electron-browser/lifecycleService.ts", + "./vs/platform/lifecycle/electron-main/lifecycleMain.ts", "./vs/platform/localizations/common/localizations.ts", + "./vs/platform/localizations/node/localizationsIpc.ts", "./vs/platform/log/common/bufferLog.ts", "./vs/platform/log/common/log.ts", + "./vs/platform/log/node/logIpc.ts", "./vs/platform/log/node/spdlogService.ts", + "./vs/platform/markers/common/markerService.ts", "./vs/platform/markers/common/markers.ts", "./vs/platform/menubar/common/menubar.ts", + "./vs/platform/menubar/node/menubarIpc.ts", "./vs/platform/node/minimalTranslations.ts", "./vs/platform/node/package.ts", "./vs/platform/node/product.ts", @@ -372,33 +468,64 @@ "./vs/platform/quickOpen/common/quickOpen.ts", "./vs/platform/quickinput/common/quickInput.ts", "./vs/platform/registry/common/platform.ts", + "./vs/platform/request/electron-browser/requestService.ts", + "./vs/platform/request/electron-main/requestService.ts", "./vs/platform/request/node/request.ts", + "./vs/platform/request/node/requestService.ts", + "./vs/platform/search/common/replace.ts", "./vs/platform/search/common/search.ts", + "./vs/platform/search/test/common/replace.test.ts", "./vs/platform/state/common/state.ts", "./vs/platform/statusbar/common/statusbar.ts", "./vs/platform/storage/common/storage.ts", + "./vs/platform/storage/common/storageLegacyService.ts", "./vs/platform/telemetry/browser/errorTelemetry.ts", "./vs/platform/telemetry/common/telemetry.ts", "./vs/platform/telemetry/common/telemetryService.ts", "./vs/platform/telemetry/common/telemetryUtils.ts", + "./vs/platform/telemetry/node/commonProperties.ts", + "./vs/platform/telemetry/node/telemetryIpc.ts", "./vs/platform/telemetry/node/telemetryNodeUtils.ts", "./vs/platform/theme/common/colorRegistry.ts", "./vs/platform/theme/common/styler.ts", "./vs/platform/theme/common/themeService.ts", "./vs/platform/theme/test/common/testThemeService.ts", "./vs/platform/update/common/update.ts", + "./vs/platform/update/electron-main/abstractUpdateService.ts", + "./vs/platform/update/electron-main/updateService.darwin.ts", + "./vs/platform/update/electron-main/updateService.linux.ts", "./vs/platform/update/node/update.config.contribution.ts", + "./vs/platform/update/node/updateIpc.ts", "./vs/platform/url/common/url.ts", "./vs/platform/url/common/urlService.ts", + "./vs/platform/url/electron-main/electronUrlListener.ts", + "./vs/platform/url/node/urlIpc.ts", + "./vs/platform/widget/common/contextScopedWidget.ts", + "./vs/platform/windows/common/windows.ts", + "./vs/platform/windows/electron-browser/windowService.ts", + "./vs/platform/windows/electron-main/windows.ts", + "./vs/platform/windows/node/windowsIpc.ts", "./vs/platform/workbench/common/contextkeys.ts", "./vs/platform/workspace/common/workspace.ts", "./vs/platform/workspace/test/common/testWorkspace.ts", "./vs/platform/workspaces/common/workspaces.ts", + "./vs/platform/workspaces/electron-main/workspacesMainService.ts", "./vs/platform/workspaces/node/workspaces.ts", + "./vs/platform/workspaces/node/workspacesIpc.ts", "./vs/vscode.d.ts", "./vs/vscode.proposed.d.ts", + "./vs/workbench/api/node/extHostExtensionActivator.ts", "./vs/workbench/api/shared/tasks.ts", + "./vs/workbench/browser/actions/toggleActivityBarVisibility.ts", + "./vs/workbench/browser/actions/toggleCenteredLayout.ts", + "./vs/workbench/browser/actions/toggleSidebarPosition.ts", + "./vs/workbench/browser/actions/toggleSidebarVisibility.ts", + "./vs/workbench/browser/actions/toggleStatusbarVisibility.ts", + "./vs/workbench/browser/actions/toggleTabsVisibility.ts", + "./vs/workbench/browser/actions/toggleZenMode.ts", "./vs/workbench/browser/part.ts", + "./vs/workbench/browser/parts/editor/editorWidgets.ts", + "./vs/workbench/browser/parts/notifications/notificationsAlerts.ts", "./vs/workbench/browser/parts/quickinput/quickInputUtils.ts", "./vs/workbench/browser/parts/quickopen/quickopen.ts", "./vs/workbench/browser/parts/statusbar/statusbar.ts", @@ -409,47 +536,81 @@ "./vs/workbench/common/contributions.ts", "./vs/workbench/common/extensionHostProtocol.ts", "./vs/workbench/common/memento.ts", + "./vs/workbench/common/notifications.ts", "./vs/workbench/common/panel.ts", "./vs/workbench/common/resources.ts", "./vs/workbench/common/theme.ts", "./vs/workbench/common/viewlet.ts", "./vs/workbench/common/views.ts", + "./vs/workbench/parts/cli/electron-browser/cli.contribution.ts", "./vs/workbench/parts/codeEditor/browser/menuPreventer.ts", "./vs/workbench/parts/codeEditor/browser/simpleEditorOptions.ts", "./vs/workbench/parts/codeEditor/electron-browser/accessibility.ts", "./vs/workbench/parts/codeEditor/electron-browser/largeFileOptimizations.ts", "./vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts", + "./vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts", + "./vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts", + "./vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts", + "./vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts", "./vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts", "./vs/workbench/parts/comments/common/commentModel.ts", "./vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts", + "./vs/workbench/parts/comments/electron-browser/commentService.ts", "./vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts", "./vs/workbench/parts/emmet/browser/emmet.browser.contribution.ts", + "./vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.ts", + "./vs/workbench/parts/emmet/electron-browser/emmet.contribution.ts", + "./vs/workbench/parts/emmet/electron-browser/emmetActions.ts", + "./vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts", "./vs/workbench/parts/execution/common/execution.ts", + "./vs/workbench/parts/execution/electron-browser/terminal.ts", "./vs/workbench/parts/extensions/common/extensionQuery.ts", "./vs/workbench/parts/extensions/common/extensions.ts", "./vs/workbench/parts/extensions/common/extensionsFileTemplate.ts", + "./vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts", + "./vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts", "./vs/workbench/parts/logs/common/logConstants.ts", + "./vs/workbench/parts/logs/electron-browser/logs.contribution.ts", + "./vs/workbench/parts/logs/electron-browser/logsActions.ts", "./vs/workbench/parts/markers/electron-browser/constants.ts", "./vs/workbench/parts/markers/electron-browser/markers.ts", + "./vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts", "./vs/workbench/parts/markers/electron-browser/markersFilterOptions.ts", "./vs/workbench/parts/markers/electron-browser/markersModel.ts", "./vs/workbench/parts/markers/electron-browser/messages.ts", "./vs/workbench/parts/outline/electron-browser/outline.ts", "./vs/workbench/parts/output/common/output.ts", + "./vs/workbench/parts/output/common/outputLinkComputer.ts", + "./vs/workbench/parts/output/common/outputLinkProvider.ts", "./vs/workbench/parts/performance/electron-browser/stats.ts", "./vs/workbench/parts/preferences/common/smartSnippetInserter.ts", "./vs/workbench/parts/scm/common/scm.ts", "./vs/workbench/parts/scm/electron-browser/scmUtil.ts", "./vs/workbench/parts/search/common/constants.ts", + "./vs/workbench/parts/search/common/queryBuilder.ts", "./vs/workbench/parts/surveys/electron-browser/nps.contribution.ts", + "./vs/workbench/parts/tasks/common/problemCollectors.ts", + "./vs/workbench/parts/tasks/common/problemMatcher.ts", + "./vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts", + "./vs/workbench/parts/tasks/common/taskService.ts", + "./vs/workbench/parts/tasks/common/taskSystem.ts", "./vs/workbench/parts/tasks/common/taskTemplates.ts", + "./vs/workbench/parts/tasks/common/tasks.ts", + "./vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.ts", + "./vs/workbench/parts/tasks/node/tasks.ts", + "./vs/workbench/parts/terminal/browser/terminalTab.ts", "./vs/workbench/parts/terminal/browser/terminalWidgetManager.ts", "./vs/workbench/parts/terminal/common/terminal.ts", "./vs/workbench/parts/terminal/common/terminalColorRegistry.ts", "./vs/workbench/parts/terminal/common/terminalCommands.ts", "./vs/workbench/parts/terminal/common/terminalMenu.ts", + "./vs/workbench/parts/terminal/common/terminalService.ts", + "./vs/workbench/parts/terminal/node/terminal.ts", "./vs/workbench/parts/terminal/node/terminalCommandTracker.ts", "./vs/workbench/parts/terminal/node/terminalEnvironment.ts", + "./vs/workbench/parts/terminal/node/terminalProcess.ts", + "./vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts", + "./vs/workbench/parts/url/electron-browser/url.contribution.ts", "./vs/workbench/parts/webview/electron-browser/webviewProtocols.ts", "./vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.ts", "./vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts", @@ -458,19 +619,30 @@ "./vs/workbench/services/backup/common/backup.ts", "./vs/workbench/services/commands/common/commandService.ts", "./vs/workbench/services/configuration/common/configuration.ts", + "./vs/workbench/services/configuration/common/configurationExtensionPoint.ts", + "./vs/workbench/services/configuration/common/configurationModels.ts", "./vs/workbench/services/configuration/common/jsonEditing.ts", "./vs/workbench/services/configurationResolver/common/configurationResolver.ts", + "./vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts", "./vs/workbench/services/decorations/browser/decorations.ts", "./vs/workbench/services/extensions/common/extensions.ts", "./vs/workbench/services/extensions/common/extensionsRegistry.ts", + "./vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts", "./vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts", "./vs/workbench/services/extensions/node/extensionManagementServerService.ts", "./vs/workbench/services/extensions/node/lazyPromise.ts", "./vs/workbench/services/extensions/node/proxyIdentifier.ts", "./vs/workbench/services/extensions/node/rpcProtocol.ts", + "./vs/workbench/services/files/electron-browser/encoding.ts", "./vs/workbench/services/files/node/watcher/common.ts", "./vs/workbench/services/files/node/watcher/nsfw/watcher.ts", + "./vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts", + "./vs/workbench/services/files/node/watcher/nsfw/watcherService.ts", "./vs/workbench/services/files/node/watcher/unix/watcher.ts", + "./vs/workbench/services/files/node/watcher/unix/watcherIpc.ts", + "./vs/workbench/services/files/node/watcher/unix/watcherService.ts", + "./vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts", + "./vs/workbench/services/files/node/watcher/win32/watcherService.ts", "./vs/workbench/services/files/test/electron-browser/utils.ts", "./vs/workbench/services/hash/common/hashService.ts", "./vs/workbench/services/hash/node/hashService.ts", @@ -482,13 +654,23 @@ "./vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts", "./vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts", "./vs/workbench/services/mode/common/workbenchModeService.ts", + "./vs/workbench/services/notification/common/notificationService.ts", "./vs/workbench/services/panel/common/panelService.ts", - "./vs/workbench/services/progress/common/progress.ts", + "./vs/workbench/services/part/common/partService.ts", "./vs/workbench/services/scm/common/scm.ts", "./vs/workbench/services/scm/common/scmService.ts", + "./vs/workbench/services/search/common/searchHelpers.ts", + "./vs/workbench/services/search/node/fileSearchManager.ts", + "./vs/workbench/services/search/node/legacy/search.ts", + "./vs/workbench/services/search/node/ripgrepSearchProvider.ts", + "./vs/workbench/services/search/node/ripgrepSearchUtils.ts", + "./vs/workbench/services/search/node/ripgrepTextSearchEngine.ts", "./vs/workbench/services/search/node/search.ts", "./vs/workbench/services/search/node/searchHistoryService.ts", "./vs/workbench/services/search/node/searchIpc.ts", + "./vs/workbench/services/search/node/textSearchManager.ts", + "./vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts", + "./vs/workbench/services/search/test/node/textSearchManager.test.ts", "./vs/workbench/services/textMate/electron-browser/TMGrammars.ts", "./vs/workbench/services/textMate/electron-browser/TMHelper.ts", "./vs/workbench/services/textMate/electron-browser/textMateService.ts", @@ -502,6 +684,7 @@ "./vs/workbench/test/electron-browser/api/mock.ts" ], "exclude": [ - "./typings/require-monaco.d.ts" + "./typings/require-monaco.d.ts", + "../node_modules/windows-process-tree/typings/windows-process-tree.d.ts" ] } \ No newline at end of file diff --git a/src/typings/node-pty.d.ts b/src/typings/node-pty.d.ts index e3ee561a60c..2fdb9dff99a 100644 --- a/src/typings/node-pty.d.ts +++ b/src/typings/node-pty.d.ts @@ -11,7 +11,7 @@ declare module 'node-pty' { * escaped properly. * @param options The options of the terminal. * @see CommandLineToArgvW https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx - * @see Parsing C++ Comamnd-Line Arguments https://msdn.microsoft.com/en-us/library/17w5ykft.aspx + * @see Parsing C++ Command-Line Arguments https://msdn.microsoft.com/en-us/library/17w5ykft.aspx * @see GetCommandLine https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156.aspx */ export function spawn(file: string, args: string[] | string, options: IPtyForkOptions): IPty; @@ -58,7 +58,7 @@ declare module 'node-pty' { /** * Resizes the dimensions of the pty. - * @param columns THe number of columns to use. + * @param columns The number of columns to use. * @param rows The number of rows to use. */ resize(columns: number, rows: number): void; diff --git a/src/typings/vscode-proxy-agent.d.ts b/src/typings/vscode-proxy-agent.d.ts new file mode 100644 index 00000000000..a997fa97801 --- /dev/null +++ b/src/typings/vscode-proxy-agent.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. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode-proxy-agent'; diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index 33c9f3d7b4d..08d9b2b8ec8 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as Platform from 'vs/base/common/platform'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; class WindowManager { @@ -74,11 +74,11 @@ class WindowManager { } // --- Accessibility - private _accessibilitySupport = Platform.AccessibilitySupport.Unknown; + private _accessibilitySupport = platform.AccessibilitySupport.Unknown; private readonly _onDidChangeAccessibilitySupport: Emitter = new Emitter(); public readonly onDidChangeAccessibilitySupport: Event = this._onDidChangeAccessibilitySupport.event; - public setAccessibilitySupport(accessibilitySupport: Platform.AccessibilitySupport): void { + public setAccessibilitySupport(accessibilitySupport: platform.AccessibilitySupport): void { if (this._accessibilitySupport === accessibilitySupport) { return; } @@ -86,7 +86,7 @@ class WindowManager { this._accessibilitySupport = accessibilitySupport; this._onDidChangeAccessibilitySupport.fire(); } - public getAccessibilitySupport(): Platform.AccessibilitySupport { + public getAccessibilitySupport(): platform.AccessibilitySupport { return this._accessibilitySupport; } } @@ -126,10 +126,10 @@ export function isFullscreen(): boolean { } export const onDidChangeFullscreen = WindowManager.INSTANCE.onDidChangeFullscreen; -export function setAccessibilitySupport(accessibilitySupport: Platform.AccessibilitySupport): void { +export function setAccessibilitySupport(accessibilitySupport: platform.AccessibilitySupport): void { WindowManager.INSTANCE.setAccessibilitySupport(accessibilitySupport); } -export function getAccessibilitySupport(): Platform.AccessibilitySupport { +export function getAccessibilitySupport(): platform.AccessibilitySupport { return WindowManager.INSTANCE.getAccessibilitySupport(); } export function onDidChangeAccessibilitySupport(callback: () => void): IDisposable { diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index 92e1e6d8533..5b7891e108a 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -23,7 +23,7 @@ export class ContextSubMenu extends SubmenuAction { export interface IContextMenuDelegate { getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number; }; - getActions(): Thenable<(IAction | ContextSubMenu)[]>; + getActions(): (IAction | ContextSubMenu)[]; getActionItem?(action: IAction): IActionItem; getActionsContext?(event?: IContextMenuEvent): any; getKeyBinding?(action: IAction): ResolvedKeybinding; diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 3ad8440553d..d1628b42d91 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as platform from 'vs/base/common/platform'; -import { TimeoutTimer } from 'vs/base/common/async'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as browser from 'vs/base/browser/browser'; +import { domEvent } from 'vs/base/browser/event'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { TimeoutTimer } from 'vs/base/common/async'; import { CharCode } from 'vs/base/common/charCode'; -import { Event, Emitter } from 'vs/base/common/event'; -import { domEvent } from 'vs/base/browser/event'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; export function clearNode(node: HTMLElement): void { while (node.firstChild) { @@ -560,7 +560,7 @@ export class Dimension { this.height = height; } - static equals(a: Dimension, b: Dimension): boolean { + static equals(a: Dimension | undefined, b: Dimension | undefined): boolean { if (a === b) { return true; } diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index ed365f47786..50b25d776e6 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import { IframeUtils } from 'vs/base/browser/iframe'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; export interface IStandardMouseMoveEventData { leftButton: boolean; diff --git a/src/vs/base/browser/keyboardEvent.ts b/src/vs/base/browser/keyboardEvent.ts index 87f6635f6ec..0d7fcc3f1ff 100644 --- a/src/vs/base/browser/keyboardEvent.ts +++ b/src/vs/base/browser/keyboardEvent.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as browser from 'vs/base/browser/browser'; import { KeyCode, KeyCodeUtils, KeyMod, SimpleKeybinding } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; -import * as browser from 'vs/base/browser/browser'; let KEY_CODE_MAP: { [keyCode: number]: KeyCode } = new Array(230); let INVERSE_KEY_CODE_MAP: KeyCode[] = new Array(KeyCode.MAX_VALUE); diff --git a/src/vs/base/browser/mouseEvent.ts b/src/vs/base/browser/mouseEvent.ts index 3b943702023..ab32deee7f2 100644 --- a/src/vs/base/browser/mouseEvent.ts +++ b/src/vs/base/browser/mouseEvent.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as platform from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; import { IframeUtils } from 'vs/base/browser/iframe'; +import * as platform from 'vs/base/common/platform'; export interface IMouseEvent { readonly browserEvent: MouseEvent; diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 4a2b7396a9e..42f43d27322 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -118,8 +118,8 @@ export class BreadcrumbsWidget { this._freeNodes.length = 0; } - layout(dim: dom.Dimension): void { - if (dom.Dimension.equals(dim, this._dimension)) { + layout(dim: dom.Dimension | undefined): void { + if (dim && dom.Dimension.equals(dim, this._dimension)) { return; } if (this._pendingLayout) { @@ -146,9 +146,11 @@ export class BreadcrumbsWidget { private _updateScrollbar(): IDisposable { return dom.measure(() => { - this._scrollable.setRevealOnScroll(false); - this._scrollable.scanDomNode(); - this._scrollable.setRevealOnScroll(true); + dom.measure(() => { // double RAF + this._scrollable.setRevealOnScroll(false); + this._scrollable.scanDomNode(); + this._scrollable.setRevealOnScroll(true); + }); }); } @@ -277,8 +279,8 @@ export class BreadcrumbsWidget { } setItems(items: BreadcrumbsItem[]): void { - let prefix: number; - let removed: BreadcrumbsItem[]; + let prefix: number | undefined; + let removed: BreadcrumbsItem[] = []; try { prefix = commonPrefixLength(this._items, items, (a, b) => a.equals(b)); removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix)); @@ -302,17 +304,21 @@ export class BreadcrumbsWidget { // case a: more nodes -> remove them while (start < this._nodes.length) { const free = this._nodes.pop(); - this._freeNodes.push(free); - free.remove(); + if (free) { + this._freeNodes.push(free); + free.remove(); + } } // case b: more items -> render them for (; start < this._items.length; start++) { let item = this._items[start]; let node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div'); - this._renderItem(item, node); - this._domNode.appendChild(node); - this._nodes.push(node); + if (node) { + this._renderItem(item, node); + this._domNode.appendChild(node); + this._nodes.push(node); + } } this.layout(undefined); } @@ -327,7 +333,7 @@ export class BreadcrumbsWidget { } private _onClick(event: IMouseEvent): void { - for (let el = event.target; el; el = el.parentElement) { + for (let el: HTMLElement | null = event.target; el; el = el.parentElement) { let idx = this._nodes.indexOf(el as any); if (idx >= 0) { this._focus(idx, event); diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 03b876df89b..7964af23c07 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -50,15 +50,15 @@ export interface ICenteredViewStyles extends ISplitViewStyles { export class CenteredViewLayout { - private splitView: SplitView; + private splitView?: SplitView; private width: number = 0; private height: number = 0; private style: ICenteredViewStyles; private didLayout = false; - private emptyViews: ISplitViewView[]; + private emptyViews: ISplitViewView[] | undefined; private splitViewDisposables: IDisposable[] = []; - constructor(private container: HTMLElement, private view: IView, public readonly state: CenteredViewState = GOLDEN_RATIO) { + constructor(private container: HTMLElement, private view: IView, public readonly state: CenteredViewState = { leftMarginRatio: GOLDEN_RATIO.leftMarginRatio, rightMarginRatio: GOLDEN_RATIO.rightMarginRatio }) { this.container.appendChild(this.view.element); // Make sure to hide the split view overflow like sashes #52892 this.container.style.overflow = 'hidden'; @@ -84,6 +84,9 @@ export class CenteredViewLayout { } private resizeMargins(): void { + if (!this.splitView) { + return; + } this.splitView.resizeView(0, this.state.leftMarginRatio * this.width); this.splitView.resizeView(2, this.state.rightMarginRatio * this.width); } @@ -94,7 +97,7 @@ export class CenteredViewLayout { styles(style: ICenteredViewStyles): void { this.style = style; - if (this.splitView) { + if (this.splitView && this.emptyViews) { this.splitView.style(this.style); this.emptyViews[0].element.style.backgroundColor = this.style.background.toString(); this.emptyViews[1].element.style.backgroundColor = this.style.background.toString(); @@ -115,8 +118,10 @@ export class CenteredViewLayout { }); this.splitViewDisposables.push(this.splitView.onDidSashChange(() => { - this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.width; - this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.width; + if (this.splitView) { + this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.width; + this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.width; + } })); this.splitViewDisposables.push(this.splitView.onDidSashReset(() => { this.state.leftMarginRatio = GOLDEN_RATIO.leftMarginRatio; @@ -130,9 +135,13 @@ export class CenteredViewLayout { this.splitView.addView(this.emptyViews[0], this.state.leftMarginRatio * this.width, 0); this.splitView.addView(this.emptyViews[1], this.state.rightMarginRatio * this.width, 2); } else { - this.container.removeChild(this.splitView.el); + if (this.splitView) { + this.container.removeChild(this.splitView.el); + } this.splitViewDisposables = dispose(this.splitViewDisposables); - this.splitView.dispose(); + if (this.splitView) { + this.splitView.dispose(); + } this.splitView = undefined; this.emptyViews = undefined; this.container.appendChild(this.view.element); diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 1c44f12d262..409f3c9497f 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -212,8 +212,8 @@ export class ContextView extends Disposable { around = { top: realAnchor.y, left: realAnchor.x, - width: realAnchor.width || 0, - height: realAnchor.height || 0 + width: realAnchor.width || 1, + height: realAnchor.height || 2 }; } diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index dc82ccd5b90..53f814bbf75 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -243,7 +243,7 @@ export class DropdownMenu extends BaseDropdown { this._contextMenuProvider.showContextMenu({ getAnchor: () => this.element, - getActions: () => Promise.resolve(this.actions), + getActions: () => this.actions, getActionsContext: () => this.menuOptions ? this.menuOptions.context : null, getActionItem: action => this.menuOptions && this.menuOptions.actionItemProvider ? this.menuOptions.actionItemProvider(action) : null, getKeyBinding: action => this.menuOptions && this.menuOptions.getKeyBinding ? this.menuOptions.getKeyBinding(action) : null, @@ -332,4 +332,4 @@ export class DropdownMenuActionItem extends BaseActionItem { this.dropdownMenu.show(); } } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index f09231bc05f..336329a05e2 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -139,10 +139,15 @@ export function getRelativeLocation(rootOrientation: Orientation, location: numb function indexInParent(element: HTMLElement): number { const parentElement = element.parentElement; + + if (!parentElement) { + throw new Error('Invalid grid element'); + } + let el = parentElement.firstElementChild; let index = 0; - while (el !== element && el !== parentElement.lastElementChild) { + while (el !== element && el !== parentElement.lastElementChild && el) { el = el.nextElementSibling; index++; } @@ -157,12 +162,18 @@ function indexInParent(element: HTMLElement): number { * This will break as soon as DOM structures of the Splitview or Gridview change. */ function getGridLocation(element: HTMLElement): number[] { - if (/\bmonaco-grid-view\b/.test(element.parentElement.className)) { + const parentElement = element.parentElement; + + if (!parentElement) { + throw new Error('Invalid grid element'); + } + + if (/\bmonaco-grid-view\b/.test(parentElement.className)) { return []; } - const index = indexInParent(element.parentElement); - const ancestor = element.parentElement.parentElement.parentElement.parentElement; + const index = indexInParent(parentElement); + const ancestor = parentElement.parentElement!.parentElement!.parentElement!; return [...getGridLocation(ancestor), index]; } @@ -193,7 +204,7 @@ export class Grid implements IDisposable { get minimumHeight(): number { return this.gridview.minimumHeight; } get maximumWidth(): number { return this.gridview.maximumWidth; } get maximumHeight(): number { return this.gridview.maximumHeight; } - get onDidChange(): Event<{ width: number; height: number; }> { return this.gridview.onDidChange; } + get onDidChange(): Event<{ width: number; height: number; } | undefined> { return this.gridview.onDidChange; } get element(): HTMLElement { return this.gridview.element; } @@ -368,7 +379,7 @@ export interface ISerializableView extends IView { } export interface IViewDeserializer { - fromJSON(json: object): T; + fromJSON(json: object | null): T; } interface InitialLayoutContext { @@ -379,7 +390,7 @@ interface InitialLayoutContext { export interface ISerializedLeafNode { type: 'leaf'; - data: object; + data: object | null; size: number; } @@ -415,18 +426,15 @@ export class SerializableGrid extends Grid { throw new Error('Invalid JSON'); } - const type = json.type; - const data = json.data; - - if (type === 'branch') { - if (!Array.isArray(data)) { + if (json.type === 'branch') { + if (!Array.isArray(json.data)) { throw new Error('Invalid JSON: \'data\' property of branch must be an array.'); } const children: GridNode[] = []; let offset = 0; - for (const child of data) { + for (const child of json.data) { if (typeof child.size !== 'number') { throw new Error('Invalid JSON: \'size\' property of node must be a number.'); } @@ -441,8 +449,8 @@ export class SerializableGrid extends Grid { return { children, box }; - } else if (type === 'leaf') { - const view = deserializer.fromJSON(data) as T; + } else if (json.type === 'leaf') { + const view = deserializer.fromJSON(json.data) as T; return { view, box }; } @@ -527,13 +535,13 @@ export class SerializableGrid extends Grid { const firstLeaves = node.children.map(c => SerializableGrid.getFirstLeaf(c)); for (let i = 1; i < firstLeaves.length; i++) { - const size = orientation === Orientation.VERTICAL ? firstLeaves[i].box.height : firstLeaves[i].box.width; - this.addView(firstLeaves[i].view, size, referenceView, direction); - referenceView = firstLeaves[i].view; + const size = orientation === Orientation.VERTICAL ? firstLeaves[i]!.box.height : firstLeaves[i]!.box.width; + this.addView(firstLeaves[i]!.view, size, referenceView, direction); + referenceView = firstLeaves[i]!.view; } for (let i = 0; i < node.children.length; i++) { - this.restoreViews(firstLeaves[i].view, orthogonal(orientation), node.children[i]); + this.restoreViews(firstLeaves[i]!.view, orthogonal(orientation), node.children[i]); } } @@ -611,10 +619,10 @@ function getDimensions(node: ISerializedNode, orientation: Orientation): { width if (orientation === Orientation.VERTICAL) { const width = node.size || (childrenDimensions.length === 0 ? undefined : Math.max(...childrenDimensions.map(d => d.width || 0))); - const height = childrenDimensions.length === 0 ? undefined : childrenDimensions.reduce((r, d) => r + d.height, 0); + const height = childrenDimensions.length === 0 ? undefined : childrenDimensions.reduce((r, d) => r + (d.height || 0), 0); return { width, height }; } else { - const width = childrenDimensions.length === 0 ? undefined : childrenDimensions.reduce((r, d) => r + d.width, 0); + const width = childrenDimensions.length === 0 ? undefined : childrenDimensions.reduce((r, d) => r + (d.width || 0), 0); const height = node.size || (childrenDimensions.length === 0 ? undefined : Math.max(...childrenDimensions.map(d => d.height || 0))); return { width, height }; } diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 531575bb123..f0ac2e0984d 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -6,13 +6,13 @@ import 'vs/css!./gridview'; import { Event, anyEvent, Emitter, mapEvent, Relay } from 'vs/base/common/event'; import { Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; -import { SplitView, IView as ISplitView, Sizing, ISplitViewStyles } from 'vs/base/browser/ui/splitview/splitview'; +import { SplitView, IView as ISplitView, Sizing, LayoutPriority, ISplitViewStyles } from 'vs/base/browser/ui/splitview/splitview'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { $ } from 'vs/base/browser/dom'; import { tail2 as tail } from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; -export { Sizing } from 'vs/base/browser/ui/splitview/splitview'; +export { Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; export { Orientation } from 'vs/base/browser/ui/sash/sash'; export interface IView { @@ -22,6 +22,8 @@ export interface IView { readonly minimumHeight: number; readonly maximumHeight: number; readonly onDidChange: Event<{ width: number; height: number; }>; + readonly priority?: LayoutPriority; + readonly snapSize?: number; layout(width: number, height: number): void; } @@ -60,6 +62,7 @@ const defaultStyles: IGridViewStyles = { export interface IGridViewOptions { styles?: IGridViewStyles; + proportionalLayout?: boolean; // default true } class BranchNode implements ISplitView, IDisposable { @@ -135,6 +138,7 @@ class BranchNode implements ISplitView, IDisposable { constructor( readonly orientation: Orientation, styles: IGridViewStyles, + readonly proportionalLayout: boolean, size: number = 0, orthogonalSize: number = 0 ) { @@ -450,6 +454,14 @@ class LeafNode implements ISplitView, IDisposable { return this.orientation === Orientation.HORIZONTAL ? this.maximumHeight : this.maximumWidth; } + get priority(): LayoutPriority | undefined { + return this.view.priority; + } + + get snapSize(): number | undefined { + return this.view.snapSize; + } + get minimumOrthogonalSize(): number { return this.orientation === Orientation.HORIZONTAL ? this.minimumWidth : this.minimumHeight; } @@ -483,7 +495,7 @@ type Node = BranchNode | LeafNode; function flipNode(node: T, size: number, orthogonalSize: number): T { if (node instanceof BranchNode) { - const result = new BranchNode(orthogonal(node.orientation), node.styles, size, orthogonalSize); + const result = new BranchNode(orthogonal(node.orientation), node.styles, node.proportionalLayout, size, orthogonalSize); let totalSize = 0; @@ -512,6 +524,7 @@ export class GridView implements IDisposable { readonly element: HTMLElement; private styles: IGridViewStyles; + private proportionalLayout: boolean; private _root: BranchNode; private onDidSashResetRelay = new Relay(); @@ -560,13 +573,14 @@ export class GridView implements IDisposable { get maximumWidth(): number { return this.root.maximumHeight; } get maximumHeight(): number { return this.root.maximumHeight; } - private _onDidChange = new Relay<{ width: number; height: number; }>(); + private _onDidChange = new Relay<{ width: number; height: number; } | undefined>(); readonly onDidChange = this._onDidChange.event; constructor(options: IGridViewOptions = {}) { this.element = $('.monaco-grid-view'); this.styles = options.styles || defaultStyles; - this.root = new BranchNode(Orientation.VERTICAL, this.styles); + this.proportionalLayout = typeof options.proportionalLayout !== 'undefined' ? !!options.proportionalLayout : true; + this.root = new BranchNode(Orientation.VERTICAL, this.styles, this.proportionalLayout); } style(styles: IGridViewStyles): void { @@ -596,7 +610,7 @@ export class GridView implements IDisposable { const [, parentIndex] = tail(rest); grandParent.removeChild(parentIndex); - const newParent = new BranchNode(parent.orientation, this.styles, parent.size, parent.orthogonalSize); + const newParent = new BranchNode(parent.orientation, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize); grandParent.addChild(newParent, parent.size, parentIndex); newParent.orthogonalLayout(parent.orthogonalSize); diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index 58e154c751e..a8e3d6c3200 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -21,7 +21,7 @@ export class HighlightedLabel implements IDisposable { private highlights: IHighlight[]; private didEverRender: boolean; - constructor(container: HTMLElement) { + constructor(container: HTMLElement, private supportOcticons: boolean) { this.domNode = document.createElement('span'); this.domNode.className = 'monaco-highlighted-label'; this.didEverRender = false; @@ -68,19 +68,22 @@ export class HighlightedLabel implements IDisposable { } if (pos < highlight.start) { htmlContent.push(''); - htmlContent.push(renderOcticons(this.text.substring(pos, highlight.start))); + const substring = this.text.substring(pos, highlight.start); + htmlContent.push(this.supportOcticons ? renderOcticons(substring) : substring); htmlContent.push(''); pos = highlight.end; } htmlContent.push(''); - htmlContent.push(renderOcticons(this.text.substring(highlight.start, highlight.end))); + const substring = this.text.substring(highlight.start, highlight.end); + htmlContent.push(this.supportOcticons ? renderOcticons(substring) : substring); htmlContent.push(''); pos = highlight.end; } if (pos < this.text.length) { htmlContent.push(''); - htmlContent.push(renderOcticons(this.text.substring(pos))); + const substring = this.text.substring(pos); + htmlContent.push(this.supportOcticons ? renderOcticons(substring) : substring); htmlContent.push(''); } @@ -90,8 +93,8 @@ export class HighlightedLabel implements IDisposable { } dispose() { - this.text = null; - this.highlights = null; + this.text = null!; // StrictNullOverride: nulling out ok in dispose + this.highlights = null!; // StrictNullOverride: nulling out ok in dispose } static escapeNewLines(text: string, highlights: IHighlight[]): string { diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index a2af0b7e72b..752437df1ba 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -12,6 +12,7 @@ import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/life export interface IIconLabelCreationOptions { supportHighlights?: boolean; supportDescriptionHighlights?: boolean; + donotSupportOcticons?: boolean; } export interface IIconLabelValueOptions { @@ -99,13 +100,13 @@ export class IconLabel extends Disposable { this.labelDescriptionContainer = this._register(new FastLabelNode(dom.append(this.domNode.element, dom.$('.monaco-icon-label-description-container')))); if (options && options.supportHighlights) { - this.labelNode = this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')))); + this.labelNode = this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')), !options.donotSupportOcticons)); } else { this.labelNode = this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')))); } if (options && options.supportDescriptionHighlights) { - this.descriptionNodeFactory = () => this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')))); + this.descriptionNodeFactory = () => this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')), !options.donotSupportOcticons)); } else { this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')))); } @@ -137,7 +138,7 @@ export class IconLabel extends Disposable { this.domNode.title = options && options.title ? options.title : ''; if (this.labelNode instanceof HighlightedLabel) { - this.labelNode.set(label || '', options ? options.matches : void 0, void 0, options && options.labelEscapeNewLines); + this.labelNode.set(label || '', options ? options.matches : void 0, options && options.title ? options.title : void 0, options && options.labelEscapeNewLines); } else { this.labelNode.textContent = label || ''; } diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index b9815b6ef0b..cb0be292bff 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -44,7 +44,7 @@ export interface IListGestureEvent { export interface IListContextMenuEvent { browserEvent: UIEvent; - element: T; + element: T | undefined; index: number; - anchor: HTMLElement | { x: number; y: number; }; + anchor: HTMLElement | { x: number; y: number; } | undefined; } diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 1317c4f5b43..d4491cecefd 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -17,8 +17,8 @@ export interface IPagedRenderer extends IListRenderer { - data: T; - disposable: IDisposable; + data?: T; + disposable?: IDisposable; } class PagedRenderer implements IListRenderer> { @@ -36,7 +36,13 @@ class PagedRenderer implements IListRenderer): void { - data.disposable.dispose(); + if (data.disposable) { + data.disposable.dispose(); + } + + if (!data.data) { + return; + } const model = this.modelProvider(); @@ -49,7 +55,7 @@ class PagedRenderer implements IListRenderer cts.cancel() }; this.renderer.renderPlaceholder(index, data.data); - promise.then(entry => this.renderer.renderElement(entry, index, data.data)); + promise.then(entry => this.renderer.renderElement(entry, index, data.data!)); } disposeElement(): void { @@ -57,10 +63,14 @@ class PagedRenderer implements IListRenderer): void { - data.disposable.dispose(); - data.disposable = null; - this.renderer.disposeTemplate(data.data); - data.data = null; + if (data.disposable) { + data.disposable.dispose(); + data.disposable = undefined; + } + if (data.data) { + this.renderer.disposeTemplate(data.data); + data.data = undefined; + } } } @@ -124,7 +134,7 @@ export class PagedList implements IDisposable { } get onContextMenu(): Event> { - return mapEvent(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => ({ element: this._model.get(element), index, anchor, browserEvent })); + return mapEvent(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => ({ element: this._model.get(element!), index, anchor, browserEvent })); } get model(): IPagedModel { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 61b6d478b26..a045bf269a8 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getOrDefault } from 'vs/base/common/objects'; +import { getOrDefault2 } from 'vs/base/common/objects'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import * as DOM from 'vs/base/browser/dom'; @@ -39,7 +39,7 @@ interface IItem { element: T; size: number; templateId: string; - row: IRow; + row: IRow | null; } export interface IListViewOptions { @@ -48,7 +48,7 @@ export interface IListViewOptions { setRowLineHeight?: boolean; } -const DefaultOptions: IListViewOptions = { +const DefaultOptions = { useShadows: true, verticalScrollMode: ScrollbarVisibility.Auto, setRowLineHeight: true @@ -105,8 +105,8 @@ export class ListView implements ISpliceable, IDisposable { this.scrollableElement = new ScrollableElement(this.rowsContainer, { alwaysConsumeMouseWheel: true, horizontal: ScrollbarVisibility.Hidden, - vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), - useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows) + vertical: getOrDefault2(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), + useShadows: getOrDefault2(options, o => o.useShadows, DefaultOptions.useShadows) }); this._domNode.appendChild(this.scrollableElement.getDomNode()); @@ -125,7 +125,7 @@ export class ListView implements ISpliceable, IDisposable { const onDragOver = mapEvent(domEvent(this.rowsContainer, 'dragover'), e => new DragMouseEvent(e)); onDragOver(this.onDragOver, this, this.disposables); - this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); + this.setRowLineHeight = getOrDefault2(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); this.layout(); } @@ -169,8 +169,18 @@ export class ListView implements ISpliceable, IDisposable { row: null })); - this.rangeMap.splice(start, deleteCount, ...inserted); - const deleted = this.items.splice(start, deleteCount, ...inserted); + let deleted: IItem[]; + + // TODO@joao: improve this optimization to catch even more cases + if (start === 0 && deleteCount >= this.items.length) { + this.rangeMap = new RangeMap(); + this.rangeMap.splice(0, 0, inserted); + this.items = inserted; + deleted = []; + } else { + this.rangeMap.splice(start, deleteCount, inserted); + deleted = this.items.splice(start, deleteCount, ...inserted); + } const delta = elements.length - deleteCount; const renderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); @@ -232,7 +242,7 @@ export class ListView implements ISpliceable, IDisposable { return this.items[index].element; } - domElement(index: number): HTMLElement { + domElement(index: number): HTMLElement | null { const row = this.items[index].row; return row && row.domNode; } @@ -302,18 +312,18 @@ export class ListView implements ISpliceable, IDisposable { item.row = this.cache.alloc(item.templateId); } - if (!item.row.domNode.parentElement) { + if (!item.row.domNode!.parentElement) { if (beforeElement) { - this.rowsContainer.insertBefore(item.row.domNode, beforeElement); + this.rowsContainer.insertBefore(item.row.domNode!, beforeElement); } else { - this.rowsContainer.appendChild(item.row.domNode); + this.rowsContainer.appendChild(item.row.domNode!); } } - item.row.domNode.style.height = `${item.size}px`; + item.row.domNode!.style.height = `${item.size}px`; if (this.setRowLineHeight) { - item.row.domNode.style.lineHeight = `${item.size}px`; + item.row.domNode!.style.lineHeight = `${item.size}px`; } this.updateItemInDOM(item, index); @@ -323,11 +333,11 @@ export class ListView implements ISpliceable, IDisposable { } private updateItemInDOM(item: IItem, index: number): void { - item.row.domNode.style.top = `${this.elementTop(index)}px`; - item.row.domNode.setAttribute('data-index', `${index}`); - item.row.domNode.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); - item.row.domNode.setAttribute('aria-setsize', `${this.length}`); - item.row.domNode.setAttribute('aria-posinset', `${index + 1}`); + item.row!.domNode!.style.top = `${this.elementTop(index)}px`; + item.row!.domNode!.setAttribute('data-index', `${index}`); + item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); + item.row!.domNode!.setAttribute('aria-setsize', `${this.length}`); + item.row!.domNode!.setAttribute('aria-posinset', `${index + 1}`); } private removeItemFromDOM(index: number): void { @@ -335,10 +345,10 @@ export class ListView implements ISpliceable, IDisposable { const renderer = this.renderers.get(item.templateId); if (renderer.disposeElement) { - renderer.disposeElement(item.element, index, item.row.templateData); + renderer.disposeElement(item.element, index, item.row!.templateData); } - this.cache.release(item.row); + this.cache.release(item.row!); item.row = null; } @@ -378,21 +388,21 @@ export class ListView implements ISpliceable, IDisposable { @memoize get onTap(): Event> { return filterEvent(mapEvent(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)), e => e.index >= 0); } private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent { - const index = this.getItemIndexFromEventTarget(browserEvent.target); + const index = this.getItemIndexFromEventTarget(browserEvent.target || null); const item = index < 0 ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; } private toTouchEvent(browserEvent: TouchEvent): IListTouchEvent { - const index = this.getItemIndexFromEventTarget(browserEvent.target); + const index = this.getItemIndexFromEventTarget(browserEvent.target || null); const item = index < 0 ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; } private toGestureEvent(browserEvent: GestureEvent): IListGestureEvent { - const index = this.getItemIndexFromEventTarget(browserEvent.initialTarget); + const index = this.getItemIndexFromEventTarget(browserEvent.initialTarget || null); const item = index < 0 ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; @@ -445,7 +455,7 @@ export class ListView implements ISpliceable, IDisposable { this.dragAndDropScrollTimeout = window.setTimeout(() => { this.cancelDragAndDropScrollInterval(); - this.dragAndDropScrollTimeout = null; + this.dragAndDropScrollTimeout = -1; }, 1000); } } @@ -453,7 +463,7 @@ export class ListView implements ISpliceable, IDisposable { private cancelDragAndDropScrollInterval(): void { if (this.dragAndDropScrollInterval) { window.clearInterval(this.dragAndDropScrollInterval); - this.dragAndDropScrollInterval = null; + this.dragAndDropScrollInterval = -1; } this.cancelDragAndDropScrollTimeout(); @@ -462,15 +472,16 @@ export class ListView implements ISpliceable, IDisposable { private cancelDragAndDropScrollTimeout(): void { if (this.dragAndDropScrollTimeout) { window.clearTimeout(this.dragAndDropScrollTimeout); - this.dragAndDropScrollTimeout = null; + this.dragAndDropScrollTimeout = -1; } } // Util - private getItemIndexFromEventTarget(target: EventTarget): number { - while (target instanceof HTMLElement && target !== this.rowsContainer) { - const element = target as HTMLElement; + private getItemIndexFromEventTarget(target: EventTarget | null): number { + let element: HTMLElement | null = target as (HTMLElement | null); + + while (element instanceof HTMLElement && element !== this.rowsContainer) { const rawIndex = element.getAttribute('data-index'); if (rawIndex) { @@ -481,7 +492,7 @@ export class ListView implements ISpliceable, IDisposable { } } - target = element.parentElement; + element = element.parentElement; } return -1; @@ -522,16 +533,14 @@ export class ListView implements ISpliceable, IDisposable { if (item.row) { const renderer = this.renderers.get(item.row.templateId); renderer.disposeTemplate(item.row.templateData); - item.row = null; } } - this.items = null; + this.items = []; } - if (this._domNode && this._domNode.parentElement) { + if (this._domNode && this._domNode.parentNode) { this._domNode.parentNode.removeChild(this._domNode); - this._domNode = null; } this.disposables = dispose(this.disposables); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 5754251a97d..84700441bdb 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -25,8 +25,8 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice'; import { clamp } from 'vs/base/common/numbers'; -export interface IIdentityProvider { - (element: T): string; +export interface IIdentityProvider { + (element: T): R; } interface ITraitChangeEvent { @@ -180,7 +180,6 @@ class Trait implements ISpliceable, IDisposable { } dispose() { - this.indexes = null; this._onChange = dispose(this._onChange); } } @@ -188,7 +187,7 @@ class Trait implements ISpliceable, IDisposable { class FocusTrait extends Trait { constructor( - private getDomId: IIdentityProvider + private getDomId: IIdentityProvider ) { super('focused'); } @@ -224,8 +223,9 @@ class TraitSpliceable implements ISpliceable { return this.trait.splice(start, deleteCount, elements.map(e => false)); } - const pastElementsWithTrait = this.trait.get().map(i => this.getId(this.view.element(i))); - const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(this.getId(e)) > -1); + const getId = this.getId; + const pastElementsWithTrait = this.trait.get().map(i => getId(this.view.element(i))); + const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(getId(e)) > -1); this.trait.splice(start, deleteCount, elementsWithTrait); } @@ -357,6 +357,11 @@ class DOMFocusController implements IDisposable { } const focusedDomElement = this.view.domElement(focus[0]); + + if (!focusedDomElement) { + return; + } + const tabIndexElement = focusedDomElement.querySelector('[tabIndex]'); if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement) || tabIndexElement.tabIndex === -1) { @@ -421,7 +426,7 @@ class MouseController implements IDisposable { .map(event => { const index = this.list.getFocus()[0]; const element = this.view.element(index); - const anchor = this.view.domElement(index); + const anchor = this.view.domElement(index) || undefined; return { index, element, anchor, browserEvent: event.browserEvent }; }) .event; @@ -436,7 +441,7 @@ class MouseController implements IDisposable { .map(browserEvent => { const index = this.list.getFocus()[0]; const element = this.view.element(index); - const anchor = this.view.domElement(index); + const anchor = this.view.domElement(index) || undefined; return { index, element, anchor, browserEvent }; }) .filter(({ anchor }) => !!anchor) @@ -751,7 +756,7 @@ function getContiguousRangeContaining(range: number[], value: number): number[] return []; } - const result = []; + const result: number[] = []; let i = index - 1; while (i >= 0 && range[i] === value - (index - i)) { result.push(range[i--]); @@ -771,7 +776,7 @@ function getContiguousRangeContaining(range: number[], value: number): number[] * betweem them (OR). */ function disjunction(one: number[], other: number[]): number[] { - const result = []; + const result: number[] = []; let i = 0, j = 0; while (i < one.length || j < other.length) { @@ -799,7 +804,7 @@ function disjunction(one: number[], other: number[]): number[] { * complement between them (XOR). */ function relativeComplement(one: number[], other: number[]): number[] { - const result = []; + const result: number[] = []; let i = 0, j = 0; while (i < one.length || j < other.length) { @@ -974,10 +979,7 @@ export class List implements ISpliceable, IDisposable { this.styleElement = DOM.createStyleSheet(this.view.domNode); - this.styleController = options.styleController; - if (!this.styleController) { - this.styleController = new DefaultStyleController(this.styleElement, this.idPrefix); - } + this.styleController = options.styleController || new DefaultStyleController(this.styleElement, this.idPrefix); this.spliceable = new CombinedSpliceable([ new TraitSpliceable(this.focus, this.view, options.identityProvider), @@ -987,8 +989,8 @@ export class List implements ISpliceable, IDisposable { this.disposables = [this.focus, this.selection, this.view, this._onDidDispose]; - this.onDidFocus = mapEvent(domEvent(this.view.domNode, 'focus', true), () => null); - this.onDidBlur = mapEvent(domEvent(this.view.domNode, 'blur', true), () => null); + this.onDidFocus = mapEvent(domEvent(this.view.domNode, 'focus', true), () => null!); + this.onDidBlur = mapEvent(domEvent(this.view.domNode, 'blur', true), () => null!); this.disposables.push(new DOMFocusController(this, this.view)); @@ -1087,7 +1089,7 @@ export class List implements ISpliceable, IDisposable { if (this.length === 0) { return; } const focus = this.focus.get(); let index = focus.length > 0 ? focus[0] + n : 0; - this.setFocus(loop ? [index % this.length] : [Math.min(index, this.length - 1)]); + this.setFocus(loop ? [index % this.length] : [Math.min(index, this.length - 1)], browserEvent); } focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { @@ -1095,7 +1097,7 @@ export class List implements ISpliceable, IDisposable { const focus = this.focus.get(); let index = focus.length > 0 ? focus[0] - n : 0; if (loop && index < 0) { index = (this.length + (index % this.length)) % this.length; } - this.setFocus([Math.max(index, 0)]); + this.setFocus([Math.max(index, 0)], browserEvent); } focusNextPage(browserEvent?: UIEvent): void { @@ -1105,14 +1107,14 @@ export class List implements ISpliceable, IDisposable { const currentlyFocusedElement = this.getFocusedElements()[0]; if (currentlyFocusedElement !== lastPageElement) { - this.setFocus([lastPageIndex]); + this.setFocus([lastPageIndex], browserEvent); } else { const previousScrollTop = this.view.getScrollTop(); this.view.setScrollTop(previousScrollTop + this.view.renderHeight - this.view.elementHeight(lastPageIndex)); if (this.view.getScrollTop() !== previousScrollTop) { // Let the scroll event listener run - setTimeout(() => this.focusNextPage(), 0); + setTimeout(() => this.focusNextPage(browserEvent), 0); } } } @@ -1131,26 +1133,26 @@ export class List implements ISpliceable, IDisposable { const currentlyFocusedElement = this.getFocusedElements()[0]; if (currentlyFocusedElement !== firstPageElement) { - this.setFocus([firstPageIndex]); + this.setFocus([firstPageIndex], browserEvent); } else { const previousScrollTop = scrollTop; this.view.setScrollTop(scrollTop - this.view.renderHeight); if (this.view.getScrollTop() !== previousScrollTop) { // Let the scroll event listener run - setTimeout(() => this.focusPreviousPage(), 0); + setTimeout(() => this.focusPreviousPage(browserEvent), 0); } } } focusLast(browserEvent?: UIEvent): void { if (this.length === 0) { return; } - this.setFocus([this.length - 1]); + this.setFocus([this.length - 1], browserEvent); } focusFirst(browserEvent?: UIEvent): void { if (this.length === 0) { return; } - this.setFocus([0]); + this.setFocus([0], browserEvent); } getFocus(): number[] { diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index 3c4d78563b9..45b164c5392 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -92,7 +92,7 @@ export class RangeMap { private groups: IRangedGroup[] = []; private _size = 0; - splice(index: number, deleteCount: number, ...items: IItem[]): void { + splice(index: number, deleteCount: number, items: IItem[] = []): void { const diff = items.length - deleteCount; const before = groupIntersect({ start: 0, end: index }, this.groups); const after = groupIntersect({ start: index + deleteCount, end: Number.POSITIVE_INFINITY }, this.groups) @@ -188,6 +188,6 @@ export class RangeMap { } dispose() { - this.groups = null; + this.groups = null!; // StrictNullOverride: nulling out ok in dispose } } \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index 756b1f9a678..b7007c2fc0f 100644 --- a/src/vs/base/browser/ui/list/rowCache.ts +++ b/src/vs/base/browser/ui/list/rowCache.ts @@ -8,14 +8,16 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { $, removeClass } from 'vs/base/browser/dom'; export interface IRow { - domNode: HTMLElement; + domNode: HTMLElement | null; templateId: string; templateData: any; } function removeFromParent(element: HTMLElement): void { try { - element.parentElement.removeChild(element); + if (element.parentElement) { + element.parentElement.removeChild(element); + } } catch (e) { // this will throw if this happens due to a blur event, nasty business } @@ -57,8 +59,10 @@ export class RowCache implements IDisposable { private releaseRow(row: IRow): void { const { domNode, templateId } = row; - removeClass(domNode, 'scrolling'); - removeFromParent(domNode); + if (domNode) { + removeClass(domNode, 'scrolling'); + removeFromParent(domNode); + } const cache = this.getTemplateCache(templateId); cache.push(row); @@ -95,6 +99,6 @@ export class RowCache implements IDisposable { dispose(): void { this.garbageCollect(); this.cache.clear(); - this.renderers = null; + this.renderers = null!; // StrictNullOverride: nulling out ok in dispose } } \ No newline at end of file diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css index 63c1860664d..ecbb197f55c 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -55,8 +55,8 @@ .monaco-menu .monaco-action-bar.vertical .submenu-indicator { height: 100%; - -webkit-mask: url('submenu.svg') no-repeat 90% 50%/14px 14px; - mask: url('submenu.svg') no-repeat 90% 50%/14px 14px; + -webkit-mask: url('submenu.svg') no-repeat 90% 50%/13px 13px; + mask: url('submenu.svg') no-repeat 90% 50%/13px 13px; } .monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding, @@ -106,7 +106,7 @@ /* Context Menu */ .context-view.monaco-menu-container { - font-family: "Segoe WPC", "Segoe UI", ".SFNSDisplay-Light", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; outline: 0; border: none; -webkit-animation: fadeIn 0.083s linear; diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 1cc3f375b27..75554c14fd5 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -191,7 +191,7 @@ export class Menu extends ActionBar { if (options.enableMnemonics) { const mnemonic = menuActionItem.getMnemonic(); if (mnemonic && menuActionItem.isEnabled()) { - let actionItems = []; + let actionItems: MenuActionItem[] = []; if (this.mnemonics.has(mnemonic)) { actionItems = this.mnemonics.get(mnemonic); } @@ -217,7 +217,7 @@ export class Menu extends ActionBar { if (options.enableMnemonics) { const mnemonic = menuActionItem.getMnemonic(); if (mnemonic && menuActionItem.isEnabled()) { - let actionItems = []; + let actionItems: MenuActionItem[] = []; if (this.mnemonics.has(mnemonic)) { actionItems = this.mnemonics.get(mnemonic); } diff --git a/src/vs/base/browser/ui/menu/submenu.svg b/src/vs/base/browser/ui/menu/submenu.svg index 8eeac7b7b66..98a0aa5924a 100644 --- a/src/vs/base/browser/ui/menu/submenu.svg +++ b/src/vs/base/browser/ui/menu/submenu.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index bcee192ab85..c9d3aa68d0e 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as Platform from 'vs/base/common/platform'; -import * as DomUtils from 'vs/base/browser/dom'; -import { IMouseEvent, StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; -import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; -import { Widget } from 'vs/base/browser/ui/widget'; +import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; +import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; +import { IMouseEvent, StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { ScrollbarArrow, ScrollbarArrowOptions } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; +import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { ScrollbarVisibilityController } from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController'; -import { Scrollable, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable'; +import { Widget } from 'vs/base/browser/ui/widget'; +import * as platform from 'vs/base/common/platform'; +import { INewScrollPosition, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; /** * The orthogonal distance to the slider at which dragging "resets". This implements "snapping" @@ -197,7 +197,7 @@ export abstract class AbstractScrollbar extends Widget { offsetX = e.browserEvent.offsetX; offsetY = e.browserEvent.offsetY; } else { - const domNodePosition = DomUtils.getDomNodePagePosition(this.domNode.domNode); + const domNodePosition = dom.getDomNodePagePosition(this.domNode.domNode); offsetX = e.posx - domNodePosition.left; offsetY = e.posy - domNodePosition.top; } @@ -220,7 +220,7 @@ export abstract class AbstractScrollbar extends Widget { const mouseOrthogonalPosition = this._sliderOrthogonalMousePosition(mouseMoveData); const mouseOrthogonalDelta = Math.abs(mouseOrthogonalPosition - initialMouseOrthogonalPosition); - if (Platform.isWindows && mouseOrthogonalDelta > MOUSE_DRAG_RESET_DISTANCE) { + if (platform.isWindows && mouseOrthogonalDelta > MOUSE_DRAG_RESET_DISTANCE) { // The mouse has wondered away from the scrollbar => reset dragging this._setDesiredScrollPositionNow(initialScrollbarState.getScrollPosition()); return; diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index ce87928154d..f49deda9ec7 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { AbstractScrollbar, ScrollbarHost, ISimplifiedMouseEvent } from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { AbstractScrollbar, ISimplifiedMouseEvent, ScrollbarHost } from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; -import { Scrollable, ScrollEvent, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable'; -import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; +import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; +import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; export class HorizontalScrollbar extends AbstractScrollbar { diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 69bdfbca025..ea42ceac6e8 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -4,20 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/scrollbars'; - -import * as DomUtils from 'vs/base/browser/dom'; -import * as Platform from 'vs/base/common/platform'; -import { StandardMouseWheelEvent, IMouseEvent } from 'vs/base/browser/mouseEvent'; +import * as dom from 'vs/base/browser/dom'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { IMouseEvent, StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { ScrollbarHost } from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; import { HorizontalScrollbar } from 'vs/base/browser/ui/scrollbar/horizontalScrollbar'; +import { ScrollableElementChangeOptions, ScrollableElementCreationOptions, ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; import { VerticalScrollbar } from 'vs/base/browser/ui/scrollbar/verticalScrollbar'; -import { ScrollableElementCreationOptions, ScrollableElementChangeOptions, ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Scrollable, ScrollEvent, ScrollbarVisibility, INewScrollDimensions, IScrollDimensions, INewScrollPosition, IScrollPosition } from 'vs/base/common/scrollable'; import { Widget } from 'vs/base/browser/ui/widget'; import { TimeoutTimer } from 'vs/base/common/async'; -import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { ScrollbarHost } from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; const HIDE_TIMEOUT = 500; const SCROLL_WHEEL_SENSITIVITY = 50; @@ -267,7 +266,7 @@ export abstract class AbstractScrollableElement extends Widget { public updateClassName(newClassName: string): void { this._options.className = newClassName; // Defaults are different on Macs - if (Platform.isMacintosh) { + if (platform.isMacintosh) { this._options.className += ' mac'; } this._domNode.className = 'monaco-scrollable-element ' + this._options.className; @@ -313,8 +312,8 @@ export abstract class AbstractScrollableElement extends Widget { this._onMouseWheel(e); }; - this._mouseWheelToDispose.push(DomUtils.addDisposableListener(this._listenOnDomNode, 'mousewheel', onMouseWheel)); - this._mouseWheelToDispose.push(DomUtils.addDisposableListener(this._listenOnDomNode, 'DOMMouseScroll', onMouseWheel)); + this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, 'mousewheel', onMouseWheel)); + this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, 'DOMMouseScroll', onMouseWheel)); } } @@ -337,7 +336,7 @@ export abstract class AbstractScrollableElement extends Widget { // Convert vertical scrolling to horizontal if shift is held, this // is handled at a higher level on Mac - const shiftConvert = !Platform.isMacintosh && e.browserEvent && e.browserEvent.shiftKey; + const shiftConvert = !platform.isMacintosh && e.browserEvent && e.browserEvent.shiftKey; if ((this._options.scrollYToX || shiftConvert) && !deltaX) { deltaX = deltaY; deltaY = 0; @@ -478,7 +477,7 @@ export class ScrollableElement extends AbstractScrollableElement { constructor(element: HTMLElement, options: ScrollableElementCreationOptions) { options = options || {}; options.mouseWheelSmoothScroll = false; - const scrollable = new Scrollable(0, (callback) => DomUtils.scheduleAtNextAnimationFrame(callback)); + const scrollable = new Scrollable(0, (callback) => dom.scheduleAtNextAnimationFrame(callback)); super(element, options, scrollable); this._register(scrollable); } @@ -563,7 +562,7 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme result.verticalSliderSize = (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : result.verticalScrollbarSize); // Defaults are different on Macs - if (Platform.isMacintosh) { + if (platform.isMacintosh) { result.className += ' mac'; } diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts index b66a7196ab1..b174952d7ae 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Widget } from 'vs/base/browser/ui/widget'; -import { TimeoutTimer, IntervalTimer } from 'vs/base/common/async'; +import { IntervalTimer, TimeoutTimer } from 'vs/base/common/async'; /** * The arrow image size. diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts b/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts index 52a4b60b592..a77792ee223 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; -import { TimeoutTimer } from 'vs/base/common/async'; import { FastDomNode } from 'vs/base/browser/fastDomNode'; +import { TimeoutTimer } from 'vs/base/common/async'; +import { Disposable } from 'vs/base/common/lifecycle'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; export class ScrollbarVisibilityController extends Disposable { diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index ab07329614a..77d19898d28 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { AbstractScrollbar, ScrollbarHost, ISimplifiedMouseEvent } from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { AbstractScrollbar, ISimplifiedMouseEvent, ScrollbarHost } from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; -import { Scrollable, ScrollEvent, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable'; -import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; +import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; +import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; export class VerticalScrollbar extends AbstractScrollbar { diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 65a883bcb17..36b40a6995c 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -129,6 +129,13 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { - this.selectElement.title = e.target.value; this.selected = e.target.selectedIndex; this._onDidSelect.fire({ index: e.target.selectedIndex, @@ -301,7 +307,6 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate(); readonly onDidChange: Event = this._onDidChange.event; @@ -131,7 +129,7 @@ export abstract class Panel implements IView { this._expanded = !!expanded; this.updateHeader(); - this._onDidChange.fire(expanded ? this.cachedExpandedSize : undefined); + this._onDidChange.fire(expanded ? this.expandedSize : undefined); } get headerVisible(): boolean { @@ -192,14 +190,8 @@ export abstract class Panel implements IView { const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0; if (this.isExpanded()) { - const bodySize = size - headerSize; - - if (bodySize !== this.cachedBodySize) { - this.layoutBody(bodySize); - this.cachedBodySize = bodySize; - } - - this.cachedExpandedSize = size; + this.layoutBody(size - headerSize); + this.expandedSize = size; } } @@ -264,7 +256,7 @@ class PanelDraggable extends Disposable { } private onDragStart(e: DragEvent): void { - if (!this.dnd.canDrag(this.panel)) { + if (!this.dnd.canDrag(this.panel) || !e.dataTransfer) { e.preventDefault(); e.stopPropagation(); return; @@ -272,7 +264,7 @@ class PanelDraggable extends Disposable { e.dataTransfer.effectAllowed = 'move'; - const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.draggableElement.textContent)); + const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.draggableElement.textContent || '')); e.dataTransfer.setDragImage(dragImage, -10, -10); setTimeout(() => document.body.removeChild(dragImage), 0); @@ -371,7 +363,7 @@ interface IPanelItem { export class PanelView extends Disposable { - private dnd: IPanelDndController | null; + private dnd: IPanelDndController | undefined; private dndContext: IDndContext = { draggable: null }; private el: HTMLElement; private panelItems: IPanelItem[] = []; diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index bdef6277a62..0de80a6c763 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -29,6 +29,16 @@ export interface ISplitViewOptions { orthogonalStartSash?: Sash; orthogonalEndSash?: Sash; inverseAltBehavior?: boolean; + proportionalLayout?: boolean; // default true +} + +/** + * Only used when `proportionalLayout` is false. + */ +export const enum LayoutPriority { + Normal, + Low, + High } export interface IView { @@ -36,6 +46,8 @@ export interface IView { readonly minimumSize: number; readonly maximumSize: number; readonly onDidChange: Event; + readonly priority?: LayoutPriority; + readonly snapSize?: number; layout(size: number, orientation: Orientation): void; } @@ -87,7 +99,6 @@ export namespace Sizing { export class SplitView extends Disposable { readonly orientation: Orientation; - // TODO@Joao have the same pattern as grid here readonly el: HTMLElement; private sashContainer: HTMLElement; private viewContainer: HTMLElement; @@ -99,6 +110,7 @@ export class SplitView extends Disposable { private sashDragState: ISashDragState; private state: State = State.Idle; private inverseAltBehavior: boolean; + private proportionalLayout: boolean; private _onDidSashChange = this._register(new Emitter()); readonly onDidSashChange = this._onDidSashChange.event; @@ -147,6 +159,7 @@ export class SplitView extends Disposable { this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation; this.inverseAltBehavior = !!options.inverseAltBehavior; + this.proportionalLayout = types.isUndefined(options.proportionalLayout) ? true : !!options.proportionalLayout; this.el = document.createElement('div'); dom.addClass(this.el, 'monaco-split-view2'); @@ -317,8 +330,10 @@ export class SplitView extends Disposable { private relayout(lowPriorityIndex?: number, highPriorityIndex?: number): void { const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); + const lowPriorityIndexes = typeof lowPriorityIndex === 'number' ? [lowPriorityIndex] : undefined; + const highPriorityIndexes = typeof highPriorityIndex === 'number' ? [highPriorityIndex] : undefined; - this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex, highPriorityIndex); + this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndexes, highPriorityIndexes); this.distributeEmptySpace(); this.layoutViews(); this.saveProportions(); @@ -329,11 +344,15 @@ export class SplitView extends Disposable { this.size = size; if (!this.proportions) { - this.resize(this.viewItems.length - 1, size - previousSize); + const indexes = range(this.viewItems.length); + const lowPriorityIndexes = indexes.filter(i => this.viewItems[i].view.priority === LayoutPriority.Low); + const highPriorityIndexes = indexes.filter(i => this.viewItems[i].view.priority === LayoutPriority.High); + + this.resize(this.viewItems.length - 1, size - previousSize, undefined, lowPriorityIndexes, highPriorityIndexes); } else { for (let i = 0; i < this.viewItems.length; i++) { const item = this.viewItems[i]; - item.size = clamp(Math.round(this.proportions[i] * size), item.view.minimumSize, item.view.maximumSize); + item.size = SplitView.clamp(item, Math.round(this.proportions[i] * size)); } } @@ -342,7 +361,7 @@ export class SplitView extends Disposable { } private saveProportions(): void { - if (this.contentSize > 0) { + if (this.proportionalLayout && this.contentSize > 0) { this.proportions = this.viewItems.map(i => i.size / this.contentSize); } } @@ -425,7 +444,7 @@ export class SplitView extends Disposable { } size = typeof size === 'number' ? size : item.size; - size = clamp(size, item.view.minimumSize, item.view.maximumSize); + size = SplitView.clamp(item, size); if (this.inverseAltBehavior && index > 0) { // In this case, we want the view to grow or shrink both sides equally @@ -500,8 +519,8 @@ export class SplitView extends Disposable { index: number, delta: number, sizes = this.viewItems.map(i => i.size), - lowPriorityIndex?: number, - highPriorityIndex?: number, + lowPriorityIndexes?: number[], + highPriorityIndexes?: number[], overloadMinDelta: number = Number.NEGATIVE_INFINITY, overloadMaxDelta: number = Number.POSITIVE_INFINITY ): number { @@ -512,14 +531,18 @@ export class SplitView extends Disposable { const upIndexes = range(index, -1); const downIndexes = range(index + 1, this.viewItems.length); - if (typeof highPriorityIndex === 'number') { - pushToStart(upIndexes, highPriorityIndex); - pushToStart(downIndexes, highPriorityIndex); + if (highPriorityIndexes) { + for (const index of highPriorityIndexes) { + pushToStart(upIndexes, index); + pushToStart(downIndexes, index); + } } - if (typeof lowPriorityIndex === 'number') { - pushToEnd(upIndexes, lowPriorityIndex); - pushToEnd(downIndexes, lowPriorityIndex); + if (lowPriorityIndexes) { + for (const index of lowPriorityIndexes) { + pushToEnd(upIndexes, index); + pushToEnd(downIndexes, index); + } } const upItems = upIndexes.map(i => this.viewItems[i]); @@ -528,27 +551,29 @@ export class SplitView extends Disposable { const downItems = downIndexes.map(i => this.viewItems[i]); const downSizes = downIndexes.map(i => sizes[i]); - const minDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.minimumSize - sizes[i]), 0); + const minDeltaUp = upIndexes.reduce((r, i) => r + ((typeof this.viewItems[i].view.snapSize === 'number' ? 0 : this.viewItems[i].view.minimumSize) - sizes[i]), 0); const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0); - const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0); + const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - (typeof this.viewItems[i].view.snapSize === 'number' ? 0 : this.viewItems[i].view.minimumSize)), 0); const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.maximumSize), 0); const minDelta = Math.max(minDeltaUp, minDeltaDown, overloadMinDelta); const maxDelta = Math.min(maxDeltaDown, maxDeltaUp, overloadMaxDelta); - delta = clamp(delta, minDelta, maxDelta); + const tentativeDelta = clamp(delta, minDelta, maxDelta); + let actualDelta = 0; - for (let i = 0, deltaUp = delta; i < upItems.length; i++) { + for (let i = 0, deltaUp = tentativeDelta; i < upItems.length; i++) { const item = upItems[i]; - const size = clamp(upSizes[i] + deltaUp, item.view.minimumSize, item.view.maximumSize); + const size = SplitView.clamp(item, upSizes[i] + deltaUp/* , upIndexes[i] === index */); const viewDelta = size - upSizes[i]; + actualDelta += viewDelta; deltaUp -= viewDelta; item.size = size; } - for (let i = 0, deltaDown = delta; i < downItems.length; i++) { + for (let i = 0, deltaDown = actualDelta; i < downItems.length; i++) { const item = downItems[i]; - const size = clamp(downSizes[i] - deltaDown, item.view.minimumSize, item.view.maximumSize); + const size = SplitView.clamp(item, downSizes[i] - deltaDown); const viewDelta = size - downSizes[i]; deltaDown += viewDelta; @@ -558,13 +583,24 @@ export class SplitView extends Disposable { return delta; } + private static clamp(item: IViewItem, size: number): number { + const result = clamp(size, item.view.minimumSize, item.view.maximumSize); + + if (typeof item.view.snapSize !== 'number' || size >= item.view.minimumSize) { + return result; + } + + const snapSize = Math.min(item.view.snapSize, item.view.minimumSize); + return size < snapSize ? 0 : item.view.minimumSize; + } + private distributeEmptySpace(): void { let contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); let emptyDelta = this.size - contentSize; for (let i = this.viewItems.length - 1; emptyDelta !== 0 && i >= 0; i--) { const item = this.viewItems[i]; - const size = clamp(item.size + emptyDelta, item.view.minimumSize, item.view.maximumSize); + const size = SplitView.clamp(item, item.size + emptyDelta); const viewDelta = size - item.size; emptyDelta -= viewDelta; diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 807cead7989..2b339b430b2 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -11,11 +11,10 @@ import { append, $, toggleClass } from 'vs/base/browser/dom'; import { Event, Relay, chain } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ITreeModel, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeModelOptions } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; -import { IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; -export function createComposedTreeListOptions(options?: IListOptions): IListOptions { +export function createComposedTreeListOptions(options?: IListOptions): IListOptions | undefined { if (!options) { return undefined; } @@ -23,18 +22,20 @@ export function createComposedTreeListOptions(optio let identityProvider: IIdentityProvider | undefined = undefined; if (options.identityProvider) { - identityProvider = el => options.identityProvider(el.element); + const ip = options.identityProvider; + identityProvider = el => ip(el.element); } let multipleSelectionController: IMultipleSelectionController | undefined = undefined; if (options.multipleSelectionController) { + const msc = options.multipleSelectionController; multipleSelectionController = { isSelectionSingleChangeEvent(e) { - return options.multipleSelectionController.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); + return msc.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); }, isSelectionRangeChangeEvent(e) { - return options.multipleSelectionController.isSelectionRangeChangeEvent({ ...e, element: e.element } as any); + return msc.isSelectionRangeChangeEvent({ ...e, element: e.element } as any); } }; } @@ -42,9 +43,10 @@ export function createComposedTreeListOptions(optio let accessibilityProvider: IAccessibilityProvider | undefined = undefined; if (options.accessibilityProvider) { + const ap = options.accessibilityProvider; accessibilityProvider = { getAriaLabel(e) { - return options.accessibilityProvider.getAriaLabel(e.element); + return ap.getAriaLabel(e.element); } }; } @@ -168,7 +170,7 @@ function isInputElement(e: HTMLElement): boolean { return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA'; } -export interface ITreeOptions extends IListOptions, IIndexTreeModelOptions { } +export interface ITreeOptions extends IListOptions, ITreeModelOptions { } export interface ITreeEvent extends IListEvent> { } export interface ITreeContextMenuEvent extends IListContextMenuEvent> { } @@ -193,7 +195,7 @@ export abstract class AbstractTree implements IDisposable container: HTMLElement, delegate: IListVirtualDelegate, renderers: ITreeRenderer[], - options?: ITreeOptions + options: ITreeOptions = {} ) { const treeDelegate = new ComposedTreeDelegate>(delegate); @@ -302,33 +304,33 @@ export abstract class AbstractTree implements IDisposable return nodes.map(n => n.element); } - setFocus(elements: TRef[]): void { + setFocus(elements: TRef[], browserEvent?: UIEvent): void { const indexes = elements.map(e => this.model.getListIndex(e)); - this.view.setFocus(indexes); + this.view.setFocus(indexes, browserEvent); } - focusNext(n = 1, loop = false): void { - this.view.focusNext(n, loop); + focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { + this.view.focusNext(n, loop, browserEvent); } - focusPrevious(n = 1, loop = false): void { - this.view.focusPrevious(n, loop); + focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { + this.view.focusPrevious(n, loop, browserEvent); } - focusNextPage(): void { - this.view.focusNextPage(); + focusNextPage(browserEvent?: UIEvent): void { + this.view.focusNextPage(browserEvent); } - focusPreviousPage(): void { - this.view.focusPreviousPage(); + focusPreviousPage(browserEvent?: UIEvent): void { + this.view.focusPreviousPage(browserEvent); } - focusLast(): void { - this.view.focusLast(); + focusLast(browserEvent?: UIEvent): void { + this.view.focusLast(browserEvent); } - focusFirst(): void { - this.view.focusFirst(); + focusFirst(browserEvent?: UIEvent): void { + this.view.focusFirst(browserEvent); } getFocus(): T[] { @@ -357,8 +359,12 @@ export abstract class AbstractTree implements IDisposable private onMouseClick(e: IListMouseEvent>): void { const node = e.element; - const location = this.model.getNodeLocation(node); + if (!node) { + return; + } + + const location = this.model.getNodeLocation(node); this.model.toggleCollapsed(location); } @@ -437,7 +443,5 @@ export abstract class AbstractTree implements IDisposable dispose(): void { this.disposables = dispose(this.disposables); this.view.dispose(); - this.view = null; - this.model = null; } } \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index 2c7471795dd..a4b627e3f62 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -99,7 +99,7 @@ export class DataTree, TFilterData = void> implements private tree: ObjectTree, TFilterData>; private root: IDataTreeNode; - private nodes = new Map>(); + private nodes = new Map>(); private _onDidChangeNodeState = new Emitter>(); @@ -112,9 +112,9 @@ export class DataTree, TFilterData = void> implements private dataSource: IDataSource, options?: ITreeOptions ) { - const objectTreeDelegate = new ComposedTreeDelegate>(delegate); + const objectTreeDelegate = new ComposedTreeDelegate>(delegate); const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeState.event)); - const objectTreeOptions = createComposedTreeListOptions>(options); + const objectTreeOptions = createComposedTreeListOptions>(options); this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions); this.root = { @@ -143,7 +143,7 @@ export class DataTree, TFilterData = void> implements if (!hasChildren) { this.tree.setChildren(node === this.root ? null : node); - return Promise.resolve(null); + return Promise.resolve(); } else { node.state = DataTreeNodeState.Loading; this._onDidChangeNodeState.fire(node); diff --git a/src/vs/base/browser/ui/tree/indexTree.ts b/src/vs/base/browser/ui/tree/indexTree.ts index 86b19455381..02e9b4492bb 100644 --- a/src/vs/base/browser/ui/tree/indexTree.ts +++ b/src/vs/base/browser/ui/tree/indexTree.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./tree'; +import 'vs/css!./media/tree'; import { Iterator, ISequence } from 'vs/base/common/iterator'; import { AbstractTree, ITreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; import { ISpliceable } from 'vs/base/common/sequence'; diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 47f1c37607a..1b4ee5b0c70 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -7,7 +7,7 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { Iterator, ISequence } from 'vs/base/common/iterator'; import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; import { tail2 } from 'vs/base/common/arrays'; -import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree'; +import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement, ITreeModelOptions } from 'vs/base/browser/ui/tree/tree'; interface IMutableTreeNode extends ITreeNode { readonly parent: IMutableTreeNode | undefined; @@ -30,25 +30,19 @@ function treeNodeToElement(node: IMutableTreeNode): ITreeElement { return { element, children, collapsed }; } -function getVisibleState(visibility: boolean | TreeVisibility): boolean | undefined { +function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility { switch (visibility) { - case true: return true; - case false: return false; - case TreeVisibility.Hidden: return false; - case TreeVisibility.Visible: return true; - case TreeVisibility.Recurse: return undefined; + case true: return TreeVisibility.Visible; + case false: return TreeVisibility.Hidden; + default: return visibility; } } -export interface IIndexTreeModelOptions { - filter?: ITreeFilter; -} - export class IndexTreeModel implements ITreeModel { private root: IMutableTreeNode = { parent: undefined, - element: undefined, + element: undefined!, children: [], depth: 0, collapsible: false, @@ -68,7 +62,7 @@ export class IndexTreeModel implements ITreeModel; - constructor(private list: ISpliceable>, options: IIndexTreeModelOptions = {}) { + constructor(private list: ISpliceable>, options: ITreeModelOptions = {}) { this.filter = options.filter; } @@ -85,7 +79,7 @@ export class IndexTreeModel implements ITreeModel[] = []; - const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, revealed, treeListElementsToInsert, onDidCreateNode)); + const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode)); const nodesToInsert: IMutableTreeNode[] = []; let renderNodeCount = 0; @@ -137,7 +131,7 @@ export class IndexTreeModel implements ITreeModel { while (queue.length > 0) { - const node = queue.shift(); + const node = queue.shift()!; const revealed = listIndex < this.root.children.length; this._setCollapsed(node, listIndex, revealed, true); @@ -186,6 +180,7 @@ export class IndexTreeModel implements ITreeModel, parent: IMutableTreeNode, + parentVisibility: TreeVisibility, revealed: boolean, treeListElements: ITreeNode[], onDidCreateNode?: (node: ITreeNode) => void @@ -202,15 +197,15 @@ export class IndexTreeModel implements ITreeModel this.createTreeNode(el, node, childRevealed, treeListElements, onDidCreateNode)); + const childRevealed = revealed && visibility !== TreeVisibility.Hidden && !node.collapsed; + const childNodes = Iterator.map(childElements, el => this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode)); let hasVisibleDescendants = false; let renderNodeCount = 1; @@ -222,7 +217,7 @@ export class IndexTreeModel implements ITreeModel 0; - node.visible = typeof visible === 'undefined' ? hasVisibleDescendants : visible; + node.visible = visibility === TreeVisibility.Recurse ? hasVisibleDescendants : (visibility === TreeVisibility.Visible); if (!node.visible) { node.renderNodeCount = 0; @@ -273,19 +268,19 @@ export class IndexTreeModel implements ITreeModel[] = []; - this._updateNodeAfterFilterChange(node, result); + this._updateNodeAfterFilterChange(node, node.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, result); this._updateAncestorsRenderNodeCount(node.parent, result.length - previousRenderNodeCount); return result; } - private _updateNodeAfterFilterChange(node: IMutableTreeNode, result: ITreeNode[], revealed = true): boolean { - let visible: boolean | undefined; + private _updateNodeAfterFilterChange(node: IMutableTreeNode, parentVisibility: TreeVisibility, result: ITreeNode[], revealed = true): boolean { + let visibility: TreeVisibility; if (node !== this.root) { - visible = this._filterNode(node); + visibility = this._filterNode(node, parentVisibility); - if (visible === false) { + if (visibility === TreeVisibility.Hidden) { node.visible = false; return false; } @@ -299,14 +294,14 @@ export class IndexTreeModel implements ITreeModel implements ITreeModel, diff: number): void { + private _updateAncestorsRenderNodeCount(node: IMutableTreeNode | undefined, diff: number): void { if (diff === 0) { return; } @@ -335,12 +330,12 @@ export class IndexTreeModel implements ITreeModel): boolean | undefined { - const result = this.filter ? this.filter.filter(node.element) : TreeVisibility.Visible; + private _filterNode(node: IMutableTreeNode, parentVisibility: TreeVisibility): TreeVisibility { + const result = this.filter ? this.filter.filter(node.element, parentVisibility) : TreeVisibility.Visible; if (typeof result === 'boolean') { node.filterData = undefined; - return result; + return TreeVisibility.Visible; } else if (isFilterResult(result)) { node.filterData = result.data; return getVisibleState(result.visibility); @@ -351,8 +346,8 @@ export class IndexTreeModel implements ITreeModel = this.root): IMutableTreeNode { - if (location.length === 0) { + private getTreeNode(location: number[] | null, node: IMutableTreeNode = this.root): IMutableTreeNode { + if (!location || location.length === 0) { return node; } @@ -406,7 +401,7 @@ export class IndexTreeModel implements ITreeModel): number[] { - const location = []; + const location: number[] = []; while (node.parent) { location.push(node.parent.children.indexOf(node)); @@ -418,7 +413,7 @@ export class IndexTreeModel implements ITreeModel extends IIndexTreeModelOptions { } +import { ITreeModel, ITreeNode, ITreeElement, ITreeModelOptions } from 'vs/base/browser/ui/tree/tree'; export class ObjectTreeModel, TFilterData = void> implements ITreeModel { @@ -23,7 +19,7 @@ export class ObjectTreeModel, TFilterData = void> imp get size(): number { return this.nodes.size; } - constructor(list: ISpliceable>, options: IObjectTreeModelOptions = {}) { + constructor(list: ISpliceable>, options: ITreeModelOptions = {}) { this.model = new IndexTreeModel(list, options); this.onDidChangeCollapseState = this.model.onDidChangeCollapseState; this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount; @@ -90,7 +86,7 @@ export class ObjectTreeModel, TFilterData = void> imp this.model.refilter(); } - getNode(element: T = null): ITreeNode { + getNode(element: T | null = null): ITreeNode { const location = this.getElementLocation(element); return this.model.getNode(location); } @@ -106,7 +102,7 @@ export class ObjectTreeModel, TFilterData = void> imp throw new Error(`Tree element not found: ${element}`); } - return node.parent.element; + return node.parent!.element; } private getElementLocation(element: T | null): number[] { diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index b66284dcc2b..2d0705b9ccb 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -64,7 +64,7 @@ export interface ITreeFilter { * * @param element The tree element. */ - filter(element: T): TreeFilterResult; + filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult; } export interface ITreeElement { @@ -94,9 +94,9 @@ export interface ITreeModel { getNodeLocation(node: ITreeNode): TRef; getParentNodeLocation(location: TRef): TRef | null; - getParentElement(location: TRef): T | null; - getFirstChildElement(location: TRef): T | null; - getLastAncestorElement(location: TRef): T | null; + getParentElement(location: TRef | null): T | null; + getFirstChildElement(location: TRef | null): T | null; + getLastAncestorElement(location: TRef | null): T | null; isCollapsed(location: TRef): boolean; setCollapsed(location: TRef, collapsed: boolean): boolean; @@ -109,4 +109,8 @@ export interface ITreeModel { export interface ITreeRenderer extends IListRenderer, TTemplateData> { renderTwistie?(element: T, twistieElement: HTMLElement): boolean; onDidChangeTwistieState?: Event; +} + +export interface ITreeModelOptions { + filter?: ITreeFilter; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/widget.ts b/src/vs/base/browser/ui/widget.ts index 22906aeaf70..569ef4925fc 100644 --- a/src/vs/base/browser/ui/widget.ts +++ b/src/vs/base/browser/ui/widget.ts @@ -3,50 +3,50 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as dom from 'vs/base/browser/dom'; +import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Disposable } from 'vs/base/common/lifecycle'; -import { StandardMouseEvent, IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import * as DomUtils from 'vs/base/browser/dom'; export abstract class Widget extends Disposable { protected onclick(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { - this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.CLICK, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); + this._register(dom.addDisposableListener(domNode, dom.EventType.CLICK, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); } protected onmousedown(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { - this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.MOUSE_DOWN, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); + this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); } protected onmouseover(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { - this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.MOUSE_OVER, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); + this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_OVER, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); } protected onnonbubblingmouseout(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { - this._register(DomUtils.addDisposableNonBubblingMouseOutListener(domNode, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); + this._register(dom.addDisposableNonBubblingMouseOutListener(domNode, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); } protected onkeydown(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void { - this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.KEY_DOWN, (e: KeyboardEvent) => listener(new StandardKeyboardEvent(e)))); + this._register(dom.addDisposableListener(domNode, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => listener(new StandardKeyboardEvent(e)))); } protected onkeyup(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void { - this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.KEY_UP, (e: KeyboardEvent) => listener(new StandardKeyboardEvent(e)))); + this._register(dom.addDisposableListener(domNode, dom.EventType.KEY_UP, (e: KeyboardEvent) => listener(new StandardKeyboardEvent(e)))); } protected oninput(domNode: HTMLElement, listener: (e: Event) => void): void { - this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.INPUT, listener)); + this._register(dom.addDisposableListener(domNode, dom.EventType.INPUT, listener)); } protected onblur(domNode: HTMLElement, listener: (e: Event) => void): void { - this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.BLUR, listener)); + this._register(dom.addDisposableListener(domNode, dom.EventType.BLUR, listener)); } protected onfocus(domNode: HTMLElement, listener: (e: Event) => void): void { - this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.FOCUS, listener)); + this._register(dom.addDisposableListener(domNode, dom.EventType.FOCUS, listener)); } protected onchange(domNode: HTMLElement, listener: (e: Event) => void): void { - this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.CHANGE, listener)); + this._register(dom.addDisposableListener(domNode, dom.EventType.CHANGE, listener)); } } diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 7aaab83a0a8..0cd3bfe1387 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -6,7 +6,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { ISplice } from 'vs/base/common/sequence'; -import { TPromise } from 'vs/base/common/winjs.base'; /** * Returns the last element of an array. @@ -25,7 +24,7 @@ export function tail2(arr: T[]): [T[], T] { return [arr.slice(0, arr.length - 1), arr[arr.length - 1]]; } -export function equals(one: ReadonlyArray, other: ReadonlyArray, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { +export function equals(one: ReadonlyArray | undefined, other: ReadonlyArray | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { if (one === other) { return true; } @@ -261,12 +260,12 @@ export function top(array: T[], compare: (a: T, b: T) => number, n: number): * @param batch The number of elements to examine before yielding to the event loop. * @return The first n elemnts from array when sorted with compare. */ -export function topAsync(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): TPromise { +export function topAsync(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): Promise { if (n === 0) { - return TPromise.as([]); + return Promise.resolve([]); } - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { (async () => { const o = array.length; const result = array.slice(0, n).sort(compare); @@ -541,3 +540,9 @@ export function find(arr: ArrayLike, predicate: (value: T, index: number, return undefined; } + +export function mapArrayOrNot(items: T | T[], fn: (_: T) => U): U | U[] { + return Array.isArray(items) ? + items.map(fn) : + fn(items); +} diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index c47f0f49a03..3386737eef7 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -242,8 +242,10 @@ export class Delayer implements IDisposable { * A helper to delay execution of a task that is being requested often, while * preventing accumulation of consecutive executions, while the task runs. * - * Simply combine the two mail men's strategies from the Throttler and Delayer - * helpers, for an analogy. + * The mail man is clever and waits for a certain amount of time, before going + * out to deliver letters. While the mail man is going out, more letters arrive + * and can only be delivered once he is back. Once he is back the mail man will + * do one more trip to deliver the letters that have accumulated while he was out. */ export class ThrottledDelayer extends Delayer> { @@ -266,12 +268,12 @@ export class ThrottledDelayer extends Delayer> { export class Barrier { private _isOpen: boolean; - private _promise: TPromise; + private _promise: Promise; private _completePromise: (v: boolean) => void; constructor() { this._isOpen = false; - this._promise = new TPromise((c, e) => { + this._promise = new Promise((c, e) => { this._completePromise = c; }); } @@ -285,7 +287,7 @@ export class Barrier { this._completePromise(true); } - wait(): TPromise { + wait(): Promise { return this._promise; } } @@ -671,7 +673,7 @@ export function nfcall(fn: Function, ...args: any[]): any { export function ninvoke(thisArg: any, fn: Function, ...args: any[]): TPromise; export function ninvoke(thisArg: any, fn: Function, ...args: any[]): TPromise; export function ninvoke(thisArg: any, fn: Function, ...args: any[]): any { - return new TPromise((c, e) => fn.call(thisArg, ...args, (err: any, result: any) => err ? e(err) : c(result))); + return new Promise((resolve, reject) => fn.call(thisArg, ...args, (err: any, result: any) => err ? reject(err) : resolve(result))); } diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index a3f3ca3582f..3a73fa7d4f4 100644 --- a/src/vs/base/common/comparers.ts +++ b/src/vs/base/common/comparers.ts @@ -5,24 +5,23 @@ import * as strings from 'vs/base/common/strings'; import * as paths from 'vs/base/common/paths'; +import { IdleValue } from 'vs/base/common/async'; -let intlFileNameCollator: Intl.Collator; -let intlFileNameCollatorIsNumeric: boolean; +let intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>; -export function setFileNameComparer(collator: Intl.Collator): void { +export function setFileNameComparer(collator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>): void { intlFileNameCollator = collator; - intlFileNameCollatorIsNumeric = collator.resolvedOptions().numeric; } export function compareFileNames(one: string, other: string, caseSensitive = false): number { if (intlFileNameCollator) { const a = one || ''; const b = other || ''; - const result = intlFileNameCollator.compare(a, b); + const result = intlFileNameCollator.getValue().collator.compare(a, b); // Using the numeric option in the collator will // make compare(`foo1`, `foo01`) === 0. We must disambiguate. - if (intlFileNameCollatorIsNumeric && result === 0 && a !== b) { + if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && a !== b) { return a < b ? -1 : 1; } @@ -59,19 +58,19 @@ export function compareFileExtensions(one: string, other: string): number { const [oneName, oneExtension] = extractNameAndExtension(one); const [otherName, otherExtension] = extractNameAndExtension(other); - let result = intlFileNameCollator.compare(oneExtension, otherExtension); + let result = intlFileNameCollator.getValue().collator.compare(oneExtension, otherExtension); if (result === 0) { // Using the numeric option in the collator will // make compare(`foo1`, `foo01`) === 0. We must disambiguate. - if (intlFileNameCollatorIsNumeric && oneExtension !== otherExtension) { + if (intlFileNameCollator.getValue().collatorIsNumeric && oneExtension !== otherExtension) { return oneExtension < otherExtension ? -1 : 1; } // Extensions are equal, compare filenames - result = intlFileNameCollator.compare(oneName, otherName); + result = intlFileNameCollator.getValue().collator.compare(oneName, otherName); - if (intlFileNameCollatorIsNumeric && result === 0 && oneName !== otherName) { + if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && oneName !== otherName) { return oneName < otherName ? -1 : 1; } } @@ -194,4 +193,4 @@ export function compareByPrefix(one: string, other: string, lookFor: string): nu } return 0; -} \ No newline at end of file +} diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index ec367f23a78..3a2c2553461 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -7,7 +7,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { once as onceFn } from 'vs/base/common/functional'; import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; -import { TPromise } from 'vs/base/common/winjs.base'; /** * To an event a function with one or zero parameters @@ -292,10 +291,6 @@ export function fromPromise(promise: Thenable): Event { } export function toPromise(event: Event): Thenable { - return new TPromise(c => once(event)(c)); -} - -export function toNativePromise(event: Event): Thenable { return new Promise(c => once(event)(c)); } diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index cb4771b2d3c..29828e1e7a6 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -8,7 +8,6 @@ import * as strings from 'vs/base/common/strings'; import * as paths from 'vs/base/common/paths'; import { LRUCache } from 'vs/base/common/map'; import { CharCode } from 'vs/base/common/charCode'; -import { TPromise } from 'vs/base/common/winjs.base'; import { isThenable } from 'vs/base/common/async'; export interface IExpression { @@ -248,7 +247,7 @@ const T5 = /^([\w\.-]+(\/[\w\.-]+)*)\/?$/; // something/else export type ParsedPattern = (path: string, basename?: string) => boolean; // The ParsedExpression returns a Promise iff hasSibling returns a Promise. -export type ParsedExpression = (path: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise) => string | null | TPromise /* the matching pattern */; +export type ParsedExpression = (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise) => string | null | Promise /* the matching pattern */; export interface IGlobOptions { /** @@ -258,14 +257,14 @@ export interface IGlobOptions { } interface ParsedStringPattern { - (path: string, basename: string): string | null | TPromise /* the matching pattern */; + (path: string, basename: string): string | null | Promise /* the matching pattern */; basenames?: string[]; patterns?: string[]; allBasenames?: string[]; allPaths?: string[]; } interface ParsedExpressionPattern { - (path: string, basename: string, name?: string, hasSibling?: (name: string) => boolean | TPromise): string | null | TPromise /* the matching pattern */; + (path: string, basename: string, name?: string, hasSibling?: (name: string) => boolean | Promise): string | null | Promise /* the matching pattern */; requiresSiblings?: boolean; allBasenames?: string[]; allPaths?: string[]; @@ -481,15 +480,15 @@ export function parse(arg1: string | IExpression | IRelativePattern, options: IG return parsedExpression(arg1, options); } -export function hasSiblingPromiseFn(siblingsFn?: () => TPromise) { +export function hasSiblingPromiseFn(siblingsFn?: () => Promise) { if (!siblingsFn) { return undefined; } - let siblings: TPromise>; + let siblings: Promise>; return (name: string) => { if (!siblings) { - siblings = (siblingsFn() || TPromise.as([])) + siblings = (siblingsFn() || Promise.resolve([])) .then(list => list ? listToMap(list) : {}); } return siblings.then(map => !!map[name]); @@ -530,9 +529,9 @@ export function isRelativePattern(obj: any): obj is IRelativePattern { */ export function parseToAsync(expression: IExpression, options?: IGlobOptions): ParsedExpression { const parsedExpression = parse(expression, options); - return (path: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise): string | null | TPromise => { + return (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise): string | null | Promise => { const result = parsedExpression(path, basename, hasSibling); - return result instanceof TPromise ? result : TPromise.as(result); + return isThenable(result) ? result : Promise.resolve(result); }; } @@ -584,7 +583,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse return resultExpression; } - const resultExpression: ParsedStringPattern = function (path: string, basename: string, hasSibling?: (name: string) => boolean | TPromise) { + const resultExpression: ParsedStringPattern = function (path: string, basename: string, hasSibling?: (name: string) => boolean | Promise) { let name: string | undefined = undefined; for (let i = 0, n = parsedPatterns.length; i < n; i++) { @@ -639,7 +638,7 @@ function parseExpressionPattern(pattern: string, value: any, options: IGlobOptio if (value) { const when = (value).when; if (typeof when === 'string') { - const result: ParsedExpressionPattern = (path: string, basename: string, name: string, hasSibling: (name: string) => boolean | TPromise) => { + const result: ParsedExpressionPattern = (path: string, basename: string, name: string, hasSibling: (name: string) => boolean | Promise) => { if (!hasSibling || !parsedPattern(path, basename)) { return null; } diff --git a/src/vs/base/common/hash.ts b/src/vs/base/common/hash.ts index 613c5e307fc..6c0025b1d74 100644 --- a/src/vs/base/common/hash.ts +++ b/src/vs/base/common/hash.ts @@ -56,3 +56,17 @@ function objectHash(obj: any, initialHashVal: number): number { return hash(obj[key], hashVal); }, initialHashVal); } + +export class Hasher { + + private _value = 0; + + get value(): number { + return this._value; + } + + hash(obj: any): number { + this._value = hash(obj, this._value); + return this._value; + } +} \ No newline at end of file diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index c0685017f82..9158a73fe1e 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -4,33 +4,33 @@ *--------------------------------------------------------------------------------------------*/ export const enum ScanError { - None, - UnexpectedEndOfComment, - UnexpectedEndOfString, - UnexpectedEndOfNumber, - InvalidUnicode, - InvalidEscapeCharacter, - InvalidCharacter + None = 0, + UnexpectedEndOfComment = 1, + UnexpectedEndOfString = 2, + UnexpectedEndOfNumber = 3, + InvalidUnicode = 4, + InvalidEscapeCharacter = 5, + InvalidCharacter = 6 } export const enum SyntaxKind { - Unknown = 0, - OpenBraceToken, - CloseBraceToken, - OpenBracketToken, - CloseBracketToken, - CommaToken, - ColonToken, - NullKeyword, - TrueKeyword, - FalseKeyword, - StringLiteral, - NumericLiteral, - LineCommentTrivia, - BlockCommentTrivia, - LineBreakTrivia, - Trivia, - EOF + OpenBraceToken = 1, + CloseBraceToken = 2, + OpenBracketToken = 3, + CloseBracketToken = 4, + CommaToken = 5, + ColonToken = 6, + NullKeyword = 7, + TrueKeyword = 8, + FalseKeyword = 9, + StringLiteral = 10, + NumericLiteral = 11, + LineCommentTrivia = 12, + BlockCommentTrivia = 13, + LineBreakTrivia = 14, + Trivia = 15, + Unknown = 16, + EOF = 17 } /** @@ -42,7 +42,7 @@ export interface JSONScanner { */ setPosition(pos: number): void; /** - * Read the next token. Returns the tolen code. + * Read the next token. Returns the token code. */ scan(): SyntaxKind; /** @@ -70,6 +70,128 @@ export interface JSONScanner { */ getTokenError(): ScanError; } + + +export interface ParseError { + error: ParseErrorCode; + offset: number; + length: number; +} + +export const enum ParseErrorCode { + InvalidSymbol = 1, + InvalidNumberFormat = 2, + PropertyNameExpected = 3, + ValueExpected = 4, + ColonExpected = 5, + CommaExpected = 6, + CloseBraceExpected = 7, + CloseBracketExpected = 8, + EndOfFileExpected = 9, + InvalidCommentToken = 10, + UnexpectedEndOfComment = 11, + UnexpectedEndOfString = 12, + UnexpectedEndOfNumber = 13, + InvalidUnicode = 14, + InvalidEscapeCharacter = 15, + InvalidCharacter = 16 +} + +export type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null'; + +export interface Node { + readonly type: NodeType; + readonly value?: any; + readonly offset: number; + readonly length: number; + readonly colonOffset?: number; + readonly parent?: Node; + readonly children?: Node[]; +} + +export type Segment = string | number; +export type JSONPath = Segment[]; + +export interface Location { + /** + * The previous property key or literal value (string, number, boolean or null) or undefined. + */ + previousNode?: Node; + /** + * The path describing the location in the JSON document. The path consists of a sequence strings + * representing an object property or numbers for array indices. + */ + path: JSONPath; + /** + * Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices). + * '*' will match a single segment, of any property name or index. + * '**' will match a sequece of segments or no segment, of any property name or index. + */ + matches: (patterns: JSONPath) => boolean; + /** + * If set, the location's offset is at a property key. + */ + isAtPropertyKey: boolean; +} + +export interface ParseOptions { + disallowComments?: boolean; + allowTrailingComma?: boolean; +} + +export namespace ParseOptions { + export const DEFAULT = { + allowTrailingComma: true + }; +} + +export interface JSONVisitor { + /** + * Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace. + */ + onObjectBegin?: (offset: number, length: number) => void; + + /** + * Invoked when a property is encountered. The offset and length represent the location of the property name. + */ + onObjectProperty?: (property: string, offset: number, length: number) => void; + + /** + * Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace. + */ + onObjectEnd?: (offset: number, length: number) => void; + + /** + * Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket. + */ + onArrayBegin?: (offset: number, length: number) => void; + + /** + * Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket. + */ + onArrayEnd?: (offset: number, length: number) => void; + + /** + * Invoked when a literal value is encountered. The offset and length represent the location of the literal value. + */ + onLiteralValue?: (value: any, offset: number, length: number) => void; + + /** + * Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator. + */ + onSeparator?: (character: string, offset: number, length: number) => void; + + /** + * When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment. + */ + onComment?: (offset: number, length: number) => void; + + /** + * Invoked on an error. + */ + onError?: (error: ParseErrorCode, offset: number, length: number) => void; +} + /** * Creates a JSON scanner on the given text. * If ignoreTrivia is set, whitespaces or comments are ignored. @@ -584,58 +706,141 @@ const enum CharacterCodes { verticalTab = 0x0B, // \v } - - -export interface ParseError { - error: ParseErrorCode; -} - -export const enum ParseErrorCode { - InvalidSymbol, - InvalidNumberFormat, - PropertyNameExpected, - ValueExpected, - ColonExpected, - CommaExpected, - CloseBraceExpected, - CloseBracketExpected, - EndOfFileExpected -} - -export type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null'; - -function getLiteralNodeType(value: any): NodeType { - switch (typeof value) { - case 'boolean': return 'boolean'; - case 'number': return 'number'; - case 'string': return 'string'; - default: return 'null'; - } -} - -export interface Node { +interface NodeImpl extends Node { type: NodeType; value?: any; offset: number; length: number; - columnOffset?: number; - parent?: Node; - children?: Node[]; + colonOffset?: number; + parent?: NodeImpl; + children?: NodeImpl[]; } -export type Segment = string | number; -export type JSONPath = Segment[]; +/** + * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index. + */ +export function getLocation(text: string, position: number): Location { + let segments: Segment[] = []; // strings or numbers + let earlyReturnException = new Object(); + let previousNode: NodeImpl | undefined = void 0; + const previousNodeInst: NodeImpl = { + value: {}, + offset: 0, + length: 0, + type: 'object', + parent: void 0 + }; + let isAtPropertyKey = false; + function setPreviousNode(value: string, offset: number, length: number, type: NodeType) { + previousNodeInst.value = value; + previousNodeInst.offset = offset; + previousNodeInst.length = length; + previousNodeInst.type = type; + previousNodeInst.colonOffset = void 0; + previousNode = previousNodeInst; + } + try { -export interface ParseOptions { - disallowComments?: boolean; - disallowTrailingComma?: boolean; + visit(text, { + onObjectBegin: (offset: number, length: number) => { + if (position <= offset) { + throw earlyReturnException; + } + previousNode = void 0; + isAtPropertyKey = position > offset; + segments.push(''); // push a placeholder (will be replaced) + }, + onObjectProperty: (name: string, offset: number, length: number) => { + if (position < offset) { + throw earlyReturnException; + } + setPreviousNode(name, offset, length, 'property'); + segments[segments.length - 1] = name; + if (position <= offset + length) { + throw earlyReturnException; + } + }, + onObjectEnd: (offset: number, length: number) => { + if (position <= offset) { + throw earlyReturnException; + } + previousNode = void 0; + segments.pop(); + }, + onArrayBegin: (offset: number, length: number) => { + if (position <= offset) { + throw earlyReturnException; + } + previousNode = void 0; + segments.push(0); + }, + onArrayEnd: (offset: number, length: number) => { + if (position <= offset) { + throw earlyReturnException; + } + previousNode = void 0; + segments.pop(); + }, + onLiteralValue: (value: any, offset: number, length: number) => { + if (position < offset) { + throw earlyReturnException; + } + setPreviousNode(value, offset, length, getLiteralNodeType(value)); + + if (position <= offset + length) { + throw earlyReturnException; + } + }, + onSeparator: (sep: string, offset: number, length: number) => { + if (position <= offset) { + throw earlyReturnException; + } + if (sep === ':' && previousNode && previousNode.type === 'property') { + previousNode.colonOffset = offset; + isAtPropertyKey = false; + previousNode = void 0; + } else if (sep === ',') { + let last = segments[segments.length - 1]; + if (typeof last === 'number') { + segments[segments.length - 1] = last + 1; + } else { + isAtPropertyKey = true; + segments[segments.length - 1] = ''; + } + previousNode = void 0; + } + } + }); + } catch (e) { + if (e !== earlyReturnException) { + throw e; + } + } + + return { + path: segments, + previousNode, + isAtPropertyKey, + matches: (pattern: Segment[]) => { + let k = 0; + for (let i = 0; k < pattern.length && i < segments.length; i++) { + if (pattern[k] === segments[i] || pattern[k] === '*') { + k++; + } else if (pattern[k] !== '**') { + return false; + } + } + return k === pattern.length; + } + }; } + /** * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. * Therefore always check the errors list to find out if the input was valid. */ -export function parse(text: string, errors: ParseError[] = [], options?: ParseOptions): any { +export function parse(text: string, errors: ParseError[] = [], options: ParseOptions = ParseOptions.DEFAULT): any { let currentProperty: string | null = null; let currentParent: any = []; let previousParents: any[] = []; @@ -673,8 +878,8 @@ export function parse(text: string, errors: ParseError[] = [], options?: ParseOp currentParent = previousParents.pop(); }, onLiteralValue: onValue, - onError: (error: ParseErrorCode) => { - errors.push({ error: error }); + onError: (error: ParseErrorCode, offset: number, length: number) => { + errors.push({ error, offset, length }); } }; visit(text, visitor, options); @@ -685,23 +890,18 @@ export function parse(text: string, errors: ParseError[] = [], options?: ParseOp /** * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. */ -export function parseTree(text: string, errors: ParseError[] = [], options?: ParseOptions): Node { - let currentParent: Node | undefined = { type: 'array', offset: -1, length: -1, children: [] }; // artificial root +export function parseTree(text: string, errors: ParseError[] = [], options: ParseOptions = ParseOptions.DEFAULT): Node { + let currentParent: NodeImpl = { type: 'array', offset: -1, length: -1, children: [], parent: void 0 }; // artificial root function ensurePropertyComplete(endOffset: number) { - if (currentParent && currentParent.type === 'property') { + if (currentParent.type === 'property') { currentParent.length = endOffset - currentParent.offset; - currentParent = currentParent.parent; + currentParent = currentParent.parent!; } } function onValue(valueNode: Node): Node { - if (currentParent) { - if (!currentParent.children) { - currentParent.children = []; - } - currentParent.children.push(valueNode); - } + currentParent.children!.push(valueNode); return valueNode; } @@ -714,22 +914,16 @@ export function parseTree(text: string, errors: ParseError[] = [], options?: Par currentParent.children!.push({ type: 'string', value: name, offset, length, parent: currentParent }); }, onObjectEnd: (offset: number, length: number) => { - if (!currentParent) { - throw new Error('No current parent node'); - } currentParent.length = offset + length - currentParent.offset; - currentParent = currentParent.parent; + currentParent = currentParent.parent!; ensurePropertyComplete(offset + length); }, onArrayBegin: (offset: number, length: number) => { currentParent = onValue({ type: 'array', offset, length: -1, parent: currentParent, children: [] }); }, onArrayEnd: (offset: number, length: number) => { - if (!currentParent) { - throw new Error('No current parent node'); - } currentParent.length = offset + length - currentParent.offset; - currentParent = currentParent.parent; + currentParent = currentParent.parent!; ensurePropertyComplete(offset + length); }, onLiteralValue: (value: any, offset: number, length: number) => { @@ -737,27 +931,30 @@ export function parseTree(text: string, errors: ParseError[] = [], options?: Par ensurePropertyComplete(offset + length); }, onSeparator: (sep: string, offset: number, length: number) => { - if (currentParent && currentParent.type === 'property') { + if (currentParent.type === 'property') { if (sep === ':') { - currentParent.columnOffset = offset; + currentParent.colonOffset = offset; } else if (sep === ',') { ensurePropertyComplete(offset); } } }, - onError: (error: ParseErrorCode) => { - errors.push({ error: error }); + onError: (error: ParseErrorCode, offset: number, length: number) => { + errors.push({ error, offset, length }); } }; visit(text, visitor, options); - let result = currentParent!.children![0]; + let result = currentParent.children![0]; if (result) { delete result.parent; } return result; } +/** + * Finds the node at the given path in a JSON DOM. + */ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined { if (!root) { return void 0; @@ -765,12 +962,12 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined let node = root; for (let segment of path) { if (typeof segment === 'string') { - if (node.type !== 'object') { + if (node.type !== 'object' || !Array.isArray(node.children)) { return void 0; } let found = false; - for (const propertyNode of node.children || []) { - if (propertyNode.children && propertyNode.children[0].value === segment) { + for (const propertyNode of node.children) { + if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment) { node = propertyNode.children[1]; found = true; break; @@ -781,40 +978,98 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined } } else { let index = segment; - if (node.type !== 'array' || index < 0 || index >= node.children!.length) { + if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) { return void 0; } - node = node.children![index]; + node = node.children[index]; } } return node; } -export function getNodeValue(node: Node): any { - if (node.type === 'array') { - return node.children ? node.children.map(getNodeValue) : []; - } else if (node.type === 'object') { - let obj = {}; - for (let prop of node.children || []) { - obj[prop.children![0].value] = getNodeValue(prop.children![1]); - } - return obj; +/** + * Gets the JSON path of the given JSON DOM node + */ +export function getNodePath(node: Node): JSONPath { + if (!node.parent || !node.parent.children) { + return []; } - return node.value; + let path = getNodePath(node.parent); + if (node.parent.type === 'property') { + let key = node.parent.children[0].value; + path.push(key); + } else if (node.parent.type === 'array') { + let index = node.parent.children.indexOf(node); + if (index !== -1) { + path.push(index); + } + } + return path; +} + +/** + * Evaluates the JavaScript object of the given JSON DOM node + */ +export function getNodeValue(node: Node): any { + switch (node.type) { + case 'array': + return node.children!.map(getNodeValue); + case 'object': + let obj = Object.create(null); + for (let prop of node.children!) { + let valueNode = prop.children![1]; + if (valueNode) { + obj[prop.children![0].value] = getNodeValue(valueNode); + } + } + return obj; + case 'null': + case 'string': + case 'number': + case 'boolean': + return node.value; + default: + return void 0; + } + +} + +export function contains(node: Node, offset: number, includeRightBound = false): boolean { + return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length)); +} + +/** + * Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset. + */ +export function findNodeAtOffset(node: Node, offset: number, includeRightBound = false): Node | undefined { + if (contains(node, offset, includeRightBound)) { + let children = node.children; + if (Array.isArray(children)) { + for (let i = 0; i < children.length && children[i].offset <= offset; i++) { + let item = findNodeAtOffset(children[i], offset, includeRightBound); + if (item) { + return item; + } + } + + } + return node; + } + return void 0; } /** * Parses the given text and invokes the visitor functions for each object, array and literal reached. */ -export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions): any { +export function visit(text: string, visitor: JSONVisitor, options: ParseOptions = ParseOptions.DEFAULT): any { let _scanner = createScanner(text, false); - function toNoArgVisit(visitFunction: ((offset: number, length: number) => void) | undefined): () => void { + function toNoArgVisit(visitFunction?: (offset: number, length: number) => void): () => void { return visitFunction ? () => visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true; } - function toOneArgVisit(visitFunction: ((arg: T, offset: number, length: number) => void) | undefined): (arg: T) => void { + function toOneArgVisit(visitFunction?: (arg: T, offset: number, length: number) => void): (arg: T) => void { return visitFunction ? (arg: T) => visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true; } @@ -825,18 +1080,43 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions onArrayEnd = toNoArgVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisit(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), + onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError); let disallowComments = options && options.disallowComments; - let disallowTrailingComma = options && options.disallowTrailingComma; + let allowTrailingComma = options && options.allowTrailingComma; function scanNext(): SyntaxKind { while (true) { let token = _scanner.scan(); + switch (_scanner.getTokenError()) { + case ScanError.InvalidUnicode: + handleError(ParseErrorCode.InvalidUnicode); + break; + case ScanError.InvalidEscapeCharacter: + handleError(ParseErrorCode.InvalidEscapeCharacter); + break; + case ScanError.UnexpectedEndOfNumber: + handleError(ParseErrorCode.UnexpectedEndOfNumber); + break; + case ScanError.UnexpectedEndOfComment: + if (!disallowComments) { + handleError(ParseErrorCode.UnexpectedEndOfComment); + } + break; + case ScanError.UnexpectedEndOfString: + handleError(ParseErrorCode.UnexpectedEndOfString); + break; + case ScanError.InvalidCharacter: + handleError(ParseErrorCode.InvalidCharacter); + break; + } switch (token) { case SyntaxKind.LineCommentTrivia: case SyntaxKind.BlockCommentTrivia: if (disallowComments) { - handleError(ParseErrorCode.InvalidSymbol); + handleError(ParseErrorCode.InvalidCommentToken); + } else { + onComment(); } break; case SyntaxKind.Unknown: @@ -940,7 +1220,7 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions } onSeparator(','); scanNext(); // consume comma - if (_scanner.getToken() === SyntaxKind.CloseBraceToken && !disallowTrailingComma) { + if (_scanner.getToken() === SyntaxKind.CloseBraceToken && allowTrailingComma) { break; } } else if (needsComma) { @@ -972,7 +1252,7 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions } onSeparator(','); scanNext(); // consume comma - if (_scanner.getToken() === SyntaxKind.CloseBracketToken && !disallowTrailingComma) { + if (_scanner.getToken() === SyntaxKind.CloseBracketToken && allowTrailingComma) { break; } } else if (needsComma) { @@ -1019,44 +1299,45 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions return true; } -export interface JSONVisitor { - /** - * Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace. - */ - onObjectBegin?: (offset: number, length: number) => void; +/** + * Takes JSON with JavaScript-style comments and remove + * them. Optionally replaces every none-newline character + * of comments with a replaceCharacter + */ +export function stripComments(text: string, replaceCh?: string): string { - /** - * Invoked when a property is encountered. The offset and length represent the location of the property name. - */ - onObjectProperty?: (property: string, offset: number, length: number) => void; + let _scanner = createScanner(text), + parts: string[] = [], + kind: SyntaxKind, + offset = 0, + pos: number; - /** - * Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace. - */ - onObjectEnd?: (offset: number, length: number) => void; + do { + pos = _scanner.getPosition(); + kind = _scanner.scan(); + switch (kind) { + case SyntaxKind.LineCommentTrivia: + case SyntaxKind.BlockCommentTrivia: + case SyntaxKind.EOF: + if (offset !== pos) { + parts.push(text.substring(offset, pos)); + } + if (replaceCh !== void 0) { + parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh)); + } + offset = _scanner.getPosition(); + break; + } + } while (kind !== SyntaxKind.EOF); - /** - * Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket. - */ - onArrayBegin?: (offset: number, length: number) => void; - - /** - * Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket. - */ - onArrayEnd?: (offset: number, length: number) => void; - - /** - * Invoked when a literal value is encountered. The offset and length represent the location of the literal value. - */ - onLiteralValue?: (value: any, offset: number, length: number) => void; - - /** - * Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator. - */ - onSeparator?: (charcter: string, offset: number, length: number) => void; - - /** - * Invoked on an error. - */ - onError?: (error: ParseErrorCode, offset: number, length: number) => void; + return parts.join(''); } + +function getLiteralNodeType(value: any): NodeType { + switch (typeof value) { + case 'boolean': return 'boolean'; + case 'number': return 'number'; + case 'string': return 'string'; + default: return 'null'; + } +} \ No newline at end of file diff --git a/src/vs/base/common/jsonEdit.ts b/src/vs/base/common/jsonEdit.ts index 6c45671e337..485d9f48b4c 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -3,14 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ParseError, Node, parseTree, findNodeAtLocation, JSONPath, Segment } from 'vs/base/common/json'; -import { Edit, FormattingOptions, format, applyEdit } from 'vs/base/common/jsonFormatter'; +import { ParseError, Node, JSONPath, Segment, parseTree, findNodeAtLocation } from './json'; +import { Edit, format, isEOL, FormattingOptions } from './jsonFormatter'; + export function removeProperty(text: string, path: JSONPath, formattingOptions: FormattingOptions): Edit[] { return setProperty(text, path, void 0, formattingOptions); } -export function setProperty(text: string, path: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[] { +export function setProperty(text: string, originalPath: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[] { + let path = originalPath.slice(); let errors: ParseError[] = []; let root = parseTree(text, errors); let parent: Node | undefined = void 0; @@ -36,20 +38,23 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting throw new Error('Can not delete in empty document'); } return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, formattingOptions); - } else if (parent.type === 'object' && typeof lastSegment === 'string') { + } else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) { let existing = findNodeAtLocation(parent, [lastSegment]); if (existing !== void 0) { if (value === void 0) { // delete - let propertyIndex = parent.children && existing.parent ? parent.children.indexOf(existing.parent) : -1; + if (!existing.parent) { + throw new Error('Malformed AST'); + } + let propertyIndex = parent.children.indexOf(existing.parent); let removeBegin: number; - let removeEnd = existing.parent!.offset + existing.parent!.length; + let removeEnd = existing.parent.offset + existing.parent.length; if (propertyIndex > 0) { // remove the comma of the previous node - let previous = parent.children![propertyIndex - 1]; + let previous = parent.children[propertyIndex - 1]; removeBegin = previous.offset + previous.length; } else { removeBegin = parent.offset + 1; - if (parent.children && parent.children.length > 1) { + if (parent.children.length > 1) { // remove the comma of the next node let next = parent.children[1]; removeEnd = next.offset; @@ -65,25 +70,25 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting return []; // property does not exist, nothing to do } let newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`; - let index = getInsertionIndex ? getInsertionIndex(parent.children!.map(p => p.children![0].value)) : parent.children!.length; + let index = getInsertionIndex ? getInsertionIndex(parent.children.map(p => p.children![0].value)) : parent.children.length; let edit: Edit; if (index > 0) { - let previous = parent.children![index - 1]; + let previous = parent.children[index - 1]; edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; - } else if (parent.children!.length === 0) { + } else if (parent.children.length === 0) { edit = { offset: parent.offset + 1, length: 0, content: newProperty }; } else { edit = { offset: parent.offset + 1, length: 0, content: newProperty + ',' }; } return withFormatting(text, edit, formattingOptions); } - } else if (parent.type === 'array' && typeof lastSegment === 'number') { + } else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) { let insertIndex = lastSegment; if (insertIndex === -1) { // Insert let newProperty = `${JSON.stringify(value)}`; let edit: Edit; - if (!parent.children || parent.children.length === 0) { + if (parent.children.length === 0) { edit = { offset: parent.offset + 1, length: 0, content: newProperty }; } else { let previous = parent.children[parent.children.length - 1]; @@ -91,7 +96,7 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting } return withFormatting(text, edit, formattingOptions); } else { - if (value === void 0 && parent.children && parent.children.length >= 0) { + if (value === void 0 && parent.children.length >= 0) { //Removal let removalIndex = lastSegment; let toRemove = parent.children[removalIndex]; @@ -125,6 +130,15 @@ function withFormatting(text: string, edit: Edit, formattingOptions: FormattingO // format the new text let begin = edit.offset; let end = edit.offset + edit.content.length; + if (edit.length === 0 || edit.content.length === 0) { // insert or remove + while (begin > 0 && !isEOL(newText, begin - 1)) { + begin--; + } + while (end < newText.length && !isEOL(newText, end)) { + end++; + } + } + let edits = format(newText, { offset: begin, length: end - begin }, formattingOptions); // apply the formatting edits and track the begin and end offsets of the changes @@ -138,4 +152,12 @@ function withFormatting(text: string, edit: Edit, formattingOptions: FormattingO // create a single edit with all changes let editLength = text.length - (newText.length - end) - begin; return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }]; +} + +export function applyEdit(text: string, edit: Edit): string { + return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length); +} + +export function isWS(text: string, offset: number) { + return '\r\n \t'.indexOf(text.charAt(offset)) !== -1; } \ No newline at end of file diff --git a/src/vs/base/common/jsonFormatter.ts b/src/vs/base/common/jsonFormatter.ts index e7b8fe6d71f..df2a97ee5cd 100644 --- a/src/vs/base/common/jsonFormatter.ts +++ b/src/vs/base/common/jsonFormatter.ts @@ -3,63 +3,82 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as Json from './json'; +import { createScanner, SyntaxKind, ScanError } from './json'; export interface FormattingOptions { /** * If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent? */ - tabSize: number; + tabSize?: number; /** * Is indentation based on spaces? */ - insertSpaces: boolean; + insertSpaces?: boolean; /** - * The default end of line line character + * The default 'end of line' character. If not set, '\n' is used as default. */ - eol: string; + eol?: string; } +/** + * Represents a text modification + */ export interface Edit { + /** + * The start offset of the modification. + */ offset: number; + /** + * The length of the modification. Must not be negative. Empty length represents an *insert*. + */ length: number; + /** + * The new content. Empty content represents a *remove*. + */ content: string; } -export function applyEdit(text: string, edit: Edit): string { - return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length); +/** + * A text range in the document +*/ +export interface Range { + /** + * The start offset of the range. + */ + offset: number; + /** + * The length of the range. Must not be negative. + */ + length: number; } -export function applyEdits(text: string, edits: Edit[]): string { - for (let i = edits.length - 1; i >= 0; i--) { - text = applyEdit(text, edits[i]); - } - return text; -} -export function format(documentText: string, range: { offset: number, length: number }, options: FormattingOptions): Edit[] { +export function format(documentText: string, range: Range | undefined, options: FormattingOptions): Edit[] { let initialIndentLevel: number; - let value: string; + let formatText: string; + let formatTextStart: number; let rangeStart: number; let rangeEnd: number; if (range) { rangeStart = range.offset; rangeEnd = rangeStart + range.length; - while (rangeStart > 0 && !isEOL(documentText, rangeStart - 1)) { - rangeStart--; - } - let scanner = Json.createScanner(documentText, true); - scanner.setPosition(rangeEnd); - scanner.scan(); - rangeEnd = scanner.getPosition(); - value = documentText.substring(rangeStart, rangeEnd); - initialIndentLevel = computeIndentLevel(value, 0, options); + formatTextStart = rangeStart; + while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) { + formatTextStart--; + } + let endOffset = rangeEnd; + while (endOffset < documentText.length && !isEOL(documentText, endOffset)) { + endOffset++; + } + formatText = documentText.substring(formatTextStart, endOffset); + initialIndentLevel = computeIndentLevel(formatText, options); } else { - value = documentText; + formatText = documentText; + initialIndentLevel = 0; + formatTextStart = 0; rangeStart = 0; rangeEnd = documentText.length; - initialIndentLevel = 0; } let eol = getEOL(options, documentText); @@ -67,75 +86,78 @@ export function format(documentText: string, range: { offset: number, length: nu let indentLevel = 0; let indentValue: string; if (options.insertSpaces) { - indentValue = repeat(' ', options.tabSize); + indentValue = repeat(' ', options.tabSize || 4); } else { indentValue = '\t'; } - let scanner = Json.createScanner(value, false); + let scanner = createScanner(formatText, false); + let hasError = false; function newLineAndIndent(): string { return eol + repeat(indentValue, initialIndentLevel + indentLevel); } - function scanNext(): Json.SyntaxKind { + function scanNext(): SyntaxKind { let token = scanner.scan(); lineBreak = false; - while (token === Json.SyntaxKind.Trivia || token === Json.SyntaxKind.LineBreakTrivia) { - lineBreak = lineBreak || (token === Json.SyntaxKind.LineBreakTrivia); + while (token === SyntaxKind.Trivia || token === SyntaxKind.LineBreakTrivia) { + lineBreak = lineBreak || (token === SyntaxKind.LineBreakTrivia); token = scanner.scan(); } + hasError = token === SyntaxKind.Unknown || scanner.getTokenError() !== ScanError.None; return token; } let editOperations: Edit[] = []; function addEdit(text: string, startOffset: number, endOffset: number) { - if (documentText.substring(startOffset, endOffset) !== text) { + if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) { editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text }); } } let firstToken = scanNext(); - if (firstToken !== Json.SyntaxKind.EOF) { - let firstTokenStart = scanner.getTokenOffset() + rangeStart; + + if (firstToken !== SyntaxKind.EOF) { + let firstTokenStart = scanner.getTokenOffset() + formatTextStart; let initialIndent = repeat(indentValue, initialIndentLevel); - addEdit(initialIndent, rangeStart, firstTokenStart); + addEdit(initialIndent, formatTextStart, firstTokenStart); } - while (firstToken !== Json.SyntaxKind.EOF) { - let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + rangeStart; + while (firstToken !== SyntaxKind.EOF) { + let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; let secondToken = scanNext(); let replaceContent = ''; - while (!lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) { + while (!lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) { // comments on the same line: keep them on the same line, but ignore them otherwise - let commentTokenStart = scanner.getTokenOffset() + rangeStart; + let commentTokenStart = scanner.getTokenOffset() + formatTextStart; addEdit(' ', firstTokenEnd, commentTokenStart); - firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + rangeStart; - replaceContent = secondToken === Json.SyntaxKind.LineCommentTrivia ? newLineAndIndent() : ''; + firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; + replaceContent = secondToken === SyntaxKind.LineCommentTrivia ? newLineAndIndent() : ''; secondToken = scanNext(); } - if (secondToken === Json.SyntaxKind.CloseBraceToken) { - if (firstToken !== Json.SyntaxKind.OpenBraceToken) { + if (secondToken === SyntaxKind.CloseBraceToken) { + if (firstToken !== SyntaxKind.OpenBraceToken) { indentLevel--; replaceContent = newLineAndIndent(); } - } else if (secondToken === Json.SyntaxKind.CloseBracketToken) { - if (firstToken !== Json.SyntaxKind.OpenBracketToken) { + } else if (secondToken === SyntaxKind.CloseBracketToken) { + if (firstToken !== SyntaxKind.OpenBracketToken) { indentLevel--; replaceContent = newLineAndIndent(); } - } else if (secondToken !== Json.SyntaxKind.EOF) { + } else { switch (firstToken) { - case Json.SyntaxKind.OpenBracketToken: - case Json.SyntaxKind.OpenBraceToken: + case SyntaxKind.OpenBracketToken: + case SyntaxKind.OpenBraceToken: indentLevel++; replaceContent = newLineAndIndent(); break; - case Json.SyntaxKind.CommaToken: - case Json.SyntaxKind.LineCommentTrivia: + case SyntaxKind.CommaToken: + case SyntaxKind.LineCommentTrivia: replaceContent = newLineAndIndent(); break; - case Json.SyntaxKind.BlockCommentTrivia: + case SyntaxKind.BlockCommentTrivia: if (lineBreak) { replaceContent = newLineAndIndent(); } else { @@ -143,24 +165,37 @@ export function format(documentText: string, range: { offset: number, length: nu replaceContent = ' '; } break; - case Json.SyntaxKind.ColonToken: + case SyntaxKind.ColonToken: replaceContent = ' '; break; - case Json.SyntaxKind.NullKeyword: - case Json.SyntaxKind.TrueKeyword: - case Json.SyntaxKind.FalseKeyword: - case Json.SyntaxKind.NumericLiteral: - if (secondToken === Json.SyntaxKind.NullKeyword || secondToken === Json.SyntaxKind.FalseKeyword || secondToken === Json.SyntaxKind.NumericLiteral) { + case SyntaxKind.StringLiteral: + if (secondToken === SyntaxKind.ColonToken) { + replaceContent = ''; + break; + } + // fall through + case SyntaxKind.NullKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NumericLiteral: + case SyntaxKind.CloseBraceToken: + case SyntaxKind.CloseBracketToken: + if (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia) { replaceContent = ' '; + } else if (secondToken !== SyntaxKind.CommaToken && secondToken !== SyntaxKind.EOF) { + hasError = true; } break; + case SyntaxKind.Unknown: + hasError = true; + break; } - if (lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) { + if (lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) { replaceContent = newLineAndIndent(); } } - let secondTokenStart = scanner.getTokenOffset() + rangeStart; + let secondTokenStart = scanner.getTokenOffset() + formatTextStart; addEdit(replaceContent, firstTokenEnd, secondTokenStart); firstToken = secondToken; } @@ -175,7 +210,7 @@ function repeat(s: string, count: number): string { return result; } -function computeIndentLevel(content: string, offset: number, options: FormattingOptions): number { +function computeIndentLevel(content: string, options: FormattingOptions): number { let i = 0; let nChars = 0; let tabSize = options.tabSize || 4; @@ -208,6 +243,6 @@ function getEOL(options: FormattingOptions, text: string): string { return (options && options.eol) || '\n'; } -function isEOL(text: string, offset: number) { +export function isEOL(text: string, offset: number) { return '\r\n'.indexOf(text.charAt(offset)) !== -1; } \ No newline at end of file diff --git a/src/vs/base/common/keybindingParser.ts b/src/vs/base/common/keybindingParser.ts index 99b05253dc8..1d2a0db8914 100644 --- a/src/vs/base/common/keybindingParser.ts +++ b/src/vs/base/common/keybindingParser.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Keybinding, SimpleKeybinding, ChordKeybinding, KeyCodeUtils } from 'vs/base/common/keyCodes'; +import { ChordKeybinding, KeyCodeUtils, Keybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; import { OperatingSystem } from 'vs/base/common/platform'; import { ScanCodeBinding, ScanCodeUtils } from 'vs/base/common/scanCode'; diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 854fbbee051..94f776bb64b 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -24,7 +24,7 @@ export interface IUserHomeProvider { /** * @deprecated use LabelService instead */ -export function getPathLabel(resource: URI | string, userHomeProvider: IUserHomeProvider, rootProvider?: IWorkspaceFolderProvider): string { +export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHomeProvider, rootProvider?: IWorkspaceFolderProvider): string { if (typeof resource === 'string') { resource = URI.file(resource); } diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index 7d0937bd2f9..5acdcab444c 100644 --- a/src/vs/base/common/marshalling.ts +++ b/src/vs/base/common/marshalling.ts @@ -40,7 +40,7 @@ export function revive(obj: any, depth: number): any { if (typeof obj === 'object') { switch ((obj).$mid) { - case 100: return URI.revive(obj); + case 1: return URI.revive(obj); case 2: return new RegExp(obj.source, obj.flags); } diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 5e90f440f07..fe461fe6b7b 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -223,6 +223,11 @@ export function getOrDefault(obj: T, fn: (obj: T) => R, defaultValue: R | return typeof result === 'undefined' ? defaultValue : result; } +export function getOrDefault2(obj: T, fn: (obj: T) => R | undefined, defaultValue: R): R { + const result = fn(obj); + return typeof result === 'undefined' ? defaultValue : result; +} + type obj = { [key: string]: any; }; /** * Returns an object that has keys for each value that is different in the base object. Keys diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts index b2b255b0f1b..1255aed38cf 100644 --- a/src/vs/base/common/paths.ts +++ b/src/vs/base/common/paths.ts @@ -72,7 +72,10 @@ function _isNormal(path: string, win: boolean): boolean { : !_posixBadPath.test(path); } -export function normalize(path: string, toOSPath?: boolean): string { +export function normalize(path: undefined, toOSPath?: boolean): undefined; +export function normalize(path: null, toOSPath?: boolean): null; +export function normalize(path: string, toOSPath?: boolean): string; +export function normalize(path: string | null | undefined, toOSPath?: boolean): string | null | undefined { if (path === null || path === void 0) { return path; @@ -288,7 +291,7 @@ export function isUNC(path: string): boolean { // Reference: https://en.wikipedia.org/wiki/Filename const INVALID_FILE_CHARS = isWindows ? /[\\/:\*\?"<>\|]/g : /[\\/]/g; const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; -export function isValidBasename(name: string): boolean { +export function isValidBasename(name: string | null | undefined): boolean { if (!name || name.length === 0 || /^\s+$/.test(name)) { return false; // require a name that is not just whitespace } diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 6e0f3908e55..dd8edeb5fc2 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -25,7 +25,7 @@ export function basenameOrAuthority(resource: URI): string { } /** - * Tests wheter a `candidate` URI is a parent or equal of a given `base` URI. + * Tests whether a `candidate` URI is a parent or equal of a given `base` URI. * @param base A uri which is "longer" * @param parentCandidate A uri which is "shorter" then `base` */ @@ -73,6 +73,9 @@ export function basename(resource: URI): string { * @returns The URI representing the directory of the input URI. */ export function dirname(resource: URI): URI | null { + if (resource.scheme === Schemas.file) { + return URI.file(paths.dirname(fsPath(resource))); + } let dirname = paths.dirname(resource.path, '/'); if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) { return null; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character @@ -173,7 +176,7 @@ export function distinctParents(items: T[], resourceAccessor: (item: T) => UR } /** - * Tests wheter the given URL is a file URI created by `URI.parse` instead of `URI.file`. + * Tests whether the given URL is a file URI created by `URI.parse` instead of `URI.file`. * Such URI have no scheme or scheme that consist of a single letter (windows drive letter) * @param candidate The URI to test * @returns A corrected, real file URI if the input seems to be malformed. diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index b31cc9f5223..f33897a9f3b 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -export enum ScrollbarVisibility { +export const enum ScrollbarVisibility { Auto = 1, Hidden = 2, Visible = 3 diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 7c6a9820fbe..f9f44b040db 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -78,7 +78,7 @@ export function escapeRegExpCharacters(value: string): string { * @param haystack string to trim * @param needle the thing to trim (default is a blank) */ -export function trim(haystack: string, needle: string = ' '): string | undefined { +export function trim(haystack: string, needle: string = ' '): string { let trimmed = ltrim(haystack, needle); return rtrim(trimmed, needle); } diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index b4dc302fe91..2bddd1c6ba1 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -336,7 +336,7 @@ export class URI implements UriComponents { // ---- printing/externalize --------------------------- /** - * Creates a string presentation for this URI. It's guardeed that calling + * Creates a string presentation for this URI. It's guaranteed that calling * `URI.parse` with the result of this function creates an URI which is equal * to this URI. * @@ -361,10 +361,8 @@ export class URI implements UriComponents { return data; } else { let result = new _URI(data); - if ((data).$mid === 100) { - result._fsPath = (data).fsPath; - result._formatted = (data).external; - } + result._fsPath = (data).fsPath; + result._formatted = (data).external; return result; } } @@ -379,7 +377,7 @@ export interface UriComponents { } interface UriState extends UriComponents { - $mid: 100; + $mid: number; fsPath: string; external: string; } @@ -412,7 +410,7 @@ class _URI extends URI { toJSON(): object { const res = { - $mid: 100 + $mid: 1 }; // cached state if (this._fsPath) { @@ -466,11 +464,11 @@ const encodeTable: { [ch: number]: string } = { [CharCode.Space]: '%20', }; -function encodeURIComponentFast(uriComponent: string, allowSlash: boolean, firstPos: number = 0): string { +function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): string { let res: string | undefined = undefined; let nativeEncodePos = -1; - for (let pos = firstPos; pos < uriComponent.length; pos++) { + for (let pos = 0; pos < uriComponent.length; pos++) { let code = uriComponent.charCodeAt(pos); // unreserved characters: https://tools.ietf.org/html/rfc3986#section-2.3 @@ -620,31 +618,19 @@ function _asFormatted(uri: URI, skipEncoding: boolean): string { } if (path) { // lower-case windows drive letters in /C:/fff or C:/fff - let encodeOffset = 0; if (path.length >= 3 && path.charCodeAt(0) === CharCode.Slash && path.charCodeAt(2) === CharCode.Colon) { let code = path.charCodeAt(1); if (code >= CharCode.A && code <= CharCode.Z) { path = `/${String.fromCharCode(code + 32)}:${path.substr(3)}`; // "/c:".length === 3 - encodeOffset = 3; - } else if (code >= CharCode.a && code <= CharCode.z) { - encodeOffset = 3; } - } else if (path.length >= 2 && path.charCodeAt(1) === CharCode.Colon) { let code = path.charCodeAt(0); if (code >= CharCode.A && code <= CharCode.Z) { path = `${String.fromCharCode(code + 32)}:${path.substr(2)}`; // "/c:".length === 3 - encodeOffset = 2; - } else if (code >= CharCode.a && code <= CharCode.z) { - encodeOffset = 2; } } - if (scheme !== 'file' || path.length > encodeOffset && path.charCodeAt(encodeOffset) !== CharCode.Slash) { - encodeOffset = 0; - } - // encode the rest of the path - res += encoder(path, true, encodeOffset); + res += encoder(path, true); } if (query) { res += '?'; diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts index 42e28c88852..374ffd99055 100644 --- a/src/vs/base/common/uriIpc.ts +++ b/src/vs/base/common/uriIpc.ts @@ -8,9 +8,17 @@ import { URI, UriComponents } from 'vs/base/common/uri'; export interface IURITransformer { transformIncoming(uri: UriComponents): UriComponents; transformOutgoing(uri: URI): URI; + transformOutgoing(uri: UriComponents): UriComponents; } -export const DefaultURITransformer: IURITransformer = { - transformIncoming: (uri: UriComponents) => uri, - transformOutgoing: (uri: URI) => uri, +export const DefaultURITransformer: IURITransformer = new class { + transformIncoming(uri: UriComponents) { + return uri; + } + + transformOutgoing(uri: URI): URI; + transformOutgoing(uri: UriComponents): UriComponents; + transformOutgoing(uri: URI | UriComponents): URI | UriComponents { + return uri; + } }; \ No newline at end of file diff --git a/src/vs/base/node/console.ts b/src/vs/base/node/console.ts index 8163beff434..047bca70f27 100644 --- a/src/vs/base/node/console.ts +++ b/src/vs/base/node/console.ts @@ -29,7 +29,7 @@ export function isRemoteConsoleLog(obj: any): obj is IRemoteConsoleLog { export function parse(entry: IRemoteConsoleLog): { args: any[], stack?: string } { const args: any[] = []; - let stack: string; + let stack: string | undefined; // Parse Entry try { @@ -50,11 +50,11 @@ export function parse(entry: IRemoteConsoleLog): { args: any[], stack?: string } return { args, stack }; } -export function getFirstFrame(entry: IRemoteConsoleLog): IStackFrame; -export function getFirstFrame(stack: string): IStackFrame; -export function getFirstFrame(arg0: IRemoteConsoleLog | string): IStackFrame { +export function getFirstFrame(entry: IRemoteConsoleLog): IStackFrame | undefined; +export function getFirstFrame(stack: string | undefined): IStackFrame | undefined; +export function getFirstFrame(arg0: IRemoteConsoleLog | string | undefined): IStackFrame | undefined { if (typeof arg0 !== 'string') { - return getFirstFrame(parse(arg0).stack); + return getFirstFrame(parse(arg0!).stack); } // Parse a source information out of the stack if we have one. Format can be: @@ -73,7 +73,7 @@ export function getFirstFrame(arg0: IRemoteConsoleLog | string): IStackFrame { // (?:(?:[a-zA-Z]+:)|(?:[\/])|(?:\\\\) => windows drive letter OR unix root OR unc root // (?:.+) => simple pattern for the path, only works because of the line/col pattern after // :(?:\d+):(?:\d+) => :line:column data - const matches = /at [^\/]*((?:(?:[a-zA-Z]+:)|(?:[\/])|(?:\\\\))(?:.+)):(\d+):(\d+)/.exec(topFrame); + const matches = /at [^\/]*((?:(?:[a-zA-Z]+:)|(?:[\/])|(?:\\\\))(?:.+)):(\d+):(\d+)/.exec(topFrame || ''); if (matches && matches.length === 4) { return { uri: URI.file(matches[1]), @@ -86,7 +86,7 @@ export function getFirstFrame(arg0: IRemoteConsoleLog | string): IStackFrame { return void 0; } -function findFirstFrame(stack: string): string { +function findFirstFrame(stack: string | undefined): string | undefined { if (!stack) { return stack; } @@ -109,7 +109,7 @@ export function log(entry: IRemoteConsoleLog, label: string): void { topFrame = `(${topFrame.trim()})`; } - let consoleArgs = []; + let consoleArgs: string[] = []; // First arg is a string if (typeof args[0] === 'string') { diff --git a/src/vs/base/node/crypto.ts b/src/vs/base/node/crypto.ts index aa36c56aafe..8d3dc0e63a2 100644 --- a/src/vs/base/node/crypto.ts +++ b/src/vs/base/node/crypto.ts @@ -6,11 +6,10 @@ import * as fs from 'fs'; import * as crypto from 'crypto'; import * as stream from 'stream'; -import { TPromise } from 'vs/base/common/winjs.base'; import { once } from 'vs/base/common/functional'; -export function checksum(path: string, sha1hash: string): TPromise { - const promise = new TPromise((c, e) => { +export function checksum(path: string, sha1hash: string): Promise { + const promise = new Promise((c, e) => { const input = fs.createReadStream(path); const hash = crypto.createHash('sha1'); const hashStream = hash as any as stream.PassThrough; @@ -30,14 +29,14 @@ export function checksum(path: string, sha1hash: string): TPromise { input.once('error', done); input.once('end', done); hashStream.once('error', done); - hashStream.once('data', (data: Buffer) => done(null, data.toString('hex'))); + hashStream.once('data', (data: Buffer) => done(undefined, data.toString('hex'))); }); return promise.then(hash => { if (hash !== sha1hash) { - return TPromise.wrapError(new Error('Hash mismatch')); + return Promise.reject(new Error('Hash mismatch')); } - return TPromise.as(null); + return Promise.resolve(); }); -} \ No newline at end of file +} diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index 7bf469abe20..d06ada4b3de 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -30,6 +30,9 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions } return new Promise<{ detected: IDetectedEncodingResult, stream: NodeJS.ReadableStream }>((resolve, reject) => { + + readable.on('error', reject); + readable.pipe(new class extends Writable { private _decodeStream: NodeJS.ReadWriteStream; @@ -60,7 +63,7 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions // waiting for the decoder to be ready this._decodeStreamConstruction.then(_ => callback(), err => callback(err)); - } else if (this._bytesBuffered >= options.minBytesRequiredForDetection) { + } else if (typeof options.minBytesRequiredForDetection === 'number' && this._bytesBuffered >= options.minBytesRequiredForDetection) { // buffered enough data, create stream and forward data this._startDecodeStream(callback); @@ -75,7 +78,9 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions this._decodeStreamConstruction = Promise.resolve(detectEncodingFromBuffer({ buffer: Buffer.concat(this._buffer), bytesRead: this._bytesBuffered }, options.guessEncoding)).then(detected => { - detected.encoding = options.overwriteEncoding(detected.encoding); + if (options.overwriteEncoding && detected.encoding) { + detected.encoding = options.overwriteEncoding(detected.encoding); + } this._decodeStream = decodeStream(detected.encoding); for (const buffer of this._buffer) { this._decodeStream.write(buffer); @@ -126,7 +131,7 @@ export function encodingExists(encoding: string): boolean { return iconv.encodingExists(toNodeEncoding(encoding)); } -export function decodeStream(encoding: string): NodeJS.ReadWriteStream { +export function decodeStream(encoding: string | null): NodeJS.ReadWriteStream { return iconv.decodeStream(toNodeEncoding(encoding)); } @@ -134,15 +139,15 @@ export function encodeStream(encoding: string, options?: { addBOM?: boolean }): return iconv.encodeStream(toNodeEncoding(encoding), options); } -function toNodeEncoding(enc: string): string { - if (enc === UTF8_with_bom) { +function toNodeEncoding(enc: string | null): string { + if (enc === UTF8_with_bom || enc === null) { return UTF8; // iconv does not distinguish UTF 8 with or without BOM, so we need to help it } return enc; } -export function detectEncodingByBOMFromBuffer(buffer: Buffer, bytesRead: number): string { +export function detectEncodingByBOMFromBuffer(buffer: Buffer | null, bytesRead: number): string | null { if (!buffer || bytesRead < 2) { return null; } @@ -178,7 +183,7 @@ export function detectEncodingByBOMFromBuffer(buffer: Buffer, bytesRead: number) * Detects the Byte Order Mark in a given file. * If no BOM is detected, null will be passed to callback. */ -export function detectEncodingByBOM(file: string): Promise { +export function detectEncodingByBOM(file: string): Promise { return stream.readExactlyByFile(file, 3).then(({ buffer, bytesRead }) => detectEncodingByBOMFromBuffer(buffer, bytesRead)); } @@ -188,7 +193,7 @@ const IGNORE_ENCODINGS = ['ascii', 'utf-8', 'utf-16', 'utf-32']; /** * Guesses the encoding from buffer. */ -export function guessEncodingByBuffer(buffer: Buffer): Promise { +export function guessEncodingByBuffer(buffer: Buffer): Promise { return import('jschardet').then(jschardet => { jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD; @@ -263,7 +268,7 @@ const NO_GUESS_BUFFER_MAX_LEN = 512; // when not auto guessing the encoding, const AUTO_GUESS_BUFFER_MAX_LEN = 512 * 8; // with auto guessing we want a lot more content to be read for guessing export interface IDetectedEncodingResult { - encoding: string; + encoding: string | null; seemsBinary: boolean; } @@ -277,7 +282,7 @@ export function detectEncodingFromBuffer({ buffer, bytesRead }: stream.ReadResul // Detect 0 bytes to see if file is binary or UTF-16 LE/BE // unless we already know that this file has a UTF-16 encoding let seemsBinary = false; - if (encoding !== UTF16be && encoding !== UTF16le) { + if (encoding !== UTF16be && encoding !== UTF16le && buffer) { let couldBeUTF16LE = true; // e.g. 0xAA 0x00 let couldBeUTF16BE = true; // e.g. 0x00 0xAA let containsZeroByte = false; @@ -325,7 +330,7 @@ export function detectEncodingFromBuffer({ buffer, bytesRead }: stream.ReadResul } // Auto guess encoding if configured - if (autoGuessEncoding && !seemsBinary && !encoding) { + if (autoGuessEncoding && !seemsBinary && !encoding && buffer) { return guessEncodingByBuffer(buffer.slice(0, bytesRead)).then(guessedEncoding => { return { seemsBinary: false, diff --git a/src/vs/base/node/extfs.ts b/src/vs/base/node/extfs.ts index ab91d0dc26a..3cc6f7cbefc 100644 --- a/src/vs/base/node/extfs.ts +++ b/src/vs/base/node/extfs.ts @@ -28,13 +28,13 @@ export function readdirSync(path: string): string[] { return fs.readdirSync(path); } -export function readdir(path: string, callback: (error: Error, files: string[]) => void): void { +export function readdir(path: string, callback: (error: Error | null, files: string[]) => void): void { // Mac: uses NFD unicode form on disk, but we want NFC // See also https://github.com/nodejs/node/issues/2165 if (platform.isMacintosh) { return fs.readdir(path, (error, children) => { if (error) { - return callback(error, null); + return callback(error, []); } return callback(null, children.map(c => normalizeNFC(c))); @@ -49,7 +49,7 @@ export interface IStatAndLink { isSymbolicLink: boolean; } -export function statLink(path: string, callback: (error: Error, statAndIsLink: IStatAndLink) => void): void { +export function statLink(path: string, callback: (error: Error | null, statAndIsLink: IStatAndLink | null) => void): void { fs.lstat(path, (error, lstat) => { if (error || lstat.isSymbolicLink()) { fs.stat(path, (error, stat) => { @@ -65,10 +65,8 @@ export function statLink(path: string, callback: (error: Error, statAndIsLink: I }); } -export function copy(source: string, target: string, callback: (error: Error) => void, copiedSources?: { [path: string]: boolean }): void { - if (!copiedSources) { - copiedSources = Object.create(null); - } +export function copy(source: string, target: string, callback: (error: Error | null) => void, copiedSourcesIn?: { [path: string]: boolean }): void { + const copiedSources = copiedSourcesIn ? copiedSourcesIn : Object.create(null); fs.stat(source, (error, stat) => { if (error) { @@ -87,8 +85,8 @@ export function copy(source: string, target: string, callback: (error: Error) => const proceed = function () { readdir(source, (err, files) => { - loop(files, (file: string, clb: (error: Error, result: string[]) => void) => { - copy(paths.join(source, file), paths.join(target, file), (error: Error) => clb(error, void 0), copiedSources); + loop(files, (file: string, clb: (error: Error | null, result: string[]) => void) => { + copy(paths.join(source, file), paths.join(target, file), (error: Error) => clb(error, []), copiedSources); }, callback); }); }; @@ -130,7 +128,7 @@ function doCopyFile(source: string, target: string, mode: number, callback: (err } export function mkdirp(path: string, mode?: number, token?: CancellationToken): TPromise { - const mkdir = () => { + const mkdir = (): Promise => { return nfcall(fs.mkdir, path, mode).then(null, (mkdirErr: NodeJS.ErrnoException) => { // ENOENT: a parent folder does not exist yet @@ -180,7 +178,7 @@ export function mkdirp(path: string, mode?: number, token?: CancellationToken): // after the rename, the contents are out of the workspace although not yet deleted. The greater benefit however is that this operation // will fail in case any file is used by another process. fs.unlink() in node will not bail if a file unlinked is used by another process. // However, the consequences are bad as outlined in all the related bugs from https://github.com/joyent/node/issues/7164 -export function del(path: string, tmpFolder: string, callback: (error: Error) => void, done?: (error: Error) => void): void { +export function del(path: string, tmpFolder: string, callback: (error: Error | null) => void, done?: (error: Error | null) => void): void { fs.exists(path, exists => { if (!exists) { return callback(null); @@ -198,7 +196,7 @@ export function del(path: string, tmpFolder: string, callback: (error: Error) => } const pathInTemp = paths.join(tmpFolder, uuid.generateUuid()); - fs.rename(path, pathInTemp, (error: Error) => { + fs.rename(path, pathInTemp, (error: Error | null) => { if (error) { return rmRecursive(path, callback); // if rename fails, delete without tmp dir } @@ -221,7 +219,7 @@ export function del(path: string, tmpFolder: string, callback: (error: Error) => }); } -function rmRecursive(path: string, callback: (error: Error) => void): void { +function rmRecursive(path: string, callback: (error: Error | null) => void): void { if (path === '\\' || path === '/') { return callback(new Error('Will not delete root!')); } @@ -297,12 +295,12 @@ export function delSync(path: string): void { } } -export function mv(source: string, target: string, callback: (error: Error) => void): void { +export function mv(source: string, target: string, callback: (error: Error | null) => void): void { if (source === target) { return callback(null); } - function updateMtime(err: Error): void { + function updateMtime(err: Error | null): void { if (err) { return callback(err); } @@ -481,7 +479,7 @@ function doWriteFileAndFlush(path: string, data: string | Buffer, options: IWrit } // Open the file with same flags and mode as fs.writeFile() - fs.open(path, options.flag, options.mode, (openError, fd) => { + fs.open(path, typeof options.flag === 'string' ? options.flag : 'r', options.mode, (openError, fd) => { if (openError) { return callback(openError); } @@ -520,7 +518,7 @@ export function writeFileAndFlushSync(path: string, data: string | Buffer, optio } // Open the file with same flags and mode as fs.writeFile() - const fd = fs.openSync(path, options.flag, options.mode); + const fd = fs.openSync(path, typeof options.flag === 'string' ? options.flag : 'r', options.mode); try { @@ -566,7 +564,7 @@ function ensureOptions(options?: IWriteFileOptions): IWriteFileOptions { * In case of errors, null is returned. But you cannot use this function to verify that a path exists. * realcaseSync does not handle '..' or '.' path segments and it does not take the locale into account. */ -export function realcaseSync(path: string): string { +export function realcaseSync(path: string): string | null { const dir = paths.dirname(path); if (path === dir) { // end recursion return path; @@ -616,7 +614,7 @@ export function realpathSync(path: string): string { } } -export function realpath(path: string, callback: (error: Error, realpath: string) => void): void { +export function realpath(path: string, callback: (error: Error | null, realpath: string) => void): void { return fs.realpath(path, (error, realpath) => { if (!error) { return callback(null, realpath); @@ -644,7 +642,7 @@ export function watch(path: string, onChange: (type: string, path?: string) => v const watcher = fs.watch(path); watcher.on('change', (type, raw) => { - let file: string | null = null; + let file: string | undefined; if (raw) { // https://github.com/Microsoft/vscode/issues/38191 file = raw.toString(); if (platform.isMacintosh) { diff --git a/src/vs/base/node/flow.ts b/src/vs/base/node/flow.ts index 8d3e918a105..6214d738c0a 100644 --- a/src/vs/base/node/flow.ts +++ b/src/vs/base/node/flow.ts @@ -9,9 +9,9 @@ import * as assert from 'assert'; * Executes the given function (fn) over the given array of items (list) in parallel and returns the resulting errors and results as * array to the callback (callback). The resulting errors and results are evaluated by calling the provided callback function. */ -export function parallel(list: T[], fn: (item: T, callback: (err: Error, result: E) => void) => void, callback: (err: Error[], result: E[]) => void): void { +export function parallel(list: T[], fn: (item: T, callback: (err: Error, result: E) => void) => void, callback: (err: Array | null, result: E[]) => void): void { let results = new Array(list.length); - let errors = new Array(list.length); + let errors = new Array(list.length); let didErrorOccur = false; let doneCount = 0; @@ -42,9 +42,9 @@ export function parallel(list: T[], fn: (item: T, callback: (err: Error, r * array to the callback (callback). The resulting errors and results are evaluated by calling the provided callback function. The first param can * either be a function that returns an array of results to loop in async fashion or be an array of items already. */ -export function loop(param: (callback: (error: Error, result: T[]) => void) => void, fn: (item: T, callback: (error: Error, result: E) => void, index: number, total: number) => void, callback: (error: Error, result: E[]) => void): void; -export function loop(param: T[], fn: (item: T, callback: (error: Error, result: E) => void, index: number, total: number) => void, callback: (error: Error, result: E[]) => void): void; -export function loop(param: any, fn: (item: any, callback: (error: Error, result: E) => void, index: number, total: number) => void, callback: (error: Error, result: E[]) => void): void { +export function loop(param: (callback: (error: Error, result: T[]) => void) => void, fn: (item: T, callback: (error: Error | null, result: E | null) => void, index: number, total: number) => void, callback: (error: Error | null, result: E[] | null) => void): void; +export function loop(param: T[], fn: (item: T, callback: (error: Error | null, result: E | null) => void, index: number, total: number) => void, callback: (error: Error | null, result: E[] | null) => void): void; +export function loop(param: any, fn: (item: any, callback: (error: Error | null, result: E | null) => void, index: number, total: number) => void, callback: (error: Error | null, result: E[] | null) => void): void { // Assert assert.ok(param, 'Missing first parameter'); diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 8aa1f32b9ae..3222a44a14a 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -183,7 +183,7 @@ export function whenDeleted(path: string): TPromise { if (!exists) { clearInterval(interval); - c(null); + c(void 0); } }); } diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 9274e0947a0..8ea9f18803c 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; +import * as fs from 'fs'; import * as cp from 'child_process'; import * as nls from 'vs/nls'; -import { TPromise, TValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; import * as Types from 'vs/base/common/types'; import { IStringDictionary } from 'vs/base/common/collections'; import * as Objects from 'vs/base/common/objects'; @@ -17,7 +17,9 @@ import { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, Te import { getPathFromAmdModule } from 'vs/base/common/amd'; export { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode }; -export type TProgressCallback = (progress: T) => void; +export type ValueCallback = (value?: T | Thenable) => void; +export type ErrorCallback = (error?: any) => void; +export type ProgressCallback = (progress: T) => void; export interface LineData { line: string; @@ -76,9 +78,9 @@ export abstract class AbstractProcess { private options: CommandOptions | ForkOptions; protected shell: boolean; - private childProcess: cp.ChildProcess; - protected childProcessPromise: TPromise; - private pidResolve: TValueCallback; + private childProcess: cp.ChildProcess | null; + protected childProcessPromise: Promise | null; + private pidResolve?: ValueCallback; protected terminateRequested: boolean; private static WellKnowCommands: IStringDictionary = { @@ -103,7 +105,7 @@ export abstract class AbstractProcess { }; public constructor(executable: Executable); - public constructor(cmd: string, args: string[], shell: boolean, options: CommandOptions); + public constructor(cmd: string, args: string[] | undefined, shell: boolean, options: CommandOptions | undefined); public constructor(arg1: string | Executable, arg2?: string[], arg3?: boolean, arg4?: CommandOptions) { if (arg2 !== void 0 && arg3 !== void 0 && arg4 !== void 0) { this.cmd = arg1; @@ -124,10 +126,10 @@ export abstract class AbstractProcess { if (this.options.env) { let newEnv: IStringDictionary = Object.create(null); Object.keys(process.env).forEach((key) => { - newEnv[key] = process.env[key]; + newEnv[key] = process.env[key]!; }); Object.keys(this.options.env).forEach((key) => { - newEnv[key] = this.options.env[key]; + newEnv[key] = this.options.env![key]!; }); this.options.env = newEnv; } @@ -145,14 +147,14 @@ export abstract class AbstractProcess { return 'other'; } - public start(pp: TProgressCallback): TPromise { - if (Platform.isWindows && ((this.options && this.options.cwd && TPath.isUNC(this.options.cwd)) || !this.options && !this.options.cwd && TPath.isUNC(process.cwd()))) { - return TPromise.wrapError(new Error(nls.localize('TaskRunner.UNC', 'Can\'t execute a shell command on a UNC drive.'))); + public start(pp: ProgressCallback): Promise { + if (Platform.isWindows && ((this.options && this.options.cwd && TPath.isUNC(this.options.cwd)) || !this.options && TPath.isUNC(process.cwd()))) { + return Promise.reject(new Error(nls.localize('TaskRunner.UNC', 'Can\'t execute a shell command on a UNC drive.'))); } return this.useExec().then((useExec) => { - let cc: TValueCallback; + let cc: ValueCallback; let ee: ErrorCallback; - let result = new TPromise((c, e) => { + let result = new Promise((c, e) => { cc = c; ee = e; }); @@ -175,7 +177,7 @@ export abstract class AbstractProcess { } }); } else { - let childProcess: cp.ChildProcess = null; + let childProcess: cp.ChildProcess | null = null; let closeHandler = (data: any) => { this.childProcess = null; this.childProcessPromise = null; @@ -228,7 +230,7 @@ export abstract class AbstractProcess { } if (childProcess) { this.childProcess = childProcess; - this.childProcessPromise = TPromise.as(childProcess); + this.childProcessPromise = Promise.resolve(childProcess); if (this.pidResolve) { this.pidResolve(Types.isNumber(childProcess.pid) ? childProcess.pid : -1); this.pidResolve = undefined; @@ -239,7 +241,7 @@ export abstract class AbstractProcess { }); if (childProcess.pid) { this.childProcess.on('close', closeHandler); - this.handleSpawn(childProcess, cc, pp, ee, true); + this.handleSpawn(childProcess, cc!, pp, ee!, true); } } } @@ -247,10 +249,10 @@ export abstract class AbstractProcess { }); } - protected abstract handleExec(cc: TValueCallback, pp: TProgressCallback, error: Error, stdout: Buffer, stderr: Buffer): void; - protected abstract handleSpawn(childProcess: cp.ChildProcess, cc: TValueCallback, pp: TProgressCallback, ee: ErrorCallback, sync: boolean): void; + protected abstract handleExec(cc: ValueCallback, pp: ProgressCallback, error: Error | null, stdout: Buffer, stderr: Buffer): void; + protected abstract handleSpawn(childProcess: cp.ChildProcess, cc: ValueCallback, pp: ProgressCallback, ee: ErrorCallback, sync: boolean): void; - protected handleClose(data: any, cc: TValueCallback, pp: TProgressCallback, ee: ErrorCallback): void { + protected handleClose(data: any, cc: ValueCallback, pp: ProgressCallback, ee: ErrorCallback): void { // Default is to do nothing. } @@ -269,19 +271,19 @@ export abstract class AbstractProcess { } } - public get pid(): TPromise { + public get pid(): Promise { if (this.childProcessPromise) { return this.childProcessPromise.then(childProcess => childProcess.pid, err => -1); } else { - return new TPromise((resolve) => { + return new Promise((resolve) => { this.pidResolve = resolve; }); } } - public terminate(): TPromise { + public terminate(): Promise { if (!this.childProcessPromise) { - return TPromise.as({ success: true }); + return Promise.resolve({ success: true }); } return this.childProcessPromise.then((childProcess) => { this.terminateRequested = true; @@ -295,8 +297,8 @@ export abstract class AbstractProcess { }); } - private useExec(): TPromise { - return new TPromise((c, e) => { + private useExec(): Promise { + return new Promise((c, e) => { if (!this.shell || !Platform.isWindows) { c(false); } @@ -322,7 +324,7 @@ export class LineProcess extends AbstractProcess { super(arg1, arg2, arg3, arg4); } - protected handleExec(cc: TValueCallback, pp: TProgressCallback, error: Error, stdout: Buffer, stderr: Buffer) { + protected handleExec(cc: ValueCallback, pp: ProgressCallback, error: Error, stdout: Buffer, stderr: Buffer) { [stdout, stderr].forEach((buffer: Buffer, index: number) => { let lineDecoder = new LineDecoder(); let lines = lineDecoder.write(buffer); @@ -337,7 +339,7 @@ export class LineProcess extends AbstractProcess { cc({ terminated: this.terminateRequested, error: error }); } - protected handleSpawn(childProcess: cp.ChildProcess, cc: TValueCallback, pp: TProgressCallback, ee: ErrorCallback, sync: boolean): void { + protected handleSpawn(childProcess: cp.ChildProcess, cc: ValueCallback, pp: ProgressCallback, ee: ErrorCallback, sync: boolean): void { this.stdoutLineDecoder = new LineDecoder(); this.stderrLineDecoder = new LineDecoder(); childProcess.stdout.on('data', (data: Buffer) => { @@ -350,7 +352,7 @@ export class LineProcess extends AbstractProcess { }); } - protected handleClose(data: any, cc: TValueCallback, pp: TProgressCallback, ee: ErrorCallback): void { + protected handleClose(data: any, cc: ValueCallback, pp: ProgressCallback, ee: ErrorCallback): void { [this.stdoutLineDecoder.end(), this.stderrLineDecoder.end()].forEach((line, index) => { if (line) { pp({ line: line, source: index === 0 ? Source.stdout : Source.stderr }); @@ -400,3 +402,51 @@ export function createQueuedSender(childProcess: cp.ChildProcess): IQueuedSender return { send }; } + +export namespace win32 { + export function findExecutable(command: string, cwd?: string, paths?: string[]): string { + // If we have an absolute path then we take it. + if (path.isAbsolute(command)) { + return command; + } + if (cwd === void 0) { + cwd = process.cwd(); + } + let dir = path.dirname(command); + if (dir !== '.') { + // We have a directory and the directory is relative (see above). Make the path absolute + // to the current working directory. + return path.join(cwd, command); + } + if (paths === void 0 && Types.isString(process.env.PATH)) { + paths = process.env.PATH.split(path.delimiter); + } + // No PATH environment. Make path absolute to the cwd. + if (paths === void 0 || paths.length === 0) { + return path.join(cwd, command); + } + // We have a simple file name. We get the path variable from the env + // and try to find the executable on the path. + for (let pathEntry of paths) { + // The path entry is absolute. + let fullPath: string; + if (path.isAbsolute(pathEntry)) { + fullPath = path.join(pathEntry, command); + } else { + fullPath = path.join(cwd, pathEntry, command); + } + if (fs.existsSync(fullPath)) { + return fullPath; + } + let withExtension = fullPath + '.com'; + if (fs.existsSync(withExtension)) { + return withExtension; + } + withExtension = fullPath + '.exe'; + if (fs.existsSync(withExtension)) { + return withExtension; + } + } + return path.join(cwd, command); + } +} \ No newline at end of file diff --git a/src/vs/base/node/proxy.ts b/src/vs/base/node/proxy.ts index 6dc5738ac80..2ef6c6768b2 100644 --- a/src/vs/base/node/proxy.ts +++ b/src/vs/base/node/proxy.ts @@ -7,7 +7,7 @@ import { Url, parse as parseUrl } from 'url'; import { isBoolean } from 'vs/base/common/types'; import { Agent } from './request'; -function getSystemProxyURI(requestURL: Url): string { +function getSystemProxyURI(requestURL: Url): string | null { if (requestURL.protocol === 'http:') { return process.env.HTTP_PROXY || process.env.http_proxy || null; } else if (requestURL.protocol === 'https:') { @@ -32,12 +32,12 @@ export async function getProxyAgent(rawRequestURL: string, options: IOptions = { const proxyEndpoint = parseUrl(proxyURL); - if (!/^https?:$/.test(proxyEndpoint.protocol)) { + if (!/^https?:$/.test(proxyEndpoint.protocol || '')) { return null; } const opts = { - host: proxyEndpoint.hostname, + host: proxyEndpoint.hostname || '', port: Number(proxyEndpoint.port), auth: proxyEndpoint.auth, rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index d11f9a8dc45..a11b552b851 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -137,14 +137,14 @@ export function listProcesses(rootPid: number): Promise { windowsProcessTree.getProcessCpuUsage(processList, (completeProcessList) => { const processItems: Map = new Map(); completeProcessList.forEach(process => { - const commandLine = cleanUNCPrefix(process.commandLine); + const commandLine = cleanUNCPrefix(process.commandLine || ''); processItems.set(process.pid, { name: findName(commandLine), cmd: commandLine, pid: process.pid, ppid: process.ppid, - load: process.cpu, - mem: process.memory + load: process.cpu || 0, + mem: process.memory || 0 }); }); @@ -194,12 +194,14 @@ export function listProcesses(rootPid: number): Promise { if (process.platform === 'linux') { // Flatten rootItem to get a list of all VSCode processes let processes = [rootItem]; - const pids = []; + const pids: number[] = []; while (processes.length) { const process = processes.shift(); - pids.push(process.pid); - if (process.children) { - processes = processes.concat(process.children); + if (process) { + pids.push(process.pid); + if (process.children) { + processes = processes.concat(process.children); + } } } diff --git a/src/vs/base/node/stats.ts b/src/vs/base/node/stats.ts index 7c188868ba3..fbf895ea0a6 100644 --- a/src/vs/base/node/stats.ts +++ b/src/vs/base/node/stats.ts @@ -5,7 +5,7 @@ import { readdir, stat, exists, readFile } from 'fs'; import { join } from 'path'; -import { parse } from 'vs/base/common/json'; +import { parse, ParseError } from 'vs/base/common/json'; export interface WorkspaceStatItem { name: string; @@ -37,7 +37,7 @@ export function collectLaunchConfigs(folder: string): Promise void): void { - let results = []; + let results: string[] = []; readdir(dir, async (err, files) => { // Ignore folders that can't be read if (err) { diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 9c26e756751..32525743f10 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -4,12 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { Database, Statement } from 'vscode-sqlite3'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import { ThrottledDelayer, timeout } from 'vs/base/common/async'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { mapToString, setToString } from 'vs/base/common/map'; import { basename } from 'path'; +import { mark } from 'vs/base/common/performance'; +import { rename } from 'vs/base/node/pfs'; export interface IStorageOptions { path: string; @@ -18,12 +20,11 @@ export interface IStorageOptions { } export interface IStorageLoggingOptions { - errorLogger?: (error: string | Error) => void; - infoLogger?: (msg: string) => void; + logError?: (error: string | Error) => void; - info?: boolean; trace?: boolean; - profile?: boolean; + logTrace?: (msg: string) => void; + } enum StorageState { @@ -32,7 +33,32 @@ enum StorageState { Closed } -export class Storage extends Disposable { +export interface IStorage extends IDisposable { + + readonly size: number; + readonly onDidChangeStorage: Event; + + init(): Promise; + + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + + getInteger(key: string, fallbackValue: number): number; + getInteger(key: string, fallbackValue?: number): number | undefined; + + set(key: string, value: any): Thenable; + delete(key: string): Thenable; + + close(): Thenable; + + getItems(): Promise>; + checkIntegrity(full: boolean): Promise; +} + +export class Storage extends Disposable implements IStorage { _serviceBrand: any; private static readonly FLUSH_DELAY = 100; @@ -45,17 +71,17 @@ export class Storage extends Disposable { private storage: SQLiteStorageImpl; private cache: Map = new Map(); - private pendingScheduler: RunOnceScheduler; + private flushDelayer: ThrottledDelayer; + private pendingDeletes: Set = new Set(); private pendingInserts: Map = new Map(); - private pendingPromises: { resolve: Function, reject: Function }[] = []; constructor(options: IStorageOptions) { super(); this.storage = new SQLiteStorageImpl(options); - this.pendingScheduler = new RunOnceScheduler(() => this.flushPending(), Storage.FLUSH_DELAY); + this.flushDelayer = this._register(new ThrottledDelayer(Storage.FLUSH_DELAY)); } get size(): number { @@ -74,7 +100,9 @@ export class Storage extends Disposable { }); } - get(key: string, fallbackValue?: any): string { + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + get(key: string, fallbackValue?: string): string | undefined { const value = this.cache.get(key); if (isUndefinedOrNull(value)) { @@ -84,7 +112,9 @@ export class Storage extends Disposable { return value; } - getBoolean(key: string, fallbackValue?: boolean): boolean { + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { const value = this.get(key); if (isUndefinedOrNull(value)) { @@ -94,7 +124,9 @@ export class Storage extends Disposable { return value === 'true'; } - getInteger(key: string, fallbackValue?: number): number { + getInteger(key: string, fallbackValue: number): number; + getInteger(key: string, fallbackValue?: number): number | undefined; + getInteger(key: string, fallbackValue?: number): number | undefined { const value = this.get(key); if (isUndefinedOrNull(value)) { @@ -104,7 +136,7 @@ export class Storage extends Disposable { return parseInt(value, 10); } - set(key: string, value: any): Promise { + set(key: string, value: any): Thenable { if (this.state === StorageState.Closed) { return Promise.resolve(); // Return early if we are already closed } @@ -131,10 +163,11 @@ export class Storage extends Disposable { // Event this._onDidChangeStorage.fire(key); - return this.update(); + // Accumulate work by scheduling after timeout + return this.flushDelayer.trigger(() => this.flushPending()); } - delete(key: string): Promise { + delete(key: string): Thenable { if (this.state === StorageState.Closed) { return Promise.resolve(); // Return early if we are already closed } @@ -154,20 +187,11 @@ export class Storage extends Disposable { // Event this._onDidChangeStorage.fire(key); - return this.update(); + // Accumulate work by scheduling after timeout + return this.flushDelayer.trigger(() => this.flushPending()); } - private update(): Promise { - - // Schedule - if (!this.pendingScheduler.isScheduled()) { - this.pendingScheduler.schedule(); - } - - return new Promise((resolve, reject) => this.pendingPromises.push({ resolve, reject })); - } - - close(): Promise { + close(): Thenable { if (this.state === StorageState.Closed) { return Promise.resolve(); // return if already closed } @@ -175,36 +199,24 @@ export class Storage extends Disposable { // Update state this.state = StorageState.Closed; - // Dispose scheduler (no more scheduling possible) - this.pendingScheduler.dispose(); - - // Flush & close - return this.flushPending().then(() => { - return this.storage.close(); - }); + // Trigger new flush to ensure data is persisted and then close + // even if there is an error flushing. We must always ensure + // the DB is closed to avoid corruption. + const onDone = () => this.storage.close(); + return this.flushDelayer.trigger(() => this.flushPending(), 0 /* immediately */).then(onDone, onDone); } - private flushPending(): Promise { + private flushPending(): Thenable { // Get pending data - const pendingPromises = this.pendingPromises; - const pendingDeletes = this.pendingDeletes; - const pendingInserts = this.pendingInserts; + const updateRequest: IUpdateRequest = { insert: this.pendingInserts, delete: this.pendingDeletes }; // Reset pending data for next run - this.pendingPromises = []; this.pendingDeletes = new Set(); this.pendingInserts = new Map(); - return this.storage.updateItems({ insert: pendingInserts, delete: pendingDeletes }).then(() => { - - // Resolve pending - pendingPromises.forEach(promise => promise.resolve()); - }, error => { - - // Forward error to pending - pendingPromises.forEach(promise => promise.reject(error)); - }); + // Update in storage + return this.storage.updateItems(updateRequest); } getItems(): Promise> { @@ -217,11 +229,16 @@ export class Storage extends Disposable { } export interface IUpdateRequest { - insert?: Map; - delete?: Set; + readonly insert?: Map; + readonly delete?: Set; } export class SQLiteStorageImpl { + + private static measuredRequireDuration: boolean; // TODO@Ben remove me after a while + + private static BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY + private db: Promise; private name: string; private logger: SQLiteStorageLogger; @@ -236,11 +253,11 @@ export class SQLiteStorageImpl { return this.db.then(db => { const items = new Map(); - return this.each(db, 'SELECT * FROM ItemTable', row => { - items.set(row.key, row.value); - }).then(() => { - if (this.logger.verbose) { - this.logger.info(`[storage ${this.name}] getItems(): ${mapToString(items)}`); + return this.all(db, 'SELECT * FROM ItemTable').then(rows => { + rows.forEach(row => items.set(row.key, row.value)); + + if (this.logger.isTracing) { + this.logger.trace(`[storage ${this.name}] getItems(): ${mapToString(items)}`); } return items; @@ -261,15 +278,15 @@ export class SQLiteStorageImpl { return Promise.resolve(); } - if (this.logger.verbose) { - this.logger.info(`[storage ${this.name}] updateItems(): insert(${request.insert ? mapToString(request.insert) : '0'}), delete(${request.delete ? setToString(request.delete) : '0'})`); + if (this.logger.isTracing) { + this.logger.trace(`[storage ${this.name}] updateItems(): insert(${request.insert ? mapToString(request.insert) : '0'}), delete(${request.delete ? setToString(request.delete) : '0'})`); } return this.db.then(db => { return this.transaction(db, () => { if (request.insert && request.insert.size > 0) { this.prepare(db, 'INSERT INTO ItemTable VALUES (?,?)', stmt => { - request.insert.forEach((value, key) => { + request.insert!.forEach((value, key) => { stmt.run([key, value]); }); }); @@ -277,7 +294,7 @@ export class SQLiteStorageImpl { if (request.delete && request.delete.size) { this.prepare(db, 'DELETE FROM ItemTable WHERE key=?', stmt => { - request.delete.forEach(key => { + request.delete!.forEach(key => { stmt.run(key); }); }); @@ -287,7 +304,7 @@ export class SQLiteStorageImpl { } close(): Promise { - this.logger.info(`[storage ${this.name}] close()`); + this.logger.trace(`[storage ${this.name}] close()`); return this.db.then(db => { return new Promise((resolve, reject) => { @@ -305,7 +322,7 @@ export class SQLiteStorageImpl { } checkIntegrity(full: boolean): Promise { - this.logger.info(`[storage ${this.name}] checkIntegrity(full: ${full})`); + this.logger.trace(`[storage ${this.name}] checkIntegrity(full: ${full})`); return this.db.then(db => { return this.get(db, full ? 'PRAGMA integrity_check' : 'PRAGMA quick_check').then(row => { @@ -315,46 +332,95 @@ export class SQLiteStorageImpl { } private open(): Promise { - this.logger.info(`[storage ${this.name}] open()`); + this.logger.trace(`[storage ${this.name}] open()`); return new Promise((resolve, reject) => { - this.doOpen(this.options.path).then(resolve, error => { + const fallbackToInMemoryDatabase = (error: Error) => { this.logger.error(`[storage ${this.name}] open(): Error (open DB): ${error}`); this.logger.error(`[storage ${this.name}] open(): Falling back to in-memory DB`); // In case of any error to open the DB, use an in-memory // DB so that we always have a valid DB to talk to. this.doOpen(':memory:').then(resolve, reject); + }; + + this.doOpen(this.options.path).then(resolve, error => { + + // TODO@Ben check if this is still happening. This error code should only arise if + // another process is locking the same DB we want to open at that time. This typically + // never happens because a DB connection is limited per window. However, in the event + // of a window reload, it may be possible that the previous connection was not properly + // closed while the new connection is already established. + if (error.code === 'SQLITE_BUSY') { + this.logger.error(`[storage ${this.name}] open(): Retrying after ${SQLiteStorageImpl.BUSY_OPEN_TIMEOUT}ms due to SQLITE_BUSY`); + + // Retry after 2s if the DB is busy + timeout(SQLiteStorageImpl.BUSY_OPEN_TIMEOUT).then(() => this.doOpen(this.options.path).then(resolve, fallbackToInMemoryDatabase)); + } + + // This error code indicates that even though the DB file exists, + // SQLite cannot open it and signals it is corrupt or not a DB. + else if (error.code === 'SQLITE_CORRUPT' || error.code === 'SQLITE_NOTADB') { + this.logger.error(`[storage ${this.name}] open(): Recreating DB due to ${error.code}`); + + // Move corrupt DB to different filename and start fresh + const randomSuffix = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 4); + rename(this.options.path, `${this.options.path}.${randomSuffix}.corrupt`) + .then(() => this.doOpen(this.options.path)).then(resolve, fallbackToInMemoryDatabase); + } + + // Otherwise give up and fallback to in-memory DB + else { + fallbackToInMemoryDatabase(error); + } }); }); } private doOpen(path: string): Promise { + // TODO@Ben clean up performance markers return new Promise((resolve, reject) => { + let measureRequireDuration = false; + if (!SQLiteStorageImpl.measuredRequireDuration) { + SQLiteStorageImpl.measuredRequireDuration = true; + measureRequireDuration = true; + + mark('willRequireSQLite'); + } import('vscode-sqlite3').then(sqlite3 => { - const db = new (this.logger.verbose ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { + if (measureRequireDuration) { + mark('didRequireSQLite'); + } + + const db = new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { if (error) { return reject(error); } - // Setup schema + // The following exec() statement serves two purposes: + // - create the DB if it does not exist yet + // - validate that the DB is not corrupt (the open() call does not throw otherwise) + mark('willSetupSQLiteSchema'); this.exec(db, [ 'PRAGMA user_version = 1;', 'CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)' - ].join('')).then(() => resolve(db), error => reject(error)); + ].join('')).then(() => { + mark('didSetupSQLiteSchema'); + + resolve(db); + }, error => { + mark('didSetupSQLiteSchema'); + + reject(error); + }); }); - // Check for errors + // Errors db.on('error', error => this.logger.error(`[storage ${this.name}] Error (event): ${error}`)); // Tracing - if (this.logger.trace) { - db.on('trace', sql => this.logger.info(`[storage ${this.name}] Trace (event): ${sql}`)); - } - - // Profiling - if (this.logger.profile) { - db.on('profile', (sql, time) => this.logger.info(`[storage ${this.name}] Profile (event): ${sql} (${time}ms)`)); + if (this.logger.isTracing) { + db.on('trace', sql => this.logger.trace(`[storage ${this.name}] Trace (event): ${sql}`)); } }); }); @@ -388,29 +454,16 @@ export class SQLiteStorageImpl { }); } - private each(db: Database, sql: string, callback: (row: any) => void): Promise { + private all(db: Database, sql: string): Promise<{ key: string, value: string }[]> { return new Promise((resolve, reject) => { - let hadError = false; - db.each(sql, (error, row) => { + db.all(sql, (error, rows) => { if (error) { - this.logger.error(`[storage ${this.name}] each(): ${error}`); - - hadError = true; + this.logger.error(`[storage ${this.name}] all(): ${error}`); return reject(error); } - if (!hadError) { - callback(row); - } - }, error => { - if (error) { - this.logger.error(`[storage ${this.name}] each(): ${error}`); - - return reject(error); - } - - return resolve(); + return resolve(rows); }); }); } @@ -438,14 +491,14 @@ export class SQLiteStorageImpl { private prepare(db: Database, sql: string, runCallback: (stmt: Statement) => void): void { const stmt = db.prepare(sql); - runCallback(stmt); - const statementErrorListener = error => { this.logger.error(`[storage ${this.name}] prepare(): ${error} (${sql})`); }; stmt.on('error', statementErrorListener); + runCallback(stmt); + stmt.finalize(error => { if (error) { statementErrorListener(error); @@ -457,35 +510,75 @@ export class SQLiteStorageImpl { } class SQLiteStorageLogger { - private logInfo: boolean; - private logError: boolean; + private readonly logTrace: boolean; + private readonly logError: boolean; - constructor(private options?: IStorageLoggingOptions) { - this.logInfo = this.verbose && options && !!options.infoLogger; - this.logError = options && !!options.errorLogger; + constructor(private readonly options?: IStorageLoggingOptions) { + this.logTrace = !!(options && options.logTrace); + this.logError = !!(options && options.logError); } - get verbose(): boolean { - return this.options && (this.options.info || this.options.trace || this.options.profile); + get isTracing(): boolean { + return this.logTrace; } - get trace(): boolean { - return this.options && this.options.trace; - } - - get profile(): boolean { - return this.options && this.options.profile; - } - - info(msg: string): void { - if (this.logInfo) { - this.options.infoLogger(msg); + trace(msg: string): void { + if (this.logTrace && this.options && this.options.logTrace) { + this.options.logTrace(msg); } } error(error: string | Error): void { - if (this.logError) { - this.options.errorLogger(error); + if (this.logError && this.options && this.options.logError) { + this.options.logError(error); } } +} + +export class NullStorage extends Disposable implements IStorage { + + readonly size = 0; + readonly onDidChangeStorage = Event.None; + + private items = new Map(); + + init(): Promise { return Promise.resolve(); } + + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + get(key: string, fallbackValue?: string): string | undefined { + return void 0; + } + + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { + return void 0; + } + + getInteger(key: string, fallbackValue: number): number; + getInteger(key: string, fallbackValue?: number): number | undefined; + getInteger(key: string, fallbackValue?: number): number | undefined { + return void 0; + } + + set(key: string, value: any): Promise { + return Promise.resolve(); + } + + delete(key: string): Promise { + return Promise.resolve(); + } + + close(): Promise { + return Promise.resolve(); + } + + getItems(): Promise> { + return Promise.resolve(this.items); + } + + checkIntegrity(full: boolean): Promise { + return Promise.resolve('ok'); + } } \ No newline at end of file diff --git a/src/vs/base/node/stream.ts b/src/vs/base/node/stream.ts index ed7f72de7c3..845afdab8e9 100644 --- a/src/vs/base/node/stream.ts +++ b/src/vs/base/node/stream.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; export interface ReadResult { - buffer: Buffer; + buffer: Buffer | null; bytesRead: number; } @@ -20,7 +20,7 @@ export function readExactlyByFile(file: string, totalBytes: number): Promise { if (closeError) { return reject(closeError); @@ -71,14 +71,14 @@ export function readExactlyByFile(file: string, totalBytes: number): Promise { - return new Promise((resolve, reject) => +export function readToMatchingString(file: string, matchingString: string, chunkBytes: number, maximumBytesToRead: number): Promise { + return new Promise((resolve, reject) => fs.open(file, 'r', null, (err, fd) => { if (err) { return reject(err); } - function end(err: Error, result: string): void { + function end(err: Error | null, result: string | null): void { fs.close(fd, closeError => { if (closeError) { return reject(closeError); diff --git a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts index 46daa9b1095..eb9cef3e413 100644 --- a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts +++ b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts @@ -8,29 +8,25 @@ import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/node/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; import { ipcMain } from 'electron'; -interface WebContents extends Electron.WebContents { - getId(): number; -} - interface IIPCEvent { - event: { sender: WebContents; }; + event: { sender: Electron.WebContents; }; message: string; } function createScopedOnMessageEvent(senderId: number, eventName: string): Event { const onMessage = fromNodeEventEmitter(ipcMain, eventName, (event, message: string) => ({ event, message })); - const onMessageFromSender = filterEvent(onMessage, ({ event }) => event.sender.getId() === senderId); + const onMessageFromSender = filterEvent(onMessage, ({ event }) => event.sender.id === senderId); return mapEvent(onMessageFromSender, ({ message }) => message); } export class Server extends IPCServer { private static getOnDidClientConnect(): Event { - const onHello = fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); + const onHello = fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); return mapEvent(onHello, webContents => { - const onMessage = createScopedOnMessageEvent(webContents.getId(), 'ipc:message'); - const onDidClientDisconnect = signalEvent(createScopedOnMessageEvent(webContents.getId(), 'ipc:disconnect')); + const onMessage = createScopedOnMessageEvent(webContents.id, 'ipc:message'); + const onDidClientDisconnect = signalEvent(createScopedOnMessageEvent(webContents.id, 'ipc:disconnect')); const protocol = new Protocol(webContents, onMessage); return { protocol, onDidClientDisconnect }; @@ -40,4 +36,4 @@ export class Server extends IPCServer { constructor() { super(Server.getOnDidClientConnect()); } -} +} \ No newline at end of file diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 9812c24e2d4..0d0f2c00166 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -22,7 +22,13 @@ import * as errors from 'vs/base/common/errors'; export class Server extends IPCServer { constructor() { super({ - send: r => { try { process.send(r.toString('base64')); } catch (e) { /* not much to do */ } }, + send: r => { + try { + if (process.send) { + process.send(r.toString('base64')); + } + } catch (e) { /* not much to do */ } + }, onMessage: fromNodeEventEmitter(process, 'message', msg => Buffer.from(msg, 'base64')) }); @@ -81,8 +87,8 @@ export class Client implements IChannelClient, IDisposable { private disposeDelayer: Delayer; private activeRequests = new Set(); - private child: ChildProcess; - private _client: IPCClient; + private child: ChildProcess | null; + private _client: IPCClient | null; private channels = new Map(); private _onDidProcessExit = new Emitter<{ code: number, signal: string }>(); @@ -200,7 +206,7 @@ export class Client implements IChannelClient, IDisposable { // Handle remote console logs specially if (isRemoteConsoleLog(msg)) { log(msg, `IPC Library: ${this.options.serverName}`); - return null; + return; } // Anything else goes to the outside @@ -253,8 +259,10 @@ export class Client implements IChannelClient, IDisposable { private disposeClient() { if (this._client) { - this.child.kill(); - this.child = null; + if (this.child) { + this.child.kill(); + this.child = null; + } this._client = null; this.channels.clear(); } @@ -263,7 +271,7 @@ export class Client implements IChannelClient, IDisposable { dispose() { this._onDidProcessExit.dispose(); this.disposeDelayer.cancel(); - this.disposeDelayer = null; + this.disposeDelayer = null!; // StrictNullOverride: nulling out ok in dispose this.disposeClient(); this.activeRequests.clear(); } diff --git a/src/vs/base/parts/ipc/node/ipc.electron.ts b/src/vs/base/parts/ipc/node/ipc.electron.ts index 5932eee4a73..831a27377c1 100644 --- a/src/vs/base/parts/ipc/node/ipc.electron.ts +++ b/src/vs/base/parts/ipc/node/ipc.electron.ts @@ -13,7 +13,7 @@ import { Event, Emitter } from 'vs/base/common/event'; */ export interface Sender { - send(channel: string, msg: string): void; + send(channel: string, msg: string | null): void; } export class Protocol implements IMessagePassingProtocol { diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 8a3d10a51c3..675e155162c 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -215,14 +215,19 @@ export class Server extends IPCServer { })); } - constructor(private server: NetServer) { + private server: NetServer | null; + + constructor(server: NetServer) { super(Server.toClientConnectionEvent(server)); + this.server = server; } dispose(): void { super.dispose(); - this.server.close(); - this.server = null; + if (this.server) { + this.server.close(); + this.server = null; + } } } diff --git a/src/vs/base/parts/ipc/node/ipc.ts b/src/vs/base/parts/ipc/node/ipc.ts index a3318454bdc..86027f7083a 100644 --- a/src/vs/base/parts/ipc/node/ipc.ts +++ b/src/vs/base/parts/ipc/node/ipc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter, once, filterEvent, toNativePromise, Relay } from 'vs/base/common/event'; +import { Event, Emitter, once, filterEvent, toPromise, Relay } from 'vs/base/common/event'; import { always, CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; @@ -447,7 +447,7 @@ export class ChannelClient implements IChannelClient, IDisposable { if (this.state === State.Idle) { return Promise.resolve(); } else { - return toNativePromise(this.onDidInitialize); + return toPromise(this.onDidInitialize); } } @@ -638,4 +638,4 @@ export function getNextTickChannel(channel: T): T { return relay.event; } } as T; -} \ No newline at end of file +} diff --git a/src/vs/base/parts/ipc/test/node/ipc.test.ts b/src/vs/base/parts/ipc/test/node/ipc.test.ts index df09c38af14..5a19bd914b6 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, IChannel } from 'vs/base/parts/ipc/node/ipc'; -import { Emitter, toNativePromise, Event } from 'vs/base/common/event'; +import { Emitter, toPromise, Event } from 'vs/base/common/event'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { timeout } from 'vs/base/common/async'; @@ -202,8 +202,8 @@ suite('Base IPC', function () { const b3 = Buffer.alloc(0); serverProtocol.send(b3); - const b2 = await toNativePromise(serverProtocol.onMessage); - const b4 = await toNativePromise(clientProtocol.onMessage); + const b2 = await toPromise(serverProtocol.onMessage); + const b4 = await toPromise(clientProtocol.onMessage); assert.strictEqual(b1, b2); assert.strictEqual(b3, b4); @@ -279,7 +279,7 @@ suite('Base IPC', function () { }); test('listen to events', async function () { - const messages = []; + const messages: string[] = []; ipcService.pong(msg => messages.push(msg)); await timeout(0); diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 4855968e00a..0fb0bae29c0 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; @@ -289,16 +288,16 @@ class NoActionProvider implements IActionProvider { return false; } - getActions(tree: ITree, element: any): TPromise { - return TPromise.as(null); + getActions(tree: ITree, element: any): IAction[] { + return null; } hasSecondaryActions(tree: ITree, element: any): boolean { return false; } - getSecondaryActions(tree: ITree, element: any): TPromise { - return TPromise.as(null); + getSecondaryActions(tree: ITree, element: any): IAction[] { + return null; } getActionItem(tree: ITree, element: any, action: Action): IActionItem { @@ -377,7 +376,7 @@ class Renderer implements IRenderer { const detailContainer = document.createElement('div'); row2.appendChild(detailContainer); DOM.addClass(detailContainer, 'quick-open-entry-meta'); - const detail = new HighlightedLabel(detailContainer); + const detail = new HighlightedLabel(detailContainer, true); // Entry Group let group: HTMLDivElement; @@ -421,13 +420,12 @@ class Renderer implements IRenderer { data.actionBar.context = entry; // make sure the context is the current element - this.actionProvider.getActions(null, entry).then((actions) => { - if (data.actionBar.isEmpty() && actions && actions.length > 0) { - data.actionBar.push(actions, { icon: true, label: false }); - } else if (!data.actionBar.isEmpty() && (!actions || actions.length === 0)) { - data.actionBar.clear(); - } - }); + const actions = this.actionProvider.getActions(null, entry); + if (data.actionBar.isEmpty() && actions && actions.length > 0) { + data.actionBar.push(actions, { icon: true, label: false }); + } else if (!data.actionBar.isEmpty() && (!actions || actions.length === 0)) { + data.actionBar.clear(); + } // Entry group class if (entry instanceof QuickOpenEntryGroup && entry.getGroupLabel()) { @@ -620,4 +618,4 @@ export function compareEntries(elementA: QuickOpenEntry, elementB: QuickOpenEntr } return compareAnything(nameA, nameB, lookFor); -} \ No newline at end of file +} diff --git a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts index 841974aaade..ace8984e735 100644 --- a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts +++ b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts @@ -61,8 +61,8 @@ export function score(target: string, query: string, queryLower: string, fuzzy: } function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): [number, number[]] { - const scores = []; - const matches = []; + const scores: number[] = []; + const matches: number[] = []; // // Build Scorer Matrix: @@ -121,7 +121,7 @@ function doScore(query: string, queryLower: string, queryLength: number, target: } // Restore Positions (starting from bottom right of matrix) - const positions = []; + const positions: number[] = []; let queryIndex = queryLength - 1; let targetIndex = targetLength - 1; while (queryIndex >= 0 && targetIndex >= 0) { diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index 3cb0b9323b9..307b19f9584 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -731,7 +731,7 @@ export interface IActionProvider { /** * Returns a promise of an array with the actions of the element that should show up in place right to the element in the tree. */ - getActions(tree: ITree, element: any): WinJS.TPromise; + getActions(tree: ITree, element: any): IAction[]; /** * Returns whether or not the element has secondary actions. These show up once the user has expanded the element's action bar. @@ -741,7 +741,7 @@ export interface IActionProvider { /** * Returns a promise of an array with the secondary actions of the element that should show up once the user has expanded the element's action bar. */ - getSecondaryActions(tree: ITree, element: any): WinJS.TPromise; + getSecondaryActions(tree: ITree, element: any): IAction[]; /** * Returns an action item to render an action. diff --git a/src/vs/base/test/browser/browser.test.ts b/src/vs/base/test/browser/browser.test.ts index 060dabf85b5..da58ddd6a91 100644 --- a/src/vs/base/test/browser/browser.test.ts +++ b/src/vs/base/test/browser/browser.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { isWindows, isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isWindows } from 'vs/base/common/platform'; suite('Browsers', () => { test('all', () => { diff --git a/src/vs/base/test/browser/comparers.test.ts b/src/vs/base/test/browser/comparers.test.ts index 6c3a4497e15..df74e2bbfa2 100644 --- a/src/vs/base/test/browser/comparers.test.ts +++ b/src/vs/base/test/browser/comparers.test.ts @@ -5,13 +5,20 @@ import { compareFileNames, compareFileExtensions, setFileNameComparer } from 'vs/base/common/comparers'; import * as assert from 'assert'; +import { IdleValue } from 'vs/base/common/async'; suite('Comparers', () => { test('compareFileNames', () => { // Setup Intl - setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })); + setFileNameComparer(new IdleValue(() => { + const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); + return { + collator: collator, + collatorIsNumeric: collator.resolvedOptions().numeric + }; + })); assert(compareFileNames(null, null) === 0, 'null should be equal'); assert(compareFileNames(null, 'abc') < 0, 'null should be come before real values'); @@ -29,7 +36,13 @@ suite('Comparers', () => { test('compareFileExtensions', () => { // Setup Intl - setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })); + setFileNameComparer(new IdleValue(() => { + const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); + return { + collator: collator, + collatorIsNumeric: collator.resolvedOptions().numeric + }; + })); assert(compareFileExtensions(null, null) === 0, 'null should be equal'); assert(compareFileExtensions(null, '.abc') < 0, 'null should come before real files'); diff --git a/src/vs/base/test/browser/highlightedLabel.test.ts b/src/vs/base/test/browser/highlightedLabel.test.ts index 72cbac61321..30105b727ec 100644 --- a/src/vs/base/test/browser/highlightedLabel.test.ts +++ b/src/vs/base/test/browser/highlightedLabel.test.ts @@ -9,7 +9,7 @@ suite('HighlightedLabel', () => { let label: HighlightedLabel; setup(() => { - label = new HighlightedLabel(document.createElement('div')); + label = new HighlightedLabel(document.createElement('div'), true); }); teardown(() => { diff --git a/src/vs/base/test/browser/ui/list/rangeMap.test.ts b/src/vs/base/test/browser/ui/list/rangeMap.test.ts index 8d4ae2542c9..3e1aa10693b 100644 --- a/src/vs/base/test/browser/ui/list/rangeMap.test.ts +++ b/src/vs/base/test/browser/ui/list/rangeMap.test.ts @@ -156,52 +156,52 @@ suite('RangeMap', () => { const ten = { size: 10 }; test('length & count', () => { - rangeMap.splice(0, 0, one); + rangeMap.splice(0, 0, [one]); assert.equal(rangeMap.size, 1); assert.equal(rangeMap.count, 1); }); test('length & count #2', () => { - rangeMap.splice(0, 0, one, one, one, one, one); + rangeMap.splice(0, 0, [one, one, one, one, one]); assert.equal(rangeMap.size, 5); assert.equal(rangeMap.count, 5); }); test('length & count #3', () => { - rangeMap.splice(0, 0, five); + rangeMap.splice(0, 0, [five]); assert.equal(rangeMap.size, 5); assert.equal(rangeMap.count, 1); }); test('length & count #4', () => { - rangeMap.splice(0, 0, five, five, five, five, five); + rangeMap.splice(0, 0, [five, five, five, five, five]); assert.equal(rangeMap.size, 25); assert.equal(rangeMap.count, 5); }); test('insert', () => { - rangeMap.splice(0, 0, five, five, five, five, five); + rangeMap.splice(0, 0, [five, five, five, five, five]); assert.equal(rangeMap.size, 25); assert.equal(rangeMap.count, 5); - rangeMap.splice(0, 0, five, five, five, five, five); + rangeMap.splice(0, 0, [five, five, five, five, five]); assert.equal(rangeMap.size, 50); assert.equal(rangeMap.count, 10); - rangeMap.splice(5, 0, ten, ten); + rangeMap.splice(5, 0, [ten, ten]); assert.equal(rangeMap.size, 70); assert.equal(rangeMap.count, 12); - rangeMap.splice(12, 0, { size: 200 }); + rangeMap.splice(12, 0, [{ size: 200 }]); assert.equal(rangeMap.size, 270); assert.equal(rangeMap.count, 13); }); test('delete', () => { - rangeMap.splice(0, 0, five, five, five, five, five, + rangeMap.splice(0, 0, [five, five, five, five, five, five, five, five, five, five, five, five, five, five, five, - five, five, five, five, five); + five, five, five, five, five]); assert.equal(rangeMap.size, 100); assert.equal(rangeMap.count, 20); @@ -226,7 +226,7 @@ suite('RangeMap', () => { assert.equal(rangeMap.size, 0); assert.equal(rangeMap.count, 0); - rangeMap.splice(0, 0, one); + rangeMap.splice(0, 0, [one]); assert.equal(rangeMap.size, 1); assert.equal(rangeMap.count, 1); @@ -236,29 +236,29 @@ suite('RangeMap', () => { }); test('insert & delete #2', () => { - rangeMap.splice(0, 0, one, one, one, one, one, - one, one, one, one, one); + rangeMap.splice(0, 0, [one, one, one, one, one, + one, one, one, one, one]); rangeMap.splice(2, 6); assert.equal(rangeMap.count, 4); assert.equal(rangeMap.size, 4); }); test('insert & delete #3', () => { - rangeMap.splice(0, 0, one, one, one, one, one, + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one, two, two, two, two, two, - two, two, two, two, two); + two, two, two, two, two]); rangeMap.splice(8, 4); assert.equal(rangeMap.count, 16); assert.equal(rangeMap.size, 24); }); test('insert & delete #3', () => { - rangeMap.splice(0, 0, one, one, one, one, one, + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one, two, two, two, two, two, - two, two, two, two, two); - rangeMap.splice(5, 0, three, three, three, three, three); + two, two, two, two, two]); + rangeMap.splice(5, 0, [three, three, three, three, three]); assert.equal(rangeMap.count, 25); assert.equal(rangeMap.size, 45); @@ -278,7 +278,7 @@ suite('RangeMap', () => { }); test('simple', () => { - rangeMap.splice(0, 0, one); + rangeMap.splice(0, 0, [one]); assert.equal(rangeMap.indexAt(0), 0); assert.equal(rangeMap.indexAt(1), 1); assert.equal(rangeMap.positionAt(0), 0); @@ -286,7 +286,7 @@ suite('RangeMap', () => { }); test('simple #2', () => { - rangeMap.splice(0, 0, ten); + rangeMap.splice(0, 0, [ten]); assert.equal(rangeMap.indexAt(0), 0); assert.equal(rangeMap.indexAt(5), 0); assert.equal(rangeMap.indexAt(9), 0); @@ -296,7 +296,7 @@ suite('RangeMap', () => { }); test('insert', () => { - rangeMap.splice(0, 0, one, one, one, one, one, one, one, one, one, one); + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); assert.equal(rangeMap.indexAt(0), 0); assert.equal(rangeMap.indexAt(1), 1); assert.equal(rangeMap.indexAt(5), 5); @@ -304,7 +304,7 @@ suite('RangeMap', () => { assert.equal(rangeMap.indexAt(10), 10); assert.equal(rangeMap.indexAt(11), 10); - rangeMap.splice(10, 0, one, one, one, one, one, one, one, one, one, one); + rangeMap.splice(10, 0, [one, one, one, one, one, one, one, one, one, one]); assert.equal(rangeMap.indexAt(10), 10); assert.equal(rangeMap.indexAt(19), 19); assert.equal(rangeMap.indexAt(20), 20); @@ -316,7 +316,7 @@ suite('RangeMap', () => { }); test('delete', () => { - rangeMap.splice(0, 0, one, one, one, one, one, one, one, one, one, one); + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); rangeMap.splice(2, 6); assert.equal(rangeMap.indexAt(0), 0); @@ -331,7 +331,7 @@ suite('RangeMap', () => { }); test('delete #2', () => { - rangeMap.splice(0, 0, ten, ten, ten, ten, ten, ten, ten, ten, ten, ten); + rangeMap.splice(0, 0, [ten, ten, ten, ten, ten, ten, ten, ten, ten, ten]); rangeMap.splice(2, 6); assert.equal(rangeMap.indexAt(0), 0); diff --git a/src/vs/base/test/browser/ui/scrollbar/scrollableElement.test.ts b/src/vs/base/test/browser/ui/scrollbar/scrollableElement.test.ts index 78e9ebb8bd6..46fc3edf1a7 100644 --- a/src/vs/base/test/browser/ui/scrollbar/scrollableElement.test.ts +++ b/src/vs/base/test/browser/ui/scrollbar/scrollableElement.test.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import * as assert from 'assert'; +import { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableElement'; export type IMouseWheelEvent = [number, number, number]; diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 9d7c4f3456a..185c9f25aa4 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; -import { SplitView, IView, Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; +import { SplitView, IView, Orientation, Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; import { Sash, SashState } from 'vs/base/browser/ui/sash/sash'; class TestView implements IView { @@ -35,7 +35,8 @@ class TestView implements IView { constructor( private _minimumSize: number, - private _maximumSize: number + private _maximumSize: number, + readonly priority: LayoutPriority = LayoutPriority.Normal ) { assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size'); } @@ -430,4 +431,100 @@ suite('Splitview', () => { view2.dispose(); view1.dispose(); }); + + test('proportional layout', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + splitview.addView(view2, Sizing.Distribute); + assert.deepEqual([view1.size, view2.size], [100, 100]); + + splitview.layout(100); + assert.deepEqual([view1.size, view2.size], [50, 50]); + + splitview.dispose(); + view2.dispose(); + view1.dispose(); + }); + + test('disable proportional layout', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container, { proportionalLayout: false }); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + splitview.addView(view2, Sizing.Distribute); + assert.deepEqual([view1.size, view2.size], [100, 100]); + + splitview.layout(100); + assert.deepEqual([view1.size, view2.size], [80, 20]); + + splitview.dispose(); + view2.dispose(); + view1.dispose(); + }); + + test('high layout priority', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.High); + const view3 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container, { proportionalLayout: false }); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + splitview.addView(view2, Sizing.Distribute); + splitview.addView(view3, Sizing.Distribute); + assert.deepEqual([view1.size, view2.size, view3.size], [66, 66, 68]); + + splitview.layout(180); + assert.deepEqual([view1.size, view2.size, view3.size], [66, 46, 68]); + + splitview.layout(124); + assert.deepEqual([view1.size, view2.size, view3.size], [66, 20, 38]); + + splitview.layout(60); + assert.deepEqual([view1.size, view2.size, view3.size], [20, 20, 20]); + + splitview.layout(200); + assert.deepEqual([view1.size, view2.size, view3.size], [20, 160, 20]); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); + + test('low layout priority', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const view3 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low); + const splitview = new SplitView(container, { proportionalLayout: false }); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + splitview.addView(view2, Sizing.Distribute); + splitview.addView(view3, Sizing.Distribute); + assert.deepEqual([view1.size, view2.size, view3.size], [66, 66, 68]); + + splitview.layout(180); + assert.deepEqual([view1.size, view2.size, view3.size], [66, 46, 68]); + + splitview.layout(132); + assert.deepEqual([view1.size, view2.size, view3.size], [44, 20, 68]); + + splitview.layout(60); + assert.deepEqual([view1.size, view2.size, view3.size], [20, 20, 20]); + + splitview.layout(200); + assert.deepEqual([view1.size, view2.size, view3.size], [20, 160, 20]); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); }); \ No newline at end of file diff --git a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts index 53d2add5df4..07431d166b8 100644 --- a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts @@ -24,14 +24,14 @@ function toArray(list: ITreeNode[]): T[] { suite('IndexTreeModel', function () { test('ctor', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); assert(model); assert.equal(list.length, 0); }); test('insert', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -53,7 +53,7 @@ suite('IndexTreeModel', function () { }); test('deep insert', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -90,7 +90,7 @@ suite('IndexTreeModel', function () { }); test('deep insert collapsed', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -118,7 +118,7 @@ suite('IndexTreeModel', function () { }); test('delete', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -143,7 +143,7 @@ suite('IndexTreeModel', function () { }); test('nested delete', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -177,7 +177,7 @@ suite('IndexTreeModel', function () { }); test('deep delete', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -205,7 +205,7 @@ suite('IndexTreeModel', function () { }); test('hidden delete', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -230,7 +230,7 @@ suite('IndexTreeModel', function () { }); test('collapse', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -261,7 +261,7 @@ suite('IndexTreeModel', function () { }); test('expand', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -301,7 +301,7 @@ suite('IndexTreeModel', function () { }); test('collapse should recursively adjust visible count', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -334,7 +334,7 @@ suite('IndexTreeModel', function () { }); test('simple filter', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const filter = new class implements ITreeFilter { filter(element: number): TreeVisibility { return element % 2 === 0 ? TreeVisibility.Visible : TreeVisibility.Hidden; @@ -368,7 +368,7 @@ suite('IndexTreeModel', function () { }); test('recursive filter on initial model', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const filter = new class implements ITreeFilter { filter(element: number): TreeVisibility { return element === 0 ? TreeVisibility.Recurse : TreeVisibility.Hidden; @@ -390,7 +390,7 @@ suite('IndexTreeModel', function () { }); test('refilter', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; let shouldFilter = false; const filter = new class implements ITreeFilter { filter(element: number): TreeVisibility { @@ -429,7 +429,7 @@ suite('IndexTreeModel', function () { }); test('recursive filter', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; let query = new RegExp(''); const filter = new class implements ITreeFilter { filter(element: string): TreeVisibility { @@ -475,7 +475,7 @@ suite('IndexTreeModel', function () { }); test('recursive filter with collapse', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; let query = new RegExp(''); const filter = new class implements ITreeFilter { filter(element: string): TreeVisibility { @@ -521,7 +521,7 @@ suite('IndexTreeModel', function () { }); test('recursive filter while collapsed', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; let query = new RegExp(''); const filter = new class implements ITreeFilter { filter(element: string): TreeVisibility { @@ -576,7 +576,7 @@ suite('IndexTreeModel', function () { suite('getNodeLocation', function () { test('simple', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new IndexTreeModel(toSpliceable(list)); model.splice([0], 0, Iterator.fromArray([ @@ -600,7 +600,7 @@ suite('IndexTreeModel', function () { }); test('with filter', function () { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const filter = new class implements ITreeFilter { filter(element: number): TreeVisibility { return element % 2 === 0 ? TreeVisibility.Visible : TreeVisibility.Hidden; diff --git a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts index 4fc0edf0f12..6aac4a6ad68 100644 --- a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts @@ -24,7 +24,7 @@ function toArray(list: ITreeNode[]): T[] { suite('ObjectTreeModel', function () { test('ctor', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list)); assert(model); assert.equal(list.length, 0); @@ -32,7 +32,7 @@ suite('ObjectTreeModel', function () { }); test('flat', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list)); model.setChildren(null, Iterator.fromArray([ @@ -59,7 +59,7 @@ suite('ObjectTreeModel', function () { }); test('nested', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list)); model.setChildren(null, Iterator.fromArray([ @@ -95,7 +95,7 @@ suite('ObjectTreeModel', function () { }); test('setChildren on collapsed node', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list)); model.setChildren(null, Iterator.fromArray([ @@ -116,7 +116,7 @@ suite('ObjectTreeModel', function () { }); test('setChildren on expanded, unrevealed node', () => { - const list = [] as ITreeNode[]; + const list: ITreeNode[] = []; const model = new ObjectTreeModel(toSpliceable(list)); model.setChildren(null, [ diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index 5297a4de659..dbc2162bc1d 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -274,7 +274,7 @@ suite('Arrays', () => { assert.equal(a[1], 2); assert.equal(a[2], 3); - let b = []; + let b: number[] = []; b[10] = 1; b[20] = 2; b[30] = 3; @@ -284,7 +284,7 @@ suite('Arrays', () => { assert.equal(b[1], 2); assert.equal(b[2], 3); - let sparse = []; + let sparse: number[] = []; sparse[0] = 1; sparse[1] = 1; sparse[17] = 1; @@ -312,7 +312,7 @@ suite('Arrays', () => { assert.equal(a[1], 2); assert.equal(a[2], 3); - let b = []; + let b: number[] = []; b[10] = 1; b[20] = 2; b[30] = 3; @@ -322,7 +322,7 @@ suite('Arrays', () => { assert.equal(b[1], 2); assert.equal(b[2], 3); - let sparse = []; + let sparse: number[] = []; sparse[0] = 1; sparse[1] = 1; sparse[17] = 1; diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 834b2545c28..23cb2ec8eca 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -53,7 +53,7 @@ suite('Async', () => { // Cancelling a sync cancelable promise will fire the cancelled token. // Also, every `then` callback runs in another execution frame. test('CancelablePromise execution order (sync)', function () { - const order = []; + const order: string[] = []; const cancellablePromise = async.createCancelablePromise(token => { order.push('in callback'); @@ -75,7 +75,7 @@ suite('Async', () => { // Cancelling an async cancelable promise is just the same as a sync cancellable promise. test('CancelablePromise execution order (async)', function () { - const order = []; + const order: string[] = []; const cancellablePromise = async.createCancelablePromise(token => { order.push('in callback'); diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index c33c84bd2e9..19acb225e99 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import { Event, Emitter, debounceEvent, EventBufferer, once, fromPromise, stopwatch, buffer, echo, EventMultiplexer, latch, AsyncEmitter, IWaitUntil } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as Errors from 'vs/base/common/errors'; -import { TPromise } from 'vs/base/common/winjs.base'; import { timeout } from 'vs/base/common/async'; namespace Samples { @@ -441,7 +440,7 @@ suite('Event utils', () => { const emitter = new Emitter(); const event = stopwatch(emitter.event); - return new TPromise((c, e) => { + return new Promise((c, e) => { event(duration => { try { assert(duration > 0); diff --git a/src/vs/base/test/common/history.test.ts b/src/vs/base/test/common/history.test.ts index 9805941f0d0..b5d274656fb 100644 --- a/src/vs/base/test/common/history.test.ts +++ b/src/vs/base/test/common/history.test.ts @@ -114,7 +114,7 @@ suite('History Navigator', () => { }); function toArray(historyNavigator: HistoryNavigator): string[] { - let result = []; + let result: string[] = []; historyNavigator.first(); if (historyNavigator.current()) { do { diff --git a/src/vs/base/test/common/json.test.ts b/src/vs/base/test/common/json.test.ts index 1ad67bb26f1..86988f99f92 100644 --- a/src/vs/base/test/common/json.test.ts +++ b/src/vs/base/test/common/json.test.ts @@ -24,7 +24,7 @@ function assertScanError(text: string, expectedKind: SyntaxKind, scanError: Scan } function assertValidParse(input: string, expected: any, options?: ParseOptions): void { - var errors: { error: ParseErrorCode }[] = []; + var errors: ParseError[] = []; var actual = parse(input, errors, options); if (errors.length !== 0) { @@ -34,7 +34,7 @@ function assertValidParse(input: string, expected: any, options?: ParseOptions): } function assertInvalidParse(input: string, expected: any, options?: ParseOptions): void { - var errors: { error: ParseErrorCode }[] = []; + var errors: ParseError[] = []; var actual = parse(input, errors, options); assert(errors.length > 0); @@ -50,7 +50,7 @@ function assertTree(input: string, expected: any, expectedErrors: number[] = [], if (node.children) { for (let child of node.children) { assert.equal(node, child.parent); - delete child.parent; // delete to avoid recursion in deep equal + delete (child).parent; // delete to avoid recursion in deep equal checkParent(child); } } @@ -201,7 +201,7 @@ suite('JSON', () => { test('parse: objects with errors', () => { assertInvalidParse('{,}', {}); - assertInvalidParse('{ "foo": true, }', { foo: true }, { disallowTrailingComma: true }); + assertInvalidParse('{ "foo": true, }', { foo: true }, { allowTrailingComma: false }); assertInvalidParse('{ "bar": 8 "xoo": "foo" }', { bar: 8, xoo: 'foo' }); assertInvalidParse('{ ,"bar": 8 }', { bar: 8 }); assertInvalidParse('{ ,"bar": 8, "foo" }', { bar: 8 }); @@ -211,10 +211,10 @@ suite('JSON', () => { test('parse: array with errors', () => { assertInvalidParse('[,]', []); - assertInvalidParse('[ 1, 2, ]', [1, 2], { disallowTrailingComma: true }); + assertInvalidParse('[ 1, 2, ]', [1, 2], { allowTrailingComma: false }); assertInvalidParse('[ 1 2, 3 ]', [1, 2, 3]); assertInvalidParse('[ ,1, 2, 3 ]', [1, 2, 3]); - assertInvalidParse('[ ,1, 2, 3, ]', [1, 2, 3], { disallowTrailingComma: true }); + assertInvalidParse('[ ,1, 2, 3, ]', [1, 2, 3], { allowTrailingComma: false }); }); test('parse: disallow commments', () => { @@ -230,14 +230,14 @@ suite('JSON', () => { // default is allow assertValidParse('{ "hello": [], }', { hello: [] }); - let options = { disallowTrailingComma: false }; + let options = { allowTrailingComma: true }; assertValidParse('{ "hello": [], }', { hello: [] }, options); assertValidParse('{ "hello": [] }', { hello: [] }, options); assertValidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options); assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options); assertValidParse('{ "hello": [1,] }', { hello: [1] }, options); - options = { disallowTrailingComma: true }; + options = { allowTrailingComma: false }; assertInvalidParse('{ "hello": [], }', { hello: [] }, options); assertInvalidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options); }); @@ -272,7 +272,7 @@ suite('JSON', () => { assertTree('{ "val": 1 }', { type: 'object', offset: 0, length: 12, children: [ { - type: 'property', offset: 2, length: 8, columnOffset: 7, children: [ + type: 'property', offset: 2, length: 8, colonOffset: 7, children: [ { type: 'string', offset: 2, length: 5, value: 'val' }, { type: 'number', offset: 9, length: 1, value: 1 } ] @@ -283,13 +283,13 @@ suite('JSON', () => { { type: 'object', offset: 0, length: 32, children: [ { - type: 'property', offset: 1, length: 9, columnOffset: 5, children: [ + type: 'property', offset: 1, length: 9, colonOffset: 5, children: [ { type: 'string', offset: 1, length: 4, value: 'id' }, { type: 'string', offset: 7, length: 3, value: '$' } ] }, { - type: 'property', offset: 12, length: 18, columnOffset: 15, children: [ + type: 'property', offset: 12, length: 18, colonOffset: 15, children: [ { type: 'string', offset: 12, length: 3, value: 'v' }, { type: 'array', offset: 17, length: 13, children: [ @@ -306,12 +306,12 @@ suite('JSON', () => { { type: 'object', offset: 0, length: 27, children: [ { - type: 'property', offset: 3, length: 20, columnOffset: 7, children: [ + type: 'property', offset: 3, length: 20, colonOffset: 7, children: [ { type: 'string', offset: 3, length: 4, value: 'id' }, { type: 'object', offset: 9, length: 14, children: [ { - type: 'property', offset: 11, length: 10, columnOffset: 16, children: [ + type: 'property', offset: 11, length: 10, colonOffset: 16, children: [ { type: 'string', offset: 11, length: 5, value: 'foo' }, { type: 'object', offset: 18, length: 3, children: [] } ] @@ -322,6 +322,6 @@ suite('JSON', () => { } ] } - , [ParseErrorCode.PropertyNameExpected, ParseErrorCode.ValueExpected], { disallowTrailingComma: true }); + , [ParseErrorCode.PropertyNameExpected, ParseErrorCode.ValueExpected], { allowTrailingComma: false }); }); }); diff --git a/src/vs/base/test/common/jsonEdit.test.ts b/src/vs/base/test/common/jsonEdit.test.ts index 87ced6125b0..a7c5978d96b 100644 --- a/src/vs/base/test/common/jsonEdit.test.ts +++ b/src/vs/base/test/common/jsonEdit.test.ts @@ -101,13 +101,13 @@ suite('JSON - edits', () => { content = '//comment'; edits = setProperty(content, ['foo', 0], 'bar', formatterOptions); - assertEdit(content, edits, '{\n "foo": [\n "bar"\n ]\n} //comment\n'); + assertEdit(content, edits, '{\n "foo": [\n "bar"\n ]\n} //comment'); }); test('remove property', () => { let content = '{\n "x": "y"\n}'; let edits = removeProperty(content, ['x'], formatterOptions); - assertEdit(content, edits, '{}'); + assertEdit(content, edits, '{\n}'); content = '{\n "x": "y", "a": []\n}'; edits = removeProperty(content, ['x'], formatterOptions); diff --git a/src/vs/base/test/common/jsonFormatter.test.ts b/src/vs/base/test/common/jsonFormatter.test.ts index 2d15812917a..2ef51e1b383 100644 --- a/src/vs/base/test/common/jsonFormatter.test.ts +++ b/src/vs/base/test/common/jsonFormatter.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; suite('JSON - formatter', () => { function format(content: string, expected: string, insertSpaces = true) { - let range = void 0; + let range: Formatter.Range | undefined = void 0; var rangeStart = content.indexOf('|'); var rangeEnd = content.lastIndexOf('|'); if (rangeStart !== -1 && rangeEnd !== -1) { @@ -344,12 +344,12 @@ suite('JSON - formatter', () => { '{ "a": {},', ' |"b": [null],', '"c": {}', - '} |' + '}|' ].join('\n'); var expected = [ '{ "a": {},', - ' "b": [', + ' "b": [', ' null', ' ],', ' "c": {}', diff --git a/src/vs/base/test/common/keyCodes.test.ts b/src/vs/base/test/common/keyCodes.test.ts index 617586bae07..bc746461f94 100644 --- a/src/vs/base/test/common/keyCodes.test.ts +++ b/src/vs/base/test/common/keyCodes.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { KeyCode, KeyMod, KeyChord, Keybinding, createKeybinding, SimpleKeybinding, ChordKeybinding } from 'vs/base/common/keyCodes'; +import { ChordKeybinding, KeyChord, KeyCode, KeyMod, Keybinding, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; import { OperatingSystem } from 'vs/base/common/platform'; suite('keyCodes', () => { diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 02ec69cca86..0716d2748c5 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -42,11 +42,11 @@ suite('Resources', () => { test('dirname', () => { if (isWindows) { - assert.equal(dirname(URI.file('c:\\some\\file\\test.txt')).toString(), 'file:///c:/some/file'); - assert.equal(dirname(URI.file('c:\\some\\file')).toString(), 'file:///c:/some'); - assert.equal(dirname(URI.file('c:\\some\\file\\')).toString(), 'file:///c:/some'); - assert.equal(dirname(URI.file('c:\\some')).toString(), 'file:///c:/'); - assert.equal(dirname(URI.file('C:\\some')).toString(), 'file:///c:/'); + assert.equal(dirname(URI.file('c:\\some\\file\\test.txt')).toString(), 'file:///c%3A/some/file'); + assert.equal(dirname(URI.file('c:\\some\\file')).toString(), 'file:///c%3A/some'); + assert.equal(dirname(URI.file('c:\\some\\file\\')).toString(), 'file:///c%3A/some'); + assert.equal(dirname(URI.file('c:\\some')).toString(), 'file:///c%3A/'); + assert.equal(dirname(URI.file('C:\\some')).toString(), 'file:///c%3A/'); } else { assert.equal(dirname(URI.file('/some/file/test.txt')).toString(), 'file:///some/file'); assert.equal(dirname(URI.file('/some/file/')).toString(), 'file:///some'); @@ -83,15 +83,15 @@ suite('Resources', () => { test('joinPath', () => { if (isWindows) { - assert.equal(joinPath(URI.file('c:\\foo\\bar'), '/file.js').toString(), 'file:///c:/foo/bar/file.js'); - assert.equal(joinPath(URI.file('c:\\foo\\bar\\'), 'file.js').toString(), 'file:///c:/foo/bar/file.js'); - assert.equal(joinPath(URI.file('c:\\foo\\bar\\'), '/file.js').toString(), 'file:///c:/foo/bar/file.js'); - assert.equal(joinPath(URI.file('c:\\'), '/file.js').toString(), 'file:///c:/file.js'); - assert.equal(joinPath(URI.file('c:\\'), 'bar/file.js').toString(), 'file:///c:/bar/file.js'); - assert.equal(joinPath(URI.file('c:\\foo'), './file.js').toString(), 'file:///c:/foo/file.js'); - assert.equal(joinPath(URI.file('c:\\foo'), '/./file.js').toString(), 'file:///c:/foo/file.js'); - assert.equal(joinPath(URI.file('C:\\foo'), '../file.js').toString(), 'file:///c:/file.js'); - assert.equal(joinPath(URI.file('C:\\foo\\.'), '../file.js').toString(), 'file:///c:/file.js'); + assert.equal(joinPath(URI.file('c:\\foo\\bar'), '/file.js').toString(), 'file:///c%3A/foo/bar/file.js'); + assert.equal(joinPath(URI.file('c:\\foo\\bar\\'), 'file.js').toString(), 'file:///c%3A/foo/bar/file.js'); + assert.equal(joinPath(URI.file('c:\\foo\\bar\\'), '/file.js').toString(), 'file:///c%3A/foo/bar/file.js'); + assert.equal(joinPath(URI.file('c:\\'), '/file.js').toString(), 'file:///c%3A/file.js'); + assert.equal(joinPath(URI.file('c:\\'), 'bar/file.js').toString(), 'file:///c%3A/bar/file.js'); + assert.equal(joinPath(URI.file('c:\\foo'), './file.js').toString(), 'file:///c%3A/foo/file.js'); + assert.equal(joinPath(URI.file('c:\\foo'), '/./file.js').toString(), 'file:///c%3A/foo/file.js'); + assert.equal(joinPath(URI.file('C:\\foo'), '../file.js').toString(), 'file:///c%3A/file.js'); + assert.equal(joinPath(URI.file('C:\\foo\\.'), '../file.js').toString(), 'file:///c%3A/file.js'); } else { assert.equal(joinPath(URI.file('/foo/bar'), '/file.js').toString(), 'file:///foo/bar/file.js'); assert.equal(joinPath(URI.file('/foo/bar'), 'file.js').toString(), 'file:///foo/bar/file.js'); @@ -116,15 +116,15 @@ suite('Resources', () => { test('normalizePath', () => { if (isWindows) { - assert.equal(normalizePath(URI.file('c:\\foo\\.\\bar')).toString(), 'file:///c:/foo/bar'); - assert.equal(normalizePath(URI.file('c:\\foo\\.')).toString(), 'file:///c:/foo'); - assert.equal(normalizePath(URI.file('c:\\foo\\.\\')).toString(), 'file:///c:/foo/'); - assert.equal(normalizePath(URI.file('c:\\foo\\..')).toString(), 'file:///c:/'); - assert.equal(normalizePath(URI.file('c:\\foo\\..\\bar')).toString(), 'file:///c:/bar'); - assert.equal(normalizePath(URI.file('c:\\foo\\..\\..\\bar')).toString(), 'file:///c:/bar'); - assert.equal(normalizePath(URI.file('c:\\foo\\foo\\..\\..\\bar')).toString(), 'file:///c:/bar'); - assert.equal(normalizePath(URI.file('C:\\foo\\foo\\.\\..\\..\\bar')).toString(), 'file:///c:/bar'); - assert.equal(normalizePath(URI.file('C:\\foo\\foo\\.\\..\\some\\..\\bar')).toString(), 'file:///c:/foo/bar'); + assert.equal(normalizePath(URI.file('c:\\foo\\.\\bar')).toString(), 'file:///c%3A/foo/bar'); + assert.equal(normalizePath(URI.file('c:\\foo\\.')).toString(), 'file:///c%3A/foo'); + assert.equal(normalizePath(URI.file('c:\\foo\\.\\')).toString(), 'file:///c%3A/foo/'); + assert.equal(normalizePath(URI.file('c:\\foo\\..')).toString(), 'file:///c%3A/'); + assert.equal(normalizePath(URI.file('c:\\foo\\..\\bar')).toString(), 'file:///c%3A/bar'); + assert.equal(normalizePath(URI.file('c:\\foo\\..\\..\\bar')).toString(), 'file:///c%3A/bar'); + assert.equal(normalizePath(URI.file('c:\\foo\\foo\\..\\..\\bar')).toString(), 'file:///c%3A/bar'); + assert.equal(normalizePath(URI.file('C:\\foo\\foo\\.\\..\\..\\bar')).toString(), 'file:///c%3A/bar'); + assert.equal(normalizePath(URI.file('C:\\foo\\foo\\.\\..\\some\\..\\bar')).toString(), 'file:///c%3A/foo/bar'); } else { assert.equal(normalizePath(URI.file('/foo/./bar')).toString(), 'file:///foo/bar'); assert.equal(normalizePath(URI.file('/foo/.')).toString(), 'file:///foo'); @@ -214,15 +214,15 @@ suite('Resources', () => { test('isMalformedFileUri', () => { if (isWindows) { - assertMalformedFileUri('c:/foo/bar', 'file:///c:/foo/bar'); - assertMalformedFileUri('c:\\foo\\bar', 'file:///c:/foo/bar'); - assertMalformedFileUri('C:\\foo\\bar', 'file:///c:/foo/bar'); + assertMalformedFileUri('c:/foo/bar', 'file:///c%3A/foo/bar'); + assertMalformedFileUri('c:\\foo\\bar', 'file:///c%3A/foo/bar'); + assertMalformedFileUri('C:\\foo\\bar', 'file:///c%3A/foo/bar'); assertMalformedFileUri('\\\\localhost\\c$\\devel\\test', 'file://localhost/c%24/devel/test'); } assertMalformedFileUri('/foo/bar', 'file:///foo/bar'); assertMalformedFileUri('file:///foo/bar', void 0); - assertMalformedFileUri('file:///c:/foo/bar', void 0); + assertMalformedFileUri('file:///c%3A/foo/bar', void 0); assertMalformedFileUri('file://localhost/c$/devel/test', void 0); assertMalformedFileUri('foo://dadie/foo/bar', void 0); assertMalformedFileUri('foo:///dadie/foo/bar', void 0); diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 3b5eec4fd1c..77351bee75e 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -10,19 +10,20 @@ import { isWindows } from 'vs/base/common/platform'; suite('URI', () => { test('file#toString', () => { - assert.equal(URI.file('c:/win/path').toString(), 'file:///c:/win/path'); - assert.equal(URI.file('C:/win/path').toString(), 'file:///c:/win/path'); - assert.equal(URI.file('c:/win/path/').toString(), 'file:///c:/win/path/'); - assert.equal(URI.file('/c:/win/path').toString(), 'file:///c:/win/path'); + assert.equal(URI.file('c:/win/path').toString(), 'file:///c%3A/win/path'); + assert.equal(URI.file('C:/win/path').toString(), 'file:///c%3A/win/path'); + assert.equal(URI.file('c:/win/path/').toString(), 'file:///c%3A/win/path/'); + assert.equal(URI.file('/c:/win/path').toString(), 'file:///c%3A/win/path'); }); test('URI.file (win-special)', () => { if (isWindows) { - assert.equal(URI.file('c:\\win\\path').toString(), 'file:///c:/win/path'); - assert.equal(URI.file('c:\\win/path').toString(), 'file:///c:/win/path'); + assert.equal(URI.file('c:\\win\\path').toString(), 'file:///c%3A/win/path'); + assert.equal(URI.file('c:\\win/path').toString(), 'file:///c%3A/win/path'); } else { assert.equal(URI.file('c:\\win\\path').toString(), 'file:///c%3A%5Cwin%5Cpath'); assert.equal(URI.file('c:\\win/path').toString(), 'file:///c%3A%5Cwin/path'); + } }); @@ -239,7 +240,7 @@ suite('URI', () => { if (isWindows) { var value = URI.file('c:\\test\\drive'); assert.equal(value.path, '/c:/test/drive'); - assert.equal(value.toString(), 'file:///c:/test/drive'); + assert.equal(value.toString(), 'file:///c%3A/test/drive'); value = URI.file('\\\\shäres\\path\\c#\\plugin.json'); assert.equal(value.scheme, 'file'); @@ -259,15 +260,15 @@ suite('URI', () => { value = URI.file('c:\\test with %\\path'); assert.equal(value.path, '/c:/test with %/path'); - assert.equal(value.toString(), 'file:///c:/test%20with%20%25/path'); + assert.equal(value.toString(), 'file:///c%3A/test%20with%20%25/path'); value = URI.file('c:\\test with %25\\path'); assert.equal(value.path, '/c:/test with %25/path'); - assert.equal(value.toString(), 'file:///c:/test%20with%20%2525/path'); + assert.equal(value.toString(), 'file:///c%3A/test%20with%20%2525/path'); value = URI.file('c:\\test with %25\\c#code'); assert.equal(value.path, '/c:/test with %25/c#code'); - assert.equal(value.toString(), 'file:///c:/test%20with%20%2525/c%23code'); + assert.equal(value.toString(), 'file:///c%3A/test%20with%20%2525/c%23code'); value = URI.file('\\\\shares'); assert.equal(value.scheme, 'file'); @@ -457,37 +458,4 @@ suite('URI', () => { // } // console.profileEnd(); }); - - test('Opening files from quick open not showing file contents #60163', function () { - - const data = { - '$mid': 1, - 'fsPath': 'c:\\Users\\bpasero\\Desktop\\Golda\'s Kitchen\\CHANGELOG.md', - 'external': 'file:///c%3A/Users/bpasero/Desktop/Golda%27s%20Kitchen/CHANGELOG.md', - 'path': '/c:/Users/bpasero/Desktop/Golda\'s Kitchen/CHANGELOG.md', - 'scheme': 'file' - }; - const uri = URI.revive(data); - assert.equal(uri.scheme, data.scheme); - assert.equal(uri.path, data.path); - assert.equal((uri as any)._formatted, null); - assert.equal((uri as any)._fsPath, null); - - // when the $mid is the current one then we trust - // the data(no matter what) - const data2 = { - '$mid': 100, - 'fsPath': 'c:\\Users\\bpasero\\Desktop\\Golda\'s Kitchen\\CHANGELOG.md', - 'external': 'file:///c%3A/Users/bpasero/Desktop/Golda%27s%20Kitchen/CHANGELOG.md', - 'path': '/c:/Users/bpasero/Desktop/Golda\'s Kitchen/CHANGELOG.md', - 'scheme': 'file' - }; - const uri2 = URI.revive(data2); - assert.equal(uri2.scheme, data2.scheme); - assert.equal(uri2.path, data2.path); - assert.ok((uri2 as any)._formatted); - assert.ok((uri2 as any)._fsPath); - - - }); }); diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index a00e86f3ea6..dae0e054b00 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -5,33 +5,46 @@ import * as paths from 'vs/base/common/paths'; import { URI } from 'vs/base/common/uri'; -import { TPromise, TValueCallback } from 'vs/base/common/winjs.base'; import { canceled } from 'vs/base/common/errors'; -export class DeferredTPromise extends TPromise { +export type ValueCallback = (value: T | Thenable) => void; - private completeCallback: TValueCallback; +export class DeferredPromise { + + private completeCallback: ValueCallback; private errorCallback: (err: any) => void; + public p: Promise; + constructor() { - let captured: any; - super((c, e) => { - captured = { c, e }; + this.p = new Promise((c, e) => { + this.completeCallback = c; + this.errorCallback = e; }); - this.completeCallback = captured.c; - this.errorCallback = captured.e; } public complete(value: T) { - this.completeCallback(value); + return new Promise(resolve => { + process.nextTick(() => { + this.completeCallback(value); + resolve(); + }); + }); } public error(err: any) { - this.errorCallback(err); + return new Promise(resolve => { + process.nextTick(() => { + this.errorCallback(err); + resolve(); + }); + }); } public cancel() { - this.errorCallback(canceled()); + process.nextTick(() => { + this.errorCallback(canceled()); + }); } } diff --git a/src/vs/base/test/node/pfs.test.ts b/src/vs/base/test/node/pfs.test.ts index 75b38405ae1..283f03d66f9 100644 --- a/src/vs/base/test/node/pfs.test.ts +++ b/src/vs/base/test/node/pfs.test.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import * as assert from 'assert'; import * as os from 'os'; @@ -46,7 +45,7 @@ suite('PFS', () => { return pfs.mkdirp(newDir, 493).then(() => { assert.ok(fs.existsSync(newDir)); - return TPromise.join([ + return Promise.all([ pfs.writeFile(testFile1, 'Hello World 1', null), pfs.writeFile(testFile2, 'Hello World 2', null), pfs.writeFile(testFile3, 'Hello World 3', null), @@ -73,7 +72,7 @@ suite('PFS', () => { return pfs.mkdirp(newDir, 493).then(() => { assert.ok(fs.existsSync(newDir)); - return TPromise.join([ + return Promise.all([ pfs.writeFile(testFile, 'Hello World 1', null), pfs.writeFile(testFile, 'Hello World 2', null), timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 3', null)), @@ -119,4 +118,4 @@ suite('PFS', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/test/node/storage/storage.test.ts index 0dbcf3dc84f..b1c3fcb9f1e 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/test/node/storage/storage.test.ts @@ -9,6 +9,7 @@ import { join } from 'path'; import { tmpdir } from 'os'; import { equal, ok } from 'assert'; import { mkdirp, del } from 'vs/base/node/pfs'; +import { timeout } from 'vs/base/common/async'; suite('Storage Library', () => { @@ -204,11 +205,11 @@ suite('SQLite Storage Library', () => { return set; } - async function testDBBasics(path, errorLogger?: (error) => void) { + async function testDBBasics(path, logError?: (error) => void) { const options: IStorageOptions = { path }; - if (errorLogger) { + if (logError) { options.logging = { - errorLogger + logError }; } @@ -288,15 +289,15 @@ suite('SQLite Storage Library', () => { await del(storageDir, tmpdir()); }); - test('basics (broken DB falls back to in-memory)', async () => { - let expectedError: any; + // test('basics (broken DB falls back to in-memory)', async () => { + // let expectedError: any; - await testDBBasics(join(__dirname, 'broken.db'), error => { - expectedError = error; - }); + // await testDBBasics(join(__dirname, 'broken.db'), error => { + // expectedError = error; + // }); - ok(expectedError); - }); + // ok(expectedError); + // }); test('real world example', async () => { const storageDir = uniqueStorageDir(); @@ -405,7 +406,7 @@ suite('SQLite Storage Library', () => { items.set('commandpalette.mru.cache', '{"usesLRU":true,"entries":[{"key":"revealFileInOS","value":3},{"key":"extension.openInGitHub","value":4},{"key":"workbench.extensions.action.openExtensionsFolder","value":11},{"key":"workbench.action.showRuntimeExtensions","value":14},{"key":"workbench.action.toggleTabsVisibility","value":15},{"key":"extension.liveServerPreview.open","value":16},{"key":"workbench.action.openIssueReporter","value":18},{"key":"workbench.action.openProcessExplorer","value":19},{"key":"workbench.action.toggleSharedProcess","value":20},{"key":"workbench.action.configureLocale","value":21},{"key":"workbench.action.appPerf","value":22},{"key":"workbench.action.reportPerformanceIssueUsingReporter","value":23},{"key":"workbench.action.openGlobalKeybindings","value":25},{"key":"workbench.action.output.toggleOutput","value":27},{"key":"extension.sayHello","value":29}]}'); let uuid = generateUuid(); - let value = []; + let value: string[] = []; for (let i = 0; i < 100000; i++) { value.push(uuid); } @@ -445,4 +446,55 @@ suite('SQLite Storage Library', () => { await del(storageDir, tmpdir()); }); -}); \ No newline at end of file + + test('multiple concurrent writes execute in sequence', async () => { + const storageDir = uniqueStorageDir(); + await mkdirp(storageDir); + + const storage = new Storage({ path: join(storageDir, 'storage.db') }); + + await storage.init(); + + storage.set('foo', 'bar'); + storage.set('some/foo/path', 'some/bar/path'); + + await timeout(10); + + storage.set('foo1', 'bar'); + storage.set('some/foo1/path', 'some/bar/path'); + + await timeout(10); + + storage.set('foo2', 'bar'); + storage.set('some/foo2/path', 'some/bar/path'); + + await timeout(10); + + storage.delete('foo1'); + storage.delete('some/foo1/path'); + + await timeout(10); + + storage.delete('foo4'); + storage.delete('some/foo4/path'); + + await timeout(70); + + storage.set('foo3', 'bar'); + await storage.set('some/foo3/path', 'some/bar/path'); + + const items = await storage.getItems(); + equal(items.get('foo'), 'bar'); + equal(items.get('some/foo/path'), 'some/bar/path'); + equal(items.has('foo1'), false); + equal(items.has('some/foo1/path'), false); + equal(items.get('foo2'), 'bar'); + equal(items.get('some/foo2/path'), 'some/bar/path'); + equal(items.get('foo3'), 'bar'); + equal(items.get('some/foo3/path'), 'some/bar/path'); + + await storage.close(); + + await del(storageDir, tmpdir()); + }); +}); diff --git a/src/vs/base/test/node/utils.ts b/src/vs/base/test/node/utils.ts index 439ed82eb03..2ee7de39d71 100644 --- a/src/vs/base/test/node/utils.ts +++ b/src/vs/base/test/node/utils.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'path'; import { tmpdir } from 'os'; @@ -11,10 +10,10 @@ import { mkdirp, del } from 'vs/base/node/pfs'; export interface ITestFileResult { testFile: string; - cleanUp: () => TPromise; + cleanUp: () => Thenable; } -export function testFile(folder: string, file: string): TPromise { +export function testFile(folder: string, file: string): Thenable { const id = generateUuid(); const parentDir = join(tmpdir(), 'vsctests', id); const newDir = join(parentDir, 'config', id); @@ -26,4 +25,4 @@ export function testFile(folder: string, file: string): TPromise del(parentDir, tmpdir()) } as ITestFileResult; }); -} \ No newline at end of file +} diff --git a/src/vs/base/worker/defaultWorkerFactory.ts b/src/vs/base/worker/defaultWorkerFactory.ts index eba95454048..30ad9bf632f 100644 --- a/src/vs/base/worker/defaultWorkerFactory.ts +++ b/src/vs/base/worker/defaultWorkerFactory.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { globals } from 'vs/base/common/platform'; -import { logOnceWebWorkerWarning, IWorker, IWorkerCallback, IWorkerFactory } from 'vs/base/common/worker/simpleWorker'; +import { IWorker, IWorkerCallback, IWorkerFactory, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker'; function getWorker(workerId: string, label: string): Worker { // Option for hosts to overwrite the worker script (used in the standalone editor) diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts index 986e3ea4da9..0ec8b7834dc 100644 --- a/src/vs/base/worker/workerMain.ts +++ b/src/vs/base/worker/workerMain.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ (function () { - 'use strict'; let MonacoEnvironment = (self).MonacoEnvironment; let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../'; diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 1e63205795e..1d46130f930 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -31,9 +31,8 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; -import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures } from 'vs/platform/issue/common/issue'; +import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; -import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; @@ -211,11 +210,9 @@ export class IssueReporter extends Disposable { document.body.style.color = styles.color; } - private handleExtensionData(extensions: ILocalExtension[]) { + private handleExtensionData(extensions: IssueReporterExtensionData[]) { const { nonThemes, themes } = collections.groupBy(extensions, ext => { - const manifestKeys = ext.manifest.contributes ? Object.keys(ext.manifest.contributes) : []; - const onlyTheme = !ext.manifest.activationEvents && manifestKeys.length === 1 && manifestKeys[0] === 'themes'; - return onlyTheme ? 'themes' : 'nonThemes'; + return ext.isTheme ? 'themes' : 'nonThemes'; }); const numberOfThemeExtesions = themes && themes.length; @@ -457,12 +454,12 @@ export class IssueReporter extends Disposable { private getExtensionRepositoryUrl(): string { const selectedExtension = this.issueReporterModel.getData().selectedExtension; - return selectedExtension && selectedExtension.manifest && selectedExtension.manifest.repository && selectedExtension.manifest.repository.url; + return selectedExtension && selectedExtension.repositoryUrl; } private getExtensionBugsUrl(): string { const selectedExtension = this.issueReporterModel.getData().selectedExtension; - return selectedExtension && selectedExtension.manifest && selectedExtension.manifest.bugs && selectedExtension.manifest.bugs.url; + return selectedExtension && selectedExtension.bugsUrl; } private searchVSCodeIssues(title: string, issueDescription: string): void { @@ -830,7 +827,7 @@ export class IssueReporter extends Disposable { target.innerHTML = `${tableHtml}
`; } - private updateExtensionSelector(extensions: ILocalExtension[]): void { + private updateExtensionSelector(extensions: IssueReporterExtensionData[]): void { interface IOption { name: string; id: string; @@ -838,8 +835,8 @@ export class IssueReporter extends Disposable { const extensionOptions: IOption[] = extensions.map(extension => { return { - name: extension.manifest.displayName || extension.manifest.name || '', - id: extension.identifier.id + name: extension.displayName || extension.name || '', + id: extension.id }; }); @@ -865,7 +862,7 @@ export class IssueReporter extends Disposable { this.addEventListener('extension-selector', 'change', (e: Event) => { const selectedExtensionId = (e.target).value; const extensions = this.issueReporterModel.getData().allExtensions; - const matches = extensions.filter(extension => extension.identifier.id === selectedExtensionId); + const matches = extensions.filter(extension => extension.id === selectedExtensionId); if (matches.length) { this.issueReporterModel.update({ selectedExtension: matches[0] }); @@ -887,7 +884,7 @@ export class IssueReporter extends Disposable { document.querySelector('.block-workspace .block-info code').textContent = '\n' + state.workspaceInfo; } - private updateExtensionTable(extensions: ILocalExtension[], numThemeExtensions: number): void { + private updateExtensionTable(extensions: IssueReporterExtensionData[], numThemeExtensions: number): void { const target = document.querySelector('.block-extensions .block-info'); if (this.environmentService.disableExtensions) { @@ -907,7 +904,7 @@ export class IssueReporter extends Disposable { target.innerHTML = `${table}
${themeExclusionStr}`; } - private updateSearchedExtensionTable(extensions: ILocalExtension[]): void { + private updateSearchedExtensionTable(extensions: IssueReporterExtensionData[]): void { const target = document.querySelector('.block-searchedExtensions .block-info'); if (!extensions.length) { @@ -919,7 +916,7 @@ export class IssueReporter extends Disposable { target.innerHTML = `${table}
`; } - private getExtensionTableHtml(extensions: ILocalExtension[]): string { + private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): string { let table = ` Extension @@ -930,9 +927,9 @@ export class IssueReporter extends Disposable { table += extensions.map(extension => { return ` - ${extension.manifest.name} - ${extension.manifest.publisher.substr(0, 3)} - ${extension.manifest.version} + ${extension.name} + ${extension.publisher.substr(0, 3)} + ${extension.version} `; }).join(''); diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-browser/issue/issueReporterModel.ts index afd544ed005..2ef6e5f8e0c 100644 --- a/src/vs/code/electron-browser/issue/issueReporterModel.ts +++ b/src/vs/code/electron-browser/issue/issueReporterModel.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { assign } from 'vs/base/common/objects'; -import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IssueType, ISettingSearchResult } from 'vs/platform/issue/common/issue'; +import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; export interface IssueReporterData { issueType?: IssueType; @@ -24,11 +23,11 @@ export interface IssueReporterData { includeSettingsSearchDetails?: boolean; numberOfThemeExtesions?: number; - allExtensions?: ILocalExtension[]; - enabledNonThemeExtesions?: ILocalExtension[]; + allExtensions?: IssueReporterExtensionData[]; + enabledNonThemeExtesions?: IssueReporterExtensionData[]; extensionsDisabled?: boolean; fileOnExtension?: boolean; - selectedExtension?: ILocalExtension; + selectedExtension?: IssueReporterExtensionData; actualSearchResults?: ISettingSearchResult[]; query?: string; filterResultCount?: number; @@ -81,7 +80,7 @@ ${this.getInfos()} private getExtensionVersion(): string { if (this.fileOnExtension() && this._data.selectedExtension) { - return `\nExtension version: ${this._data.selectedExtension.manifest.version}`; + return `\nExtension version: ${this._data.selectedExtension.version}`; } else { return ''; } @@ -198,7 +197,7 @@ ${this._data.workspaceInfo}; let tableHeader = `Extension|Author (truncated)|Version ---|---|---`; const table = this._data.enabledNonThemeExtesions.map(e => { - return `${e.manifest.name}|${e.manifest.publisher.substr(0, 3)}|${e.manifest.version}`; + return `${e.name}|${e.publisher.substr(0, 3)}|${e.version}`; }).join('\n'); return `
Extensions (${this._data.enabledNonThemeExtesions.length}) diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts index a197306f72d..5418d034093 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -77,6 +77,10 @@ function getProcessIdWithHighestProperty(processList, propertyName: string) { function updateProcessInfo(processList): void { const target = document.getElementById('process-list'); + if (!target) { + return; + } + const highestCPUProcess = getProcessIdWithHighestProperty(processList, 'cpu'); const highestMemoryProcess = getProcessIdWithHighestProperty(processList, 'memory'); @@ -127,7 +131,9 @@ function applyStyles(styles: ProcessExplorerStyles): void { } styleTag.innerHTML = content.join('\n'); - document.head.appendChild(styleTag); + if (document.head) { + document.head.appendChild(styleTag); + } document.body.style.color = styles.color; } diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts index 953d7efaf97..2da898399ab 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts @@ -50,7 +50,7 @@ export class LanguagePackCachedDataCleaner { } private _manageCachedDataSoon(): void { - let handle = setTimeout(async () => { + let handle: any = setTimeout(async () => { handle = undefined; this._logService.info('Starting to clean up unused language packs.'); const maxAge = product.nameLong.indexOf('Insiders') >= 0 diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts index 78be7ec10ed..3f14814bdee 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts @@ -41,7 +41,7 @@ export class NodeCachedDataCleaner { const nodeCachedDataRootDir = dirname(this._environmentService.nodeCachedDataDir); const nodeCachedDataCurrent = basename(this._environmentService.nodeCachedDataDir); - let handle = setTimeout(() => { + let handle: any = setTimeout(() => { handle = undefined; readdir(nodeCachedDataRootDir).then(entries => { diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 5b574e3f341..030392102cf 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -62,8 +62,14 @@ const eventPrefix = 'monacoworkbench'; function main(server: Server, initData: ISharedProcessInitData, configuration: ISharedProcessConfiguration): void { const services = new ServiceCollection(); + const disposables: IDisposable[] = []; - process.once('exit', () => dispose(disposables)); + + const onExit = () => dispose(disposables); + process.once('exit', onExit); + ipcRenderer.once('handshake:goodbye', onExit); + + disposables.push(server); const environmentService = new EnvironmentService(initData.args, process.execPath); const mainRoute = () => TPromise.as('main'); @@ -100,23 +106,22 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I telemetryLogService.info('==========================================================='); let appInsightsAppender: ITelemetryAppender | null = NullAppender; - if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) { - appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, telemetryLogService); - disposables.push(appInsightsAppender); // Ensure the AI appender is disposed so that it flushes remaining data - } - server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appInsightsAppender)); - if (!extensionDevelopmentLocationURI && !environmentService.args['disable-telemetry'] && product.enableTelemetry) { + if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) { + appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, telemetryLogService); + disposables.push(appInsightsAppender); // Ensure the AI appender is disposed so that it flushes remaining data + } const config: ITelemetryServiceConfig = { appender: combinedAppender(appInsightsAppender, new LogAppender(logService)), commonProperties: resolveCommonProperties(product.commit, pkg.version, configuration.machineId, installSourcePath), piiPaths: [appRoot, extensionsPath] }; - services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); } else { services.set(ITelemetryService, NullTelemetryService); } + server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appInsightsAppender)); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); @@ -187,4 +192,4 @@ function handshake(configuration: ISharedProcessConfiguration): TPromise { return startHandshake() .then(data => setupIPC(data.sharedIPCHandle).then(server => main(server, data, configuration))) .then(() => ipcRenderer.send('handshake:im ready')); -} \ No newline at end of file +} diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 6469759571d..d5017d77804 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -53,15 +53,11 @@ bootstrapWindow.load([ function showPartsSplash(configuration) { perf.mark('willShowPartsSplash'); - // TODO@Ben remove me after a while - perf.mark('willAccessLocalStorage'); - let storage = window.localStorage; - perf.mark('didAccessLocalStorage'); - let data; try { + // TODO@Ben remove me after a while perf.mark('willReadLocalStorage'); - let raw = storage.getItem('storage://global/parts-splash-data'); + let raw = window.localStorage.getItem('storage://global/parts-splash-data'); perf.mark('didReadLocalStorage'); data = JSON.parse(raw); } catch (e) { diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 44464ac8044..dfe71ff9fd1 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -68,6 +68,7 @@ import { THEME_STORAGE_KEY, THEME_BG_STORAGE_KEY } from 'vs/code/electron-main/t import { nativeSep, join } from 'vs/base/common/paths'; import { homedir } from 'os'; import { localize } from 'vs/nls'; +import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap'; export class CodeApplication { @@ -426,15 +427,19 @@ export class CodeApplication { if (process.platform === 'win32') { services.set(IUpdateService, new SyncDescriptor(Win32UpdateService)); } else if (process.platform === 'linux') { - services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService)); + if (process.env.SNAP && process.env.SNAP_REVISION) { + services.set(IUpdateService, new SyncDescriptor(SnapUpdateService)); + } else { + services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService)); + } } else if (process.platform === 'darwin') { services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService)); } - services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, machineId)); - services.set(IWindowsService, new SyncDescriptor(WindowsService, this.sharedProcess)); + services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId])); + services.set(IWindowsService, new SyncDescriptor(WindowsService, [this.sharedProcess])); services.set(ILaunchService, new SyncDescriptor(LaunchService)); - services.set(IIssueService, new SyncDescriptor(IssueService, machineId, this.userEnv)); + services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv])); services.set(IMenubarService, new SyncDescriptor(MenubarService)); // Telemtry @@ -445,7 +450,7 @@ export class CodeApplication { const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths }; - services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); } else { services.set(ITelemetryService, NullTelemetryService); } diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 7aafa8100b7..d777186c48f 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -49,6 +49,7 @@ import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { CommandLineDialogService } from 'vs/platform/dialogs/node/dialogService'; import { ILabelService, LabelService } from 'vs/platform/label/common/label'; +import { createWaitMarkerFile } from 'vs/code/node/wait'; function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService { const services = new ServiceCollection(); @@ -56,7 +57,7 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I const environmentService = new EnvironmentService(args, process.execPath); const consoleLogService = new ConsoleLogMainService(getLogLevel(environmentService)); const logService = new MultiplexLogService([consoleLogService, bufferLogService]); - const labelService = new LabelService(environmentService, undefined); + const labelService = new LabelService(environmentService, undefined, undefined); process.once('exit', () => logService.dispose()); @@ -96,10 +97,10 @@ async function cleanupOlderLogs(environmentService: EnvironmentService): Promise function createPaths(environmentService: IEnvironmentService): TPromise { const paths = [ - environmentService.appSettingsHome, environmentService.extensionsPath, environmentService.nodeCachedDataDir, - environmentService.logsPath + environmentService.logsPath, + environmentService.workspaceStorageHome ]; return TPromise.join(paths.map(p => p && mkdirp(p))) as TPromise; @@ -293,23 +294,7 @@ function quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void lifecycleService.kill(exitCode); } -function main() { - - // Set the error handler early enough so that we are not getting the - // default electron error dialog popping up - setUnexpectedErrorHandler(err => console.error(err)); - - let args: ParsedArgs; - try { - args = parseMainProcessArgv(process.argv); - args = validatePaths(args); - } catch (err) { - console.error(err.message); - app.exit(1); - - return void 0; - } - +function startup(args: ParsedArgs): void { // We need to buffer the spdlog logs until we are sure // we are the only instance running, otherwise we'll have concurrent // log file access on Windows @@ -317,7 +302,7 @@ function main() { const bufferLogService = new BufferLogService(); const instantiationService = createServices(args, bufferLogService); - return instantiationService.invokeFunction(accessor => { + instantiationService.invokeFunction(accessor => { // Patch `process.env` with the instance's environment const environmentService = accessor.get(IEnvironmentService); @@ -343,4 +328,46 @@ function main() { }).then(null, err => instantiationService.invokeFunction(quit, err)); } +function main(): void { + + // Set the error handler early enough so that we are not getting the + // default electron error dialog popping up + setUnexpectedErrorHandler(err => console.error(err)); + + // Parse arguments + let args: ParsedArgs; + try { + args = parseMainProcessArgv(process.argv); + args = validatePaths(args); + } catch (err) { + console.error(err.message); + app.exit(1); + + return void 0; + } + + // If we are started with --wait create a random temporary file + // and pass it over to the starting instance. We can use this file + // to wait for it to be deleted to monitor that the edited file + // is closed and then exit the waiting process. + // + // Note: we are not doing this if the wait marker has been already + // added as argument. This can happen if Code was started from CLI. + if (args.wait && !args.waitMarkerFilePath) { + createWaitMarkerFile(args.verbose).then(waitMarkerFilePath => { + if (waitMarkerFilePath) { + process.argv.push('--waitMarkerFilePath', waitMarkerFilePath); + args.waitMarkerFilePath = waitMarkerFilePath; + } + + startup(args); + }); + } + + // Otherwise just startup normally + else { + startup(args); + } +} + main(); diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index 49519e62453..b937a61d079 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -15,12 +15,13 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IStateService } from 'vs/platform/state/common/state'; import { getBackgroundColor } from 'vs/code/electron-main/theme'; +import { dispose, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; export class SharedProcess implements ISharedProcess { private barrier = new Barrier(); - private window: Electron.BrowserWindow; + private window: Electron.BrowserWindow | null; constructor( private readonly machineId: string, @@ -62,26 +63,34 @@ export class SharedProcess implements ISharedProcess { e.preventDefault(); // Still hide the window though if visible - if (this.window.isVisible()) { + if (this.window && this.window.isVisible()) { this.window.hide(); } }; this.window.on('close', onClose); + const disposables: IDisposable[] = []; + this.lifecycleService.onShutdown(() => { + dispose(disposables); + // Shut the shared process down when we are quitting // // Note: because we veto the window close, we must first remove our veto. // Otherwise the application would never quit because the shared process // window is refusing to close! // - this.window.removeListener('close', onClose); + if (this.window) { + this.window.removeListener('close', onClose); + } // Electron seems to crash on Windows without this setTimeout :| setTimeout(() => { try { - this.window.close(); + if (this.window) { + this.window.close(); + } } catch (err) { // ignore, as electron is already shutting down } @@ -98,7 +107,8 @@ export class SharedProcess implements ISharedProcess { logLevel: this.logService.getLevel() }); - ipcMain.once('handshake:im ready', () => c(null)); + disposables.push(toDisposable(() => sender.send('handshake:goodbye'))); + ipcMain.once('handshake:im ready', () => c(void 0)); }); }); } @@ -112,7 +122,7 @@ export class SharedProcess implements ISharedProcess { } toggle(): void { - if (this.window.isVisible()) { + if (!this.window || this.window.isVisible()) { this.hide(); } else { this.show(); @@ -120,12 +130,16 @@ export class SharedProcess implements ISharedProcess { } show(): void { - this.window.show(); - this.window.webContents.openDevTools(); + if (this.window) { + this.window.show(); + this.window.webContents.openDevTools(); + } } hide(): void { - this.window.webContents.closeDevTools(); - this.window.hide(); + if (this.window) { + this.window.webContents.closeDevTools(); + this.window.hide(); + } } } diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 9ee3f1c5d1b..4b889bd8ef6 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -143,6 +143,10 @@ export class CodeWindow implements ICodeWindow { const windowConfig = this.configurationService.getValue('window'); + if (isMacintosh && !this.useNativeFullScreen()) { + options.fullscreenable = false; // enables simple fullscreen mode + } + if (isMacintosh) { options.acceptFirstMouse = true; // enabled by default @@ -189,7 +193,7 @@ export class CodeWindow implements ICodeWindow { this._win = new BrowserWindow(options); this._id = this._win.id; - if (useCustomTitleStyle) { + if (isMacintosh && useCustomTitleStyle) { this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any } @@ -197,7 +201,7 @@ export class CodeWindow implements ICodeWindow { this._win.maximize(); if (this.windowState.mode === WindowMode.Fullscreen) { - this._win.setFullScreen(true); + this.setFullScreen(true); } if (!this._win.isVisible()) { @@ -280,6 +284,10 @@ export class CodeWindow implements ICodeWindow { return this.currentConfig ? this.currentConfig.folderUri : void 0; } + get remoteAuthority(): string { + return this.currentConfig ? this.currentConfig.remoteAuthority : void 0; + } + setReady(): void { this._readyState = ReadyState.READY; @@ -372,6 +380,14 @@ export class CodeWindow implements ICodeWindow { this._lastFocusTime = Date.now(); }); + // Simple fullscreen doesn't resize automatically when the resolution changes + screen.on('display-metrics-changed', () => { + if (isMacintosh && this.isFullScreen() && !this.useNativeFullScreen()) { + this.setFullScreen(false); + this.setFullScreen(true); + } + }); + // Window (Un)Maximize this._win.on('maximize', (e) => { if (this.currentConfig) { @@ -408,6 +424,34 @@ export class CodeWindow implements ICodeWindow { // Handle Workspace events this.toDispose.push(this.workspacesMainService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e))); + + // TODO@Ben workaround for https://github.com/Microsoft/vscode/issues/13612 + // It looks like smooth scrolling disappears as soon as the window is minimized + // and maximized again. Touching some window properties "fixes" it, like toggling + // the visibility of the menu. + if (isWindows) { + const windowConfig = this.configurationService.getValue('window'); + if (windowConfig && windowConfig.smoothScrollingWorkaround === true) { + let minimized = false; + + const restoreSmoothScrolling = () => { + if (minimized) { + const visibility = this.getMenuBarVisibility(); + const temporaryVisibility: MenuBarVisibility = (visibility === 'hidden' || visibility === 'toggle') ? 'default' : 'hidden'; + setTimeout(() => { + this.doSetMenuBarVisibility(temporaryVisibility); + this.doSetMenuBarVisibility(visibility); + }, 0); + } + + minimized = false; + }; + + this._win.on('minimize', () => minimized = true); + this._win.on('restore', () => restoreSmoothScrolling()); + this._win.on('maximize', () => restoreSmoothScrolling()); + } + } } private onUntitledWorkspaceDeleted(workspace: IWorkspaceIdentifier): void { @@ -558,7 +602,7 @@ export class CodeWindow implements ICodeWindow { } // Set fullscreen state - windowConfiguration.fullscreen = this._win.isFullScreen(); + windowConfiguration.fullscreen = this.isFullScreen(); // Set Accessibility Config let autoDetectHighContrast = true; @@ -612,7 +656,7 @@ export class CodeWindow implements ICodeWindow { } // fullscreen gets special treatment - if (this._win.isFullScreen()) { + if (this.isFullScreen()) { const display = screen.getDisplayMatching(this.getBounds()); const defaultState = defaultWindowState(); @@ -778,15 +822,55 @@ export class CodeWindow implements ICodeWindow { } toggleFullScreen(): void { - const willBeFullScreen = !this._win.isFullScreen(); + this.setFullScreen(!this.isFullScreen()); + } - // set fullscreen flag on window - this._win.setFullScreen(willBeFullScreen); + private setFullScreen(fullscreen: boolean): void { - // respect configured menu bar visibility or default to toggle if not set + // Set fullscreen state + if (this.useNativeFullScreen()) { + this.setNativeFullScreen(fullscreen); + } else { + this.setSimpleFullScreen(fullscreen); + } + + // Events + this.sendWhenReady(fullscreen ? 'vscode:enterFullScreen' : 'vscode:leaveFullScreen'); + + // Respect configured menu bar visibility or default to toggle if not set this.setMenuBarVisibility(this.currentMenuBarVisibility, false); } + isFullScreen(): boolean { + return this._win.isFullScreen() || this._win.isSimpleFullScreen(); + } + + private setNativeFullScreen(fullscreen: boolean): void { + if (this._win.isSimpleFullScreen()) { + this._win.setSimpleFullScreen(false); + } + + this._win.setFullScreen(fullscreen); + } + + private setSimpleFullScreen(fullscreen: boolean): void { + if (this._win.isFullScreen()) { + this._win.setFullScreen(false); + } + + this._win.setSimpleFullScreen(fullscreen); + this._win.webContents.focus(); // workaround issue where focus is not going into window + } + + private useNativeFullScreen(): boolean { + const windowConfig = this.configurationService.getValue('window'); + if (!windowConfig || typeof windowConfig.nativeFullScreen !== 'boolean') { + return true; // default + } + + return windowConfig.nativeFullScreen !== false; + } + private getMenuBarVisibility(): MenuBarVisibility { const windowConfig = this.configurationService.getValue('window'); if (!windowConfig || !windowConfig.menuBarVisibility) { @@ -827,7 +911,7 @@ export class CodeWindow implements ICodeWindow { } private doSetMenuBarVisibility(visibility: MenuBarVisibility): void { - const isFullscreen = this._win.isFullScreen(); + const isFullscreen = this.isFullScreen(); switch (visibility) { case ('default'): diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 79499a31bde..1b27914ba10 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -37,6 +37,7 @@ import { Queue, timeout } from 'vs/base/common/async'; import { exists } from 'vs/base/node/pfs'; import { getComparisonKey, isEqual, normalizePath } from 'vs/base/common/resources'; import { endsWith } from 'vs/base/common/strings'; +import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; enum WindowError { UNRESPONSIVE, @@ -51,6 +52,7 @@ interface IWindowState { workspace?: IWorkspaceIdentifier; folderUri?: URI; backupPath: string; + remoteAuthority?: string; uiState: ISingleWindowState; } @@ -73,6 +75,8 @@ interface IOpenBrowserWindowOptions { workspace?: IWorkspaceIdentifier; folderUri?: URI; + remoteAuthority: string; + initialStartup?: boolean; fileInputs?: IFileInputs; @@ -88,6 +92,7 @@ interface IPathParseOptions { ignoreFileNotFound?: boolean; gotoLineMode?: boolean; forceOpenWorkspaceAsFile?: boolean; + remoteAuthority?: string; } interface IFileInputs { @@ -95,6 +100,7 @@ interface IFileInputs { filesToCreate: IPath[]; filesToDiff: IPath[]; filesToWait?: IPathsToWaitFor; + remoteAuthority?: string; } interface IPathToOpen extends IPath { @@ -108,6 +114,9 @@ interface IPathToOpen extends IPath { // the backup path for a Code instance to use backupPath?: string; + // the remote authority for the Code instance to open. Undefined if not remote. + remoteAuthority?: string; + // indicator to create the file path in the Code instance createFilePath?: boolean; } @@ -358,6 +367,7 @@ export class WindowsManager implements IWindowsMainService { workspace: win.openedWorkspace, folderUri: win.openedFolderUri, backupPath: win.backupPath, + remoteAuthority: win.remoteAuthority, uiState: win.serializeWindowState() }; } @@ -381,7 +391,7 @@ export class WindowsManager implements IWindowsMainService { for (const path of pathsToOpen) { if (path.fileUri) { if (!fileInputs) { - fileInputs = { filesToCreate: [], filesToOpen: [], filesToDiff: [] }; + fileInputs = { filesToCreate: [], filesToOpen: [], filesToDiff: [], remoteAuthority: path.remoteAuthority }; } if (!path.createFilePath) { fileInputs.filesToOpen.push(path); @@ -427,8 +437,8 @@ export class WindowsManager implements IWindowsMainService { workspacesToRestore.push(...this.workspacesMainService.getUntitledWorkspacesSync()); // collect from previous window session emptyToRestore = this.backupMainService.getEmptyWindowBackupPaths(); - emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderUri && w.backupPath).map(w => ({ backupFolder: basename(w.backupPath) }))); // add empty windows with backupPath - emptyToRestore = arrays.distinct(emptyToRestore); // prevent duplicates + emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderUri && w.backupPath).map(w => ({ backupFolder: basename(w.backupPath), remoteAuthority: w.remoteAuthority }))); // add empty windows with backupPath + emptyToRestore = arrays.distinct(emptyToRestore, info => info.backupFolder); // prevent duplicates } // @@ -537,7 +547,8 @@ export class WindowsManager implements IWindowsMainService { // Handle folders to add by looking for the last active workspace (not on initial startup) if (!openConfig.initialStartup && foldersToAdd.length > 0) { - const lastActiveWindow = this.getLastActiveWindow(); + const authority = getRemoteAuthority(foldersToAdd[0]); + const lastActiveWindow = this.getLastActiveWindowForAuthority(authority); if (lastActiveWindow) { usedWindows.push(this.doAddFoldersToExistingWindow(lastActiveWindow, foldersToAdd)); } @@ -552,7 +563,8 @@ export class WindowsManager implements IWindowsMainService { // Find suitable window or folder path to open files in const fileToCheck = fileInputs.filesToOpen[0] || fileInputs.filesToCreate[0] || fileInputs.filesToDiff[0]; - const windows = WindowsManager.WINDOWS; + // only look at the windows with correct authority + const windows = WindowsManager.WINDOWS.filter(w => w.remoteAuthority === fileInputs.remoteAuthority); let bestWindowOrFolder = findBestWindowOrFolderForFile({ windows, @@ -595,6 +607,7 @@ export class WindowsManager implements IWindowsMainService { initialStartup: openConfig.initialStartup, fileInputs, forceNewWindow: true, + remoteAuthority: fileInputs.remoteAuthority, forceNewTabbedWindow: openConfig.forceNewTabbedWindow })); @@ -611,7 +624,7 @@ export class WindowsManager implements IWindowsMainService { const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, workspaceToOpen))); if (windowsOnWorkspace.length > 0) { const windowOnWorkspace = windowsOnWorkspace[0]; - const fileInputsForWindow = fileInputs; + const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : void 0; // Do open files usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, fileInputsForWindow)); @@ -630,7 +643,7 @@ export class WindowsManager implements IWindowsMainService { return; // ignore folders that are already open } - const fileInputsForWindow = fileInputs; + const fileInputsForWindow = (fileInputs && !fileInputs.remoteAuthority) ? fileInputs : void 0; // Do open folder usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { workspace: workspaceToOpen }, openFolderInNewWindow, fileInputsForWindow)); @@ -653,7 +666,7 @@ export class WindowsManager implements IWindowsMainService { const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, folderToOpen))); if (windowsOnFolderPath.length > 0) { const windowOnFolderPath = windowsOnFolderPath[0]; - const fileInputsForWindow = fileInputs; + const fileInputsForWindow = fileInputs && fileInputs.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : void 0; // Do open files usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, fileInputsForWindow)); @@ -673,10 +686,11 @@ export class WindowsManager implements IWindowsMainService { return; // ignore folders that are already open } - const fileInputsForWindow = fileInputs; + const remoteAuthority = getRemoteAuthority(folderToOpen); + const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : void 0; // Do open folder - usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderUri: folderToOpen }, openFolderInNewWindow, fileInputsForWindow)); + usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderUri: folderToOpen, remoteAuthority }, openFolderInNewWindow, fileInputsForWindow)); // Reset these because we handled them if (fileInputsForWindow) { @@ -690,13 +704,15 @@ export class WindowsManager implements IWindowsMainService { // Handle empty to restore if (emptyToRestore.length > 0) { emptyToRestore.forEach(emptyWindowBackupInfo => { - const fileInputsForWindow = fileInputs; + const remoteAuthority = emptyWindowBackupInfo.remoteAuthority; + const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : void 0; usedWindows.push(this.openInBrowserWindow({ userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, fileInputs: fileInputsForWindow, + remoteAuthority, forceNewWindow: true, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, emptyWindowBackupInfo @@ -716,11 +732,13 @@ export class WindowsManager implements IWindowsMainService { if (fileInputs && !emptyToOpen) { emptyToOpen++; } + const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || void 0); for (let i = 0; i < emptyToOpen; i++) { usedWindows.push(this.openInBrowserWindow({ userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, + remoteAuthority, forceNewWindow: openFolderInNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, fileInputs @@ -777,6 +795,7 @@ export class WindowsManager implements IWindowsMainService { workspace: folderOrWorkspace.workspace, folderUri: folderOrWorkspace.folderUri, fileInputs, + remoteAuthority: folderOrWorkspace.remoteAuthority, forceNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, windowToUse @@ -821,7 +840,7 @@ export class WindowsManager implements IWindowsMainService { const workspace = this.workspacesMainService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri }))); // Add workspace and remove folders thereby - windowsToOpen.push({ workspace }); + windowsToOpen.push({ workspace, remoteAuthority: foldersToOpen[0].remoteAuthority }); windowsToOpen = windowsToOpen.filter(path => !path.folderUri); } } @@ -830,7 +849,7 @@ export class WindowsManager implements IWindowsMainService { } private doExtractPathsFromAPI(openConfig: IOpenConfiguration): IPathToOpen[] { - const pathsToOpen = []; + const pathsToOpen: IPathToOpen[] = []; const cli = openConfig.cli; let parseOptions: IPathParseOptions = { gotoLineMode: cli && cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile }; for (const pathToOpen of openConfig.urisToOpen) { @@ -868,8 +887,8 @@ export class WindowsManager implements IWindowsMainService { } private doExtractPathsFromCLI(cli: ParsedArgs): IPath[] { - const pathsToOpen = []; - const parseOptions: IPathParseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto }; + const pathsToOpen: IPathToOpen[] = []; + const parseOptions: IPathParseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto, remoteAuthority: cli.remote || void 0 }; // folder uris const folderUris = asArray(cli['folder-uri']); @@ -932,17 +951,17 @@ export class WindowsManager implements IWindowsMainService { const windowsToOpen: IPathToOpen[] = []; for (const openedWindow of openedWindows) { if (openedWindow.workspace) { // Workspaces - const pathToOpen = this.parsePath(openedWindow.workspace.configPath); + const pathToOpen = this.parsePath(openedWindow.workspace.configPath, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen && pathToOpen.workspace) { windowsToOpen.push(pathToOpen); } } else if (openedWindow.folderUri) { // Folders - const pathToOpen = this.parseUri(openedWindow.folderUri, false); + const pathToOpen = this.parseUri(openedWindow.folderUri, false, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen && pathToOpen.folderUri) { windowsToOpen.push(pathToOpen); } } else if (restoreWindows !== 'folders' && openedWindow.backupPath) { // Windows that were Empty - windowsToOpen.push({ backupPath: openedWindow.backupPath }); + windowsToOpen.push({ backupPath: openedWindow.backupPath, remoteAuthority: openedWindow.remoteAuthority }); } } @@ -994,6 +1013,10 @@ export class WindowsManager implements IWindowsMainService { if (uri.scheme === Schemas.file) { return this.parsePath(uri.fsPath, options); } + + // open remote if either specified in the cli or if it's a remotehost URI + const remoteAuthority = options && options.remoteAuthority || getRemoteAuthority(uri); + // normalize URI uri = normalizePath(uri); if (endsWith(uri.path, '/')) { @@ -1005,15 +1028,18 @@ export class WindowsManager implements IWindowsMainService { return { fileUri: uri.with({ path: parsedPath.path }), lineNumber: parsedPath.line, - columnNumber: parsedPath.column + columnNumber: parsedPath.column, + remoteAuthority }; } return { - fileUri: uri + fileUri: uri, + remoteAuthority }; } return { - folderUri: uri + folderUri: uri, + remoteAuthority }; } @@ -1030,6 +1056,9 @@ export class WindowsManager implements IWindowsMainService { anyPath = parsedPath.path; } + // open remote if either specified in the cli even if it is a local file. TODO: Future idea: resolve in remote host context. + const remoteAuthority = options && options.remoteAuthority; + const candidate = normalize(anyPath); try { const candidateStat = fs.statSync(candidate); @@ -1040,7 +1069,7 @@ export class WindowsManager implements IWindowsMainService { if (!options || !options.forceOpenWorkspaceAsFile) { const workspace = this.workspacesMainService.resolveWorkspaceSync(candidate); if (workspace) { - return { workspace: { id: workspace.id, configPath: workspace.configPath } }; + return { workspace: { id: workspace.id, configPath: workspace.configPath }, remoteAuthority }; } } @@ -1048,7 +1077,8 @@ export class WindowsManager implements IWindowsMainService { return { fileUri: URI.file(candidate), lineNumber: gotoLineMode ? parsedPath.line : void 0, - columnNumber: gotoLineMode ? parsedPath.column : void 0 + columnNumber: gotoLineMode ? parsedPath.column : void 0, + remoteAuthority }; } @@ -1057,7 +1087,8 @@ export class WindowsManager implements IWindowsMainService { // over to us) else if (candidateStat.isDirectory()) { return { - folderUri: URI.file(candidate) + folderUri: URI.file(candidate), + remoteAuthority }; } } @@ -1066,7 +1097,7 @@ export class WindowsManager implements IWindowsMainService { this.historyMainService.removeFromRecentlyOpened([fileUri]); // since file does not seem to exist anymore, remove from recent if (options && options.ignoreFileNotFound) { - return { fileUri, createFilePath: true }; // assume this is a file that does not yet exist + return { fileUri, createFilePath: true, remoteAuthority }; // assume this is a file that does not yet exist } } @@ -1181,6 +1212,7 @@ export class WindowsManager implements IWindowsMainService { configuration.isInitialStartup = options.initialStartup; configuration.workspace = options.workspace; configuration.folderUri = options.folderUri; + configuration.remoteAuthority = options.remoteAuthority; const fileInputs = options.fileInputs; if (fileInputs) { @@ -1286,7 +1318,7 @@ export class WindowsManager implements IWindowsMainService { configuration.backupPath = this.backupMainService.registerFolderBackupSync(configuration.folderUri); } else { const backupFolder = options.emptyWindowBackupInfo && options.emptyWindowBackupInfo.backupFolder; - configuration.backupPath = this.backupMainService.registerEmptyWindowBackupSync({ backupFolder }); + configuration.backupPath = this.backupMainService.registerEmptyWindowBackupSync({ backupFolder, remoteAuthority: configuration.remoteAuthority }); } } @@ -1444,7 +1476,8 @@ export class WindowsManager implements IWindowsMainService { closeWorkspace(win: ICodeWindow): void { this.openInBrowserWindow({ cli: this.environmentService.args, - windowToUse: win + windowToUse: win, + remoteAuthority: win.remoteAuthority }); } @@ -1528,8 +1561,16 @@ export class WindowsManager implements IWindowsMainService { return getLastActiveWindow(WindowsManager.WINDOWS); } + getLastActiveWindowForAuthority(remoteAuthority: string): ICodeWindow { + return getLastActiveWindow(WindowsManager.WINDOWS.filter(w => w.remoteAuthority === remoteAuthority)); + } + openNewWindow(context: OpenContext, options?: INewWindowOptions): ICodeWindow[] { let cli = this.environmentService.args; + let remote = options && options.remoteAuthority || void 0; + if (cli && (cli.remote !== remote)) { + cli = { ...cli, remote }; + } return this.open({ context, cli, forceNewWindow: true, forceEmpty: true }); } diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 2b1c0bcda21..5b6bc130ee1 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -20,6 +20,7 @@ import * as iconv from 'iconv-lite'; import { writeFileAndFlushSync } from 'vs/base/node/extfs'; import { isWindows } from 'vs/base/common/platform'; import { ProfilingSession } from 'v8-inspect-profiler'; +import { createWaitMarkerFile } from 'vs/code/node/wait'; function shouldSpawnCliProcess(argv: ParsedArgs): boolean { return !!argv['install-source'] @@ -228,22 +229,9 @@ export async function main(argv: string[]): Promise { // is closed and then exit the waiting process. let waitMarkerFilePath: string; if (args.wait) { - let waitMarkerError: Error; - const randomTmpFile = paths.join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); - try { - fs.writeFileSync(randomTmpFile, ''); - waitMarkerFilePath = randomTmpFile; + waitMarkerFilePath = await createWaitMarkerFile(verbose); + if (waitMarkerFilePath) { argv.push('--waitMarkerFilePath', waitMarkerFilePath); - } catch (error) { - waitMarkerError = error; - } - - if (verbose) { - if (waitMarkerError) { - console.error(`Failed to create marker file for --wait: ${waitMarkerError.toString()}`); - } else { - console.log(`Marker file for --wait created: ${waitMarkerFilePath}`); - } } } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 084ebab7f97..36fee9df212 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -259,7 +259,7 @@ export function main(argv: ParsedArgs): TPromise { piiPaths: [appRoot, extensionsPath] }; - services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); } else { services.set(ITelemetryService, NullTelemetryService); } diff --git a/src/vs/code/node/paths.ts b/src/vs/code/node/paths.ts index 5774c6b6265..f322145cbac 100644 --- a/src/vs/code/node/paths.ts +++ b/src/vs/code/node/paths.ts @@ -35,7 +35,7 @@ function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { const result = args.map(arg => { let pathCandidate = String(arg); - let parsedPath: IPathWithLineAndColumn; + let parsedPath: IPathWithLineAndColumn | undefined = undefined; if (gotoLineMode) { parsedPath = parseLineAndColumnAware(pathCandidate); pathCandidate = parsedPath.path; @@ -52,7 +52,7 @@ function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { return null; // do not allow invalid file names } - if (gotoLineMode) { + if (gotoLineMode && parsedPath) { parsedPath.path = sanitizedFilePath; return toPath(parsedPath); @@ -62,7 +62,7 @@ function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { }); const caseInsensitive = platform.isWindows || platform.isMacintosh; - const distinct = arrays.distinct(result, e => e && caseInsensitive ? e.toLowerCase() : e); + const distinct = arrays.distinct(result, e => e && caseInsensitive ? e.toLowerCase() : (e || '')); return arrays.coalesce(distinct); } @@ -98,7 +98,7 @@ export interface IPathWithLineAndColumn { export function parseLineAndColumnAware(rawPath: string): IPathWithLineAndColumn { const segments = rawPath.split(':'); // C:\file.txt:: - let path: string; + let path: string | null = null; let line: number | null = null; let column: number | null = null; diff --git a/src/vs/code/node/wait.ts b/src/vs/code/node/wait.ts new file mode 100644 index 00000000000..06705d72b79 --- /dev/null +++ b/src/vs/code/node/wait.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { join } from 'path'; +import { tmpdir } from 'os'; +import { writeFile } from 'vs/base/node/pfs'; + +export function createWaitMarkerFile(verbose?: boolean): Promise { + const randomWaitMarkerPath = join(tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); + + return writeFile(randomWaitMarkerPath, '').then(() => { + if (verbose) { + console.log(`Marker file for --wait created: ${randomWaitMarkerPath}`); + } + + return randomWaitMarkerPath; + }, error => { + if (verbose) { + console.error(`Failed to create marker file for --wait: ${error}`); + } + + return Promise.resolve(void 0); + }); +} \ No newline at end of file diff --git a/src/vs/code/node/windowsFinder.ts b/src/vs/code/node/windowsFinder.ts index 02ee9c40159..8541b69dbe5 100644 --- a/src/vs/code/node/windowsFinder.ts +++ b/src/vs/code/node/windowsFinder.ts @@ -29,7 +29,7 @@ export interface IBestWindowOrFolderOptions { workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace; } -export function findBestWindowOrFolderForFile({ windows, newWindow, reuseWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions): W { +export function findBestWindowOrFolderForFile({ windows, newWindow, reuseWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions): W | null { if (!newWindow && fileUri && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) { const windowOnFilePath = findWindowOnFilePath(windows, fileUri, workspaceResolver); if (windowOnFilePath) { @@ -39,13 +39,13 @@ export function findBestWindowOrFolderForFile({ windows return !newWindow ? getLastActiveWindow(windows) : null; } -function findWindowOnFilePath(windows: W[], fileUri: URI, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace): W { +function findWindowOnFilePath(windows: W[], fileUri: URI, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace): W | null { // First check for windows with workspaces that have a parent folder of the provided path opened const workspaceWindows = windows.filter(window => !!window.openedWorkspace); for (let i = 0; i < workspaceWindows.length; i++) { const window = workspaceWindows[i]; - const resolvedWorkspace = workspaceResolver(window.openedWorkspace); + const resolvedWorkspace = workspaceResolver(window.openedWorkspace!); if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => isEqualOrParent(fileUri, folder.uri))) { return window; } @@ -54,7 +54,7 @@ function findWindowOnFilePath(windows: W[], fileUri: UR // Then go with single folder windows that are parent of the provided file path const singleFolderWindowsOnFilePath = windows.filter(window => window.openedFolderUri && isEqualOrParent(fileUri, window.openedFolderUri)); if (singleFolderWindowsOnFilePath.length) { - return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderUri.path.length - b.openedFolderUri.path.length))[0]; + return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderUri!.path.length - b.openedFolderUri!.path.length))[0]; } return null; @@ -66,7 +66,7 @@ export function getLastActiveWindow(windows: W[]): W { return windows.filter(window => window.lastFocusTime === lastFocusedDate)[0]; } -export function findWindowOnWorkspace(windows: W[], workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)): W { +export function findWindowOnWorkspace(windows: W[], workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)): W | null { if (isSingleFolderWorkspaceIdentifier(workspace)) { for (const window of windows) { // match on folder @@ -87,17 +87,17 @@ export function findWindowOnWorkspace(windows: W[], wor return null; } -export function findWindowOnExtensionDevelopmentPath(windows: W[], extensionDevelopmentPath: string): W { +export function findWindowOnExtensionDevelopmentPath(windows: W[], extensionDevelopmentPath: string): W | null { for (const window of windows) { // match on extension development path. The path can be a path or uri string, using paths.isEqual is not 100% correct but good enough - if (paths.isEqual(window.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) { + if (window.extensionDevelopmentPath && paths.isEqual(window.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) { return window; } } return null; } -export function findWindowOnWorkspaceOrFolderUri(windows: W[], uri: URI): W { +export function findWindowOnWorkspaceOrFolderUri(windows: W[], uri: URI): W | null { if (!uri) { return null; } diff --git a/src/vs/css.build.js b/src/vs/css.build.js index 4a617c57ed8..146ebe33277 100644 --- a/src/vs/css.build.js +++ b/src/vs/css.build.js @@ -319,7 +319,7 @@ var CSSBuildLoaderPlugin; global.cssInlinedResources = global.cssInlinedResources || []; var normalizedFSPath = fsPath.replace(/\\/g, '/'); if (global.cssInlinedResources.indexOf(normalizedFSPath) >= 0) { - console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); + // console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); } global.cssInlinedResources.push(normalizedFSPath); var MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index 5afe8c44ee9..8923599c822 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import * as browser from 'vs/base/browser/browser'; +import { FastDomNode } from 'vs/base/browser/fastDomNode'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; -import * as browser from 'vs/base/browser/browser'; -import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; -import { IDimension } from 'vs/editor/common/editorCommon'; -import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo'; -import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; -import { FastDomNode } from 'vs/base/browser/fastDomNode'; import { CharWidthRequest, CharWidthRequestType, readCharWidths } from 'vs/editor/browser/config/charWidthReader'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; +import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; +import { IDimension } from 'vs/editor/common/editorCommon'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; class CSSBasedConfigurationCache { diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index 344e17c744c..8e12e1c6ece 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -4,28 +4,27 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import * as types from 'vs/base/common/types'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Command, EditorCommand, ICommandOptions, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { ColumnSelection, IColumnSelectResult } from 'vs/editor/common/controller/cursorColumnSelection'; +import { CursorContext, CursorState, EditOperationType, IColumnSelectData, ICursors, PartialCursorState, RevealTarget } from 'vs/editor/common/controller/cursorCommon'; +import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { CursorMove as CursorMove_, CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands'; +import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { CursorState, ICursors, RevealTarget, IColumnSelectData, CursorContext, EditOperationType, PartialCursorState } from 'vs/editor/common/controller/cursorCommon'; -import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; -import { CursorMoveCommands, CursorMove as CursorMove_ } from 'vs/editor/common/controller/cursorMoveCommands'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { registerEditorCommand, ICommandOptions, EditorCommand, Command } from 'vs/editor/browser/editorExtensions'; -import { IColumnSelectResult, ColumnSelection } from 'vs/editor/common/controller/cursorColumnSelection'; +import { Handler, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import H = editorCommon.Handler; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import * as types from 'vs/base/common/types'; -import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; -import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; -import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { MenuId } from 'vs/platform/actions/common/actions'; +import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const CORE_WEIGHT = KeybindingWeight.EditorCore; @@ -261,7 +260,7 @@ export namespace CoreNavigationCommands { CursorMoveCommands.moveTo(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position, args.viewPosition) ] ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } } @@ -286,7 +285,7 @@ export namespace CoreNavigationCommands { toViewLineNumber: result.toLineNumber, toViewVisualColumn: result.toVisualColumn }); - cursors.reveal(true, (result.reversed ? RevealTarget.TopMost : RevealTarget.BottomMost), editorCommon.ScrollType.Smooth); + cursors.reveal(true, (result.reversed ? RevealTarget.TopMost : RevealTarget.BottomMost), ScrollType.Smooth); } protected abstract _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult; @@ -456,7 +455,7 @@ export namespace CoreNavigationCommands { CursorChangeReason.Explicit, CursorMoveCommands.move(cursors.context, cursors.getAll(), args) ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } } @@ -795,7 +794,7 @@ export namespace CoreNavigationCommands { CursorChangeReason.Explicit, CursorMoveCommands.moveToBeginningOfLine(cursors.context, cursors.getAll(), this._inSelectionMode) ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } } @@ -844,7 +843,7 @@ export namespace CoreNavigationCommands { CursorChangeReason.Explicit, this._exec(cursors.context, cursors.getAll()) ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } private _exec(context: CursorContext, cursors: CursorState[]): PartialCursorState[] { @@ -874,7 +873,7 @@ export namespace CoreNavigationCommands { CursorChangeReason.Explicit, CursorMoveCommands.moveToEndOfLine(cursors.context, cursors.getAll(), this._inSelectionMode) ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } } @@ -923,7 +922,7 @@ export namespace CoreNavigationCommands { CursorChangeReason.Explicit, this._exec(cursors.context, cursors.getAll()) ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } private _exec(context: CursorContext, cursors: CursorState[]): PartialCursorState[] { @@ -954,7 +953,7 @@ export namespace CoreNavigationCommands { CursorChangeReason.Explicit, CursorMoveCommands.moveToBeginningOfBuffer(cursors.context, cursors.getAll(), this._inSelectionMode) ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } } @@ -998,7 +997,7 @@ export namespace CoreNavigationCommands { CursorChangeReason.Explicit, CursorMoveCommands.moveToEndOfBuffer(cursors.context, cursors.getAll(), this._inSelectionMode) ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } } @@ -1217,7 +1216,7 @@ export namespace CoreNavigationCommands { CursorMoveCommands.word(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position) ] ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } } @@ -1277,7 +1276,7 @@ export namespace CoreNavigationCommands { CursorMoveCommands.line(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position, args.viewPosition) ] ); - cursors.reveal(false, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(false, RevealTarget.Primary, ScrollType.Smooth); } } @@ -1349,7 +1348,7 @@ export namespace CoreNavigationCommands { CursorChangeReason.Explicit, CursorMoveCommands.expandLineSelection(cursors.context, cursors.getAll()) ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } }); @@ -1377,7 +1376,7 @@ export namespace CoreNavigationCommands { CursorMoveCommands.cancelSelection(cursors.context, cursors.getPrimaryCursor()) ] ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } }); @@ -1404,7 +1403,7 @@ export namespace CoreNavigationCommands { cursors.getPrimaryCursor() ] ); - cursors.reveal(true, RevealTarget.Primary, editorCommon.ScrollType.Smooth); + cursors.reveal(true, RevealTarget.Primary, ScrollType.Smooth); } }); @@ -1452,7 +1451,7 @@ export namespace CoreNavigationCommands { const viewRange = cursors.context.convertModelRangeToViewRange(range); - cursors.revealRange(false, viewRange, revealAt, editorCommon.ScrollType.Smooth); + cursors.revealRange(false, viewRange, revealAt, ScrollType.Smooth); } }); @@ -1729,9 +1728,9 @@ registerCommand(new EditorOrNativeTextInputCommand({ })); registerCommand(new EditorOrNativeTextInputCommand({ - editorHandler: H.Undo, + editorHandler: Handler.Undo, inputHandler: 'undo', - id: H.Undo, + id: Handler.Undo, precondition: EditorContextKeys.writable, kbOpts: { weight: CORE_WEIGHT, @@ -1745,12 +1744,12 @@ registerCommand(new EditorOrNativeTextInputCommand({ order: 1 } })); -registerCommand(new EditorHandlerCommand('default:' + H.Undo, H.Undo)); +registerCommand(new EditorHandlerCommand('default:' + Handler.Undo, Handler.Undo)); registerCommand(new EditorOrNativeTextInputCommand({ - editorHandler: H.Redo, + editorHandler: Handler.Redo, inputHandler: 'redo', - id: H.Redo, + id: Handler.Redo, precondition: EditorContextKeys.writable, kbOpts: { weight: CORE_WEIGHT, @@ -1766,16 +1765,16 @@ registerCommand(new EditorOrNativeTextInputCommand({ order: 2 } })); -registerCommand(new EditorHandlerCommand('default:' + H.Redo, H.Redo)); +registerCommand(new EditorHandlerCommand('default:' + Handler.Redo, Handler.Redo)); function registerOverwritableCommand(handlerId: string): void { registerCommand(new EditorHandlerCommand('default:' + handlerId, handlerId)); registerCommand(new EditorHandlerCommand(handlerId, handlerId)); } -registerOverwritableCommand(H.Type); -registerOverwritableCommand(H.ReplacePreviousChar); -registerOverwritableCommand(H.CompositionStart); -registerOverwritableCommand(H.CompositionEnd); -registerOverwritableCommand(H.Paste); -registerOverwritableCommand(H.Cut); +registerOverwritableCommand(Handler.Type); +registerOverwritableCommand(Handler.ReplacePreviousChar); +registerOverwritableCommand(Handler.CompositionStart); +registerOverwritableCommand(Handler.CompositionEnd); +registerOverwritableCommand(Handler.Paste); +registerOverwritableCommand(Handler.Cut); diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index b80c7d87eee..d21577afb8a 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -3,24 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; -import * as platform from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; +import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { RunOnceScheduler, TimeoutTimer } from 'vs/base/common/async'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import { HitTestContext, IViewZoneData, MouseTarget, MouseTargetFactory } from 'vs/editor/browser/controller/mouseTarget'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { ClientCoordinates, EditorMouseEvent, EditorMouseEventFactory, GlobalEditorMouseMoveMonitor, createEditorPagePosition } from 'vs/editor/browser/editorDom'; +import { ViewController } from 'vs/editor/browser/view/viewController'; +import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; +import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; -import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; -import { MouseTarget, MouseTargetFactory, IViewZoneData, HitTestContext } from 'vs/editor/browser/controller/mouseTarget'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import { TimeoutTimer, RunOnceScheduler } from 'vs/base/common/async'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; -import { EditorMouseEventFactory, GlobalEditorMouseMoveMonitor, EditorMouseEvent, createEditorPagePosition, ClientCoordinates } from 'vs/editor/browser/editorDom'; -import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; -import { EditorZoom } from 'vs/editor/common/config/editorZoom'; -import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { ViewController } from 'vs/editor/browser/view/viewController'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; /** * Merges mouse events when mouse move events are throttled diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 2437b01cc23..5129952e837 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -3,19 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as browser from 'vs/base/browser/browser'; +import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; +import { IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { ClientCoordinates, EditorMouseEvent, EditorPagePosition, PageCoordinates } from 'vs/editor/browser/editorDom'; +import { PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; +import { ViewLine } from 'vs/editor/browser/viewParts/lines/viewLine'; +import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; +import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range as EditorRange } from 'vs/editor/common/core/range'; -import { MouseTargetType, IMouseTarget } from 'vs/editor/browser/editorBrowser'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; -import { EditorMouseEvent, PageCoordinates, ClientCoordinates, EditorPagePosition } from 'vs/editor/browser/editorDom'; -import * as browser from 'vs/base/browser/browser'; -import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; -import { PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; -import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; -import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; -import { ViewLine } from 'vs/editor/browser/viewParts/lines/viewLine'; import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; export interface IViewZoneData { viewZoneId: number; diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index f58bcc0adc2..c97ebab90ad 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; -import { MouseHandler, IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IPointerHandlerHelper, MouseHandler } from 'vs/editor/browser/controller/mouseHandler'; import { IMouseTarget } from 'vs/editor/browser/editorBrowser'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; import { EditorMouseEvent } from 'vs/editor/browser/editorDom'; import { ViewController } from 'vs/editor/browser/view/viewController'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; interface IThrottledGestureEvent { translationX: number; diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 1dc23da1542..df30bd1a03f 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -4,29 +4,29 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./textAreaHandler'; -import * as platform from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; -import { TextAreaInput, ITextAreaInputHost, IPasteData, ICompositionData, CopyOptions } from 'vs/editor/browser/controller/textAreaInput'; -import { ISimpleModel, ITypeData, TextAreaState, PagedScreenReaderStrategy } from 'vs/editor/browser/controller/textAreaState'; +import { Configuration } from 'vs/editor/browser/config/configuration'; +import { CopyOptions, ICompositionData, IPasteData, ITextAreaInputHost, TextAreaInput } from 'vs/editor/browser/controller/textAreaInput'; +import { ISimpleModel, ITypeData, PagedScreenReaderStrategy, TextAreaState } from 'vs/editor/browser/controller/textAreaState'; +import { ViewController } from 'vs/editor/browser/view/viewController'; +import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; +import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers'; +import { Margin } from 'vs/editor/browser/viewParts/margin/margin'; +import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; +import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { Position } from 'vs/editor/common/core/position'; -import { Configuration } from 'vs/editor/browser/config/configuration'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { HorizontalRange, RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { ViewController } from 'vs/editor/browser/view/viewController'; import { ScrollType } from 'vs/editor/common/editorCommon'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { PartFingerprints, PartFingerprint, ViewPart } from 'vs/editor/browser/view/viewPart'; -import { Margin } from 'vs/editor/browser/viewParts/margin/margin'; -import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers'; -import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; -import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; import { EndOfLinePreference } from 'vs/editor/common/model'; -import { getMapForWordSeparators, WordCharacterClass } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { HorizontalRange, RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; export interface ITextAreaHandlerHelper { visibleRangeForPositionRelativeToEditor(lineNumber: number, column: number): HorizontalRange | null; diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index e0b6bd1417c..7ccf0e99d29 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -3,19 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as browser from 'vs/base/browser/browser'; +import * as dom from 'vs/base/browser/dom'; +import { FastDomNode } from 'vs/base/browser/fastDomNode'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { Position } from 'vs/editor/common/core/position'; -import { Selection } from 'vs/editor/common/core/selection'; -import * as strings from 'vs/base/common/strings'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ITypeData, TextAreaState, ITextAreaWrapper } from 'vs/editor/browser/controller/textAreaState'; -import * as browser from 'vs/base/browser/browser'; import * as platform from 'vs/base/common/platform'; -import * as dom from 'vs/base/browser/dom'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { FastDomNode } from 'vs/base/browser/fastDomNode'; +import * as strings from 'vs/base/common/strings'; +import { ITextAreaWrapper, ITypeData, TextAreaState } from 'vs/editor/browser/controller/textAreaState'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; export interface ICompositionData { data: string; diff --git a/src/vs/editor/browser/controller/textAreaState.ts b/src/vs/editor/browser/controller/textAreaState.ts index 4f1dc67db86..697ce880544 100644 --- a/src/vs/editor/browser/controller/textAreaState.ts +++ b/src/vs/editor/browser/controller/textAreaState.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; -import { EndOfLinePreference } from 'vs/editor/common/model'; import * as strings from 'vs/base/common/strings'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { EndOfLinePreference } from 'vs/editor/common/model'; export interface ITextAreaWrapper { getValue(): string; diff --git a/src/vs/editor/browser/core/editorState.ts b/src/vs/editor/browser/core/editorState.ts index eede8d1a3d6..f1351bec8db 100644 --- a/src/vs/editor/browser/core/editorState.ts +++ b/src/vs/editor/browser/core/editorState.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from 'vs/base/common/strings'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; export const enum CodeEditorStateFlag { Value = 1, diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 2d919f84ff8..991a44798ba 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable } from 'vs/base/common/lifecycle'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { Selection } from 'vs/editor/common/core/selection'; -import { Range, IRange } from 'vs/editor/common/core/range'; +import { IDisposable } from 'vs/base/common/lifecycle'; import * as editorOptions from 'vs/editor/common/config/editorOptions'; -import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; -import { IModelContentChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; -import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ICursors } from 'vs/editor/common/controller/cursorCommon'; +import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; -import { ITextModel, IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration } from 'vs/editor/common/model'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; /** * A view zone is a full horizontal rectangle that 'pushes' text down. @@ -99,7 +99,7 @@ export interface IViewZoneChangeAccessor { /** * A positioning preference for rendering content widgets. */ -export enum ContentWidgetPositionPreference { +export const enum ContentWidgetPositionPreference { /** * Place the content widget exactly at a position */ @@ -160,7 +160,7 @@ export interface IContentWidget { /** * A positioning preference for rendering overlay widgets. */ -export enum OverlayWidgetPositionPreference { +export const enum OverlayWidgetPositionPreference { /** * Position the overlay widget in the top right corner */ @@ -207,7 +207,7 @@ export interface IOverlayWidget { /** * Type of hit element with the mouse in the editor. */ -export enum MouseTargetType { +export const enum MouseTargetType { /** * Mouse is on top of an unknown element. */ @@ -609,7 +609,7 @@ export interface ICodeEditor extends editorCommon.IEditor { executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean; /** - * Execute multiple (concommitent) commands on the editor. + * Execute multiple (concomitant) commands on the editor. * @param source The source of the call. * @param command The commands to execute */ @@ -695,7 +695,7 @@ export interface ICodeEditor extends editorCommon.IEditor { addContentWidget(widget: IContentWidget): void; /** * Layout/Reposition a content widget. This is a ping to the editor to call widget.getPosition() - * and update appropiately. + * and update appropriately. */ layoutContentWidget(widget: IContentWidget): void; /** @@ -709,7 +709,7 @@ export interface ICodeEditor extends editorCommon.IEditor { addOverlayWidget(widget: IOverlayWidget): void; /** * Layout/Reposition an overlay widget. This is a ping to the editor to call widget.getPosition() - * and update appropiately. + * and update appropriately. */ layoutOverlayWidget(widget: IOverlayWidget): void; /** @@ -747,7 +747,7 @@ export interface ICodeEditor extends editorCommon.IEditor { * The result position takes scrolling into account and is relative to the top left corner of the editor. * Explanation 1: the results of this method will change for the same `position` if the user scrolls the editor. * Explanation 2: the results of this method will not change if the container of the editor gets repositioned. - * Warning: the results of this method are innacurate for positions that are outside the current editor viewport. + * Warning: the results of this method are inaccurate for positions that are outside the current editor viewport. */ getScrolledVisiblePosition(position: IPosition): { top: number; left: number; height: number; } | null; @@ -812,7 +812,7 @@ export interface IActiveCodeEditor extends ICodeEditor { * The result position takes scrolling into account and is relative to the top left corner of the editor. * Explanation 1: the results of this method will change for the same `position` if the user scrolls the editor. * Explanation 2: the results of this method will not change if the container of the editor gets repositioned. - * Warning: the results of this method are innacurate for positions that are outside the current editor viewport. + * Warning: the results of this method are inaccurate for positions that are outside the current editor viewport. */ getScrolledVisiblePosition(position: IPosition): { top: number; left: number; height: number; }; } diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 705fb558f5e..4168c714fbb 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import * as dom from 'vs/base/browser/dom'; import { GlobalMouseMoveMonitor } from 'vs/base/browser/globalMouseMoveMonitor'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; /** * Coordinates relative to the whole document (e.g. mouse event's pageX and pageY) diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 727365c539b..1eafc931b06 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -3,25 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IPosition } from 'vs/base/browser/ui/contextview/contextview'; +import { always } from 'vs/base/common/async'; import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { ServicesAccessor, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { Position } from 'vs/editor/common/core/position'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ITextModel } from 'vs/editor/common/model'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; -import { KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IConstructorSignature1, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { Position } from 'vs/editor/common/core/position'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; -import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { ITextModel } from 'vs/editor/common/model'; -import { IPosition } from 'vs/base/browser/ui/contextview/contextview'; export type ServicesAccessor = ServicesAccessor; -export type IEditorContributionCtor = IConstructorSignature1; +export type IEditorContributionCtor = IConstructorSignature1; //#region Command @@ -116,7 +118,7 @@ export abstract class Command { export interface IContributionCommandOptions extends ICommandOptions { handler: (controller: T) => void; } -export interface EditorControllerCommand { +export interface EditorControllerCommand { new(opts: IContributionCommandOptions): EditorCommand; } export abstract class EditorCommand extends Command { @@ -124,7 +126,7 @@ export abstract class EditorCommand extends Command { /** * Create a command class that is bound to a certain editor contribution. */ - public static bindToContribution(controllerGetter: (editor: ICodeEditor) => T): EditorControllerCommand { + public static bindToContribution(controllerGetter: (editor: ICodeEditor) => T): EditorControllerCommand { return class EditorControllerCommandImpl extends EditorCommand { private _callback: (controller: T) => void; @@ -258,13 +260,23 @@ export function registerDefaultLanguageCommand(id: string, handler: (model: ITex } const model = accessor.get(IModelService).getModel(resource); - if (!model) { - throw illegalArgument('Can not find open model for ' + resource); + if (model) { + const editorPosition = Position.lift(position); + return handler(model, editorPosition, args); } - const editorPosition = Position.lift(position); - - return handler(model, editorPosition, args); + return accessor.get(ITextModelService).createModelReference(resource).then(reference => { + return always(new Promise((resolve, reject) => { + try { + let result = handler(reference.object.textEditorModel, Position.lift(position), args); + resolve(result); + } catch (err) { + reject(err); + } + }), () => { + reference.dispose(); + }); + }); }); } diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index 469c512781b..7c8eabb46dd 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IResourceInput } from 'vs/platform/editor/common/editor'; -import { Disposable } from 'vs/base/common/lifecycle'; export abstract class AbstractCodeEditorService extends Disposable implements ICodeEditorService { diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 9e1d2717dd2..33927be7765 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { ICodeEditor } from '../editorBrowser'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgressRunner } from 'vs/platform/progress/common/progress'; export const IBulkEditService = createDecorator('IWorkspaceEditService'); diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index ed649b3041e..914a77ae3b3 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model'; -import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const ICodeEditorService = createDecorator('codeEditorService'); diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 1b41b3e99e1..3e03681e22b 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as dom from 'vs/base/browser/dom'; +import { IDisposable, dispose as disposeAll } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import * as dom from 'vs/base/browser/dom'; -import { IDecorationRenderOptions, IThemeDecorationRenderOptions, IContentDecorationRenderOptions, isThemeColor } from 'vs/editor/common/editorCommon'; -import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; -import { AbstractCodeEditorService } from 'vs/editor/browser/services/abstractCodeEditorService'; -import { IDisposable, dispose as disposeAll } from 'vs/base/common/lifecycle'; -import { IThemeService, ITheme, ThemeColor } from 'vs/platform/theme/common/themeService'; -import { IResourceInput } from 'vs/platform/editor/common/editor'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { AbstractCodeEditorService } from 'vs/editor/browser/services/abstractCodeEditorService'; +import { IContentDecorationRenderOptions, IDecorationRenderOptions, IThemeDecorationRenderOptions, isThemeColor } from 'vs/editor/common/editorCommon'; +import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { ITheme, IThemeService, ThemeColor } from 'vs/platform/theme/common/themeService'; export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { @@ -401,11 +401,7 @@ class DecorationCSSRules { if (typeof opts !== 'undefined') { this.collectBorderSettingsCSSText(opts, cssTextArr); if (typeof opts.contentIconPath !== 'undefined') { - if (typeof opts.contentIconPath === 'string') { - cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, URI.file(opts.contentIconPath).toString().replace(/'/g, '%27'))); - } else { - cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, URI.revive(opts.contentIconPath).toString(true).replace(/'/g, '%27'))); - } + cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, URI.revive(opts.contentIconPath).toString(true).replace(/'/g, '%27'))); } if (typeof opts.contentText === 'string') { const truncated = opts.contentText.match(/^.*$/m)![0]; // only take first line @@ -432,11 +428,7 @@ class DecorationCSSRules { let cssTextArr: string[] = []; if (typeof opts.gutterIconPath !== 'undefined') { - if (typeof opts.gutterIconPath === 'string') { - cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.file(opts.gutterIconPath).toString())); - } else { - cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.revive(opts.gutterIconPath).toString(true).replace(/'/g, '%27'))); - } + cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.revive(opts.gutterIconPath).toString(true).replace(/'/g, '%27'))); if (typeof opts.gutterIconSize !== 'undefined') { cssTextArr.push(strings.format(_CSS_MAP.gutterIconSize, opts.gutterIconSize)); } diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 89cab06049f..4d73c2bfc4f 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; import * as dom from 'vs/base/browser/dom'; -import * as resources from 'vs/base/common/resources'; import { parse } from 'vs/base/common/marshalling'; import { Schemas } from 'vs/base/common/network'; +import * as resources from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; +import { optional } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { optional } from 'vs/platform/instantiation/common/instantiation'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; export class OpenerService implements IOpenerService { diff --git a/src/vs/editor/browser/view/dynamicViewOverlay.ts b/src/vs/editor/browser/view/dynamicViewOverlay.ts index b9a15d156f1..0fa38d1bc80 100644 --- a/src/vs/editor/browser/view/dynamicViewOverlay.ts +++ b/src/vs/editor/browser/view/dynamicViewOverlay.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; export abstract class DynamicViewOverlay extends ViewEventHandler { diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index d8b16bb91ac..0279da56704 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { CoreEditorCommand, CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; +import { IEditorMouseEvent, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; +import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; -import { IEditorMouseEvent, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; -import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; -import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; -import { CoreNavigationCommands, CoreEditorCommand } from 'vs/editor/browser/controller/coreCommands'; import { IConfiguration } from 'vs/editor/common/editorCommon'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; export interface IMouseDispatchData { position: Position; diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 636f5aae47f..fc88b66125f 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -3,30 +3,32 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { Position } from 'vs/editor/common/core/position'; -import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; -import { IConfiguration } from 'vs/editor/common/editorCommon'; -import { TextAreaHandler, ITextAreaHandlerHelper } from 'vs/editor/browser/controller/textAreaHandler'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler'; +import { ITextAreaHandlerHelper, TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import { ViewController, ICommandDelegate } from 'vs/editor/browser/view/viewController'; -import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher'; +import { ICommandDelegate, ViewController } from 'vs/editor/browser/view/viewController'; +import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays'; +import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; import { ViewContentWidgets } from 'vs/editor/browser/viewParts/contentWidgets/contentWidgets'; import { CurrentLineHighlightOverlay } from 'vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight'; import { CurrentLineMarginHighlightOverlay } from 'vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight'; import { DecorationsOverlay } from 'vs/editor/browser/viewParts/decorations/decorations'; +import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar'; import { GlyphMarginOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin'; -import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers'; import { IndentGuidesOverlay } from 'vs/editor/browser/viewParts/indentGuides/indentGuides'; +import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers'; import { ViewLines } from 'vs/editor/browser/viewParts/lines/viewLines'; -import { Margin } from 'vs/editor/browser/viewParts/margin/margin'; import { LinesDecorationsOverlay } from 'vs/editor/browser/viewParts/linesDecorations/linesDecorations'; +import { Margin } from 'vs/editor/browser/viewParts/margin/margin'; import { MarginViewLineDecorationsOverlay } from 'vs/editor/browser/viewParts/marginDecorations/marginDecorations'; +import { Minimap } from 'vs/editor/browser/viewParts/minimap/minimap'; import { ViewOverlayWidgets } from 'vs/editor/browser/viewParts/overlayWidgets/overlayWidgets'; import { DecorationsOverviewRuler } from 'vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler'; import { OverviewRuler } from 'vs/editor/browser/viewParts/overviewRuler/overviewRuler'; @@ -35,19 +37,17 @@ import { ScrollDecorationViewPart } from 'vs/editor/browser/viewParts/scrollDeco import { SelectionsOverlay } from 'vs/editor/browser/viewParts/selections/selections'; import { ViewCursors } from 'vs/editor/browser/viewParts/viewCursors/viewCursors'; import { ViewZones } from 'vs/editor/browser/viewParts/viewZones/viewZones'; -import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; -import { RenderingContext } from 'vs/editor/common/view/renderingContext'; -import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; -import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; -import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar'; -import { Minimap } from 'vs/editor/browser/viewParts/minimap/minimap'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { IThemeService, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; import { Cursor } from 'vs/editor/common/controller/cursor'; -import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { Position } from 'vs/editor/common/core/position'; +import { IConfiguration } from 'vs/editor/common/editorCommon'; +import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { IThemeService, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; export interface IContentWidgetData { widget: editorBrowser.IContentWidget; diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index bd9a6556b62..aede693ffdb 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { createStringBuilder, IStringBuilder } from 'vs/editor/common/core/stringBuilder'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; /** * Represents a visible line diff --git a/src/vs/editor/browser/view/viewOutgoingEvents.ts b/src/vs/editor/browser/view/viewOutgoingEvents.ts index a50db4b9029..e95fd142a9e 100644 --- a/src/vs/editor/browser/view/viewOutgoingEvents.ts +++ b/src/vs/editor/browser/view/viewOutgoingEvents.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { MouseTarget } from 'vs/editor/browser/controller/mouseTarget'; +import { IEditorMouseEvent, IMouseTarget, IPartialEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { IScrollEvent } from 'vs/editor/common/editorCommon'; -import { IEditorMouseEvent, IMouseTarget, MouseTargetType, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; -import { MouseTarget } from 'vs/editor/browser/controller/mouseTarget'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; export interface EventCallback { (event: T): void; diff --git a/src/vs/editor/browser/view/viewOverlays.ts b/src/vs/editor/browser/view/viewOverlays.ts index f751f557734..1adf5bcad62 100644 --- a/src/vs/editor/browser/view/viewOverlays.ts +++ b/src/vs/editor/browser/view/viewOverlays.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { IConfiguration } from 'vs/editor/common/editorCommon'; -import { IVisibleLine, VisibleLinesCollection, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; -import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; import { Configuration } from 'vs/editor/browser/config/configuration'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; -import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; +import { IVisibleLine, IVisibleLinesHost, VisibleLinesCollection } from 'vs/editor/browser/view/viewLayer'; import { ViewPart } from 'vs/editor/browser/view/viewPart'; import { IStringBuilder } from 'vs/editor/common/core/stringBuilder'; +import { IConfiguration } from 'vs/editor/common/editorCommon'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; export class ViewOverlays extends ViewPart implements IVisibleLinesHost { diff --git a/src/vs/editor/browser/view/viewPart.ts b/src/vs/editor/browser/view/viewPart.ts index 958635d2c49..319ce014e58 100644 --- a/src/vs/editor/browser/view/viewPart.ts +++ b/src/vs/editor/browser/view/viewPart.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { FastDomNode } from 'vs/base/browser/fastDomNode'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; export abstract class ViewPart extends ViewEventHandler { diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index 3d2e9ee7a9d..f5803ce6613 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -6,14 +6,14 @@ import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ContentWidgetPositionPreference, IContentWidget } from 'vs/editor/browser/editorBrowser'; -import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { Constants } from 'vs/editor/common/core/uint'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { Range, IRange } from 'vs/editor/common/core/range'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { Constants } from 'vs/editor/common/core/uint'; class Coordinate { _coordinateBrand: void; diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts index 8f66b2ab9b7..20ea1f461d3 100644 --- a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts @@ -5,11 +5,11 @@ import 'vs/css!./currentLineHighlight'; import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry'; import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry'; export class CurrentLineHighlightOverlay extends DynamicViewOverlay { private _context: ViewContext; diff --git a/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts index 5f44ea2e0bc..31781146edb 100644 --- a/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts @@ -5,11 +5,11 @@ import 'vs/css!./currentLineMarginHighlight'; import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry'; import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry'; export class CurrentLineMarginHighlightOverlay extends DynamicViewOverlay { private _context: ViewContext; diff --git a/src/vs/editor/browser/viewParts/decorations/decorations.ts b/src/vs/editor/browser/viewParts/decorations/decorations.ts index 88c68c026ce..7f4500dfb73 100644 --- a/src/vs/editor/browser/viewParts/decorations/decorations.ts +++ b/src/vs/editor/browser/viewParts/decorations/decorations.ts @@ -6,10 +6,10 @@ import 'vs/css!./decorations'; import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; import { Range } from 'vs/editor/common/core/range'; +import { HorizontalRange, RenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { RenderingContext, HorizontalRange } from 'vs/editor/common/view/renderingContext'; -import { ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; export class DecorationsOverlay extends DynamicViewOverlay { diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index 796dbc63d1b..df80f0b2087 100644 --- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { ScrollableElementCreationOptions, ScrollableElementChangeOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IOverviewRulerLayoutInfo, SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { ScrollableElementChangeOptions, ScrollableElementCreationOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; +import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; import { INewScrollPosition } from 'vs/editor/common/editorCommon'; -import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; -import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; -import { IMouseEvent } from 'vs/base/browser/mouseEvent'; export class EditorScrollbar extends ViewPart { diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts index 0cd023eb277..585dd754a7e 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts @@ -5,8 +5,8 @@ import 'vs/css!./glyphMargin'; import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; export class DecorationToRender { diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index d899116e7bd..1711beeab58 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -5,12 +5,12 @@ import 'vs/css!./indentGuides'; import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { Position } from 'vs/editor/common/core/position'; +import { editorActiveIndentGuides, editorIndentGuides } from 'vs/editor/common/view/editorColorRegistry'; import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorIndentGuides, editorActiveIndentGuides } from 'vs/editor/common/view/editorColorRegistry'; -import { Position } from 'vs/editor/common/core/position'; export class IndentGuidesOverlay extends DynamicViewOverlay { diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts index b70521de3a0..339a20654aa 100644 --- a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./lineNumbers'; -import { editorLineNumbers, editorActiveLineNumber } from 'vs/editor/common/view/editorColorRegistry'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import * as platform from 'vs/base/common/platform'; import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { RenderingContext } from 'vs/editor/common/view/renderingContext'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { Position } from 'vs/editor/common/core/position'; import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; +import { Position } from 'vs/editor/common/core/position'; +import { editorActiveLineNumber, editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry'; +import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; export class LineNumbersOverlay extends DynamicViewOverlay { diff --git a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts index 3a78a9eefd2..218418ad05a 100644 --- a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts +++ b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; import { Constants } from 'vs/editor/common/core/uint'; +import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; class FloatHorizontalRange { _floatHorizontalRangeBrand: void; diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index c41993481bc..1467d539a39 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -4,18 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as browser from 'vs/base/browser/browser'; -import * as platform from 'vs/base/common/platform'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { IConfiguration } from 'vs/editor/common/editorCommon'; -import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; -import { renderViewLine, RenderLineInput, CharacterMapping, ForeignElementType } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import * as platform from 'vs/base/common/platform'; import { IVisibleLine } from 'vs/editor/browser/view/viewLayer'; import { RangeUtil } from 'vs/editor/browser/viewParts/lines/rangeUtil'; -import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; -import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { ThemeType, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; import { IStringBuilder } from 'vs/editor/common/core/stringBuilder'; +import { IConfiguration } from 'vs/editor/common/editorCommon'; +import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; +import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; +import { CharacterMapping, ForeignElementType, RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; +import { HIGH_CONTRAST, ThemeType } from 'vs/platform/theme/common/themeService'; const canUseFastRenderedViewLine = (function () { if (platform.isNative) { diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index 6bf7cdec14d..4b325d52b80 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -4,20 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./viewLines'; -import { RunOnceScheduler } from 'vs/base/common/async'; import { FastDomNode } from 'vs/base/browser/fastDomNode'; -import { Range } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; -import { VisibleLinesCollection, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; -import { ViewLineOptions, DomReadingContext, ViewLine } from 'vs/editor/browser/viewParts/lines/viewLine'; +import { RunOnceScheduler } from 'vs/base/common/async'; import { Configuration } from 'vs/editor/browser/config/configuration'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { IViewLines, HorizontalRange, LineVisibleRanges } from 'vs/editor/common/view/renderingContext'; -import { Viewport } from 'vs/editor/common/viewModel/viewModel'; -import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { IVisibleLinesHost, VisibleLinesCollection } from 'vs/editor/browser/view/viewLayer'; +import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; +import { DomReadingContext, ViewLine, ViewLineOptions } from 'vs/editor/browser/viewParts/lines/viewLine'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; +import { HorizontalRange, IViewLines, LineVisibleRanges } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { Viewport } from 'vs/editor/common/viewModel/viewModel'; class LastRenderedData { @@ -57,7 +57,7 @@ class HorizontalRevealRequest { export class ViewLines extends ViewPart implements IVisibleLinesHost, IViewLines { /** - * Adds this ammount of pixels to the right of lines (no-one wants to type near the edge of the viewport) + * Adds this amount of pixels to the right of lines (no-one wants to type near the edge of the viewport) */ private static readonly HORIZONTAL_EXTRA_PX = 30; diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts index 8b8c27e5793..5a0937df3ba 100644 --- a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts +++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts @@ -5,8 +5,8 @@ import 'vs/css!./linesDecorations'; import { DecorationToRender, DedupOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; export class LinesDecorationsOverlay extends DedupOverlay { diff --git a/src/vs/editor/browser/viewParts/margin/margin.ts b/src/vs/editor/browser/viewParts/margin/margin.ts index 1ea489ee1d6..111f300b660 100644 --- a/src/vs/editor/browser/viewParts/margin/margin.ts +++ b/src/vs/editor/browser/viewParts/margin/margin.ts @@ -5,8 +5,8 @@ import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ViewPart } from 'vs/editor/browser/view/viewPart'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; export class Margin extends ViewPart { diff --git a/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts b/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts index c1a6f80ff9e..b7a151879fe 100644 --- a/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts +++ b/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts @@ -5,8 +5,8 @@ import 'vs/css!./marginDecorations'; import { DecorationToRender, DedupOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; import { RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; export class MarginViewLineDecorationsOverlay extends DedupOverlay { diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 7bdabcb5f9e..789924de50f 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -4,35 +4,28 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./minimap'; -import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; -import * as strings from 'vs/base/common/strings'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; -import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; import * as dom from 'vs/base/browser/dom'; -import { MinimapCharRenderer, MinimapTokensColorTracker, Constants } from 'vs/editor/common/view/minimapCharRenderer'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { CharCode } from 'vs/base/common/charCode'; -import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; -import { ColorId } from 'vs/editor/common/modes'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; +import { CharCode } from 'vs/base/common/charCode'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { RenderedLinesCollection, ILine } from 'vs/editor/browser/view/viewLayer'; +import * as platform from 'vs/base/common/platform'; +import * as strings from 'vs/base/common/strings'; +import { ILine, RenderedLinesCollection } from 'vs/editor/browser/view/viewLayer'; +import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; +import { RenderMinimap } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { RGBA8 } from 'vs/editor/common/core/rgba'; +import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon'; +import { ColorId } from 'vs/editor/common/modes'; +import { Constants, MinimapCharRenderer, MinimapTokensColorTracker } from 'vs/editor/common/view/minimapCharRenderer'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; 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'; +import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; +import { scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground, scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; - -const enum RenderMinimap { - None = 0, - Small = 1, - Large = 2, - SmallBlocks = 3, - LargeBlocks = 4, -} function getMinimapLineHeight(renderMinimap: RenderMinimap): number { if (renderMinimap === RenderMinimap.Large) { @@ -112,7 +105,7 @@ class MinimapOptions { */ public readonly canvasOuterHeight: number; - constructor(configuration: editorCommon.IConfiguration) { + constructor(configuration: IConfiguration) { const pixelRatio = configuration.editor.pixelRatio; const layoutInfo = configuration.editor.layoutInfo; const viewInfo = configuration.editor.viewInfo; @@ -507,7 +500,7 @@ export class Minimap extends ViewPart { new Range(lineNumber, 1, lineNumber, 1), viewEvents.VerticalRevealType.Center, false, - editorCommon.ScrollType.Smooth + ScrollType.Smooth )); }); diff --git a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts index 03ab812f058..2b25eb1575b 100644 --- a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts +++ b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts @@ -6,9 +6,9 @@ import 'vs/css!./overlayWidgets'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IOverlayWidget, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; -import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; interface IWidgetData { diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index fab13d4a8e8..a8ee68ea1cc 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ViewPart } from 'vs/editor/browser/view/viewPart'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; -import { Position } from 'vs/editor/common/core/position'; -import { TokenizationRegistry } from 'vs/editor/common/modes'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { editorOverviewRulerBorder, editorCursorForeground } from 'vs/editor/common/view/editorColorRegistry'; -import { Color } from 'vs/base/common/color'; -import { ITheme } from 'vs/platform/theme/common/themeService'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { Color } from 'vs/base/common/color'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { Position } from 'vs/editor/common/core/position'; +import { IConfiguration } from 'vs/editor/common/editorCommon'; +import { TokenizationRegistry } from 'vs/editor/common/modes'; +import { editorCursorForeground, editorOverviewRulerBorder } from 'vs/editor/common/view/editorColorRegistry'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { ITheme } from 'vs/platform/theme/common/themeService'; class Settings { @@ -41,7 +41,7 @@ class Settings { public readonly x: number[]; public readonly w: number[]; - constructor(config: editorCommon.IConfiguration, theme: ITheme) { + constructor(config: IConfiguration, theme: ITheme) { this.lineHeight = config.editor.lineHeight; this.pixelRatio = config.editor.pixelRatio; this.overviewRulerLanes = config.editor.viewInfo.overviewRulerLanes; diff --git a/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts index b17fbe95982..6fe2cdf9a6c 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IOverviewRuler } from 'vs/editor/browser/editorBrowser'; +import { OverviewRulerPosition } from 'vs/editor/common/config/editorOptions'; +import { ColorZone, OverviewRulerZone, OverviewZoneManager } from 'vs/editor/common/view/overviewZoneManager'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { OverviewRulerPosition } from 'vs/editor/common/config/editorOptions'; -import { OverviewRulerZone, OverviewZoneManager, ColorZone } from 'vs/editor/common/view/overviewZoneManager'; -import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; export class OverviewRuler extends ViewEventHandler implements IOverviewRuler { diff --git a/src/vs/editor/browser/viewParts/rulers/rulers.ts b/src/vs/editor/browser/viewParts/rulers/rulers.ts index a196b583000..eda625d0be6 100644 --- a/src/vs/editor/browser/viewParts/rulers/rulers.ts +++ b/src/vs/editor/browser/viewParts/rulers/rulers.ts @@ -6,11 +6,11 @@ import 'vs/css!./rulers'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ViewPart } from 'vs/editor/browser/view/viewPart'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { editorRuler } from 'vs/editor/common/view/editorColorRegistry'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorRuler } from 'vs/editor/common/view/editorColorRegistry'; export class Rulers extends ViewPart { diff --git a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts index fb55aa7df74..6d65eea7386 100644 --- a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts +++ b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts @@ -6,11 +6,11 @@ import 'vs/css!./scrollDecoration'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ViewPart } from 'vs/editor/browser/view/viewPart'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; export class ScrollDecorationViewPart extends ViewPart { diff --git a/src/vs/editor/browser/viewParts/selections/selections.ts b/src/vs/editor/browser/viewParts/selections/selections.ts index ee511af7ab4..99d5696fede 100644 --- a/src/vs/editor/browser/viewParts/selections/selections.ts +++ b/src/vs/editor/browser/viewParts/selections/selections.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./selections'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorSelectionBackground, editorInactiveSelection, editorSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; -import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { HorizontalRange, LineVisibleRanges, RenderingContext } from 'vs/editor/common/view/renderingContext'; -import { Range } from 'vs/editor/common/core/range'; import * as browser from 'vs/base/browser/browser'; +import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; +import { Range } from 'vs/editor/common/core/range'; +import { HorizontalRange, LineVisibleRanges, RenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { editorInactiveSelection, editorSelectionBackground, editorSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; const enum CornerStyle { EXTERN, diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts index 9d352475cb2..3cf99a75291 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import * as strings from 'vs/base/common/strings'; +import { Configuration } from 'vs/editor/browser/config/configuration'; +import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; -import { Configuration } from 'vs/editor/browser/config/configuration'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import * as dom from 'vs/base/browser/dom'; -import * as strings from 'vs/base/common/strings'; export interface IViewCursorRenderData { domNode: HTMLElement; diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css index a0ecc219714..d77870c24a2 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css @@ -13,6 +13,11 @@ overflow: hidden; } +/* -- smooth-caret-animation -- */ +.monaco-editor .cursors-layer.cursor-smooth-caret-animation > .cursor { + transition: 80ms; +} + /* -- block-outline-style -- */ .monaco-editor .cursors-layer.cursor-block-outline-style > .cursor { box-sizing: border-box; diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index 37dbf1fda61..a39d170893e 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./viewCursors'; -import { ViewPart } from 'vs/editor/browser/view/viewPart'; -import { Position } from 'vs/editor/common/core/position'; -import { IViewCursorRenderData, ViewCursor } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { TimeoutTimer, IntervalTimer } from 'vs/base/common/async'; +import { IntervalTimer, TimeoutTimer } from 'vs/base/common/async'; +import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { IViewCursorRenderData, ViewCursor } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; +import { TextEditorCursorBlinkingStyle, TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; +import { Position } from 'vs/editor/common/core/position'; +import { editorCursorBackground, editorCursorForeground } from 'vs/editor/common/view/editorColorRegistry'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorCursorForeground, editorCursorBackground } from 'vs/editor/common/view/editorColorRegistry'; -import { TextEditorCursorBlinkingStyle, TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; export class ViewCursors extends ViewPart { @@ -23,6 +23,7 @@ export class ViewCursors extends ViewPart { private _readOnly: boolean; private _cursorBlinking: TextEditorCursorBlinkingStyle; private _cursorStyle: TextEditorCursorStyle; + private _cursorSmoothCaretAnimation: boolean; private _selectionIsEmpty: boolean; private _isVisible: boolean; @@ -45,6 +46,7 @@ export class ViewCursors extends ViewPart { this._readOnly = this._context.configuration.editor.readOnly; this._cursorBlinking = this._context.configuration.editor.viewInfo.cursorBlinking; this._cursorStyle = this._context.configuration.editor.viewInfo.cursorStyle; + this._cursorSmoothCaretAnimation = this._context.configuration.editor.viewInfo.cursorSmoothCaretAnimation; this._selectionIsEmpty = true; this._primaryCursor = new ViewCursor(this._context); @@ -295,6 +297,9 @@ export class ViewCursors extends ViewPart { } else { result += ' cursor-solid'; } + if (this._cursorSmoothCaretAnimation) { + result += ' cursor-smooth-caret-animation'; + } return result; } diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index bbc50aa2041..f148821b006 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { onUnexpectedError } from 'vs/base/common/errors'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { IViewZone } from 'vs/editor/browser/editorBrowser'; import { ViewPart } from 'vs/editor/browser/view/viewPart'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; import { Position } from 'vs/editor/common/core/position'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; -import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; export interface IMyViewZone { whitespaceId: number; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 80199bd7d64..15cef1d5200 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -7,48 +7,48 @@ import 'vs/css!./media/editor'; import 'vs/css!./media/tokens'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { Color } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; +import { hash } from 'vs/base/common/hash'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { Schemas } from 'vs/base/common/network'; +import { mark } from 'vs/base/common/performance'; import { Configuration } from 'vs/editor/browser/config/configuration'; +import { CoreEditorCommand } from 'vs/editor/browser/controller/coreCommands'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { EditorExtensionsRegistry, IEditorContributionCtor } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { ICommandDelegate } from 'vs/editor/browser/view/viewController'; +import { IContentWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view/viewImpl'; +import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor'; import { CursorColumns, ICursors } from 'vs/editor/common/controller/cursorCommon'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { Selection, ISelection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; -import { hash } from 'vs/base/common/hash'; -import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelLanguageConfigurationChangedEvent } from 'vs/editor/common/model/textModelEvents'; -import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; -import * as modes from 'vs/editor/common/modes'; -import { Schemas } from 'vs/base/common/network'; -import { ITextModel, EndOfLinePreference, IIdentifiedSingleEditOperation, IModelDecorationsChangeAccessor, IModelDecoration, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { IContentWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view/viewImpl'; -import { IEditorContributionCtor, EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; -import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { InternalEditorAction } from 'vs/editor/common/editorAction'; -import { ICommandDelegate } from 'vs/editor/browser/view/viewController'; -import { CoreEditorCommand } from 'vs/editor/browser/controller/coreCommands'; -import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoBorder, editorInfoForeground, editorHintForeground, editorHintBorder, editorUnnecessaryCodeOpacity, editorUnnecessaryCodeBorder } from 'vs/editor/common/view/editorColorRegistry'; -import { Color } from 'vs/base/common/color'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { EndOfLinePreference, IIdentifiedSingleEditOperation, IModelDecoration, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ClassName } from 'vs/editor/common/model/intervalTree'; -import { mark } from 'vs/base/common/performance'; -import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import * as modes from 'vs/editor/common/modes'; +import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity, editorWarningBorder, editorWarningForeground } from 'vs/editor/common/view/editorColorRegistry'; +import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; let EDITOR_ID = 0; diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 23a1ba15353..522676e8fd6 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -5,41 +5,41 @@ import 'vs/css!./media/diffEditor'; import * as nls from 'vs/nls'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import { Disposable } from 'vs/base/common/lifecycle'; -import * as objects from 'vs/base/common/objects'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IEditorWorkerService, IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService'; -import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; -import { renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { Configuration } from 'vs/editor/browser/config/configuration'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { Selection, ISelection } from 'vs/editor/common/core/selection'; -import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { Event, Emitter } from 'vs/base/common/event'; -import * as editorOptions from 'vs/editor/common/config/editorOptions'; -import { registerThemingParticipant, IThemeService, ITheme, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; -import { scrollbarShadow, diffInserted, diffRemoved, defaultInsertColor, defaultRemoveColor, diffInsertedOutline, diffRemovedOutline, diffBorder } from 'vs/platform/theme/common/colorRegistry'; +import { RunOnceScheduler } from 'vs/base/common/async'; import { Color } from 'vs/base/common/color'; -import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; -import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { DiffReview } from 'vs/editor/browser/widget/diffReview'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as objects from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; -import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder'; -import { IModelDeltaDecoration, IModelDecorationsChangeAccessor, ITextModel } from 'vs/editor/common/model'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { Configuration } from 'vs/editor/browser/config/configuration'; import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { DiffReview } from 'vs/editor/browser/widget/diffReview'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { ISelection, Selection } from 'vs/editor/common/core/selection'; +import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; +import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; +import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; +import { ITheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; interface IEditorDiffDecorations { decorations: IModelDeltaDecoration[]; diff --git a/src/vs/editor/browser/widget/diffNavigator.ts b/src/vs/editor/browser/widget/diffNavigator.ts index b92cb3ff688..29afe7ec63e 100644 --- a/src/vs/editor/browser/widget/diffNavigator.ts +++ b/src/vs/editor/browser/widget/diffNavigator.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'vs/base/common/assert'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; +import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { Range } from 'vs/editor/common/core/range'; import { ILineChange, ScrollType } from 'vs/editor/common/editorCommon'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; -import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { Event, Emitter } from 'vs/base/common/event'; interface IDiffRange { diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 76f8d60fef7..8725f4a13d2 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -5,31 +5,31 @@ import 'vs/css!./media/diffReview'; import * as nls from 'vs/nls'; -import { Disposable } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { renderViewLine2 as renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer'; -import { LineTokens } from 'vs/editor/common/core/lineTokens'; -import { Configuration } from 'vs/editor/browser/config/configuration'; -import { Position } from 'vs/editor/common/core/position'; -import { ColorId, MetadataConsts, FontStyle } from 'vs/editor/common/modes'; -import * as editorOptions from 'vs/editor/common/config/editorOptions'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Action } from 'vs/base/common/actions'; -import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Configuration } from 'vs/editor/browser/config/configuration'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { Position } from 'vs/editor/common/core/position'; +import { ILineChange, ScrollType } from 'vs/editor/common/editorCommon'; import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; +import { ColorId, FontStyle, MetadataConsts } from 'vs/editor/common/modes'; +import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry'; +import { RenderLineInput, renderViewLine2 as renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; const DIFF_LINES_PADDING = 3; @@ -262,7 +262,7 @@ export class DiffReview extends Disposable { if (jumpToLineNumber !== -1) { this._diffEditor.setPosition(new Position(jumpToLineNumber, 1)); - this._diffEditor.revealPosition(new Position(jumpToLineNumber, 1), editorCommon.ScrollType.Immediate); + this._diffEditor.revealPosition(new Position(jumpToLineNumber, 1), ScrollType.Immediate); } } @@ -358,7 +358,7 @@ export class DiffReview extends Disposable { return DiffReview._mergeAdjacent(lineChanges, originalModel.getLineCount(), modifiedModel.getLineCount()); } - private static _mergeAdjacent(lineChanges: editorCommon.ILineChange[], originalLineCount: number, modifiedLineCount: number): Diff[] { + private static _mergeAdjacent(lineChanges: ILineChange[], originalLineCount: number, modifiedLineCount: number): Diff[] { if (!lineChanges || lineChanges.length === 0) { return []; } diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts index a11dffed990..c969a76031e 100644 --- a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as objects from 'vs/base/common/objects'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { IConfigurationChangedEvent, IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { IConfigurationChangedEvent, IEditorOptions, IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; export class EmbeddedCodeEditorWidget extends CodeEditorWidget { diff --git a/src/vs/editor/common/commands/replaceCommand.ts b/src/vs/editor/common/commands/replaceCommand.ts index d72282cc43d..28671285ac8 100644 --- a/src/vs/editor/common/commands/replaceCommand.ts +++ b/src/vs/editor/common/commands/replaceCommand.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -export class ReplaceCommand implements editorCommon.ICommand { +export class ReplaceCommand implements ICommand { private readonly _range: Range; private readonly _text: string; @@ -20,11 +20,11 @@ export class ReplaceCommand implements editorCommon.ICommand { this.insertsAutoWhitespace = insertsAutoWhitespace; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { builder.addTrackedEditOperation(this._range, this._text); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { let inverseEditOperations = helper.getInverseEditOperations(); let srcRange = inverseEditOperations[0].range; return new Selection( @@ -36,7 +36,7 @@ export class ReplaceCommand implements editorCommon.ICommand { } } -export class ReplaceCommandWithoutChangingPosition implements editorCommon.ICommand { +export class ReplaceCommandWithoutChangingPosition implements ICommand { private readonly _range: Range; private readonly _text: string; @@ -48,11 +48,11 @@ export class ReplaceCommandWithoutChangingPosition implements editorCommon.IComm this.insertsAutoWhitespace = insertsAutoWhitespace; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { builder.addTrackedEditOperation(this._range, this._text); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { let inverseEditOperations = helper.getInverseEditOperations(); let srcRange = inverseEditOperations[0].range; return new Selection( @@ -64,7 +64,7 @@ export class ReplaceCommandWithoutChangingPosition implements editorCommon.IComm } } -export class ReplaceCommandWithOffsetCursorState implements editorCommon.ICommand { +export class ReplaceCommandWithOffsetCursorState implements ICommand { private readonly _range: Range; private readonly _text: string; @@ -80,11 +80,11 @@ export class ReplaceCommandWithOffsetCursorState implements editorCommon.IComman this.insertsAutoWhitespace = insertsAutoWhitespace; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { builder.addTrackedEditOperation(this._range, this._text); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { let inverseEditOperations = helper.getInverseEditOperations(); let srcRange = inverseEditOperations[0].range; return new Selection( @@ -96,7 +96,7 @@ export class ReplaceCommandWithOffsetCursorState implements editorCommon.IComman } } -export class ReplaceCommandThatPreservesSelection implements editorCommon.ICommand { +export class ReplaceCommandThatPreservesSelection implements ICommand { private _range: Range; private _text: string; @@ -109,12 +109,12 @@ export class ReplaceCommandThatPreservesSelection implements editorCommon.IComma this._initialSelection = initialSelection; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { builder.addEditOperation(this._range, this._text); this._selectionId = builder.trackSelection(this._initialSelection); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { return helper.getTrackedSelection(this._selectionId); } } diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index c2e1349351e..fdc0a21e052 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; import { Range } from 'vs/editor/common/core/range'; import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { CharCode } from 'vs/base/common/charCode'; import { ITextModel } from 'vs/editor/common/model'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; export interface IShiftCommandOpts { isUnshift: boolean; diff --git a/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts b/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts index 02ca62b9c06..5a9d6e3902b 100644 --- a/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts +++ b/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts @@ -5,13 +5,13 @@ import * as strings from 'vs/base/common/strings'; import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; +import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; -export class TrimTrailingWhitespaceCommand implements editorCommon.ICommand { +export class TrimTrailingWhitespaceCommand implements ICommand { private selection: Selection; private selectionId: string; @@ -22,7 +22,7 @@ export class TrimTrailingWhitespaceCommand implements editorCommon.ICommand { this.cursors = cursors; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { let ops = trimTrailingWhitespace(model, this.cursors); for (let i = 0, len = ops.length; i < len; i++) { let op = ops[i]; @@ -33,7 +33,7 @@ export class TrimTrailingWhitespaceCommand implements editorCommon.ICommand { this.selectionId = builder.trackSelection(this.selection); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { return helper.getTrackedSelection(this.selectionId); } } diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index dca6389a780..190862e8bd0 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; -import { Extensions, IConfigurationRegistry, IConfigurationNode, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { Registry } from 'vs/platform/registry/common/platform'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo'; -import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { EditorZoom } from 'vs/editor/common/config/editorZoom'; +import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; import EDITOR_DEFAULTS = editorOptions.EDITOR_DEFAULTS; import EDITOR_FONT_DEFAULTS = editorOptions.EDITOR_FONT_DEFAULTS; import EDITOR_MODEL_DEFAULTS = editorOptions.EDITOR_MODEL_DEFAULTS; @@ -675,6 +675,11 @@ const editorConfiguration: IConfigurationNode = { 'default': EDITOR_DEFAULTS.viewInfo.mouseWheelZoom, 'markdownDescription': nls.localize('mouseWheelZoom', "Zoom the font of the editor when using mouse wheel and holding `Ctrl`.") }, + 'editor.cursorSmoothCaretAnimation': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.cursorSmoothCaretAnimation, + 'description': nls.localize('cursorSmoothCaretAnimation', "Controls whether the smooth caret animation should be enabled.") + }, 'editor.cursorStyle': { 'type': 'string', 'enum': ['block', 'block-outline', 'line', 'line-thin', 'underline', 'underline-thin'], diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index bda346b4448..f1ca050c5e4 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import * as arrays from 'vs/base/common/arrays'; +import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { Constants } from 'vs/editor/common/core/uint'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; -import * as arrays from 'vs/base/common/arrays'; -import * as objects from 'vs/base/common/objects'; /** * Configuration options for editor scrollbars @@ -326,6 +326,11 @@ export interface IEditorOptions { * @internal */ mouseStyle?: 'text' | 'default' | 'copy'; + /** + * Enable smooth caret animation. + * Defaults to false. + */ + cursorSmoothCaretAnimation?: boolean; /** * Control the cursor style, either 'block' or 'line'. * Defaults to 'line'. @@ -705,7 +710,7 @@ export interface IDiffEditorOptions extends IEditorOptions { originalEditable?: boolean; } -export enum RenderMinimap { +export const enum RenderMinimap { None = 0, Small = 1, Large = 2, @@ -716,7 +721,7 @@ export enum RenderMinimap { /** * Describes how to indent wrapped lines. */ -export enum WrappingIndent { +export const enum WrappingIndent { /** * No indentation => wrapped lines begin at column 1. */ @@ -738,7 +743,7 @@ export enum WrappingIndent { /** * The kind of animation in which the editor's cursor should be rendered. */ -export enum TextEditorCursorBlinkingStyle { +export const enum TextEditorCursorBlinkingStyle { /** * Hidden */ @@ -939,6 +944,7 @@ export interface InternalEditorViewOptions { readonly overviewRulerBorder: boolean; readonly cursorBlinking: TextEditorCursorBlinkingStyle; readonly mouseWheelZoom: boolean; + readonly cursorSmoothCaretAnimation: boolean; readonly cursorStyle: TextEditorCursorStyle; readonly cursorWidth: number; readonly hideCursorInOverviewRuler: boolean; @@ -1245,6 +1251,7 @@ export class InternalEditorOptions { && a.overviewRulerBorder === b.overviewRulerBorder && a.cursorBlinking === b.cursorBlinking && a.mouseWheelZoom === b.mouseWheelZoom + && a.cursorSmoothCaretAnimation === b.cursorSmoothCaretAnimation && a.cursorStyle === b.cursorStyle && a.cursorWidth === b.cursorWidth && a.hideCursorInOverviewRuler === b.hideCursorInOverviewRuler @@ -1970,6 +1977,7 @@ export class EditorOptionsValidator { overviewRulerBorder: _boolean(opts.overviewRulerBorder, defaults.overviewRulerBorder), cursorBlinking: _cursorBlinkingStyleFromString(opts.cursorBlinking, defaults.cursorBlinking), mouseWheelZoom: _boolean(opts.mouseWheelZoom, defaults.mouseWheelZoom), + cursorSmoothCaretAnimation: _boolean(opts.cursorSmoothCaretAnimation, defaults.cursorSmoothCaretAnimation), cursorStyle: _cursorStyleFromString(opts.cursorStyle, defaults.cursorStyle), cursorWidth: _clampedInt(opts.cursorWidth, defaults.cursorWidth, 0, Number.MAX_VALUE), hideCursorInOverviewRuler: _boolean(opts.hideCursorInOverviewRuler, defaults.hideCursorInOverviewRuler), @@ -2089,6 +2097,7 @@ export class InternalEditorOptionsFactory { overviewRulerBorder: opts.viewInfo.overviewRulerBorder, cursorBlinking: opts.viewInfo.cursorBlinking, mouseWheelZoom: opts.viewInfo.mouseWheelZoom, + cursorSmoothCaretAnimation: opts.viewInfo.cursorSmoothCaretAnimation, cursorStyle: opts.viewInfo.cursorStyle, cursorWidth: opts.viewInfo.cursorWidth, hideCursorInOverviewRuler: opts.viewInfo.hideCursorInOverviewRuler, @@ -2550,6 +2559,7 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = { overviewRulerBorder: true, cursorBlinking: TextEditorCursorBlinkingStyle.Blink, mouseWheelZoom: false, + cursorSmoothCaretAnimation: false, cursorStyle: TextEditorCursorStyle.Line, cursorWidth: 0, hideCursorInOverviewRuler: false, diff --git a/src/vs/editor/common/config/editorZoom.ts b/src/vs/editor/common/config/editorZoom.ts index eb30201b5df..1be9203a573 100644 --- a/src/vs/editor/common/config/editorZoom.ts +++ b/src/vs/editor/common/config/editorZoom.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; export interface IEditorZoom { onDidChangeZoomLevel: Event; diff --git a/src/vs/editor/common/config/fontInfo.ts b/src/vs/editor/common/config/fontInfo.ts index 7a2d0251884..88cb52aa903 100644 --- a/src/vs/editor/common/config/fontInfo.ts +++ b/src/vs/editor/common/config/fontInfo.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as platform from 'vs/base/common/platform'; -import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { EditorZoom } from 'vs/editor/common/config/editorZoom'; /** * Determined from empirical observations. diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 6dc3a0e77eb..c79a039360c 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -4,22 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as strings from 'vs/base/common/strings'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import * as strings from 'vs/base/common/strings'; import { CursorCollection } from 'vs/editor/common/controller/cursorCollection'; +import { CursorColumns, CursorConfiguration, CursorContext, CursorState, EditOperationResult, EditOperationType, IColumnSelectData, ICursors, PartialCursorState, RevealTarget } from 'vs/editor/common/controller/cursorCommon'; +import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { Selection, SelectionDirection, ISelection } from 'vs/editor/common/core/selection'; +import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { CursorColumns, CursorConfiguration, EditOperationResult, CursorContext, CursorState, RevealTarget, IColumnSelectData, ICursors, EditOperationType, PartialCursorState } from 'vs/editor/common/controller/cursorCommon'; -import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; -import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; +import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; import { RawContentChangedType } from 'vs/editor/common/model/textModelEvents'; -import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; -import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ITextModel, IIdentifiedSingleEditOperation, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; function containsLineMappingChanged(events: viewEvents.ViewEvent[]): boolean { for (let i = 0, len = events.length; i < len; i++) { diff --git a/src/vs/editor/common/controller/cursorCollection.ts b/src/vs/editor/common/controller/cursorCollection.ts index 7707d141b49..78dcb514eab 100644 --- a/src/vs/editor/common/controller/cursorCollection.ts +++ b/src/vs/editor/common/controller/cursorCollection.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CursorContext, CursorState, PartialCursorState } from 'vs/editor/common/controller/cursorCommon'; import { OneCursor } from 'vs/editor/common/controller/oneCursor'; -import { Selection, ISelection } from 'vs/editor/common/core/selection'; import { Position } from 'vs/editor/common/core/position'; -import { CursorState, CursorContext, PartialCursorState } from 'vs/editor/common/controller/cursorCommon'; +import { ISelection, Selection } from 'vs/editor/common/core/selection'; export class CursorCollection { diff --git a/src/vs/editor/common/controller/cursorColumnSelection.ts b/src/vs/editor/common/controller/cursorColumnSelection.ts index c0ceabc0f47..efa35ca08e1 100644 --- a/src/vs/editor/common/controller/cursorColumnSelection.ts +++ b/src/vs/editor/common/controller/cursorColumnSelection.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Selection } from 'vs/editor/common/core/selection'; +import { CursorColumns, CursorConfiguration, ICursorSimpleModel, SingleCursorState } from 'vs/editor/common/controller/cursorCommon'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { SingleCursorState, CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; +import { Selection } from 'vs/editor/common/core/selection'; export interface IColumnSelectResult { viewStates: SingleCursorState[]; diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index 26bb589326a..d3e6e6cb06e 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -3,22 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from 'vs/editor/common/core/position'; import { CharCode } from 'vs/base/common/charCode'; -import * as strings from 'vs/base/common/strings'; -import { ICommand, IConfiguration, ScrollType } from 'vs/editor/common/editorCommon'; -import { TextModel } from 'vs/editor/common/model/textModel'; -import { Selection, ISelection } from 'vs/editor/common/core/selection'; -import { Range } from 'vs/editor/common/core/range'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { onUnexpectedError } from 'vs/base/common/errors'; +import * as strings from 'vs/base/common/strings'; +import { EditorAutoClosingStrategy, EditorAutoSurroundStrategy, IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { ISelection, Selection } from 'vs/editor/common/core/selection'; +import { ICommand, IConfiguration, ScrollType } from 'vs/editor/common/editorCommon'; +import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; +import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration'; -import { IConfigurationChangedEvent, EditorAutoClosingStrategy, EditorAutoSurroundStrategy } from 'vs/editor/common/config/editorOptions'; -import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; -import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; -import { TextModelResolvedOptions, ITextModel } from 'vs/editor/common/model'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; export interface IColumnSelectData { toViewLineNumber: number; diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index e1a7cf779d6..6b687924549 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as strings from 'vs/base/common/strings'; import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; -import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult, EditOperationType, isQuote } from 'vs/editor/common/controller/cursorCommon'; +import { CursorColumns, CursorConfiguration, EditOperationResult, EditOperationType, ICursorSimpleModel, isQuote } from 'vs/editor/common/controller/cursorCommon'; +import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; -import * as strings from 'vs/base/common/strings'; import { ICommand } from 'vs/editor/common/editorCommon'; export class DeleteOperations { diff --git a/src/vs/editor/common/controller/cursorEvents.ts b/src/vs/editor/common/controller/cursorEvents.ts index 750a959cf23..a873c2ed70c 100644 --- a/src/vs/editor/common/controller/cursorEvents.ts +++ b/src/vs/editor/common/controller/cursorEvents.ts @@ -9,7 +9,7 @@ import { Selection } from 'vs/editor/common/core/selection'; /** * Describes the reason the cursor has changed its position. */ -export enum CursorChangeReason { +export const enum CursorChangeReason { /** * Unknown or not set. */ diff --git a/src/vs/editor/common/controller/cursorMoveCommands.ts b/src/vs/editor/common/controller/cursorMoveCommands.ts index 886ad3a9cc8..493611967cd 100644 --- a/src/vs/editor/common/controller/cursorMoveCommands.ts +++ b/src/vs/editor/common/controller/cursorMoveCommands.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SingleCursorState, ICursorSimpleModel, CursorState, CursorContext, PartialCursorState } from 'vs/editor/common/controller/cursorCommon'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import * as types from 'vs/base/common/types'; +import { CursorContext, CursorState, ICursorSimpleModel, PartialCursorState, SingleCursorState } from 'vs/editor/common/controller/cursorCommon'; import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; import { WordOperations } from 'vs/editor/common/controller/cursorWordOperations'; -import * as types from 'vs/base/common/types'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; export class CursorMoveCommands { diff --git a/src/vs/editor/common/controller/cursorMoveOperations.ts b/src/vs/editor/common/controller/cursorMoveOperations.ts index 2bd2e348d6b..65ad93791b8 100644 --- a/src/vs/editor/common/controller/cursorMoveOperations.ts +++ b/src/vs/editor/common/controller/cursorMoveOperations.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SingleCursorState, CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; +import { CursorColumns, CursorConfiguration, ICursorSimpleModel, SingleCursorState } from 'vs/editor/common/controller/cursorCommon'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 0c61759a83f..d670846d0f8 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CharCode } from 'vs/base/common/charCode'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { ReplaceCommand, ReplaceCommandWithoutChangingPosition, ReplaceCommandWithOffsetCursorState } from 'vs/editor/common/commands/replaceCommand'; -import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult, EditOperationType, isQuote } from 'vs/editor/common/controller/cursorCommon'; +import * as strings from 'vs/base/common/strings'; +import { ReplaceCommand, ReplaceCommandWithOffsetCursorState, ReplaceCommandWithoutChangingPosition } from 'vs/editor/common/commands/replaceCommand'; +import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; +import { SurroundSelectionCommand } from 'vs/editor/common/commands/surroundSelectionCommand'; +import { CursorColumns, CursorConfiguration, EditOperationResult, EditOperationType, ICursorSimpleModel, isQuote } from 'vs/editor/common/controller/cursorCommon'; +import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; import { ICommand } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import * as strings from 'vs/base/common/strings'; -import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; -import { Selection } from 'vs/editor/common/core/selection'; +import { EnterAction, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { IndentAction, EnterAction } from 'vs/editor/common/modes/languageConfiguration'; -import { SurroundSelectionCommand } from 'vs/editor/common/commands/surroundSelectionCommand'; import { IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; -import { getMapForWordSeparators, WordCharacterClass } from 'vs/editor/common/controller/wordCharacterClassifier'; -import { CharCode } from 'vs/base/common/charCode'; export class TypeOperations { diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index ccda783109d..1ef3b2c04d7 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SingleCursorState, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; -import { Position } from 'vs/editor/common/core/position'; -import { WordCharacterClassifier, WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; +import { CursorConfiguration, ICursorSimpleModel, SingleCursorState } from 'vs/editor/common/controller/cursorCommon'; +import { WordCharacterClass, WordCharacterClassifier, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { CharCode } from 'vs/base/common/charCode'; interface IFindWordResult { /** diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index 79321cdc2a0..e19068883ef 100644 --- a/src/vs/editor/common/controller/oneCursor.ts +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SingleCursorState, CursorContext, CursorState } from 'vs/editor/common/controller/cursorCommon'; +import { CursorContext, CursorState, SingleCursorState } from 'vs/editor/common/controller/cursorCommon'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; diff --git a/src/vs/editor/common/core/editOperation.ts b/src/vs/editor/common/core/editOperation.ts index 6e77c535f1c..fe3cf2e6084 100644 --- a/src/vs/editor/common/core/editOperation.ts +++ b/src/vs/editor/common/core/editOperation.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; export class EditOperation { diff --git a/src/vs/editor/common/core/lineTokens.ts b/src/vs/editor/common/core/lineTokens.ts index 2b50945c52d..ffc1f05f49e 100644 --- a/src/vs/editor/common/core/lineTokens.ts +++ b/src/vs/editor/common/core/lineTokens.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ColorId, StandardTokenType, LanguageId, TokenMetadata } from 'vs/editor/common/modes'; +import { ColorId, LanguageId, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes'; export interface IViewLineTokens { equals(other: IViewLineTokens): boolean; diff --git a/src/vs/editor/common/core/range.ts b/src/vs/editor/common/core/range.ts index 46cec746b26..c84b5df9d8a 100644 --- a/src/vs/editor/common/core/range.ts +++ b/src/vs/editor/common/core/range.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position, IPosition } from 'vs/editor/common/core/position'; +import { IPosition, Position } from 'vs/editor/common/core/position'; /** * A range in the editor. This interface is suitable for serialization. diff --git a/src/vs/editor/common/core/selection.ts b/src/vs/editor/common/core/selection.ts index 8f7d468efb7..80143cdf97a 100644 --- a/src/vs/editor/common/core/selection.ts +++ b/src/vs/editor/common/core/selection.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IPosition, Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { Position, IPosition } from 'vs/editor/common/core/position'; /** * A selection in the editor. @@ -32,7 +32,7 @@ export interface ISelection { /** * The direction of a selection. */ -export enum SelectionDirection { +export const enum SelectionDirection { /** * The selection starts above where it ends. */ diff --git a/src/vs/editor/common/editorAction.ts b/src/vs/editor/common/editorAction.ts index 5fc316932a8..c1e4fb1e68e 100644 --- a/src/vs/editor/common/editorAction.ts +++ b/src/vs/editor/common/editorAction.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IEditorAction } from 'vs/editor/common/editorCommon'; -import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; export class InternalEditorAction implements IEditorAction { diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 8dbfeb3a5ed..909f6fb1e64 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { URI, UriComponents } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { Selection, ISelection } from 'vs/editor/common/core/selection'; +import { URI, UriComponents } from 'vs/base/common/uri'; import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { ISelection, Selection } from 'vs/editor/common/core/selection'; +import { IIdentifiedSingleEditOperation, IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; -import { ITextModel, IModelDecorationsChangeAccessor, TrackedRangeStickiness, OverviewRulerLane, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; /** * A builder and helper for edit operations for a command. @@ -39,7 +39,7 @@ export interface IEditOperationBuilder { * @param selection The selection to track. * @param trackPreviousOnEmpty If set, and the selection is empty, indicates whether the selection * should clamp to the previous or the next character. - * @return A unique identifer. + * @return A unique identifier. */ trackSelection(selection: Selection, trackPreviousOnEmpty?: boolean): string; } @@ -80,7 +80,7 @@ export interface ICommand { /** * Compute the cursor state after the edit operations were applied. - * @param model The model the commad has executed on. + * @param model The model the command has executed on. * @param helper A helper to get inverse edit operations and to get previously tracked selections. * @return The cursor state after the command executed. */ @@ -534,10 +534,10 @@ export interface IThemeDecorationRenderOptions { textDecoration?: string; cursor?: string; color?: string | ThemeColor; - opacity?: number; + opacity?: string; letterSpacing?: string; - gutterIconPath?: string | UriComponents; + gutterIconPath?: UriComponents; gutterIconSize?: string; overviewRulerColor?: string | ThemeColor; @@ -551,7 +551,7 @@ export interface IThemeDecorationRenderOptions { */ export interface IContentDecorationRenderOptions { contentText?: string; - contentIconPath?: string | UriComponents; + contentIconPath?: UriComponents; border?: string; borderColor?: string | ThemeColor; diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 53652c89a82..9912616baad 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { URI } from 'vs/base/common/uri'; -import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; -import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { Range, IRange } from 'vs/editor/common/core/range'; +import { URI } from 'vs/base/common/uri'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { ModelRawContentChangedEvent, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelLanguageConfigurationChangedEvent, IModelTokensChangedEvent, IModelContentChange } from 'vs/editor/common/model/textModelEvents'; -import { ThemeColor } from 'vs/platform/theme/common/themeService'; -import { ITextSnapshot } from 'vs/platform/files/common/files'; +import { IModelContentChange, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, ModelRawContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { SearchData } from 'vs/editor/common/model/textModelSearch'; +import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; +import { ITextSnapshot } from 'vs/platform/files/common/files'; +import { ThemeColor } from 'vs/platform/theme/common/themeService'; /** * Vertical Lane in the overview ruler of the editor. @@ -145,7 +145,7 @@ export interface IModelDecoration { */ readonly id: string; /** - * Identifier for a decoration's owener. + * Identifier for a decoration's owner. */ readonly ownerId: number; /** @@ -188,7 +188,7 @@ export interface IModelDecorationsChangeAccessor { */ removeDecoration(id: string): void; /** - * Perform a minimum ammount of operations, in order to transform the decorations + * Perform a minimum amount of operations, in order to transform the decorations * identified by `oldDecorations` to the decorations described by `newDecorations` * and returns the new identifiers associated with the resulting decorations. * @@ -220,7 +220,7 @@ export interface IWordAtPosition { /** * End of line character preference. */ -export enum EndOfLinePreference { +export const enum EndOfLinePreference { /** * Use the end of line character identified in the text buffer. */ @@ -238,7 +238,7 @@ export enum EndOfLinePreference { /** * The default end of line to use when instantiating models. */ -export enum DefaultEndOfLine { +export const enum DefaultEndOfLine { /** * Use line feed (\n) as the end of line character. */ @@ -252,7 +252,7 @@ export enum DefaultEndOfLine { /** * End of line character preference. */ -export enum EndOfLineSequence { +export const enum EndOfLineSequence { /** * Use line feed (\n) as the end of line character. */ @@ -437,7 +437,7 @@ export interface IFoundBracket { * Describes the behavior of decorations when typing/editing near their edges. * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` */ -export enum TrackedRangeStickiness { +export const enum TrackedRangeStickiness { AlwaysGrowsWhenTypingAtEdges = 0, NeverGrowsWhenTypingAtEdges = 1, GrowsOnlyWhenTypingBefore = 2, @@ -623,13 +623,13 @@ export interface ITextModel { validatePosition(position: IPosition): Position; /** - * Advances the given position by the given offest (negative offsets are also accepted) + * Advances the given position by the given offset (negative offsets are also accepted) * and returns it as a new valid position. * * If the offset and position are such that their combination goes beyond the beginning or * end of the model, throws an exception. * - * If the ofsset is such that the new position would be in the middle of a multi-byte + * If the offset is such that the new position would be in the middle of a multi-byte * line terminator, throws an exception. */ modifyPosition(position: IPosition, offset: number): Position; @@ -851,7 +851,7 @@ export interface ITextModel { changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T, ownerId?: number): T | null; /** - * Perform a minimum ammount of operations, in order to transform the decorations + * Perform a minimum amount of operations, in order to transform the decorations * identified by `oldDecorations` to the decorations described by `newDecorations` * and returns the new identifiers associated with the resulting decorations. * @@ -903,7 +903,7 @@ export interface ITextModel { getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; /** - * Gets all the deocorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. + * Gets all the decorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. * So for now it returns all the decorations on the same line as `range`. * @param range The range to search in * @param ownerId If set, it will ignore decorations belonging to other owners. @@ -970,7 +970,7 @@ export interface ITextModel { /** * Push edit operations, basically editing the model. This is the preferred way * of editing the model. The edit operations will land on the undo stack. - * @param beforeCursorState The cursor state before the edit operaions. This cursor state will be returned when `undo` or `redo` are invoked. + * @param beforeCursorState The cursor state before the edit operations. This cursor state will be returned when `undo` or `redo` are invoked. * @param editOperations The edit operations. * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. * @return The cursor state returned by the `cursorStateComputer`. diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index eda58f8d7b6..0698587692a 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError } from 'vs/base/common/errors'; -import { ICursorStateComputer, IIdentifiedSingleEditOperation, EndOfLineSequence } from 'vs/editor/common/model'; import { Selection } from 'vs/editor/common/core/selection'; +import { EndOfLineSequence, ICursorStateComputer, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; interface IEditOperation { diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index 9a2723697c0..1800edbf0e4 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { Range } from 'vs/editor/common/core/range'; -import { IModelDecoration, TrackedRangeStickiness as ActualTrackedRangeStickiness } from 'vs/editor/common/model'; +import { IModelDecoration, TrackedRangeStickiness, TrackedRangeStickiness as ActualTrackedRangeStickiness } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; // // The red-black tree is based on the "Introduction to Algorithms" by Cormen, Leiserson and Rivest. @@ -20,17 +20,6 @@ export const enum ClassName { EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary' } -/** - * Describes the behavior of decorations when typing/editing near their edges. - * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` - */ -const enum TrackedRangeStickiness { - AlwaysGrowsWhenTypingAtEdges = 0, - NeverGrowsWhenTypingAtEdges = 1, - GrowsOnlyWhenTypingBefore = 2, - GrowsOnlyWhenTypingAfter = 3, -} - export const enum NodeColor { Black = 0, Red = 1, diff --git a/src/vs/editor/common/model/mirrorTextModel.ts b/src/vs/editor/common/model/mirrorTextModel.ts index 377c17d52b2..c28d8febfc1 100644 --- a/src/vs/editor/common/model/mirrorTextModel.ts +++ b/src/vs/editor/common/model/mirrorTextModel.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { IRange } from 'vs/editor/common/core/range'; -import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { IModelContentChange } from 'vs/editor/common/model/textModelEvents'; import { Position } from 'vs/editor/common/core/position'; +import { IRange } from 'vs/editor/common/core/range'; +import { IModelContentChange } from 'vs/editor/common/model/textModelEvents'; +import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; export interface IModelChangedEvent { /** diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index 99ada260453..788991c3165 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from 'vs/editor/common/core/position'; import { CharCode } from 'vs/base/common/charCode'; +import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { ITextSnapshot } from 'vs/platform/files/common/files'; -import { leftest, righttest, updateTreeMetadata, rbDelete, fixInsert, NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; -import { SearchData, isValidMatch, Searcher, createFindMatch } from 'vs/editor/common/model/textModelSearch'; import { FindMatch } from 'vs/editor/common/model'; +import { NodeColor, SENTINEL, TreeNode, fixInsert, leftest, rbDelete, righttest, updateTreeMetadata } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; +import { SearchData, Searcher, createFindMatch, isValidMatch } from 'vs/editor/common/model/textModelSearch'; +import { ITextSnapshot } from 'vs/platform/files/common/files'; // const lfRegex = new RegExp(/\r\n|\r|\n/g); export const AverageBufferSize = 65535; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index ee21a3311c9..1030643a13d 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; import * as strings from 'vs/base/common/strings'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { ApplyEditsResult, EndOfLinePreference, FindMatch, IIdentifiedSingleEditOperation, IInternalModelContentChange, ISingleEditOperationIdentifier, ITextBuffer } from 'vs/editor/common/model'; import { PieceTreeBase, StringBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; -import { IIdentifiedSingleEditOperation, EndOfLinePreference, ITextBuffer, ApplyEditsResult, IInternalModelContentChange, FindMatch, ISingleEditOperationIdentifier } from 'vs/editor/common/model'; -import { ITextSnapshot } from 'vs/platform/files/common/files'; import { SearchData } from 'vs/editor/common/model/textModelSearch'; +import { ITextSnapshot } from 'vs/platform/files/common/files'; export interface IValidatedEditOperation { sortIndex: number; @@ -312,7 +312,7 @@ export class PieceTreeTextBuffer implements ITextBuffer { } // At one point, due to how events are emitted and how each operation is handled, - // some operations can trigger a high ammount of temporary string allocations, + // some operations can trigger a high amount of temporary string allocations, // that will immediately get edited again. // e.g. a formatter inserting ridiculous ammounts of \n on a model with a single line // Therefore, the strategy is to collapse all the operations into a huge single edit operation diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts index ab3c2837f28..7b00a7bf3e5 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as strings from 'vs/base/common/strings'; -import { ITextBufferBuilder, DefaultEndOfLine, ITextBufferFactory, ITextBuffer } from 'vs/editor/common/model'; -import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer'; -import { StringBuffer, createLineStarts, createLineStartsFast } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; import { CharCode } from 'vs/base/common/charCode'; +import * as strings from 'vs/base/common/strings'; +import { DefaultEndOfLine, ITextBuffer, ITextBufferBuilder, ITextBufferFactory } from 'vs/editor/common/model'; +import { StringBuffer, createLineStarts, createLineStartsFast } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; +import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer'; export class PieceTreeTextBufferFactory implements ITextBufferFactory { diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 916bb770388..8288a98e190 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -3,36 +3,35 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; -import { Event, Emitter } from 'vs/base/common/event'; -import * as model from 'vs/editor/common/model'; -import { LanguageIdentifier, TokenizationRegistry, LanguageId, IState } from 'vs/editor/common/modes'; -import { EditStack } from 'vs/editor/common/model/editStack'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; -import { ModelRawContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelTokensChangedEvent, IModelOptionsChangedEvent, IModelContentChangedEvent, InternalModelContentChangeEvent, ModelRawFlush, ModelRawEOLChanged, ModelRawChange, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from 'vs/editor/common/model/textModelEvents'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; -import * as strings from 'vs/base/common/strings'; import { CharCode } from 'vs/base/common/charCode'; -import { ThemeColor, ITheme } from 'vs/platform/theme/common/themeService'; -import { IntervalNode, IntervalTree, recomputeMaxEnd, getNodeIsInOverviewRuler } from 'vs/editor/common/model/intervalTree'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; +import * as strings from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; +import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as model from 'vs/editor/common/model'; +import { EditStack } from 'vs/editor/common/model/editStack'; +import { guessIndentation } from 'vs/editor/common/model/indentationGuesser'; +import { IntervalNode, IntervalTree, getNodeIsInOverviewRuler, recomputeMaxEnd } from 'vs/editor/common/model/intervalTree'; +import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; +import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, InternalModelContentChangeEvent, ModelRawChange, ModelRawContentChangedEvent, ModelRawEOLChanged, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from 'vs/editor/common/model/textModelEvents'; +import { SearchData, SearchParams, TextModelSearch } from 'vs/editor/common/model/textModelSearch'; +import { ModelLinesTokens, ModelTokensChangedEventBuilder } from 'vs/editor/common/model/textModelTokens'; +import { getWordAtText } from 'vs/editor/common/model/wordHelper'; +import { IState, LanguageId, LanguageIdentifier, TokenizationRegistry } from 'vs/editor/common/modes'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode'; import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; -import { BracketsUtils, RichEditBrackets, RichEditBracket } from 'vs/editor/common/modes/supports/richEditBrackets'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { LineTokens } from 'vs/editor/common/core/lineTokens'; -import { getWordAtText } from 'vs/editor/common/model/wordHelper'; -import { ModelLinesTokens, ModelTokensChangedEventBuilder } from 'vs/editor/common/model/textModelTokens'; -import { guessIndentation } from 'vs/editor/common/model/indentationGuesser'; -import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { TextModelSearch, SearchParams, SearchData } from 'vs/editor/common/model/textModelSearch'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; import { IStringStream, ITextSnapshot } from 'vs/platform/files/common/files'; -import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; +import { ITheme, ThemeColor } from 'vs/platform/theme/common/themeService'; const CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048; @@ -46,8 +45,8 @@ export function createTextBufferFactory(text: string): model.ITextBufferFactory return builder.finish(); } -export function createTextBufferFactoryFromStream(stream: IStringStream, filter?: (chunk: string) => string): TPromise { - return new TPromise((c, e) => { +export function createTextBufferFactoryFromStream(stream: IStringStream, filter?: (chunk: string) => string): Promise { + return new Promise((c, e) => { let done = false; let builder = createTextBufferBuilder(); @@ -2005,7 +2004,8 @@ export class TextModel extends Disposable implements model.ITextModel { lineContent.substring(rbStartOffset, rbEndOffset), rbStartOffset ); - if (rightBiasedWord) { + // Make sure the result touches the original passed in position + if (rightBiasedWord && rightBiasedWord.startColumn <= _position.column && _position.column <= rightBiasedWord.endColumn) { return rightBiasedWord; } @@ -2019,7 +2019,8 @@ export class TextModel extends Disposable implements model.ITextModel { lineContent.substring(lbStartOffset, lbEndOffset), lbStartOffset ); - if (leftBiasedWord) { + // Make sure the result touches the original passed in position + if (leftBiasedWord && leftBiasedWord.startColumn <= _position.column && _position.column <= leftBiasedWord.endColumn) { return leftBiasedWord; } } diff --git a/src/vs/editor/common/model/textModelSearch.ts b/src/vs/editor/common/model/textModelSearch.ts index cb4c3128f10..9f776c68654 100644 --- a/src/vs/editor/common/model/textModelSearch.ts +++ b/src/vs/editor/common/model/textModelSearch.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; +import { WordCharacterClass, WordCharacterClassifier, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { FindMatch, EndOfLinePreference } from 'vs/editor/common/model'; -import { CharCode } from 'vs/base/common/charCode'; +import { EndOfLinePreference, FindMatch } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { getMapForWordSeparators, WordCharacterClassifier, WordCharacterClass } from 'vs/editor/common/controller/wordCharacterClassifier'; const LIMIT_FIND_COUNT = 999; diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index 8144fead98c..65098e266a1 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IState, FontStyle, StandardTokenType, MetadataConsts, ColorId, LanguageId, ITokenizationSupport, LanguageIdentifier } from 'vs/editor/common/modes'; -import { LineTokens } from 'vs/editor/common/core/lineTokens'; import * as arrays from 'vs/base/common/arrays'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { IModelTokensChangedEvent } from 'vs/editor/common/model/textModelEvents'; -import { onUnexpectedError } from 'vs/base/common/errors'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; -import { nullTokenize2 } from 'vs/editor/common/modes/nullMode'; import { ITextBuffer } from 'vs/editor/common/model'; +import { IModelTokensChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import { ColorId, FontStyle, IState, ITokenizationSupport, LanguageId, LanguageIdentifier, MetadataConsts, StandardTokenType } from 'vs/editor/common/modes'; +import { nullTokenize2 } from 'vs/editor/common/modes/nullMode'; function getDefaultMetadata(topLevelLanguageId: LanguageId): number { return ( diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 90d6c2d6bf8..f1437578194 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -15,7 +15,7 @@ import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; import * as model from 'vs/editor/common/model'; -import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; +import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry'; import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry'; import { IMarkerData } from 'vs/platform/markers/common/markers'; @@ -254,7 +254,7 @@ export interface HoverProvider { provideHover(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } -export enum CompletionItemKind { +export const enum CompletionItemKind { Method, Function, Constructor, @@ -357,7 +357,7 @@ export let completionKindFromLegacyString = (function () { }; })(); -export enum CompletionItemInsertTextRule { +export const enum CompletionItemInsertTextRule { /** * Adjust whitespace/indentation of multiline insert texts to * match the current line indentation. @@ -468,7 +468,7 @@ export interface CompletionList { /** * How a suggest provider was triggered. */ -export enum CompletionTriggerKind { +export const enum CompletionTriggerKind { Invoke = 0, TriggerCharacter = 1, TriggerForIncompleteCompletions = 2 @@ -618,12 +618,13 @@ export interface SignatureHelp { export enum SignatureHelpTriggerReason { Invoke = 1, TriggerCharacter = 2, - Retrigger = 3, + ContentChange = 3, } export interface SignatureHelpContext { - triggerReason: SignatureHelpTriggerReason; - triggerCharacter?: string; + readonly triggerReason: SignatureHelpTriggerReason; + readonly triggerCharacter?: string; + readonly isRetrigger: boolean; } /** @@ -771,7 +772,7 @@ export interface TypeDefinitionProvider { /** * A symbol kind. */ -export enum SymbolKind { +export const enum SymbolKind { File = 0, Module = 1, Namespace = 2, @@ -1101,7 +1102,7 @@ export interface ResourceTextEdit { } export interface WorkspaceEdit { - edits: Array; + edits?: Array; } export interface Rejection { diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index d12a89f0c69..86b87604c01 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -86,7 +86,7 @@ export interface LanguageConfiguration { */ export interface IndentationRule { /** - * If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches). + * If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches). */ decreaseIndentPattern: RegExp; /** @@ -120,7 +120,7 @@ export interface FoldingMarkers { */ export interface FoldingRules { /** - * Used by the indentation based strategy to decide wheter empty lines belong to the previous or the next block. + * Used by the indentation based strategy to decide whether empty lines belong to the previous or the next block. * A language adheres to the off-side rule if blocks in that language are expressed by their indentation. * See [wikipedia](https://en.wikipedia.org/wiki/Off-side_rule) for more information. * If not set, `false` is used and empty lines belong to the previous block. diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index eafbd9290e6..50c6663b7fe 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -3,22 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; -import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; -import { IOnEnterSupportOptions, OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter'; -import { IndentRulesSupport, IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; -import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ITextModel } from 'vs/editor/common/model'; import { onUnexpectedError } from 'vs/base/common/errors'; -import * as strings from 'vs/base/common/strings'; +import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; -import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; +import * as strings from 'vs/base/common/strings'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { Range } from 'vs/editor/common/core/range'; -import { IndentAction, EnterAction, IAutoClosingPair, LanguageConfiguration, IndentationRule, FoldingRules, IAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; -import { LanguageIdentifier, LanguageId } from 'vs/editor/common/modes'; +import { ITextModel } from 'vs/editor/common/model'; +import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; +import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; +import { EnterAction, FoldingRules, IAutoClosingPair, IAutoClosingPairConditional, IndentAction, IndentationRule, LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; +import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; +import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; +import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; +import { IndentConsts, IndentRulesSupport } from 'vs/editor/common/modes/supports/indentRules'; +import { IOnEnterSupportOptions, OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter'; +import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; /** * Interface used to support insertion of mode specific comments. diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index eef1757486e..616a4a40d98 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ITextModel } from 'vs/editor/common/model'; import { LanguageSelector, score } from 'vs/editor/common/modes/languageSelector'; @@ -26,7 +26,7 @@ function isExclusive(selector: LanguageSelector): boolean { } } -export default class LanguageFeatureRegistry { +export class LanguageFeatureRegistry { private _clock: number = 0; private _entries: Entry[] = []; diff --git a/src/vs/editor/common/modes/languageSelector.ts b/src/vs/editor/common/modes/languageSelector.ts index 95000ff7fd0..dbb2bdc5a1e 100644 --- a/src/vs/editor/common/modes/languageSelector.ts +++ b/src/vs/editor/common/modes/languageSelector.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; -import { match as matchGlobPattern, IRelativePattern } from 'vs/base/common/glob'; // TODO@Alex +import { IRelativePattern, match as matchGlobPattern } from 'vs/base/common/glob'; +import { URI } from 'vs/base/common/uri'; // TODO@Alex export interface LanguageFilter { language?: string; diff --git a/src/vs/editor/common/modes/linkComputer.ts b/src/vs/editor/common/modes/linkComputer.ts index dbf78a728d0..513f1a13892 100644 --- a/src/vs/editor/common/modes/linkComputer.ts +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILink } from 'vs/editor/common/modes'; import { CharCode } from 'vs/base/common/charCode'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; import { Uint8Matrix } from 'vs/editor/common/core/uint'; +import { ILink } from 'vs/editor/common/modes'; export interface ILinkComputerTarget { getLineCount(): number; diff --git a/src/vs/editor/common/modes/modesRegistry.ts b/src/vs/editor/common/modes/modesRegistry.ts index 5cae5207baf..7755048e858 100644 --- a/src/vs/editor/common/modes/modesRegistry.ts +++ b/src/vs/editor/common/modes/modesRegistry.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { Event, Emitter } from 'vs/base/common/event'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; +import { Emitter, Event } from 'vs/base/common/event'; +import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { LanguageIdentifier, LanguageId } from 'vs/editor/common/modes'; +import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; +import { Registry } from 'vs/platform/registry/common/platform'; // Define extension point ids export const Extensions = { diff --git a/src/vs/editor/common/modes/nullMode.ts b/src/vs/editor/common/modes/nullMode.ts index b0d8fe393b2..4a010be00ff 100644 --- a/src/vs/editor/common/modes/nullMode.ts +++ b/src/vs/editor/common/modes/nullMode.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IState, ColorId, MetadataConsts, LanguageIdentifier, FontStyle, StandardTokenType, LanguageId } from 'vs/editor/common/modes'; import { Token, TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; +import { ColorId, FontStyle, IState, LanguageId, LanguageIdentifier, MetadataConsts, StandardTokenType } from 'vs/editor/common/modes'; class NullStateImpl implements IState { diff --git a/src/vs/editor/common/modes/supports.ts b/src/vs/editor/common/modes/supports.ts index e6115e5c337..b8cb5201ca7 100644 --- a/src/vs/editor/common/modes/supports.ts +++ b/src/vs/editor/common/modes/supports.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as modes from 'vs/editor/common/modes'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import * as modes from 'vs/editor/common/modes'; export function createScopedLineTokens(context: LineTokens, offset: number): ScopedLineTokens { let tokenCount = context.getCount(); diff --git a/src/vs/editor/common/modes/supports/characterPair.ts b/src/vs/editor/common/modes/supports/characterPair.ts index c3e09595102..f2ba775b603 100644 --- a/src/vs/editor/common/modes/supports/characterPair.ts +++ b/src/vs/editor/common/modes/supports/characterPair.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ScopedLineTokens } from 'vs/editor/common/modes/supports'; import { CharacterPair, IAutoClosingPair, IAutoClosingPairConditional, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; +import { ScopedLineTokens } from 'vs/editor/common/modes/supports'; export class CharacterPairSupport { diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts index 33273ab64be..1f83c639e5e 100644 --- a/src/vs/editor/common/modes/supports/electricCharacter.ts +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IAutoClosingPairConditional, IBracketElectricCharacterContribution, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { ScopedLineTokens, ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; import { BracketsUtils, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; -import { IAutoClosingPairConditional, IBracketElectricCharacterContribution, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; /** * Interface used to support electric characters diff --git a/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts b/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts index 3e92e29300d..d49dc63f57e 100644 --- a/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts +++ b/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IInplaceReplaceSupportResult } from 'vs/editor/common/modes'; import { IRange } from 'vs/editor/common/core/range'; +import { IInplaceReplaceSupportResult } from 'vs/editor/common/modes'; export class BasicInplaceReplace { diff --git a/src/vs/editor/common/modes/supports/onEnter.ts b/src/vs/editor/common/modes/supports/onEnter.ts index 6822b79541d..6021d4bf8e3 100644 --- a/src/vs/editor/common/modes/supports/onEnter.ts +++ b/src/vs/editor/common/modes/supports/onEnter.ts @@ -5,7 +5,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; -import { CharacterPair, IndentAction, EnterAction, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; +import { CharacterPair, EnterAction, IndentAction, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; export interface IOnEnterSupportOptions { brackets?: CharacterPair[]; diff --git a/src/vs/editor/common/modes/supports/richEditBrackets.ts b/src/vs/editor/common/modes/supports/richEditBrackets.ts index 5a389e8a1b0..758ea39f1ff 100644 --- a/src/vs/editor/common/modes/supports/richEditBrackets.ts +++ b/src/vs/editor/common/modes/supports/richEditBrackets.ts @@ -5,8 +5,8 @@ import * as strings from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; -import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; interface ISimpleInternalBracket { open: string; diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index bcc7c93cbb7..e58c791f60b 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ColorId, FontStyle, MetadataConsts, LanguageId, StandardTokenType } from 'vs/editor/common/modes'; import { Color } from 'vs/base/common/color'; +import { ColorId, FontStyle, LanguageId, MetadataConsts, StandardTokenType } from 'vs/editor/common/modes'; export interface ITokenThemeRule { token: string; diff --git a/src/vs/editor/common/modes/textToHtmlTokenizer.ts b/src/vs/editor/common/modes/textToHtmlTokenizer.ts index 88a888c06d7..80212b74264 100644 --- a/src/vs/editor/common/modes/textToHtmlTokenizer.ts +++ b/src/vs/editor/common/modes/textToHtmlTokenizer.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as strings from 'vs/base/common/strings'; -import { IState, LanguageId } from 'vs/editor/common/modes'; -import { LineTokens, IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { CharCode } from 'vs/base/common/charCode'; -import { NULL_STATE, nullTokenize2 } from 'vs/editor/common/modes/nullMode'; +import * as strings from 'vs/base/common/strings'; +import { IViewLineTokens, LineTokens } from 'vs/editor/common/core/lineTokens'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { IState, LanguageId } from 'vs/editor/common/modes'; +import { NULL_STATE, nullTokenize2 } from 'vs/editor/common/modes/nullMode'; export interface IReducedTokenizationSupport { getInitialState(): IState; diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts index 502d76a349c..9afe26388f0 100644 --- a/src/vs/editor/common/modes/tokenizationRegistry.ts +++ b/src/vs/editor/common/modes/tokenizationRegistry.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent } from 'vs/editor/common/modes'; import { Color } from 'vs/base/common/color'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent } from 'vs/editor/common/modes'; export class TokenizationRegistryImpl implements ITokenizationRegistry { diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 81be846bc03..84590d69ace 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -3,25 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; -import { stringDiff } from 'vs/base/common/diff/diff'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { MirrorTextModel as BaseMirrorModel, IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel'; -import { IInplaceReplaceSupportResult, ILink, CompletionList, CompletionItem, TextEdit, CompletionItemKind } from 'vs/editor/common/modes'; -import { computeLinks, ILinkComputerTarget } from 'vs/editor/common/modes/linkComputer'; -import { BasicInplaceReplace } from 'vs/editor/common/modes/supports/inplaceReplaceSupport'; -import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; -import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'; -import { IWordAtPosition, EndOfLineSequence } from 'vs/editor/common/model'; -import { globals } from 'vs/base/common/platform'; -import { Iterator, IteratorResult, FIN } from 'vs/base/common/iterator'; import { mergeSort } from 'vs/base/common/arrays'; +import { stringDiff } from 'vs/base/common/diff/diff'; +import { FIN, Iterator, IteratorResult } from 'vs/base/common/iterator'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { globals } from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; +import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EndOfLineSequence, IWordAtPosition } from 'vs/editor/common/model'; +import { IModelChangedEvent, MirrorTextModel as BaseMirrorModel } from 'vs/editor/common/model/mirrorTextModel'; +import { ensureValidWordDefinition, getWordAtText } from 'vs/editor/common/model/wordHelper'; +import { CompletionItem, CompletionItemKind, CompletionList, IInplaceReplaceSupportResult, ILink, TextEdit } from 'vs/editor/common/modes'; +import { ILinkComputerTarget, computeLinks } from 'vs/editor/common/modes/linkComputer'; +import { BasicInplaceReplace } from 'vs/editor/common/modes/supports/inplaceReplaceSupport'; import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService'; +import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'; export interface IMirrorModel { readonly uri: URI; diff --git a/src/vs/editor/common/services/editorWorkerService.ts b/src/vs/editor/common/services/editorWorkerService.ts index d6ca6511a2d..e2134a7f59e 100644 --- a/src/vs/editor/common/services/editorWorkerService.ts +++ b/src/vs/editor/common/services/editorWorkerService.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IRange } from 'vs/editor/common/core/range'; import { IChange, ILineChange } from 'vs/editor/common/editorCommon'; import { IInplaceReplaceSupportResult, TextEdit } from 'vs/editor/common/modes'; -import { IRange } from 'vs/editor/common/core/range'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const ID_EDITOR_WORKER_SERVICE = 'editorWorkerService'; export const IEditorWorkerService = createDecorator(ID_EDITOR_WORKER_SERVICE); diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index de2c7f9df10..5870441dc60 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -8,17 +8,17 @@ import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/l import { URI } from 'vs/base/common/uri'; import { SimpleWorkerClient, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker'; import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import * as modes from 'vs/editor/common/modes'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { IEditorWorkerService, IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; +import * as modes from 'vs/editor/common/modes'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker'; +import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; /** * Stop syncing a model to the worker if it was not needed for 1 min. diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index 98e2610cecd..7861390e05b 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -3,15 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - +import { Schemas } from 'vs/base/common/network'; +import { DataUri, basenameOrAuthority } from 'vs/base/common/resources'; import { URI as uri } from 'vs/base/common/uri'; +import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { Schemas } from 'vs/base/common/network'; import { FileKind } from 'vs/platform/files/common/files'; -import { basenameOrAuthority, DataUri } from 'vs/base/common/resources'; -import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; export function getIconClasses(modelService: IModelService, modeService: IModeService, resource: uri, fileKind?: FileKind): string[] { // we always set these base classes even if we do not have a path diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts index 85428ccca5e..6b4aef605b0 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -4,15 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; import * as mime from 'vs/base/common/mime'; import * as strings from 'vs/base/common/strings'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; -import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; -import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; -import { NULL_MODE_ID, NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode'; -import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { URI } from 'vs/base/common/uri'; +import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; +import { NULL_LANGUAGE_IDENTIFIER, NULL_MODE_ID } from 'vs/editor/common/modes/nullMode'; +import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; const hasOwnProperty = Object.prototype.hasOwnProperty; @@ -26,7 +28,10 @@ export interface IResolvedLanguage { configurationFiles: URI[]; } -export class LanguagesRegistry { +export class LanguagesRegistry extends Disposable { + + private readonly _onDidChange: Emitter = this._register(new Emitter()); + public readonly onDidChange: Event = this._onDidChange.event; private _nextLanguageId: number; private _languages: { [id: string]: IResolvedLanguage; }; @@ -39,6 +44,7 @@ export class LanguagesRegistry { private _warnOnOverwrite: boolean; constructor(useModesRegistry = true, warnOnOverwrite = false) { + super(); this._nextLanguageId = 1; this._languages = {}; this._mimeTypesMap = {}; @@ -49,7 +55,7 @@ export class LanguagesRegistry { if (useModesRegistry) { this._registerLanguages(ModesRegistry.getLanguages()); - ModesRegistry.onDidAddLanguages((m) => this._registerLanguages(m)); + this._register(ModesRegistry.onDidAddLanguages((m) => this._registerLanguages(m))); } } @@ -80,6 +86,8 @@ export class LanguagesRegistry { }); Registry.as(Extensions.Configuration).registerOverrideIdentifiers(ModesRegistry.getLanguages().map(language => language.id)); + + this._onDidChange.fire(); } private _registerLanguage(lang: ILanguageExtensionPoint): void { diff --git a/src/vs/editor/common/services/modeService.ts b/src/vs/editor/common/services/modeService.ts index 46041b2b644..2c456c57828 100644 --- a/src/vs/editor/common/services/modeService.ts +++ b/src/vs/editor/common/services/modeService.ts @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IModeService = createDecorator('modeService'); @@ -21,6 +22,11 @@ export interface ILanguageExtensionPoint { configuration?: URI; } +export interface ILanguageSelection extends IDisposable { + readonly languageIdentifier: LanguageIdentifier; + readonly onDidChange: Event; +} + export interface IModeService { _serviceBrand: any; @@ -41,8 +47,9 @@ export interface IModeService { getConfigurationFiles(modeId: string): URI[]; // --- instantiation - getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): IMode | null; - getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): Promise; - getOrCreateModeByLanguageName(languageName: string): Promise; - getOrCreateModeByFilepathOrFirstLine(filepath: string, firstLine?: string): Promise; + create(commaSeparatedMimetypesOrCommaSeparatedIds: string): ILanguageSelection; + createByLanguageName(languageName: string): ILanguageSelection; + createByFilepathOrFirstLine(filepath: string, firstLine?: string): ILanguageSelection; + + triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): void; } diff --git a/src/vs/editor/common/services/modeServiceImpl.ts b/src/vs/editor/common/services/modeServiceImpl.ts index 73bed4eca50..615c4b979f2 100644 --- a/src/vs/editor/common/services/modeServiceImpl.ts +++ b/src/vs/editor/common/services/modeServiceImpl.ts @@ -3,14 +3,41 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { onUnexpectedError } from 'vs/base/common/errors'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; import { FrankensteinMode } from 'vs/editor/common/modes/abstractMode'; -import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { URI } from 'vs/base/common/uri'; import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode'; +import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry'; +import { ILanguageSelection, IModeService } from 'vs/editor/common/services/modeService'; + +class LanguageSelection extends Disposable implements ILanguageSelection { + + public languageIdentifier: LanguageIdentifier; + + private readonly _selector: () => LanguageIdentifier; + + private readonly _onDidChange: Emitter = this._register(new Emitter()); + public readonly onDidChange: Event = this._onDidChange.event; + + constructor(onLanguagesMaybeChanged: Event, selector: () => LanguageIdentifier) { + super(); + this._selector = selector; + this.languageIdentifier = this._selector(); + this._register(onLanguagesMaybeChanged(() => this._evaluate())); + } + + private _evaluate(): void { + let languageIdentifier = this._selector(); + if (languageIdentifier.id === this.languageIdentifier.id) { + // no change + return; + } + this.languageIdentifier = languageIdentifier; + this._onDidChange.fire(this.languageIdentifier); + } +} export class ModeServiceImpl implements IModeService { public _serviceBrand: any; @@ -21,10 +48,14 @@ export class ModeServiceImpl implements IModeService { private readonly _onDidCreateMode: Emitter = new Emitter(); public readonly onDidCreateMode: Event = this._onDidCreateMode.event; + protected readonly _onLanguagesMaybeChanged: Emitter = new Emitter(); + private readonly onLanguagesMaybeChanged: Event = this._onLanguagesMaybeChanged.event; + constructor(warnOnOverwrite = false) { this._instantiatedModes = {}; this._registry = new LanguagesRegistry(true, warnOnOverwrite); + this._registry.onDidChange(() => this._onLanguagesMaybeChanged.fire()); } protected _onReady(): Promise { @@ -93,44 +124,44 @@ export class ModeServiceImpl implements IModeService { // --- instantiation - public getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): IMode | null { - const modeIds = this._registry.extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds); - - let isPlainText = false; - for (let i = 0; i < modeIds.length; i++) { - if (this._instantiatedModes.hasOwnProperty(modeIds[i])) { - return this._instantiatedModes[modeIds[i]]; - } - isPlainText = isPlainText || (modeIds[i] === 'plaintext'); - } - - if (isPlainText) { - // Try to do it synchronously - let r: IMode | null = null; - this.getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds).then((mode) => { - r = mode; - }, onUnexpectedError); - return r; - } - return null; - } - - public getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): Promise { - return this._onReady().then(() => { + public create(commaSeparatedMimetypesOrCommaSeparatedIds: string): ILanguageSelection { + return new LanguageSelection(this.onLanguagesMaybeChanged, () => { const modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds); - // Fall back to plain text if no mode was found - return this._getOrCreateMode(modeId || 'plaintext'); + return this._createModeAndGetLanguageIdentifier(modeId); }); } - public getOrCreateModeByLanguageName(languageName: string): Promise { - return this._onReady().then(() => { + public createByLanguageName(languageName: string): ILanguageSelection { + return new LanguageSelection(this.onLanguagesMaybeChanged, () => { const modeId = this._getModeIdByLanguageName(languageName); - // Fall back to plain text if no mode was found - return this._getOrCreateMode(modeId || 'plaintext'); + return this._createModeAndGetLanguageIdentifier(modeId); }); } + public createByFilepathOrFirstLine(filepath: string, firstLine?: string): ILanguageSelection { + return new LanguageSelection(this.onLanguagesMaybeChanged, () => { + const modeId = this.getModeIdByFilepathOrFirstLine(filepath, firstLine); + return this._createModeAndGetLanguageIdentifier(modeId); + }); + } + + private _createModeAndGetLanguageIdentifier(modeId: string | null): LanguageIdentifier { + // Fall back to plain text if no mode was found + const languageIdentifier = this.getLanguageIdentifier(modeId || 'plaintext') || NULL_LANGUAGE_IDENTIFIER; + this._getOrCreateMode(languageIdentifier.language); + return languageIdentifier; + } + + public triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): void { + const modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds); + // Fall back to plain text if no mode was found + this._getOrCreateMode(modeId || 'plaintext'); + } + + public waitForLanguageRegistration(): Promise { + return this._onReady().then(() => { }); + } + private _getModeIdByLanguageName(languageName: string): string | null { const modeIds = this._registry.getModeIdsFromLanguageName(languageName); @@ -141,14 +172,6 @@ export class ModeServiceImpl implements IModeService { return null; } - public getOrCreateModeByFilepathOrFirstLine(filepath: string, firstLine?: string): Promise { - return this._onReady().then(() => { - const modeId = this.getModeIdByFilepathOrFirstLine(filepath, firstLine); - // Fall back to plain text if no mode was found - return this._getOrCreateMode(modeId || 'plaintext'); - }); - } - private _getOrCreateMode(modeId: string): IMode { if (!this._instantiatedModes.hasOwnProperty(modeId)) { let languageIdentifier = this.getLanguageIdentifier(modeId) || NULL_LANGUAGE_IDENTIFIER; diff --git a/src/vs/editor/common/services/modelService.ts b/src/vs/editor/common/services/modelService.ts index 7199eb72c94..a75af8a1f49 100644 --- a/src/vs/editor/common/services/modelService.ts +++ b/src/vs/editor/common/services/modelService.ts @@ -5,20 +5,20 @@ import { Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; +import { ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ITextModel, ITextModelCreationOptions, ITextBufferFactory } from 'vs/editor/common/model'; -import { IMode } from 'vs/editor/common/modes'; export const IModelService = createDecorator('modelService'); export interface IModelService { _serviceBrand: any; - createModel(value: string | ITextBufferFactory, modeOrPromise: Promise | IMode, resource: URI | undefined, isForSimpleWidget?: boolean): ITextModel; + createModel(value: string | ITextBufferFactory, languageSelection: ILanguageSelection | null, resource: URI, isForSimpleWidget?: boolean): ITextModel; updateModel(model: ITextModel, value: string | ITextBufferFactory): void; - setMode(model: ITextModel, modeOrPromise: Promise | IMode): void; + setMode(model: ITextModel, languageSelection: ILanguageSelection): void; destroyModel(resource: URI): void; diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 82cc827cee7..b8386637f8c 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -4,37 +4,40 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as network from 'vs/base/common/network'; -import { Event, Emitter } from 'vs/base/common/event'; -import { MarkdownString } from 'vs/base/common/htmlContent'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import { IMarker, IMarkerService, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers'; -import { Range } from 'vs/editor/common/core/range'; -import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel'; -import { IMode, LanguageIdentifier } from 'vs/editor/common/modes'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import * as platform from 'vs/base/common/platform'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry'; -import { IModelLanguageChangedEvent } from 'vs/editor/common/model/textModelEvents'; -import { ClassName } from 'vs/editor/common/model/intervalTree'; -import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService'; -import { overviewRulerWarning, overviewRulerError, overviewRulerInfo } from 'vs/editor/common/view/editorColorRegistry'; -import { ITextModel, IModelDeltaDecoration, IModelDecorationOptions, TrackedRangeStickiness, OverviewRulerLane, DefaultEndOfLine, ITextModelCreationOptions, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBufferFactory, ITextBuffer, EndOfLinePreference } from 'vs/editor/common/model'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { Emitter, Event } from 'vs/base/common/event'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as network from 'vs/base/common/network'; import { basename } from 'vs/base/common/paths'; -import { isThenable } from 'vs/base/common/async'; +import * as platform from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; +import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Range } from 'vs/editor/common/core/range'; +import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, IModelDecorationOptions, IModelDeltaDecoration, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { ClassName } from 'vs/editor/common/model/intervalTree'; +import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel'; +import { IModelLanguageChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; +import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; +import { overviewRulerError, overviewRulerInfo, overviewRulerWarning } from 'vs/editor/common/view/editorColorRegistry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IMarker, IMarkerService, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers'; +import { ThemeColor, themeColorFromId } from 'vs/platform/theme/common/themeService'; function MODEL_ID(resource: URI): string { return resource.toString(); } class ModelData implements IDisposable { - model: ITextModel; + public readonly model: ITextModel; + + private _languageSelection: ILanguageSelection | null; + private _languageSelectionListener: IDisposable | null; private _markerDecorations: string[]; private _modelEventListeners: IDisposable[]; @@ -46,6 +49,9 @@ class ModelData implements IDisposable { ) { this.model = model; + this._languageSelection = null; + this._languageSelectionListener = null; + this._markerDecorations = []; this._modelEventListeners = []; @@ -53,14 +59,33 @@ class ModelData implements IDisposable { this._modelEventListeners.push(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e))); } + private _disposeLanguageSelection(): void { + if (this._languageSelectionListener) { + this._languageSelectionListener.dispose(); + this._languageSelectionListener = null; + } + if (this._languageSelection) { + this._languageSelection.dispose(); + this._languageSelection = null; + } + } + public dispose(): void { this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, []); this._modelEventListeners = dispose(this._modelEventListeners); + this._disposeLanguageSelection(); } public acceptMarkerDecorations(newDecorations: IModelDeltaDecoration[]): void { this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, newDecorations); } + + public setLanguage(languageSelection: ILanguageSelection): void { + this._disposeLanguageSelection(); + this._languageSelection = languageSelection; + this._languageSelectionListener = this._languageSelection.onDidChange(() => this.model.setMode(languageSelection.languageIdentifier)); + this.model.setMode(languageSelection.languageIdentifier); + } } class ModelMarkerHandler { @@ -508,14 +533,14 @@ export class ModelServiceImpl extends Disposable implements IModelService { return [EditOperation.replaceMove(oldRange, textBuffer.getValueInRange(newRange, EndOfLinePreference.TextDefined))]; } - public createModel(value: string | ITextBufferFactory, modeOrPromise: Promise | IMode, resource: URI, isForSimpleWidget: boolean = false): ITextModel { + public createModel(value: string | ITextBufferFactory, languageSelection: ILanguageSelection | null, resource: URI, isForSimpleWidget: boolean = false): ITextModel { let modelData: ModelData; - if (!modeOrPromise || isThenable(modeOrPromise)) { - modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource, isForSimpleWidget); - this.setMode(modelData.model, modeOrPromise); + if (languageSelection) { + modelData = this._createModelData(value, languageSelection.languageIdentifier, resource, isForSimpleWidget); + this.setMode(modelData.model, languageSelection); } else { - modelData = this._createModelData(value, modeOrPromise.getLanguageIdentifier(), resource, isForSimpleWidget); + modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource, isForSimpleWidget); } // handle markers (marker service => model) @@ -528,19 +553,15 @@ export class ModelServiceImpl extends Disposable implements IModelService { return modelData.model; } - public setMode(model: ITextModel, modeOrPromise: Promise | IMode): void { - if (!modeOrPromise) { + public setMode(model: ITextModel, languageSelection: ILanguageSelection): void { + if (!languageSelection) { return; } - if (isThenable(modeOrPromise)) { - modeOrPromise.then((mode) => { - if (!model.isDisposed()) { - model.setMode(mode.getLanguageIdentifier()); - } - }); - } else { - model.setMode(modeOrPromise.getLanguageIdentifier()); + let modelData = this._models[MODEL_ID(model.uri)]; + if (!modelData) { + return; } + modelData.setLanguage(languageSelection); } public destroyModel(resource: URI): void { diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/services/resolverService.ts index 32e9fa709b7..f1ed793a58d 100644 --- a/src/vs/editor/common/services/resolverService.ts +++ b/src/vs/editor/common/services/resolverService.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, IReference } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorModel } from 'vs/platform/editor/common/editor'; -import { IDisposable, IReference } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const ITextModelService = createDecorator('textModelService'); diff --git a/src/vs/editor/common/services/resourceConfiguration.ts b/src/vs/editor/common/services/resourceConfiguration.ts index 9cce3626fee..ad8103e5d5f 100644 --- a/src/vs/editor/common/services/resourceConfiguration.ts +++ b/src/vs/editor/common/services/resourceConfiguration.ts @@ -5,9 +5,9 @@ import { Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IPosition } from 'vs/editor/common/core/position'; import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const ITextResourceConfigurationService = createDecorator('textResourceConfigurationService'); diff --git a/src/vs/editor/common/services/resourceConfigurationImpl.ts b/src/vs/editor/common/services/resourceConfigurationImpl.ts index ff77e31a8de..6cd3f5fdb26 100644 --- a/src/vs/editor/common/services/resourceConfigurationImpl.ts +++ b/src/vs/editor/common/services/resourceConfigurationImpl.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class TextResourceConfigurationService extends Disposable implements ITextResourceConfigurationService { diff --git a/src/vs/editor/common/services/webWorker.ts b/src/vs/editor/common/services/webWorker.ts index 0ad1542e042..07035be7f57 100644 --- a/src/vs/editor/common/services/webWorker.ts +++ b/src/vs/editor/common/services/webWorker.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { IModelService } from 'vs/editor/common/services/modelService'; import { EditorWorkerClient } from 'vs/editor/common/services/editorWorkerServiceImpl'; +import { IModelService } from 'vs/editor/common/services/modelService'; /** * Create a new web worker that has model syncing capabilities built in. diff --git a/src/vs/editor/common/standalone/standaloneBase.ts b/src/vs/editor/common/standalone/standaloneBase.ts index dc3aa4befc6..2233abd72a6 100644 --- a/src/vs/editor/common/standalone/standaloneBase.ts +++ b/src/vs/editor/common/standalone/standaloneBase.ts @@ -3,36 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; -import { KeyMod as ConstKeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { KeyChord, KeyMod as ConstKeyMod } from 'vs/base/common/keyCodes'; +import { URI } from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Selection } from 'vs/editor/common/core/selection'; import { Token } from 'vs/editor/common/core/token'; -import { URI } from 'vs/base/common/uri'; +import * as standaloneEnums from 'vs/editor/common/standalone/standaloneEnums'; -// -------------------------------------------- -// This is repeated here so it can be exported -// because TS inlines const enums -// -------------------------------------------- - -export enum MarkerTag { - Unnecessary = 1, -} - -export enum MarkerSeverity { - Hint = 1, - Info = 2, - Warning = 4, - Error = 8, -} - -// -------------------------------------------- -// This is repeated here so it can be exported -// because TS inlines const enums -// -------------------------------------------- export class KeyMod { public static readonly CtrlCmd: number = ConstKeyMod.CtrlCmd; public static readonly Shift: number = ConstKeyMod.Shift; @@ -44,205 +25,20 @@ export class KeyMod { } } -// -------------------------------------------- -// This is repeated here so it can be exported -// because TS inlines const enums -// -------------------------------------------- -/** - * Virtual Key Codes, the value does not hold any inherent meaning. - * Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx - * But these are "more general", as they should work across browsers & OS`s. - */ -export enum KeyCode { - /** - * Placed first to cover the 0 value of the enum. - */ - Unknown = 0, - Backspace = 1, - Tab = 2, - Enter = 3, - Shift = 4, - Ctrl = 5, - Alt = 6, - PauseBreak = 7, - CapsLock = 8, - Escape = 9, - Space = 10, - PageUp = 11, - PageDown = 12, - End = 13, - Home = 14, - LeftArrow = 15, - UpArrow = 16, - RightArrow = 17, - DownArrow = 18, - Insert = 19, - Delete = 20, - KEY_0 = 21, - KEY_1 = 22, - KEY_2 = 23, - KEY_3 = 24, - KEY_4 = 25, - KEY_5 = 26, - KEY_6 = 27, - KEY_7 = 28, - KEY_8 = 29, - KEY_9 = 30, - KEY_A = 31, - KEY_B = 32, - KEY_C = 33, - KEY_D = 34, - KEY_E = 35, - KEY_F = 36, - KEY_G = 37, - KEY_H = 38, - KEY_I = 39, - KEY_J = 40, - KEY_K = 41, - KEY_L = 42, - KEY_M = 43, - KEY_N = 44, - KEY_O = 45, - KEY_P = 46, - KEY_Q = 47, - KEY_R = 48, - KEY_S = 49, - KEY_T = 50, - KEY_U = 51, - KEY_V = 52, - KEY_W = 53, - KEY_X = 54, - KEY_Y = 55, - KEY_Z = 56, - Meta = 57, - ContextMenu = 58, - F1 = 59, - F2 = 60, - F3 = 61, - F4 = 62, - F5 = 63, - F6 = 64, - F7 = 65, - F8 = 66, - F9 = 67, - F10 = 68, - F11 = 69, - F12 = 70, - F13 = 71, - F14 = 72, - F15 = 73, - F16 = 74, - F17 = 75, - F18 = 76, - F19 = 77, - NumLock = 78, - ScrollLock = 79, - /** - * Used for miscellaneous characters; it can vary by keyboard. - * For the US standard keyboard, the ';:' key - */ - US_SEMICOLON = 80, - /** - * For any country/region, the '+' key - * For the US standard keyboard, the '=+' key - */ - US_EQUAL = 81, - /** - * For any country/region, the ',' key - * For the US standard keyboard, the ',<' key - */ - US_COMMA = 82, - /** - * For any country/region, the '-' key - * For the US standard keyboard, the '-_' key - */ - US_MINUS = 83, - /** - * For any country/region, the '.' key - * For the US standard keyboard, the '.>' key - */ - US_DOT = 84, - /** - * Used for miscellaneous characters; it can vary by keyboard. - * For the US standard keyboard, the '/?' key - */ - US_SLASH = 85, - /** - * Used for miscellaneous characters; it can vary by keyboard. - * For the US standard keyboard, the '`~' key - */ - US_BACKTICK = 86, - /** - * Used for miscellaneous characters; it can vary by keyboard. - * For the US standard keyboard, the '[{' key - */ - US_OPEN_SQUARE_BRACKET = 87, - /** - * Used for miscellaneous characters; it can vary by keyboard. - * For the US standard keyboard, the '\|' key - */ - US_BACKSLASH = 88, - /** - * Used for miscellaneous characters; it can vary by keyboard. - * For the US standard keyboard, the ']}' key - */ - US_CLOSE_SQUARE_BRACKET = 89, - /** - * Used for miscellaneous characters; it can vary by keyboard. - * For the US standard keyboard, the ''"' key - */ - US_QUOTE = 90, - /** - * Used for miscellaneous characters; it can vary by keyboard. - */ - OEM_8 = 91, - /** - * Either the angle bracket key or the backslash key on the RT 102-key keyboard. - */ - OEM_102 = 92, - NUMPAD_0 = 93, - NUMPAD_1 = 94, - NUMPAD_2 = 95, - NUMPAD_3 = 96, - NUMPAD_4 = 97, - NUMPAD_5 = 98, - NUMPAD_6 = 99, - NUMPAD_7 = 100, - NUMPAD_8 = 101, - NUMPAD_9 = 102, - NUMPAD_MULTIPLY = 103, - NUMPAD_ADD = 104, - NUMPAD_SEPARATOR = 105, - NUMPAD_SUBTRACT = 106, - NUMPAD_DECIMAL = 107, - NUMPAD_DIVIDE = 108, - /** - * Cover all key codes when IME is processing input. - */ - KEY_IN_COMPOSITION = 109, - ABNT_C1 = 110, - ABNT_C2 = 111, - /** - * Placed last to cover the length of the enum. - * Please do not depend on this value! - */ - MAX_VALUE -} - export function createMonacoBaseAPI(): typeof monaco { return { editor: undefined!, // undefined override expected here languages: undefined!, // undefined override expected here CancellationTokenSource: CancellationTokenSource, Emitter: Emitter, - KeyCode: KeyCode, + KeyCode: standaloneEnums.KeyCode, KeyMod: KeyMod, Position: Position, Range: Range, - Selection: Selection, - SelectionDirection: SelectionDirection, - MarkerSeverity: MarkerSeverity, - MarkerTag: MarkerTag, + Selection: Selection, + SelectionDirection: standaloneEnums.SelectionDirection, + MarkerSeverity: standaloneEnums.MarkerSeverity, + MarkerTag: standaloneEnums.MarkerTag, Promise: TPromise, Uri: URI, Token: Token diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts new file mode 100644 index 00000000000..8e5409b2a0b --- /dev/null +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -0,0 +1,651 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. + + +export enum MarkerTag { + Unnecessary = 1 +} + +export enum MarkerSeverity { + Hint = 1, + Info = 2, + Warning = 4, + Error = 8 +} + +/** + * Virtual Key Codes, the value does not hold any inherent meaning. + * Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx + * But these are "more general", as they should work across browsers & OS`s. + */ +export enum KeyCode { + /** + * Placed first to cover the 0 value of the enum. + */ + Unknown = 0, + Backspace = 1, + Tab = 2, + Enter = 3, + Shift = 4, + Ctrl = 5, + Alt = 6, + PauseBreak = 7, + CapsLock = 8, + Escape = 9, + Space = 10, + PageUp = 11, + PageDown = 12, + End = 13, + Home = 14, + LeftArrow = 15, + UpArrow = 16, + RightArrow = 17, + DownArrow = 18, + Insert = 19, + Delete = 20, + KEY_0 = 21, + KEY_1 = 22, + KEY_2 = 23, + KEY_3 = 24, + KEY_4 = 25, + KEY_5 = 26, + KEY_6 = 27, + KEY_7 = 28, + KEY_8 = 29, + KEY_9 = 30, + KEY_A = 31, + KEY_B = 32, + KEY_C = 33, + KEY_D = 34, + KEY_E = 35, + KEY_F = 36, + KEY_G = 37, + KEY_H = 38, + KEY_I = 39, + KEY_J = 40, + KEY_K = 41, + KEY_L = 42, + KEY_M = 43, + KEY_N = 44, + KEY_O = 45, + KEY_P = 46, + KEY_Q = 47, + KEY_R = 48, + KEY_S = 49, + KEY_T = 50, + KEY_U = 51, + KEY_V = 52, + KEY_W = 53, + KEY_X = 54, + KEY_Y = 55, + KEY_Z = 56, + Meta = 57, + ContextMenu = 58, + F1 = 59, + F2 = 60, + F3 = 61, + F4 = 62, + F5 = 63, + F6 = 64, + F7 = 65, + F8 = 66, + F9 = 67, + F10 = 68, + F11 = 69, + F12 = 70, + F13 = 71, + F14 = 72, + F15 = 73, + F16 = 74, + F17 = 75, + F18 = 76, + F19 = 77, + NumLock = 78, + ScrollLock = 79, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the ';:' key + */ + US_SEMICOLON = 80, + /** + * For any country/region, the '+' key + * For the US standard keyboard, the '=+' key + */ + US_EQUAL = 81, + /** + * For any country/region, the ',' key + * For the US standard keyboard, the ',<' key + */ + US_COMMA = 82, + /** + * For any country/region, the '-' key + * For the US standard keyboard, the '-_' key + */ + US_MINUS = 83, + /** + * For any country/region, the '.' key + * For the US standard keyboard, the '.>' key + */ + US_DOT = 84, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the '/?' key + */ + US_SLASH = 85, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the '`~' key + */ + US_BACKTICK = 86, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the '[{' key + */ + US_OPEN_SQUARE_BRACKET = 87, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the '\|' key + */ + US_BACKSLASH = 88, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the ']}' key + */ + US_CLOSE_SQUARE_BRACKET = 89, + /** + * Used for miscellaneous characters; it can vary by keyboard. + * For the US standard keyboard, the ''"' key + */ + US_QUOTE = 90, + /** + * Used for miscellaneous characters; it can vary by keyboard. + */ + OEM_8 = 91, + /** + * Either the angle bracket key or the backslash key on the RT 102-key keyboard. + */ + OEM_102 = 92, + NUMPAD_0 = 93, + NUMPAD_1 = 94, + NUMPAD_2 = 95, + NUMPAD_3 = 96, + NUMPAD_4 = 97, + NUMPAD_5 = 98, + NUMPAD_6 = 99, + NUMPAD_7 = 100, + NUMPAD_8 = 101, + NUMPAD_9 = 102, + NUMPAD_MULTIPLY = 103, + NUMPAD_ADD = 104, + NUMPAD_SEPARATOR = 105, + NUMPAD_SUBTRACT = 106, + NUMPAD_DECIMAL = 107, + NUMPAD_DIVIDE = 108, + /** + * Cover all key codes when IME is processing input. + */ + KEY_IN_COMPOSITION = 109, + ABNT_C1 = 110, + ABNT_C2 = 111, + /** + * Placed last to cover the length of the enum. + * Please do not depend on this value! + */ + MAX_VALUE = 112 +} + +/** + * The direction of a selection. + */ +export enum SelectionDirection { + /** + * The selection starts above where it ends. + */ + LTR = 0, + /** + * The selection starts below where it ends. + */ + RTL = 1 +} + +export enum ScrollbarVisibility { + Auto = 1, + Hidden = 2, + Visible = 3 +} + +/** + * Vertical Lane in the overview ruler of the editor. + */ +export enum OverviewRulerLane { + Left = 1, + Center = 2, + Right = 4, + Full = 7 +} + +/** + * End of line character preference. + */ +export enum EndOfLinePreference { + /** + * Use the end of line character identified in the text buffer. + */ + TextDefined = 0, + /** + * Use line feed (\n) as the end of line character. + */ + LF = 1, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 2 +} + +/** + * The default end of line to use when instantiating models. + */ +export enum DefaultEndOfLine { + /** + * Use line feed (\n) as the end of line character. + */ + LF = 1, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 2 +} + +/** + * End of line character preference. + */ +export enum EndOfLineSequence { + /** + * Use line feed (\n) as the end of line character. + */ + LF = 0, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 1 +} + +/** + * Describes the behavior of decorations when typing/editing near their edges. + * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` + */ +export enum TrackedRangeStickiness { + AlwaysGrowsWhenTypingAtEdges = 0, + NeverGrowsWhenTypingAtEdges = 1, + GrowsOnlyWhenTypingBefore = 2, + GrowsOnlyWhenTypingAfter = 3 +} + +export enum ScrollType { + Smooth = 0, + Immediate = 1 +} + +/** + * Describes the reason the cursor has changed its position. + */ +export enum CursorChangeReason { + /** + * Unknown or not set. + */ + NotSet = 0, + /** + * A `model.setValue()` was called. + */ + ContentFlush = 1, + /** + * The `model` has been changed outside of this cursor and the cursor recovers its position from associated markers. + */ + RecoverFromMarkers = 2, + /** + * There was an explicit user gesture. + */ + Explicit = 3, + /** + * There was a Paste. + */ + Paste = 4, + /** + * There was an Undo. + */ + Undo = 5, + /** + * There was a Redo. + */ + Redo = 6 +} + +export enum RenderMinimap { + None = 0, + Small = 1, + Large = 2, + SmallBlocks = 3, + LargeBlocks = 4 +} + +/** + * Describes how to indent wrapped lines. + */ +export enum WrappingIndent { + /** + * No indentation => wrapped lines begin at column 1. + */ + None = 0, + /** + * Same => wrapped lines get the same indentation as the parent. + */ + Same = 1, + /** + * Indent => wrapped lines get +1 indentation toward the parent. + */ + Indent = 2, + /** + * DeepIndent => wrapped lines get +2 indentation toward the parent. + */ + DeepIndent = 3 +} + +/** + * The kind of animation in which the editor's cursor should be rendered. + */ +export enum TextEditorCursorBlinkingStyle { + /** + * Hidden + */ + Hidden = 0, + /** + * Blinking + */ + Blink = 1, + /** + * Blinking with smooth fading + */ + Smooth = 2, + /** + * Blinking with prolonged filled state and smooth fading + */ + Phase = 3, + /** + * Expand collapse animation on the y axis + */ + Expand = 4, + /** + * No-Blinking + */ + Solid = 5 +} + +/** + * The style in which the editor's cursor should be rendered. + */ +export enum TextEditorCursorStyle { + /** + * As a vertical line (sitting between two characters). + */ + Line = 1, + /** + * As a block (sitting on top of a character). + */ + Block = 2, + /** + * As a horizontal line (sitting under a character). + */ + Underline = 3, + /** + * As a thin vertical line (sitting between two characters). + */ + LineThin = 4, + /** + * As an outlined block (sitting on top of a character). + */ + BlockOutline = 5, + /** + * As a thin horizontal line (sitting under a character). + */ + UnderlineThin = 6 +} + +export enum RenderLineNumbersType { + Off = 0, + On = 1, + Relative = 2, + Interval = 3, + Custom = 4 +} + +/** + * A positioning preference for rendering content widgets. + */ +export enum ContentWidgetPositionPreference { + /** + * Place the content widget exactly at a position + */ + EXACT = 0, + /** + * Place the content widget above a position + */ + ABOVE = 1, + /** + * Place the content widget below a position + */ + BELOW = 2 +} + +/** + * A positioning preference for rendering overlay widgets. + */ +export enum OverlayWidgetPositionPreference { + /** + * Position the overlay widget in the top right corner + */ + TOP_RIGHT_CORNER = 0, + /** + * Position the overlay widget in the bottom right corner + */ + BOTTOM_RIGHT_CORNER = 1, + /** + * Position the overlay widget in the top center + */ + TOP_CENTER = 2 +} + +/** + * Type of hit element with the mouse in the editor. + */ +export enum MouseTargetType { + /** + * Mouse is on top of an unknown element. + */ + UNKNOWN = 0, + /** + * Mouse is on top of the textarea used for input. + */ + TEXTAREA = 1, + /** + * Mouse is on top of the glyph margin + */ + GUTTER_GLYPH_MARGIN = 2, + /** + * Mouse is on top of the line numbers + */ + GUTTER_LINE_NUMBERS = 3, + /** + * Mouse is on top of the line decorations + */ + GUTTER_LINE_DECORATIONS = 4, + /** + * Mouse is on top of the whitespace left in the gutter by a view zone. + */ + GUTTER_VIEW_ZONE = 5, + /** + * Mouse is on top of text in the content. + */ + CONTENT_TEXT = 6, + /** + * Mouse is on top of empty space in the content (e.g. after line text or below last line) + */ + CONTENT_EMPTY = 7, + /** + * Mouse is on top of a view zone in the content. + */ + CONTENT_VIEW_ZONE = 8, + /** + * Mouse is on top of a content widget. + */ + CONTENT_WIDGET = 9, + /** + * Mouse is on top of the decorations overview ruler. + */ + OVERVIEW_RULER = 10, + /** + * Mouse is on top of a scrollbar. + */ + SCROLLBAR = 11, + /** + * Mouse is on top of an overlay widget. + */ + OVERLAY_WIDGET = 12, + /** + * Mouse is outside of the editor. + */ + OUTSIDE_EDITOR = 13 +} + +/** + * Describes what to do with the indentation when pressing Enter. + */ +export enum IndentAction { + /** + * Insert new line and copy the previous line's indentation. + */ + None = 0, + /** + * Insert new line and indent once (relative to the previous line's indentation). + */ + Indent = 1, + /** + * Insert two new lines: + * - the first one indented which will hold the cursor + * - the second one at the same indentation level + */ + IndentOutdent = 2, + /** + * Insert new line and outdent once (relative to the previous line's indentation). + */ + Outdent = 3 +} + +export enum CompletionItemKind { + Method = 0, + Function = 1, + Constructor = 2, + Field = 3, + Variable = 4, + Class = 5, + Struct = 6, + Interface = 7, + Module = 8, + Property = 9, + Event = 10, + Operator = 11, + Unit = 12, + Value = 13, + Constant = 14, + Enum = 15, + EnumMember = 16, + Keyword = 17, + Text = 18, + Color = 19, + File = 20, + Reference = 21, + Customcolor = 22, + Folder = 23, + TypeParameter = 24, + Snippet = 25 +} + +export enum CompletionItemInsertTextRule { + /** + * Adjust whitespace/indentation of multiline insert texts to + * match the current line indentation. + */ + KeepWhitespace = 1, + /** + * `insertText` is a snippet. + */ + InsertAsSnippet = 4 +} + +/** + * How a suggest provider was triggered. + */ +export enum CompletionTriggerKind { + Invoke = 0, + TriggerCharacter = 1, + TriggerForIncompleteCompletions = 2 +} + +export enum SignatureHelpTriggerReason { + Invoke = 1, + TriggerCharacter = 2, + ContentChange = 3 +} + +/** + * A document highlight kind. + */ +export enum DocumentHighlightKind { + /** + * A textual occurrence. + */ + Text = 0, + /** + * Read-access of a symbol, like reading a variable. + */ + Read = 1, + /** + * Write-access of a symbol, like writing to a variable. + */ + Write = 2 +} + +/** + * A symbol kind. + */ +export enum SymbolKind { + File = 0, + Module = 1, + Namespace = 2, + Package = 3, + Class = 4, + Method = 5, + Property = 6, + Field = 7, + Constructor = 8, + Enum = 9, + Interface = 10, + Function = 11, + Variable = 12, + Constant = 13, + String = 14, + Number = 15, + Boolean = 16, + Array = 17, + Object = 18, + Key = 19, + Null = 20, + EnumMember = 21, + Struct = 22, + Event = 23, + Operator = 24, + TypeParameter = 25 +} \ No newline at end of file diff --git a/src/vs/editor/common/view/editorColorRegistry.ts b/src/vs/editor/common/view/editorColorRegistry.ts index c4c369cb0d2..ba9173347cc 100644 --- a/src/vs/editor/common/view/editorColorRegistry.ts +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { registerColor, editorBackground, activeContrastBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { Color, RGBA } from 'vs/base/common/color'; +import { activeContrastBorder, editorBackground, editorForeground, registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; /** * Definition of the editor colors diff --git a/src/vs/editor/common/view/minimapCharRenderer.ts b/src/vs/editor/common/view/minimapCharRenderer.ts index b641f623708..9df60197050 100644 --- a/src/vs/editor/common/view/minimapCharRenderer.ts +++ b/src/vs/editor/common/view/minimapCharRenderer.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ColorId, TokenizationRegistry } from 'vs/editor/common/modes'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { RGBA8 } from 'vs/editor/common/core/rgba'; +import { ColorId, TokenizationRegistry } from 'vs/editor/common/modes'; export class MinimapTokensColorTracker { private static _INSTANCE: MinimapTokensColorTracker | null = null; diff --git a/src/vs/editor/common/view/renderingContext.ts b/src/vs/editor/common/view/renderingContext.ts index 7b8e074f97a..6a3e7beba91 100644 --- a/src/vs/editor/common/view/renderingContext.ts +++ b/src/vs/editor/common/view/renderingContext.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IViewLayout, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; -import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { IViewLayout, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; export interface IViewLines { linesVisibleRangesForRange(range: Range, includeNewLines: boolean): LineVisibleRanges[] | null; diff --git a/src/vs/editor/common/view/viewContext.ts b/src/vs/editor/common/view/viewContext.ts index 7bb8578ad84..5f4c9be5889 100644 --- a/src/vs/editor/common/view/viewContext.ts +++ b/src/vs/editor/common/view/viewContext.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { IConfiguration } from 'vs/editor/common/editorCommon'; -import { IViewModel, IViewLayout } from 'vs/editor/common/viewModel/viewModel'; -import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { IViewLayout, IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { ITheme } from 'vs/platform/theme/common/themeService'; export class ViewContext { diff --git a/src/vs/editor/common/view/viewEventDispatcher.ts b/src/vs/editor/common/view/viewEventDispatcher.ts index 0b784b3234f..47e26f7916d 100644 --- a/src/vs/editor/common/view/viewEventDispatcher.ts +++ b/src/vs/editor/common/view/viewEventDispatcher.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; import { ViewEvent } from 'vs/editor/common/view/viewEvents'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; export class ViewEventDispatcher { diff --git a/src/vs/editor/common/view/viewEvents.ts b/src/vs/editor/common/view/viewEvents.ts index d8ccfc42c80..962579d3f0f 100644 --- a/src/vs/editor/common/view/viewEvents.ts +++ b/src/vs/editor/common/view/viewEvents.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; +import * as errors from 'vs/base/common/errors'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; -import * as errors from 'vs/base/common/errors'; -import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; import { ScrollType } from 'vs/editor/common/editorCommon'; export const enum ViewEventType { diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index 82123ed2a39..ed6cfcfe507 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; -import { Constants } from 'vs/editor/common/core/uint'; import * as strings from 'vs/base/common/strings'; +import { Constants } from 'vs/editor/common/core/uint'; +import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; export class LineDecoration { _lineDecorationBrand: void; diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index 2d6666bc098..f48f66bae7e 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { WhitespaceComputer, IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { IEditorWhitespace, WhitespaceComputer } from 'vs/editor/common/viewLayout/whitespaceComputer'; import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; /** diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index 37219081443..f753f65d393 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { Scrollable, ScrollEvent, ScrollbarVisibility, IScrollDimensions, IScrollPosition } from 'vs/base/common/scrollable'; +import { IScrollDimensions, IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout'; -import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; -import { Event } from 'vs/base/common/event'; -import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; const SMOOTH_SCROLLING_TIME = 125; diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index a63dc7ac6b9..01be9c8a86c 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { CharCode } from 'vs/base/common/charCode'; -import { LineDecoration, LineDecorationsNormalizer } from 'vs/editor/common/viewLayout/lineDecorations'; import * as strings from 'vs/base/common/strings'; +import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder'; +import { LineDecoration, LineDecorationsNormalizer } from 'vs/editor/common/viewLayout/lineDecorations'; import { InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; export const enum RenderWhitespace { diff --git a/src/vs/editor/common/viewLayout/viewLinesViewportData.ts b/src/vs/editor/common/viewLayout/viewLinesViewportData.ts index c82545c251e..9a1771f236c 100644 --- a/src/vs/editor/common/viewLayout/viewLinesViewportData.ts +++ b/src/vs/editor/common/viewLayout/viewLinesViewportData.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ViewLineRenderingData, IViewModel, ViewModelDecoration, IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; +import { IViewModel, IViewWhitespaceViewportData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; export interface IPartialViewLinesViewportData { /** diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index f9d6cf5625e..040c0108e56 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as strings from 'vs/base/common/strings'; -import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { ILineMapperFactory, ILineMapping, OutputPosition } from 'vs/editor/common/viewModel/splitLinesCollection'; import { CharCode } from 'vs/base/common/charCode'; +import * as strings from 'vs/base/common/strings'; +import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; import { toUint32Array } from 'vs/editor/common/core/uint'; -import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; +import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; +import { ILineMapperFactory, ILineMapping, OutputPosition } from 'vs/editor/common/viewModel/splitLinesCollection'; const enum CharacterClass { NONE = 0, diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 80f022d25e8..0021b1713f8 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from 'vs/editor/common/core/position'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { LineTokens } from 'vs/editor/common/core/lineTokens'; -import { PrefixSumComputerWithCache } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { ViewLineData, ICoordinatesConverter, IOverviewRulerDecorations } from 'vs/editor/common/viewModel/viewModel'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecoration, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { PrefixSumComputerWithCache } from 'vs/editor/common/viewModel/prefixSumComputer'; +import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { ITheme } from 'vs/platform/theme/common/themeService'; -import { IModelDecoration, ITextModel, IModelDeltaDecoration, EndOfLinePreference, IActiveIndentGuideInfo } from 'vs/editor/common/model'; export class OutputPosition { _outputPositionBrand: void; diff --git a/src/vs/editor/common/viewModel/viewEventHandler.ts b/src/vs/editor/common/viewModel/viewEventHandler.ts index b830fb8a273..e421f5ffde2 100644 --- a/src/vs/editor/common/viewModel/viewEventHandler.ts +++ b/src/vs/editor/common/viewModel/viewEventHandler.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { Disposable } from 'vs/base/common/lifecycle'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; export class ViewEventHandler extends Disposable { diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 602483c89d9..8aabf5e0b52 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INewScrollPosition } from 'vs/editor/common/editorCommon'; -import { EndOfLinePreference, IModelDecorationOptions, IActiveIndentGuideInfo } from 'vs/editor/common/model'; -import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { IViewEventListener } from 'vs/editor/common/view/viewEvents'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { Scrollable, IScrollPosition } from 'vs/base/common/scrollable'; +import { IScrollPosition, Scrollable } from 'vs/base/common/scrollable'; +import * as strings from 'vs/base/common/strings'; +import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { INewScrollPosition } from 'vs/editor/common/editorCommon'; +import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions } from 'vs/editor/common/model'; +import { IViewEventListener } from 'vs/editor/common/view/viewEvents'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; import { ITheme } from 'vs/platform/theme/common/themeService'; -import * as strings from 'vs/base/common/strings'; export interface IViewWhitespaceViewportData { readonly id: number; diff --git a/src/vs/editor/common/viewModel/viewModelDecorations.ts b/src/vs/editor/common/viewModel/viewModelDecorations.ts index abac42d2ecc..c8020eb4823 100644 --- a/src/vs/editor/common/viewModel/viewModelDecorations.ts +++ b/src/vs/editor/common/viewModel/viewModelDecorations.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from 'vs/base/common/lifecycle'; -import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { InlineDecoration, ViewModelDecoration, ICoordinatesConverter, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; +import { IModelDecoration, ITextModel } from 'vs/editor/common/model'; import { IViewModelLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; -import { ITextModel, IModelDecoration } from 'vs/editor/common/model'; +import { ICoordinatesConverter, InlineDecoration, InlineDecorationType, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; export interface IDecorationsViewportData { /** diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index ec5227de86b..59498f4a5ce 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -3,26 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as strings from 'vs/base/common/strings'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { TokenizationRegistry, ColorId, LanguageId } from 'vs/editor/common/modes'; -import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; -import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; -import { MinimapLinesRenderingData, ViewLineRenderingData, ViewModelDecoration, IViewModel, ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; -import { SplitLinesCollection, IViewModelLinesCollection, IdentityLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { MinimapTokensColorTracker } from 'vs/editor/common/view/minimapCharRenderer'; -import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; -import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; -import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; -import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; import { Color } from 'vs/base/common/color'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { ITheme } from 'vs/platform/theme/common/themeService'; +import * as strings from 'vs/base/common/strings'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EndOfLinePreference, IActiveIndentGuideInfo, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel'; -import { ITextModel, EndOfLinePreference, TrackedRangeStickiness, IActiveIndentGuideInfo } from 'vs/editor/common/model'; +import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; +import { ColorId, LanguageId, TokenizationRegistry } from 'vs/editor/common/modes'; +import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; +import { MinimapTokensColorTracker } from 'vs/editor/common/view/minimapCharRenderer'; +import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; +import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; +import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; +import { ITheme } from 'vs/platform/theme/common/themeService'; const USE_IDENTITY_LINES_COLLECTION = true; diff --git a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts index 09e4d6b319e..36848e40645 100644 --- a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts @@ -5,22 +5,22 @@ import 'vs/css!./bracketMatching'; import * as nls from 'vs/nls'; +import { RunOnceScheduler } from 'vs/base/common/async'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Range } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; -import { Selection } from 'vs/editor/common/core/selection'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { EditorAction, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; -import { editorBracketMatchBackground, editorBracketMatchBorder } from 'vs/editor/common/view/editorColorRegistry'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { registerColor } from 'vs/platform/theme/common/colorRegistry'; -import { TrackedRangeStickiness, IModelDeltaDecoration, OverviewRulerLane } from 'vs/editor/common/model'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { editorBracketMatchBackground, editorBracketMatchBorder } from 'vs/editor/common/view/editorColorRegistry'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; const overviewRulerBracketMatchForeground = registerColor('editorOverviewRuler.bracketMatchForeground', { dark: '#A0A0A0', light: '#A0A0A0', hc: '#A0A0A0' }, nls.localize('overviewRulerBracketMatchForeground', 'Overview ruler marker color for matching brackets.')); diff --git a/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts b/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts index 6e38ef25c47..f9deb3ce478 100644 --- a/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts +++ b/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { BracketMatchingController } from 'vs/editor/contrib/bracketMatching/bracketMatching'; +import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; suite('bracket matching', () => { class BracketMode extends MockMode { diff --git a/src/vs/editor/contrib/caretOperations/caretOperations.ts b/src/vs/editor/contrib/caretOperations/caretOperations.ts index de2020f4cb5..b56d1135224 100644 --- a/src/vs/editor/contrib/caretOperations/caretOperations.ts +++ b/src/vs/editor/contrib/caretOperations/caretOperations.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICommand } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { IActionOptions, registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { MoveCaretCommand } from './moveCaretCommand'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { MoveCaretCommand } from 'vs/editor/contrib/caretOperations/moveCaretCommand'; class MoveCaretAction extends EditorAction { diff --git a/src/vs/editor/contrib/caretOperations/transpose.ts b/src/vs/editor/contrib/caretOperations/transpose.ts index e5811b88166..719d4fa186b 100644 --- a/src/vs/editor/contrib/caretOperations/transpose.ts +++ b/src/vs/editor/contrib/caretOperations/transpose.ts @@ -5,14 +5,14 @@ import * as nls from 'vs/nls'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { isLowSurrogate, isHighSurrogate } from 'vs/base/common/strings'; +import { isHighSurrogate, isLowSurrogate } from 'vs/base/common/strings'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { Position, IPosition } from 'vs/editor/common/core/position'; import { ICommand } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ITextModel } from 'vs/editor/common/model'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index c4ee159d9dc..94eefa7c0f1 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -5,17 +5,17 @@ import 'vs/css!./clipboard'; import * as nls from 'vs/nls'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as browser from 'vs/base/browser/browser'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { registerEditorAction, IActionOptions, EditorAction, ICommandKeybindingsOptions } from 'vs/editor/browser/editorExtensions'; import { CopyOptions } from 'vs/editor/browser/controller/textAreaInput'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { EditorAction, IActionOptions, ICommandKeybindingsOptions, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { MenuId } from 'vs/platform/actions/common/actions'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste'; diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index 55d18a1b6f7..dc821a5a86d 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -49,8 +49,12 @@ export class CodeActionOracle { } private _onMarkerChanges(resources: URI[]): void { - const { uri } = this._editor.getModel(); - if (resources.some(resource => resource.toString() === uri.toString())) { + const model = this._editor.getModel(); + if (!model) { + return; + } + + if (resources.some(resource => resource.toString() === model.uri.toString())) { this._autoTriggerTimer.cancelAndSet(() => { this.trigger({ type: 'auto' }); }, this._delay); @@ -63,8 +67,11 @@ export class CodeActionOracle { }, this._delay); } - private _getRangeOfMarker(selection: Selection): Range { + private _getRangeOfMarker(selection: Selection): Range | undefined { const model = this._editor.getModel(); + if (!model) { + return undefined; + } for (const marker of this._markerService.read({ resource: model.uri })) { if (Range.intersectRanges(marker, selection)) { return Range.lift(marker); @@ -76,7 +83,7 @@ export class CodeActionOracle { private _getRangeOfSelectionUnlessWhitespaceEnclosed(trigger: CodeActionTrigger): Selection | undefined { const model = this._editor.getModel(); const selection = this._editor.getSelection(); - if (selection.isEmpty() && !(trigger.filter && trigger.filter.includeSourceActions)) { + if (model && selection && selection.isEmpty() && !(trigger.filter && trigger.filter.includeSourceActions)) { const { lineNumber, column } = selection.getPosition(); const line = model.getLineContent(lineNumber); if (line.length === 0) { @@ -99,7 +106,7 @@ export class CodeActionOracle { } } } - return selection; + return selection ? selection : undefined; } private _createEventAndSignalChange(trigger: CodeActionTrigger, selection: Selection | undefined): Thenable { @@ -114,6 +121,17 @@ export class CodeActionOracle { return Promise.resolve(undefined); } else { const model = this._editor.getModel(); + if (!model) { + // cancel + this._signalChange({ + trigger, + rangeOrSelection: undefined, + position: undefined, + actions: undefined, + }); + return Promise.resolve(undefined); + } + const markerRange = this._getRangeOfMarker(selection); const position = markerRange ? markerRange.getStartPosition() : selection.getStartPosition(); const actions = createCancelablePromise(token => getCodeActions(model, selection, trigger, token)); @@ -135,16 +153,16 @@ export class CodeActionOracle { export interface CodeActionsComputeEvent { trigger: CodeActionTrigger; - rangeOrSelection: Range | Selection; - position: Position; - actions: CancelablePromise; + rangeOrSelection: Range | Selection | undefined; + position: Position | undefined; + actions: CancelablePromise | undefined; } export class CodeActionModel { private _editor: ICodeEditor; private _markerService: IMarkerService; - private _codeActionOracle: CodeActionOracle; + private _codeActionOracle?: CodeActionOracle; private _onDidChangeFixes = new Emitter(); private _disposables: IDisposable[] = []; private readonly _supportedCodeActions: IContextKey; @@ -179,12 +197,13 @@ export class CodeActionModel { this._onDidChangeFixes.fire(undefined); } - if (this._editor.getModel() - && CodeActionProviderRegistry.has(this._editor.getModel()) + const model = this._editor.getModel(); + if (model + && CodeActionProviderRegistry.has(model) && !this._editor.getConfiguration().readOnly) { const supportedActions: string[] = []; - for (const provider of CodeActionProviderRegistry.all(this._editor.getModel())) { + for (const provider of CodeActionProviderRegistry.all(model)) { if (Array.isArray(provider.providedCodeActionKinds)) { supportedActions.push(...provider.providedCodeActionKinds); } diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index aaead454eda..a4af30a6584 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -29,7 +29,7 @@ export class CodeActionContextMenu { show(fixes: Thenable, at: { x: number; y: number } | Position) { - const actions = fixes ? fixes.then(value => { + const actionsPromise = fixes ? fixes.then(value => { return value.map(action => { return new Action(action.command ? action.command.id : action.title, action.title, undefined, true, () => { return always( @@ -45,19 +45,21 @@ export class CodeActionContextMenu { return actions; }) : Promise.resolve([] as Action[]); - this._contextMenuService.showContextMenu({ - getAnchor: () => { - if (Position.isIPosition(at)) { - at = this._toCoords(at); - } - return at; - }, - getActions: () => actions, - onHide: () => { - this._visible = false; - this._editor.focus(); - }, - autoSelectFirstItem: true + actionsPromise.then(actions => { + this._contextMenuService.showContextMenu({ + getAnchor: () => { + if (Position.isIPosition(at)) { + at = this._toCoords(at); + } + return at; + }, + getActions: () => actions, + onHide: () => { + this._visible = false; + this._editor.focus(); + }, + autoSelectFirstItem: true + }); }); } diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index fe0c8eb7320..ce8ef441c47 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -11,7 +11,6 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./lightBulbWidget'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; import { CodeActionsComputeEvent } from './codeActionModel'; export class LightBulbWidget implements IDisposable, IContentWidget { @@ -25,8 +24,8 @@ export class LightBulbWidget implements IDisposable, IContentWidget { readonly onClick: Event<{ x: number, y: number }> = this._onClick.event; - private _position: IContentWidgetPosition; - private _model: CodeActionsComputeEvent; + private _position: IContentWidgetPosition | null; + private _model: CodeActionsComputeEvent | null; private _futureFixes = new CancellationTokenSource(); constructor(editor: ICodeEditor) { @@ -40,7 +39,8 @@ export class LightBulbWidget implements IDisposable, IContentWidget { this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._futureFixes.cancel())); this._disposables.push(this._editor.onDidChangeModelContent(_ => { // cancel when the line in question has been removed - if (this._model && this.model.position.lineNumber >= this._editor.getModel().getLineCount()) { + const editorModel = this._editor.getModel(); + if (!this.model || !this.model.position || !editorModel || this.model.position.lineNumber >= editorModel.getLineCount()) { this._futureFixes.cancel(); } })); @@ -53,7 +53,7 @@ export class LightBulbWidget implements IDisposable, IContentWidget { const { lineHeight } = this._editor.getConfiguration(); let pad = Math.floor(lineHeight / 3); - if (this._position && this._position.position.lineNumber < this._model.position.lineNumber) { + if (this._position && this._model && this._model.position && this._position.position !== null && this._position.position.lineNumber < this._model.position.lineNumber) { pad += lineHeight; } @@ -96,13 +96,13 @@ export class LightBulbWidget implements IDisposable, IContentWidget { return this._domNode; } - getPosition(): IContentWidgetPosition { + getPosition(): IContentWidgetPosition | null { return this._position; } - set model(value: CodeActionsComputeEvent) { + set model(value: CodeActionsComputeEvent | null) { - if (this._position && (!value.position || this._position.position.lineNumber !== value.position.lineNumber)) { + if (!value || this._position && (!value.position || this._position.position && this._position.position.lineNumber !== value.position.lineNumber)) { // hide when getting a 'hide'-request or when currently // showing on another line this.hide(); @@ -115,23 +115,23 @@ export class LightBulbWidget implements IDisposable, IContentWidget { const { token } = this._futureFixes; this._model = value; + if (!this._model || !this._model.actions) { + return; + } + const selection = this._model.rangeOrSelection; this._model.actions.then(fixes => { - if (!token.isCancellationRequested && fixes && fixes.length > 0) { - if (selection.isEmpty() && fixes.every(fix => fix.kind && CodeActionKind.Refactor.contains(fix.kind))) { - this.hide(); - } else { - this._show(); - } + if (!token.isCancellationRequested && fixes && fixes.length > 0 && selection) { + this._show(); } else { this.hide(); } - }).catch(err => { + }).catch(() => { this.hide(); }); } - get model(): CodeActionsComputeEvent { + get model(): CodeActionsComputeEvent | null { return this._model; } @@ -148,6 +148,9 @@ export class LightBulbWidget implements IDisposable, IContentWidget { if (!config.contribInfo.lightbulbEnabled) { return; } + if (!this._model || !this._model.position) { + return; + } const { lineNumber, column } = this._model.position; const model = this._editor.getModel(); if (!model) { diff --git a/src/vs/editor/contrib/codelens/codelens.ts b/src/vs/editor/contrib/codelens/codelens.ts index 88ed6986158..6dccdfd1375 100644 --- a/src/vs/editor/contrib/codelens/codelens.ts +++ b/src/vs/editor/contrib/codelens/codelens.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; import { mergeSort } from 'vs/base/common/arrays'; -import { URI } from 'vs/base/common/uri'; -import { ITextModel } from 'vs/editor/common/model'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import { CodeLensProviderRegistry, CodeLensProvider, ICodeLensSymbol } from 'vs/editor/common/modes'; -import { IModelService } from 'vs/editor/common/services/modelService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; +import { URI } from 'vs/base/common/uri'; +import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { ITextModel } from 'vs/editor/common/model'; +import { CodeLensProvider, CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes'; +import { IModelService } from 'vs/editor/common/services/modelService'; export interface ICodeLensData { symbol: ICodeLensSymbol; @@ -73,8 +73,8 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { for (const item of value) { if (typeof itemResolveCount === 'undefined' || Boolean(item.symbol.command)) { result.push(item.symbol); - } else if (itemResolveCount-- > 0) { - resolve.push(Promise.resolve(item.provider.resolveCodeLens(model, item.symbol, CancellationToken.None)).then(symbol => result.push(symbol))); + } else if (itemResolveCount-- > 0 && item.provider.resolveCodeLens) { + resolve.push(Promise.resolve(item.provider.resolveCodeLens(model, item.symbol, CancellationToken.None)).then(symbol => result.push(symbol || item.symbol))); } } diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 90ab1067270..07a313f3984 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async'; +import { CancelablePromise, RunOnceScheduler, createCancelablePromise } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -13,10 +13,10 @@ import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOption import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; import { CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes'; +import { ICodeLensData, getCodeLensData } from 'vs/editor/contrib/codelens/codelens'; import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { getCodeLensData, ICodeLensData } from './codelens'; export class CodeLensContribution implements editorCommon.IEditorContribution { diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 0a03f0a3c26..847f99f6670 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -4,21 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./codelensWidget'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { format, escape } from 'vs/base/common/strings'; import * as dom from 'vs/base/browser/dom'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { Range } from 'vs/editor/common/core/range'; -import { ICodeLensSymbol, Command } from 'vs/editor/common/modes'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import { ICodeLensData } from './codelens'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { editorCodeLensForeground } from 'vs/editor/common/view/editorColorRegistry'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; -import { IModelDeltaDecoration, IModelDecorationsChangeAccessor, ITextModel } from 'vs/editor/common/model'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { coalesce, isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { escape, format } from 'vs/base/common/strings'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { Range } from 'vs/editor/common/core/range'; +import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { Command, ICodeLensSymbol } from 'vs/editor/common/modes'; +import { editorCodeLensForeground } from 'vs/editor/common/view/editorColorRegistry'; +import { ICodeLensData } from 'vs/editor/contrib/codelens/codelens'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; class CodeLensViewZone implements editorBrowser.IViewZone { diff --git a/src/vs/editor/contrib/colorPicker/color.ts b/src/vs/editor/contrib/colorPicker/color.ts index 18a48205012..d6b490128c5 100644 --- a/src/vs/editor/contrib/colorPicker/color.ts +++ b/src/vs/editor/contrib/colorPicker/color.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; -import { ColorProviderRegistry, DocumentColorProvider, IColorInformation, IColorPresentation } from 'vs/editor/common/modes'; -import { ITextModel } from 'vs/editor/common/model'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { illegalArgument } from 'vs/base/common/errors'; -import { IModelService } from 'vs/editor/common/services/modelService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { illegalArgument } from 'vs/base/common/errors'; +import { URI } from 'vs/base/common/uri'; +import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; +import { ColorProviderRegistry, DocumentColorProvider, IColorInformation, IColorPresentation } from 'vs/editor/common/modes'; +import { IModelService } from 'vs/editor/common/services/modelService'; export interface IColorData { diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index a676d0e8c36..1ce10338932 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -3,21 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancelablePromise, TimeoutTimer, createCancelablePromise } from 'vs/base/common/async'; import { RGBA } from 'vs/base/common/color'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { hash } from 'vs/base/common/hash'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { Range } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; -import { ColorProviderRegistry } from 'vs/editor/common/modes'; +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { getColors, IColorData } from 'vs/editor/contrib/colorPicker/color'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IModelDeltaDecoration } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { TimeoutTimer, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { ColorProviderRegistry } from 'vs/editor/common/modes'; +import { IColorData, getColors } from 'vs/editor/contrib/colorPicker/color'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const MAX_DECORATORS = 500; @@ -29,8 +30,8 @@ export class ColorDetector implements IEditorContribution { private _globalToDispose: IDisposable[] = []; private _localToDispose: IDisposable[] = []; - private _computePromise: CancelablePromise; - private _timeoutTimer: TimeoutTimer; + private _computePromise: CancelablePromise | null; + private _timeoutTimer: TimeoutTimer | null; private _decorationsIds: string[] = []; private _colorDatas = new Map(); @@ -107,11 +108,8 @@ export class ColorDetector implements IEditorContribution { return; } const model = this._editor.getModel(); - // if (!model) { - // return; - // } - if (!ColorProviderRegistry.has(model)) { + if (!model || !ColorProviderRegistry.has(model)) { return; } @@ -128,7 +126,13 @@ export class ColorDetector implements IEditorContribution { } private beginCompute(): void { - this._computePromise = createCancelablePromise(token => getColors(this._editor.getModel(), token)); + this._computePromise = createCancelablePromise(token => { + const model = this._editor.getModel(); + if (!model) { + return Promise.resolve([]); + } + return getColors(model, token); + }); this._computePromise.then((colorInfos) => { this.updateDecorations(colorInfos); this.updateColorDecorators(colorInfos); @@ -166,7 +170,7 @@ export class ColorDetector implements IEditorContribution { } private updateColorDecorators(colorData: IColorData[]): void { - let decorations = []; + let decorations: IModelDeltaDecoration[] = []; let newDecorationsTypes: { [key: string]: boolean } = {}; for (let i = 0; i < colorData.length && decorations.length < MAX_DECORATORS; i++) { @@ -225,7 +229,12 @@ export class ColorDetector implements IEditorContribution { } getColorData(position: Position): IColorData | null { - const decorations = this._editor.getModel() + const model = this._editor.getModel(); + if (!model) { + return null; + } + + const decorations = model .getDecorationsInRange(Range.fromPositions(position, position)) .filter(d => this._colorDatas.has(d.id)); diff --git a/src/vs/editor/contrib/colorPicker/colorPickerModel.ts b/src/vs/editor/contrib/colorPicker/colorPickerModel.ts index 0026017d053..cfeda974e3c 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerModel.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerModel.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; import { Color } from 'vs/base/common/color'; +import { Emitter, Event } from 'vs/base/common/event'; import { IColorPresentation } from 'vs/editor/common/modes'; export class ColorPickerModel { diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 5e462f6d9d4..5b4ba4eec64 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./colorPicker'; -import { Event, Emitter } from 'vs/base/common/event'; -import { Widget } from 'vs/base/browser/ui/widget'; -import * as dom from 'vs/base/browser/dom'; import { onDidChangeZoomLevel } from 'vs/base/browser/browser'; -import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel'; -import { Disposable } from 'vs/base/common/lifecycle'; +import * as dom from 'vs/base/browser/dom'; import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; -import { Color, RGBA, HSVA } from 'vs/base/common/color'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { Color, HSVA, RGBA } from 'vs/base/common/color'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel'; import { editorHoverBackground } from 'vs/platform/theme/common/colorRegistry'; -import { registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; const $ = dom.$; diff --git a/src/vs/editor/contrib/comment/blockCommentCommand.ts b/src/vs/editor/contrib/comment/blockCommentCommand.ts index 14d67da83bb..32524d8f80f 100644 --- a/src/vs/editor/contrib/comment/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/blockCommentCommand.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CharCode } from 'vs/base/common/charCode'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { CharCode } from 'vs/base/common/charCode'; -import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; export class BlockCommentCommand implements editorCommon.ICommand { diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index 5792573e3bc..ca4ce3d9bc2 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICommand } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { registerEditorAction, IActionOptions, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { BlockCommentCommand } from './blockCommentCommand'; -import { LineCommentCommand, Type } from './lineCommentCommand'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand'; +import { LineCommentCommand, Type } from 'vs/editor/contrib/comment/lineCommentCommand'; import { MenuId } from 'vs/platform/actions/common/actions'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; abstract class CommentLineAction extends EditorAction { diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index b442b4d402c..f4340b008ab 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { BlockCommentCommand } from './blockCommentCommand'; +import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { CharCode } from 'vs/base/common/charCode'; -import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand'; export interface IInsertionPoint { ignore: boolean; diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index 2fdd2f5f173..a32458d6abd 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { Selection } from 'vs/editor/common/core/selection'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import * as modes from 'vs/editor/common/modes'; +import { CommentRule } from 'vs/editor/common/modes/languageConfiguration'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; import { ILinePreflightData, IPreflightData, ISimpleModel, LineCommentCommand, Type } from 'vs/editor/contrib/comment/lineCommentCommand'; import { testCommand } from 'vs/editor/test/browser/testCommand'; import { CommentMode } from 'vs/editor/test/common/commentMode'; -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 { MockMode } from 'vs/editor/test/common/mocks/mockMode'; -import { CommentRule } from 'vs/editor/common/modes/languageConfiguration'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; suite('Editor Contrib - Line Comment Command', () => { diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index 3268ea4da95..76741920f13 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -4,22 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IAction } from 'vs/base/common/actions'; -import { ResolvedKeybinding, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; +import { IAction } from 'vs/base/common/actions'; +import { KeyCode, KeyMod, ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution, IScrollEvent, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; export interface IPosition { x: number; @@ -163,18 +163,12 @@ export class ContextMenuController implements IEditorContribution { anchor = { x: posx, y: posy }; } - // prevent menu from appearing right below the cursor - anchor.height = 1; - anchor.width = 2; - // Show menu this._contextMenuIsBeingShownCount++; this._contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => { - return Promise.resolve(actions); - }, + getActions: () => actions, getActionItem: (action) => { const keybinding = this._keybindingFor(action); diff --git a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts index 8df1a3c07b1..4b37afc51aa 100644 --- a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts +++ b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { Selection } from 'vs/editor/common/core/selection'; -import { ServicesAccessor, registerEditorContribution, EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { Selection } from 'vs/editor/common/core/selection'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; class CursorState { diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index 51951f45c4b..caa3ea75fd4 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -34,7 +34,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution { private _editor: ICodeEditor; private _toUnhook: IDisposable[]; - private _dragSelection: Selection; + private _dragSelection: Selection | null; private _dndDecorationIds: string[]; private _mouseDown: boolean; private _modiferPressed: boolean; @@ -115,7 +115,8 @@ export class DragAndDropController implements editorCommon.IEditorContribution { let target = mouseEvent.target; if (this._dragSelection === null) { - let possibleSelections = this._editor.getSelections().filter(selection => selection.containsPosition(target.position)); + const selections = this._editor.getSelections() || []; + let possibleSelections = selections.filter(selection => target.position && selection.containsPosition(target.position)); if (possibleSelections.length === 1) { this._dragSelection = possibleSelections[0]; } else { @@ -133,10 +134,12 @@ export class DragAndDropController implements editorCommon.IEditorContribution { }); } - if (this._dragSelection.containsPosition(target.position)) { - this._removeDecoration(); - } else { - this.showAt(target.position); + if (target.position) { + if (this._dragSelection.containsPosition(target.position)) { + this._removeDecoration(); + } else { + this.showAt(target.position); + } } } @@ -148,10 +151,12 @@ export class DragAndDropController implements editorCommon.IEditorContribution { let newSelections: Selection[] | null = null; if (mouseEvent.event.shiftKey) { let primarySelection = this._editor.getSelection(); - let { selectionStartLineNumber, selectionStartColumn } = primarySelection; - newSelections = [new Selection(selectionStartLineNumber, selectionStartColumn, newCursorPosition.lineNumber, newCursorPosition.column)]; + if (primarySelection) { + const { selectionStartLineNumber, selectionStartColumn } = primarySelection; + newSelections = [new Selection(selectionStartLineNumber, selectionStartColumn, newCursorPosition.lineNumber, newCursorPosition.column)]; + } } else { - newSelections = this._editor.getSelections().map(selection => { + newSelections = (this._editor.getSelections() || []).map(selection => { if (selection.containsPosition(newCursorPosition)) { return new Selection(newCursorPosition.lineNumber, newCursorPosition.column, newCursorPosition.lineNumber, newCursorPosition.column); } else { @@ -160,7 +165,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution { }); } // Use `mouse` as the source instead of `api`. - (this._editor).setSelections(newSelections, 'mouse'); + (this._editor).setSelections(newSelections || [], 'mouse'); } else if (!this._dragSelection.containsPosition(newCursorPosition) || ( ( diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 0d852cd9ed9..347c430e8a1 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -133,7 +133,11 @@ export class OutlineGroup extends TreeElement { } private _updateMatches(pattern: string, item: OutlineElement, topMatch: OutlineElement): OutlineElement { - item.score = fuzzyScore(pattern, pattern.toLowerCase(), 0, item.symbol.name, item.symbol.name.toLowerCase(), 0, true); + + item.score = pattern + ? fuzzyScore(pattern, pattern.toLowerCase(), 0, item.symbol.name, item.symbol.name.toLowerCase(), 0, true) + : [-100, []]; + if (item.score && (!topMatch || item.score[0] > topMatch.score[0])) { topMatch = item; } @@ -142,7 +146,7 @@ export class OutlineGroup extends TreeElement { topMatch = this._updateMatches(pattern, child, topMatch); if (!item.score && child.score) { // don't filter parents with unfiltered children - item.score = [0, []]; + item.score = [-100, []]; } } return topMatch; diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index bbbc4df60da..98b50239d44 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -146,13 +146,13 @@ export class OutlineRenderer implements IRenderer { const decoration = dom.$('.outline-element-decoration'); dom.addClass(container, 'outline-element'); dom.append(container, icon, labelContainer, detail, decoration); - return { icon, labelContainer, label: new HighlightedLabel(labelContainer), detail, decoration }; + return { icon, labelContainer, label: new HighlightedLabel(labelContainer, true), detail, decoration }; } if (templateId === 'outline-group') { const labelContainer = dom.$('.outline-element-label'); dom.addClass(container, 'outline-element'); dom.append(container, labelContainer); - return { labelContainer, label: new HighlightedLabel(labelContainer) }; + return { labelContainer, label: new HighlightedLabel(labelContainer, true) }; } throw new Error(templateId); diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index c633e761e24..edaa3ef2c9b 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -4,27 +4,27 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import { Delayer } from 'vs/base/common/async'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import * as strings from 'vs/base/common/strings'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { registerEditorContribution, registerEditorAction, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; -import { FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding, ToggleSearchScopeKeybinding, CONTEXT_FIND_WIDGET_VISIBLE, CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; -import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState'; -import { Delayer } from 'vs/base/common/async'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget'; +import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel'; import { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { optional } from 'vs/platform/instantiation/common/instantiation'; +import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState'; +import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget'; import { MenuId } from 'vs/platform/actions/common/actions'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { optional } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; const SEARCH_STRING_MAX_LENGTH = 524288; diff --git a/src/vs/editor/contrib/find/findDecorations.ts b/src/vs/editor/contrib/find/findDecorations.ts index e36236e6b3f..10222c0ab3e 100644 --- a/src/vs/editor/contrib/find/findDecorations.ts +++ b/src/vs/editor/contrib/find/findDecorations.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from 'vs/base/common/lifecycle'; +import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; +import { FindMatch, IModelDecorationsChangeAccessor, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; -import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IModelDecorationsChangeAccessor, FindMatch, IModelDeltaDecoration, TrackedRangeStickiness, OverviewRulerLane } from 'vs/editor/common/model'; export class FindDecorations implements IDisposable { diff --git a/src/vs/editor/contrib/find/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index b5b4102a821..2375b532aea 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -4,24 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import { RunOnceScheduler, TimeoutTimer } from 'vs/base/common/async'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/replacePattern'; +import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand'; +import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { FindDecorations } from './findDecorations'; -import { FindReplaceState, FindReplaceStateChangedEvent } from './findState'; -import { ReplaceAllCommand } from './replaceAllCommand'; import { Selection } from 'vs/editor/common/core/selection'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Constants } from 'vs/editor/common/core/uint'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EndOfLinePreference, FindMatch, ITextModel } from 'vs/editor/common/model'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; +import { FindDecorations } from 'vs/editor/contrib/find/findDecorations'; +import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; +import { ReplaceAllCommand } from 'vs/editor/contrib/find/replaceAllCommand'; +import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/replacePattern'; +import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; -import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { ITextModel, FindMatch, EndOfLinePreference } from 'vs/editor/common/model'; export const CONTEXT_FIND_WIDGET_VISIBLE = new RawContextKey('findWidgetVisible', false); export const CONTEXT_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = CONTEXT_FIND_WIDGET_VISIBLE.toNegated(); diff --git a/src/vs/editor/contrib/find/findOptionsWidget.ts b/src/vs/editor/contrib/find/findOptionsWidget.ts index a3cb859edbc..5774e2a014e 100644 --- a/src/vs/editor/contrib/find/findOptionsWidget.ts +++ b/src/vs/editor/contrib/find/findOptionsWidget.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import { CaseSensitiveCheckbox, RegexCheckbox, WholeWordsCheckbox } from 'vs/base/browser/ui/findinput/findInputCheckboxes'; import { Widget } from 'vs/base/browser/ui/widget'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { RunOnceScheduler } from 'vs/base/common/async'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { FIND_IDS } from 'vs/editor/contrib/find/findModel'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; -import { CaseSensitiveCheckbox, WholeWordsCheckbox, RegexCheckbox } from 'vs/base/browser/ui/findinput/findInputCheckboxes'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { inputActiveOptionBorder, editorWidgetBackground, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { contrastBorder, editorWidgetBackground, inputActiveOptionBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; export class FindOptionsWidget extends Widget implements IOverlayWidget { diff --git a/src/vs/editor/contrib/find/findState.ts b/src/vs/editor/contrib/find/findState.ts index 35ff1e4dbf5..4a6801891f3 100644 --- a/src/vs/editor/contrib/find/findState.ts +++ b/src/vs/editor/contrib/find/findState.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index faf3f7efe98..35ece46a3aa 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -258,10 +258,6 @@ display:none; } -.monaco-editor .find-widget.collapsed-find-widget > .find-part .monaco-inputbox > .wrapper > .input { - padding-right: 0px; -} - .monaco-editor .findMatch { -webkit-animation-duration: 0; -webkit-animation-name: inherit !important; diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index a86d207d76a..5db0d2b50b4 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -5,31 +5,31 @@ import 'vs/css!./findWidget'; import * as nls from 'vs/nls'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import * as platform from 'vs/base/common/platform'; -import * as strings from 'vs/base/common/strings'; -import { Delayer } from 'vs/base/common/async'; import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput'; -import { IMessage as InputBoxMessage, HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { HistoryInputBox, IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; import { Widget } from 'vs/base/browser/ui/widget'; -import { Sash, IHorizontalSashLayoutProvider, ISashEvent, Orientation } from 'vs/base/browser/ui/sash/sash'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; -import { FIND_IDS, MATCHES_LIMIT, CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; -import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; -import { Range } from 'vs/editor/common/core/range'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ITheme, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; +import { Delayer } from 'vs/base/common/async'; import { Color } from 'vs/base/common/color'; -import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; -import { editorFindRangeHighlight, editorFindMatch, editorFindMatchHighlight, contrastBorder, inputBackground, editorWidgetBackground, inputActiveOptionBorder, widgetShadow, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, errorForeground, editorWidgetBorder, editorFindMatchBorder, editorFindMatchHighlightBorder, editorFindRangeHighlightBorder, editorWidgetResizeBorder } from 'vs/platform/theme/common/colorRegistry'; -import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/widget/browser/contextScopedHistoryWidget'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { toDisposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import * as strings from 'vs/base/common/strings'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { Range } from 'vs/editor/common/core/range'; +import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED, FIND_IDS, MATCHES_LIMIT } from 'vs/editor/contrib/find/findModel'; +import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/widget/browser/contextScopedHistoryWidget'; export interface IFindController { replace(): void; diff --git a/src/vs/editor/contrib/find/simpleFindWidget.ts b/src/vs/editor/contrib/find/simpleFindWidget.ts index a1c199a6f42..21d736d2a1d 100644 --- a/src/vs/editor/contrib/find/simpleFindWidget.ts +++ b/src/vs/editor/contrib/find/simpleFindWidget.ts @@ -5,18 +5,18 @@ import 'vs/css!./simpleFindWidget'; import * as nls from 'vs/nls'; +import * as dom from 'vs/base/browser/dom'; +import { FindInput } from 'vs/base/browser/ui/findinput/findInput'; import { Widget } from 'vs/base/browser/ui/widget'; import { Delayer } from 'vs/base/common/async'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import * as dom from 'vs/base/browser/dom'; -import { FindInput } from 'vs/base/browser/ui/findinput/findInput'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService'; -import { inputBackground, inputActiveOptionBorder, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; -import { SimpleButton } from './findWidget'; -import { ContextScopedFindInput } from 'vs/platform/widget/browser/contextScopedHistoryWidget'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; +import { SimpleButton } from 'vs/editor/contrib/find/findWidget'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { editorWidgetBackground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ContextScopedFindInput } from 'vs/platform/widget/browser/contextScopedHistoryWidget'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); diff --git a/src/vs/editor/contrib/find/test/findController.test.ts b/src/vs/editor/contrib/find/test/findController.test.ts index 2a6197767b6..81f2e6bf157 100644 --- a/src/vs/editor/contrib/find/test/findController.test.ts +++ b/src/vs/editor/contrib/find/test/findController.test.ts @@ -2,22 +2,23 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; +import { Delayer } from 'vs/base/common/async'; +import { Event } from 'vs/base/common/event'; +import * as platform from 'vs/base/common/platform'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; -import { Selection } from 'vs/editor/common/core/selection'; import { Range } from 'vs/editor/common/core/range'; -import * as platform from 'vs/base/common/platform'; -import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, StartFindAction, NextSelectionMatchFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/findController'; +import { Selection } from 'vs/editor/common/core/selection'; +import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/findController'; import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { Event } from 'vs/base/common/event'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { Delayer } from 'vs/base/common/async'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IStorageService } from 'vs/platform/storage/common/storage'; export class TestFindController extends CommonFindController { diff --git a/src/vs/editor/contrib/find/test/findModel.test.ts b/src/vs/editor/contrib/find/test/findModel.test.ts index 13a3dad1385..1e529fb155d 100644 --- a/src/vs/editor/contrib/find/test/findModel.test.ts +++ b/src/vs/editor/contrib/find/test/findModel.test.ts @@ -2,18 +2,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; +import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Cursor } from 'vs/editor/common/controller/cursor'; import { Position } from 'vs/editor/common/core/position'; -import { Selection } from 'vs/editor/common/core/selection'; import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; +import { TextModel } from 'vs/editor/common/model/textModel'; import { FindModelBoundToEditorModel } from 'vs/editor/contrib/find/findModel'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { TextModel } from 'vs/editor/common/model/textModel'; -import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; suite('FindModel', () => { diff --git a/src/vs/editor/contrib/find/test/replacePattern.test.ts b/src/vs/editor/contrib/find/test/replacePattern.test.ts index f2e13b95196..b36a509e34d 100644 --- a/src/vs/editor/contrib/find/test/replacePattern.test.ts +++ b/src/vs/editor/contrib/find/test/replacePattern.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { parseReplaceString, ReplacePattern, ReplacePiece } from 'vs/editor/contrib/find/replacePattern'; +import { ReplacePattern, ReplacePiece, parseReplaceString } from 'vs/editor/contrib/find/replacePattern'; suite('Replace Pattern test', () => { diff --git a/src/vs/editor/contrib/fontZoom/fontZoom.ts b/src/vs/editor/contrib/fontZoom/fontZoom.ts index 2c6afff202c..b0c9716132f 100644 --- a/src/vs/editor/contrib/fontZoom/fontZoom.ts +++ b/src/vs/editor/contrib/fontZoom/fontZoom.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; class EditorFontZoomIn extends EditorAction { diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index f2760022746..0b29c6f2b80 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -341,7 +341,7 @@ export class FormatSelectionAction extends AbstractFormatAction { id: 'editor.action.formatSelection', label: nls.localize('formatSelection.label', "Format Selection"), alias: 'Format Code', - precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasNonEmptySelection), + precondition: ContextKeyExpr.and(EditorContextKeys.writable), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F), @@ -357,8 +357,14 @@ export class FormatSelectionAction extends AbstractFormatAction { protected _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise { const model = editor.getModel(); + let selection = editor.getSelection(); + if (selection.isEmpty()) { + const maxColumn = model.getLineMaxColumn(selection.startLineNumber); + selection = selection.setStartPosition(selection.startLineNumber, 1); + selection = selection.setEndPosition(selection.endLineNumber, maxColumn); + } const { tabSize, insertSpaces } = model.getOptions(); - return getDocumentRangeFormattingEdits(model, editor.getSelection(), { tabSize, insertSpaces }, token); + return getDocumentRangeFormattingEdits(model, selection, { tabSize, insertSpaces }, token); } protected _notifyNoProviderError(notificationService: INotificationService, language: string): void { diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts index 88507a558a8..077a28ea36c 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts @@ -10,7 +10,7 @@ import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensio import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import { DefinitionLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry } from 'vs/editor/common/modes'; -import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; +import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry'; function getDefinitions( diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index eec514c2b15..7118ce9d891 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -23,7 +23,7 @@ import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegist import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; import { DefinitionAction, DefinitionActionConfig } from './goToDefinitionCommands'; import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture'; -import { IWordAtPosition, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { IWordAtPosition, IModelDeltaDecoration, ITextModel, IFoundBracket } from 'vs/editor/common/model'; import { Position } from 'vs/editor/common/core/position'; class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorContribution { @@ -73,6 +73,12 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC } private startFindDefinition(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): void { + + // check if we are active and on a content widget + if (mouseEvent.target.type === MouseTargetType.CONTENT_WIDGET && this.decorations.length > 0) { + return; + } + if (!this.isEnabled(mouseEvent, withKey)) { this.currentWordUnderMouse = null; this.removeDecorations(); @@ -205,7 +211,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC private getPreviewRangeBasedOnBrackets(textEditorModel: ITextModel, startLineNumber: number) { const maxLineNumber = Math.min(textEditorModel.getLineCount(), startLineNumber + GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES); - const brackets = []; + const brackets: IFoundBracket[] = []; let ignoreFirstEmpty = true; let currentBracket = textEditorModel.findNextBracket(new Position(startLineNumber, 1)); @@ -270,7 +276,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): boolean { return this.editor.getModel() && mouseEvent.isNoneOrSingleMouseDown && - mouseEvent.target.type === MouseTargetType.CONTENT_TEXT && + (mouseEvent.target.type === MouseTargetType.CONTENT_TEXT) && (mouseEvent.hasTriggerModifier || (withKey && withKey.keyCodeIsTriggerKey)) && DefinitionProviderRegistry.has(this.editor.getModel()); } diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 694533f44f7..72bcedbde1a 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -111,7 +111,7 @@ class MessageWidget { this._relatedBlock.style.paddingTop = `${Math.floor(this._editor.getConfiguration().lineHeight * .66)}px`; this._lines += 1; - for (const related of relatedInformation) { + for (const related of relatedInformation || []) { let container = document.createElement('div'); @@ -159,7 +159,7 @@ export class MarkerNavigationWidget extends ZoneWidget { private _message: MessageWidget; private _callOnDispose: IDisposable[] = []; private _severity: MarkerSeverity; - private _backgroundColor: Color; + private _backgroundColor: Color | null; private _onDidSelectRelatedInformation = new Emitter(); readonly onDidSelectRelatedInformation: Event = this._onDidSelectRelatedInformation.event; @@ -195,7 +195,7 @@ export class MarkerNavigationWidget extends ZoneWidget { protected _applyStyles(): void { if (this._parentContainer) { - this._parentContainer.style.backgroundColor = this._backgroundColor.toString(); + this._parentContainer.style.backgroundColor = this._backgroundColor ? this._backgroundColor.toString() : ''; } super._applyStyles(); } @@ -244,7 +244,8 @@ export class MarkerNavigationWidget extends ZoneWidget { // show let range = Range.lift(marker); - let position = range.containsPosition(this.editor.getPosition()) ? this.editor.getPosition() : range.getStartPosition(); + const editorPosition = this.editor.getPosition(); + let position = editorPosition && range.containsPosition(editorPosition) ? editorPosition : range.getStartPosition(); super.show(position, this.computeRequiredHeight()); this.editor.revealPositionInCenter(position, ScrollType.Smooth); diff --git a/src/vs/editor/contrib/hover/getHover.ts b/src/vs/editor/contrib/hover/getHover.ts index ad0a7e082c5..d6adae27bdf 100644 --- a/src/vs/editor/contrib/hover/getHover.ts +++ b/src/vs/editor/contrib/hover/getHover.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { coalesce } from 'vs/base/common/arrays'; -import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { ITextModel } from 'vs/editor/common/model'; -import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import { Hover, HoverProviderRegistry } from 'vs/editor/common/modes'; -import { Position } from 'vs/editor/common/core/position'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { Position } from 'vs/editor/common/core/position'; +import { ITextModel } from 'vs/editor/common/model'; +import { Hover, HoverProviderRegistry } from 'vs/editor/common/modes'; export function getHover(model: ITextModel, position: Position, token: CancellationToken): Promise { diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index 3e42a8283e5..be9f921d0d0 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -5,26 +5,26 @@ import 'vs/css!./hover'; import * as nls from 'vs/nls'; -import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; -import * as platform from 'vs/base/common/platform'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution, IScrollEvent } from 'vs/editor/common/editorCommon'; -import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; -import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { ModesContentHoverWidget } from './modesContentHover'; -import { ModesGlyphHoverWidget } from './modesGlyphHover'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; -import { editorHoverHighlight, editorHoverBackground, editorHoverBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; -import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget'; +import { IModeService } from 'vs/editor/common/services/modeService'; import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation'; +import { ModesContentHoverWidget } from 'vs/editor/contrib/hover/modesContentHover'; +import { ModesGlyphHoverWidget } from 'vs/editor/contrib/hover/modesGlyphHover'; +import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCodeBlockBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; export class ModesHoverController implements IEditorContribution { diff --git a/src/vs/editor/contrib/hover/hoverOperation.ts b/src/vs/editor/contrib/hover/hoverOperation.ts index 54e3c11a0bf..0371b56a979 100644 --- a/src/vs/editor/contrib/hover/hoverOperation.ts +++ b/src/vs/editor/contrib/hover/hoverOperation.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { RunOnceScheduler, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { CancelablePromise, RunOnceScheduler, createCancelablePromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { onUnexpectedError } from 'vs/base/common/errors'; export interface IHoverComputer { @@ -60,10 +60,10 @@ export class HoverOperation { private _asyncComputationPromiseDone: boolean; private _completeCallback: (r: Result) => void; - private _errorCallback: (err: any) => void; + private _errorCallback?: (err: any) => void; private _progressCallback: (progress: any) => void; - constructor(computer: IHoverComputer, success: (r: Result) => void, error: (err: any) => void, progress: (progress: any) => void) { + constructor(computer: IHoverComputer, success: (r: Result) => void, error: undefined | ((err: any) => void), progress: (progress: any) => void) { this._computer = computer; this._state = ComputeHoverOperationState.IDLE; this._hoverTime = HoverOperation.HOVER_TIME; diff --git a/src/vs/editor/contrib/hover/hoverWidgets.ts b/src/vs/editor/contrib/hover/hoverWidgets.ts index baaae2287db..cf74ece2193 100644 --- a/src/vs/editor/contrib/hover/hoverWidgets.ts +++ b/src/vs/editor/contrib/hover/hoverWidgets.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { KeyCode } from 'vs/base/common/keyCodes'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { toggleClass } from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import { Widget } from 'vs/base/browser/ui/widget'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; export class ContentHoverWidget extends Widget implements editorBrowser.IContentWidget { diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index b8a129e1706..cae0f342054 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -5,24 +5,24 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; -import { IRange, Range } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; -import { HoverProviderRegistry, Hover, IColor, DocumentColorProvider } from 'vs/editor/common/modes'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { getHover } from 'vs/editor/contrib/hover/getHover'; -import { HoverOperation, IHoverComputer, HoverStartMode } from './hoverOperation'; -import { ContentHoverWidget } from './hoverWidgets'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Color, RGBA } from 'vs/base/common/color'; import { IMarkdownString, MarkdownString, isEmptyMarkdownString, markedStringsEquals } from 'vs/base/common/htmlContent'; -import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; +import { Disposable, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { DocumentColorProvider, Hover, HoverProviderRegistry, IColor } from 'vs/editor/common/modes'; +import { getColorPresentations } from 'vs/editor/contrib/colorPicker/color'; +import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector'; import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel'; import { ColorPickerWidget } from 'vs/editor/contrib/colorPicker/colorPickerWidget'; -import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector'; -import { Color, RGBA } from 'vs/base/common/color'; -import { IDisposable, Disposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { getColorPresentations } from 'vs/editor/contrib/colorPicker/color'; +import { getHover } from 'vs/editor/contrib/hover/getHover'; +import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation'; +import { ContentHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets'; +import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { CancellationToken } from 'vs/base/common/cancellation'; const $ = dom.$; class ColorHover { diff --git a/src/vs/editor/contrib/hover/modesGlyphHover.ts b/src/vs/editor/contrib/hover/modesGlyphHover.ts index dbfe40390ff..0c35acdfb7f 100644 --- a/src/vs/editor/contrib/hover/modesGlyphHover.ts +++ b/src/vs/editor/contrib/hover/modesGlyphHover.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { HoverOperation, IHoverComputer, HoverStartMode } from './hoverOperation'; -import { GlyphHoverWidget } from './hoverWidgets'; import { $ } from 'vs/base/browser/dom'; -import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; import { IMarkdownString, isEmptyMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation'; +import { GlyphHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets'; +import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; export interface IHoverMessage { value: IMarkdownString; @@ -46,6 +46,10 @@ class MarginComputer implements IHoverComputer { let lineDecorations = this._editor.getLineDecorations(this._lineNumber); let result: IHoverMessage[] = []; + if (!lineDecorations) { + return result; + } + for (let i = 0, len = lineDecorations.length; i < len; i++) { let d = lineDecorations[i]; @@ -53,9 +57,9 @@ class MarginComputer implements IHoverComputer { continue; } - let hoverMessage = d.options.glyphMarginHoverMessage; + const hoverMessage = d.options.glyphMarginHoverMessage; - if (isEmptyMarkdownString(hoverMessage)) { + if (!hoverMessage || isEmptyMarkdownString(hoverMessage)) { continue; } @@ -104,7 +108,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { this._hoverOperation = new HoverOperation( this._computer, (result: IHoverMessage[]) => this._withResult(result), - null, + undefined, (result: any) => this._withResult(result) ); @@ -166,7 +170,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { messages.forEach((msg) => { const renderedContents = this._markdownRenderer.render(msg.value); this._renderDisposeables.push(renderedContents); - fragment.appendChild($('div.hover-row', null, renderedContents.element)); + fragment.appendChild($('div.hover-row', undefined, renderedContents.element)); }); this.updateContents(fragment); diff --git a/src/vs/editor/contrib/indentation/indentation.ts b/src/vs/editor/contrib/indentation/indentation.ts index 9ba96fc6e06..ca4ac2590ab 100644 --- a/src/vs/editor/contrib/indentation/indentation.ts +++ b/src/vs/editor/contrib/indentation/indentation.ts @@ -6,21 +6,21 @@ import * as nls from 'vs/nls'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; -import { IEditorContribution, ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { registerEditorAction, ServicesAccessor, IActionOptions, EditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { IModelService } from 'vs/editor/common/services/modelService'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; +import { StandardTokenType, TextEdit } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; -import { TextEdit, StandardTokenType } from 'vs/editor/common/modes'; -import * as IndentUtil from './indentUtils'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; export function shiftIndent(tabSize: number, indentation: string, count?: number): string { @@ -77,7 +77,7 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu } let { tabSize, insertSpaces } = model.getOptions(); - let indentEdits = []; + let indentEdits: IIdentifiedSingleEditOperation[] = []; // indentation being passed to lines below let globalIndent: string; @@ -509,11 +509,11 @@ export class AutoIndentOnPaste implements IEditorContribution { if (indentOfFirstLine !== null) { let oldIndentation = strings.getLeadingWhitespace(firstLineText); - let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfFirstLine, tabSize); - let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize); + let newSpaceCnt = indentUtils.getSpaceCnt(indentOfFirstLine, tabSize); + let oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize); if (newSpaceCnt !== oldSpaceCnt) { - let newIndent = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces); + let newIndent = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces); textEdits.push({ range: new Range(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), text: newIndent @@ -565,17 +565,17 @@ export class AutoIndentOnPaste implements IEditorContribution { }; let indentOfSecondLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdentifier().id, startLineNumber + 1, indentConverter); if (indentOfSecondLine !== null) { - let newSpaceCntOfSecondLine = IndentUtil.getSpaceCnt(indentOfSecondLine, tabSize); - let oldSpaceCntOfSecondLine = IndentUtil.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(startLineNumber + 1)), tabSize); + let newSpaceCntOfSecondLine = indentUtils.getSpaceCnt(indentOfSecondLine, tabSize); + let oldSpaceCntOfSecondLine = indentUtils.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(startLineNumber + 1)), tabSize); if (newSpaceCntOfSecondLine !== oldSpaceCntOfSecondLine) { let spaceCntOffset = newSpaceCntOfSecondLine - oldSpaceCntOfSecondLine; for (let i = startLineNumber + 1; i <= range.endLineNumber; i++) { let lineContent = model.getLineContent(i); let originalIndent = strings.getLeadingWhitespace(lineContent); - let originalSpacesCnt = IndentUtil.getSpaceCnt(originalIndent, tabSize); + let originalSpacesCnt = indentUtils.getSpaceCnt(originalIndent, tabSize); let newSpacesCnt = originalSpacesCnt + spaceCntOffset; - let newIndent = IndentUtil.generateIndent(newSpacesCnt, tabSize, insertSpaces); + let newIndent = indentUtils.generateIndent(newSpacesCnt, tabSize, insertSpaces); if (newIndent !== originalIndent) { textEdits.push({ diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index 69838a01911..b8d09dcc0cb 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -4,26 +4,26 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; -import { SortLinesCommand } from 'vs/editor/contrib/linesOperations/sortLinesCommand'; -import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { TrimTrailingWhitespaceCommand } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand'; -import { ICommand } from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand'; -import { Range } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; -import { Position } from 'vs/editor/common/core/position'; -import { registerEditorAction, ServicesAccessor, IActionOptions, EditorAction } from 'vs/editor/browser/editorExtensions'; -import { CopyLinesCommand } from './copyLinesCommand'; -import { DeleteLinesCommand } from './deleteLinesCommand'; -import { MoveLinesCommand } from './moveLinesCommand'; -import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand'; +import { TrimTrailingWhitespaceCommand } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand'; +import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ICommand } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { CopyLinesCommand } from 'vs/editor/contrib/linesOperations/copyLinesCommand'; +import { DeleteLinesCommand } from 'vs/editor/contrib/linesOperations/deleteLinesCommand'; +import { MoveLinesCommand } from 'vs/editor/contrib/linesOperations/moveLinesCommand'; +import { SortLinesCommand } from 'vs/editor/contrib/linesOperations/sortLinesCommand'; import { MenuId } from 'vs/platform/actions/common/actions'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; // copy lines @@ -39,7 +39,7 @@ abstract class AbstractCopyLinesAction extends EditorAction { public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let commands: ICommand[] = []; - let selections = editor.getSelections(); + let selections = editor.getSelections() || []; for (let i = 0; i < selections.length; i++) { commands.push(new CopyLinesCommand(selections[i], this.down)); @@ -111,7 +111,7 @@ abstract class AbstractMoveLinesAction extends EditorAction { public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let commands: ICommand[] = []; - let selections = editor.getSelections(); + let selections = editor.getSelections() || []; let autoIndent = editor.getConfiguration().autoIndent; for (let i = 0; i < selections.length; i++) { @@ -179,7 +179,7 @@ export abstract class AbstractSortLinesAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { - const selections = editor.getSelections(); + const selections = editor.getSelections() || []; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; @@ -246,7 +246,7 @@ export class TrimTrailingWhitespaceAction extends EditorAction { // See https://github.com/editorconfig/editorconfig-vscode/issues/47 // It is very convenient for the editor config extension to invoke this action. // So, if we get a reason:'auto-save' passed in, let's preserve cursor positions. - cursors = editor.getSelections().map(s => new Position(s.positionLineNumber, s.positionColumn)); + cursors = (editor.getSelections() || []).map(s => new Position(s.positionLineNumber, s.positionColumn)); } let command = new TrimTrailingWhitespaceCommand(editor.getSelection(), cursors); @@ -649,8 +649,8 @@ export class JoinLinesAction extends EditorAction { reducedSelections.push(lastSelection); let model = editor.getModel(); - let edits = []; - let endCursorState = []; + let edits: IIdentifiedSingleEditOperation[] = []; + let endCursorState: Selection[] = []; let endPrimaryCursor = primaryCursor; let lineOffset = 0; diff --git a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts index 254b34b9a84..ae17e2f5426 100644 --- a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from 'vs/base/common/strings'; +import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { LanguageConfigurationRegistry, IIndentConverter } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; -import * as IndentUtil from 'vs/editor/contrib/indentation/indentUtils'; import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { IIndentConverter, LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; +import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils'; export class MoveLinesCommand implements ICommand { @@ -63,7 +63,7 @@ export class MoveLinesCommand implements ICommand { getLanguageIdAtPosition: (lineNumber: number, column: number) => { return model.getLanguageIdAtPosition(lineNumber, column); }, - getLineContent: null as (lineNumber: number) => string, + getLineContent: null as unknown as (lineNumber: number) => string, }; if (s.startLineNumber === s.endLineNumber && model.getLineMaxColumn(s.startLineNumber) === 1) { @@ -103,8 +103,8 @@ export class MoveLinesCommand implements ICommand { // if s.startLineNumber - 1 matches onEnter rule, we still honor that. if (movingLineMatchResult !== null) { let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber)); - let newSpaceCnt = movingLineMatchResult + IndentUtil.getSpaceCnt(oldIndentation, tabSize); - let newIndentation = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces); + let newSpaceCnt = movingLineMatchResult + indentUtils.getSpaceCnt(oldIndentation, tabSize); + let newIndentation = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces); insertingText = newIndentation + this.trimLeft(movingLineText); } else { // no enter rule matches, let's check indentatin rules then. @@ -119,10 +119,10 @@ export class MoveLinesCommand implements ICommand { movingLineNumber, 1), s.startLineNumber, indentConverter); if (indentOfMovingLine !== null) { let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber)); - let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfMovingLine, tabSize); - let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize); + let newSpaceCnt = indentUtils.getSpaceCnt(indentOfMovingLine, tabSize); + let oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize); if (newSpaceCnt !== oldSpaceCnt) { - let newIndentation = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces); + let newIndentation = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces); insertingText = newIndentation + this.trimLeft(movingLineText); } } @@ -155,8 +155,8 @@ export class MoveLinesCommand implements ICommand { if (newIndentatOfMovingBlock !== null) { const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber)); - const newSpaceCnt = IndentUtil.getSpaceCnt(newIndentatOfMovingBlock, tabSize); - const oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize); + const newSpaceCnt = indentUtils.getSpaceCnt(newIndentatOfMovingBlock, tabSize); + const oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize); if (newSpaceCnt !== oldSpaceCnt) { const spaceCntOffset = newSpaceCnt - oldSpaceCnt; @@ -199,8 +199,8 @@ export class MoveLinesCommand implements ICommand { if (indentOfFirstLine !== null) { // adjust the indentation of the moving block let oldIndent = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber)); - let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfFirstLine, tabSize); - let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndent, tabSize); + let newSpaceCnt = indentUtils.getSpaceCnt(indentOfFirstLine, tabSize); + let oldSpaceCnt = indentUtils.getSpaceCnt(oldIndent, tabSize); if (newSpaceCnt !== oldSpaceCnt) { let spaceCntOffset = newSpaceCnt - oldSpaceCnt; @@ -284,8 +284,8 @@ export class MoveLinesCommand implements ICommand { if (indentMetadataOfMovelingLine !== null && indentMetadataOfMovelingLine & IndentConsts.DECREASE_MASK) { newIndentation = indentConverter.unshiftIndent(newIndentation); } - let newSpaceCnt = IndentUtil.getSpaceCnt(newIndentation, tabSize); - let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize); + let newSpaceCnt = indentUtils.getSpaceCnt(newIndentation, tabSize); + let oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize); return newSpaceCnt - oldSpaceCnt; } } @@ -323,9 +323,9 @@ export class MoveLinesCommand implements ICommand { for (let i = s.startLineNumber; i <= s.endLineNumber; i++) { let lineContent = model.getLineContent(i); let originalIndent = strings.getLeadingWhitespace(lineContent); - let originalSpacesCnt = IndentUtil.getSpaceCnt(originalIndent, tabSize); + let originalSpacesCnt = indentUtils.getSpaceCnt(originalIndent, tabSize); let newSpacesCnt = originalSpacesCnt + offset; - let newIndent = IndentUtil.generateIndent(newSpacesCnt, tabSize, insertSpaces); + let newIndent = indentUtils.generateIndent(newSpacesCnt, tabSize, insertSpaces); if (newIndent !== originalIndent) { builder.addEditOperation(new Range(i, 1, i, originalIndent.length + 1), newIndent); diff --git a/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts b/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts index 6eb7a859179..421a67e9c4f 100644 --- a/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { EditOperation } from 'vs/editor/common/core/editOperation'; -import * as editorCommon from 'vs/editor/common/editorCommon'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; export class SortLinesCommand implements editorCommon.ICommand { diff --git a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts index 66a538d2b38..1c256e78379 100644 --- a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Selection } from 'vs/editor/common/core/selection'; +import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; +import { Cursor } from 'vs/editor/common/controller/cursor'; import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; import { Handler } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; +import { DeleteAllLeftAction, DeleteAllRightAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TransposeAction, UpperCaseAction } from 'vs/editor/contrib/linesOperations/linesOperations'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { DeleteAllLeftAction, JoinLinesAction, TransposeAction, UpperCaseAction, LowerCaseAction, DeleteAllRightAction, InsertLineBeforeAction, InsertLineAfterAction, IndentLinesAction, SortLinesAscendingAction, SortLinesDescendingAction } from 'vs/editor/contrib/linesOperations/linesOperations'; -import { Cursor } from 'vs/editor/common/controller/cursor'; -import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('Editor Contrib - Line Operations', () => { diff --git a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts index 6f45e07bb15..2abfe4c7de2 100644 --- a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Selection } from 'vs/editor/common/core/selection'; -import { MoveLinesCommand } from 'vs/editor/contrib/linesOperations/moveLinesCommand'; -import { testCommand } from 'vs/editor/test/browser/testCommand'; -import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { IndentationRule } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { MoveLinesCommand } from 'vs/editor/contrib/linesOperations/moveLinesCommand'; +import { testCommand } from 'vs/editor/test/browser/testCommand'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; function testMoveLinesDownCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { testCommand(lines, null, selection, (sel) => new MoveLinesCommand(sel, true, false), expectedLines, expectedSelection); diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index f3607812e70..8ee3eabb7f0 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { Range, IRange } from 'vs/editor/common/core/range'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { ILink, LinkProvider, LinkProviderRegistry } from 'vs/editor/common/modes'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; export class Link implements ILink { diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index 44536558099..d8a101eb334 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -5,25 +5,25 @@ import 'vs/css!./links'; import * as nls from 'vs/nls'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import * as platform from 'vs/base/common/platform'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; -import { LinkProviderRegistry } from 'vs/editor/common/modes'; -import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { getLinks, Link } from 'vs/editor/contrib/links/getLinks'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; -import { Position } from 'vs/editor/common/core/position'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture'; -import { MarkdownString } from 'vs/base/common/htmlContent'; -import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import * as async from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { Position } from 'vs/editor/common/core/position'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { LinkProviderRegistry } from 'vs/editor/common/modes'; +import { ClickLinkGesture, ClickLinkKeyboardEvent, ClickLinkMouseEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture'; +import { Link, getLinks } from 'vs/editor/contrib/links/getLinks'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; const HOVER_MESSAGE_GENERAL_META = new MarkdownString().appendText( platform.isMacintosh diff --git a/src/vs/editor/contrib/markdown/markdownRenderer.ts b/src/vs/editor/contrib/markdown/markdownRenderer.ts index 561d886ea12..62208620c8c 100644 --- a/src/vs/editor/contrib/markdown/markdownRenderer.ts +++ b/src/vs/editor/contrib/markdown/markdownRenderer.ts @@ -38,16 +38,23 @@ export class MarkdownRenderer { // In markdown, // it is possible that we stumble upon language aliases (e.g.js instead of javascript) // it is possible no alias is given in which case we fall back to the current editor lang - const modeId = languageAlias - ? this._modeService.getModeIdForLanguageName(languageAlias) - : this._editor.getModel().getLanguageIdentifier().language; + let modeId: string | null = null; + if (languageAlias) { + modeId = this._modeService.getModeIdForLanguageName(languageAlias); + } else { + const model = this._editor.getModel(); + if (model) { + modeId = model.getLanguageIdentifier().language; + } + } - return this._modeService.getOrCreateMode(modeId).then(_ => { - const promise = TokenizationRegistry.getPromise(modeId); + this._modeService.triggerMode(modeId || ''); + return Promise.resolve(true).then(_ => { + const promise = TokenizationRegistry.getPromise(modeId || ''); if (promise) { return promise.then(support => tokenizeToString(value, support)); } - return tokenizeToString(value, null); + return tokenizeToString(value, undefined); }).then(code => { return `${code}`; }); @@ -55,13 +62,13 @@ export class MarkdownRenderer { codeBlockRenderCallback: () => this._onDidRenderCodeBlock.fire(), actionHandler: { callback: (content) => { - let uri: URI; + let uri: URI | undefined; try { uri = URI.parse(content); - } catch (err) { + } catch { // ignore } - if (uri) { + if (uri && this._openerService) { this._openerService.open(uri).catch(onUnexpectedError); } }, diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index fcc3610ce62..13f3554c681 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -4,28 +4,28 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { ScrollType, IEditorContribution } from 'vs/editor/common/editorCommon'; -import { FindMatch, TrackedRangeStickiness, OverviewRulerLane, ITextModel } from 'vs/editor/common/model'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; -import { Range } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { RevealTarget } from 'vs/editor/common/controller/cursorCommon'; import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands'; -import { RevealTarget } from 'vs/editor/common/controller/cursorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; import { Constants } from 'vs/editor/common/core/uint'; +import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { FindMatch, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { DocumentHighlightProviderRegistry } from 'vs/editor/common/modes'; import { CommonFindController } from 'vs/editor/contrib/find/findController'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { FindOptionOverride, INewFindReplaceState } from 'vs/editor/contrib/find/findState'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; -import { INewFindReplaceState, FindOptionOverride } from 'vs/editor/contrib/find/findState'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { MenuId } from 'vs/platform/actions/common/actions'; export class InsertCursorAbove extends EditorAction { @@ -165,6 +165,57 @@ class InsertCursorAtEndOfEachLineSelected extends EditorAction { } } +class InsertCursorAtEndOfLineSelected extends EditorAction { + + constructor() { + super({ + id: 'editor.action.addCursorsToBottom', + label: nls.localize('mutlicursor.addCursorsToBottom', "Add Cursors To Bottom"), + alias: 'Add Cursors To Bottom', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + const selections = editor.getSelections(); + const lineCount = editor.getModel().getLineCount(); + + let newSelections = []; + for (let i = selections[0].startLineNumber; i <= lineCount; i++) { + newSelections.push(new Selection(i, selections[0].startColumn, i, selections[0].endColumn)); + } + + if (newSelections.length > 0) { + editor.setSelections(newSelections); + } + } +} + +class InsertCursorAtTopOfLineSelected extends EditorAction { + + constructor() { + super({ + id: 'editor.action.addCursorsToTop', + label: nls.localize('mutlicursor.addCursorsToTop', "Add Cursors To Top"), + alias: 'Add Cursors To Top', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + const selections = editor.getSelections(); + + let newSelections = []; + for (let i = selections[0].startLineNumber; i >= 1; i--) { + newSelections.push(new Selection(i, selections[0].startColumn, i, selections[0].endColumn)); + } + + if (newSelections.length > 0) { + editor.setSelections(newSelections); + } + } +} + export class MultiCursorSessionResult { constructor( public readonly selections: Selection[], @@ -943,3 +994,5 @@ registerEditorAction(MoveSelectionToNextFindMatchAction); registerEditorAction(MoveSelectionToPreviousFindMatchAction); registerEditorAction(SelectHighlightsAction); registerEditorAction(CompatChangeAll); +registerEditorAction(InsertCursorAtEndOfLineSelected); +registerEditorAction(InsertCursorAtTopOfLineSelected); \ No newline at end of file diff --git a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts index 614249fefbe..83f05dd1534 100644 --- a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { withTestCodeEditor, TestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { Selection } from 'vs/editor/common/core/selection'; +import { Event } from 'vs/base/common/event'; import { Range } from 'vs/editor/common/core/range'; -import { InsertCursorAbove, InsertCursorBelow, MultiCursorSelectionController, SelectHighlightsAction, AddSelectionToNextFindMatchAction } from 'vs/editor/contrib/multicursor/multicursor'; +import { Selection } from 'vs/editor/common/core/selection'; import { Handler } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence } from 'vs/editor/common/model'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { Event } from 'vs/base/common/event'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { CommonFindController } from 'vs/editor/contrib/find/findController'; +import { AddSelectionToNextFindMatchAction, InsertCursorAbove, InsertCursorBelow, MultiCursorSelectionController, SelectHighlightsAction } from 'vs/editor/contrib/multicursor/multicursor'; +import { TestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IStorageService } from 'vs/platform/storage/common/storage'; suite('Multicursor', () => { diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.ts b/src/vs/editor/contrib/parameterHints/parameterHints.ts index f85e2604b35..78784055d33 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHints.ts @@ -12,7 +12,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { ParameterHintsWidget } from './parameterHintsWidget'; +import { ParameterHintsWidget, TriggerContext } from './parameterHintsWidget'; import { Context } from 'vs/editor/contrib/parameterHints/provideSignatureHelp'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import * as modes from 'vs/editor/common/modes'; @@ -49,7 +49,7 @@ class ParameterHintsController implements IEditorContribution { this.widget.next(); } - trigger(context: modes.SignatureHelpContext): void { + trigger(context: TriggerContext): void { this.widget.trigger(context); } @@ -77,7 +77,9 @@ export class TriggerParameterHintsAction extends EditorAction { public run(accessor: ServicesAccessor, editor: ICodeEditor): void { let controller = ParameterHintsController.get(editor); if (controller) { - controller.trigger({ triggerReason: modes.SignatureHelpTriggerReason.Invoke }); + controller.trigger({ + triggerReason: modes.SignatureHelpTriggerReason.Invoke + }); } } } diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 1950a39827c..7c5475da647 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -28,6 +28,11 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; const $ = dom.$; +export interface TriggerContext { + readonly triggerReason: modes.SignatureHelpTriggerReason; + readonly triggerCharacter?: string; +} + export interface IHintEvent { hints: modes.SignatureHelp; } @@ -95,13 +100,20 @@ export class ParameterHintsModel extends Disposable { } } - trigger(context: modes.SignatureHelpContext, delay?: number): void { + trigger(context: TriggerContext, delay?: number): void { if (!modes.SignatureHelpProviderRegistry.has(this.editor.getModel())) { return; } + const wasTriggered = this.isTriggered; this.cancel(true); - this.triggerContext = context; + + this.triggerContext = { + triggerReason: context.triggerReason, + triggerCharacter: context.triggerCharacter, + isRetrigger: wasTriggered + }; + return this.throttledDelayer.schedule(delay); } @@ -112,7 +124,7 @@ export class ParameterHintsModel extends Disposable { this.pending = true; - const triggerContext = this.triggerContext || { triggerReason: modes.SignatureHelpTriggerReason.Invoke }; + const triggerContext = this.triggerContext || { triggerReason: modes.SignatureHelpTriggerReason.Invoke, isRetrigger: false }; this.triggerContext = undefined; this.provideSignatureHelpRequest = createCancelablePromise(token => @@ -180,15 +192,10 @@ export class ParameterHintsModel extends Disposable { const lastCharIndex = text.length - 1; const triggerCharCode = text.charCodeAt(lastCharIndex); - if (this.isTriggered && this.retriggerChars.has(triggerCharCode)) { - this.trigger({ - triggerReason: modes.SignatureHelpTriggerReason.Retrigger, - triggerCharacter: text.charAt(lastCharIndex) - }); - } else if (this.triggerChars.has(triggerCharCode)) { + if (this.triggerChars.has(triggerCharCode) || this.isTriggered && this.retriggerChars.has(triggerCharCode)) { this.trigger({ triggerReason: modes.SignatureHelpTriggerReason.TriggerCharacter, - triggerCharacter: text.charAt(lastCharIndex) + triggerCharacter: text.charAt(lastCharIndex), }); } } @@ -197,13 +204,13 @@ export class ParameterHintsModel extends Disposable { if (e.source === 'mouse') { this.cancel(); } else if (this.isTriggered) { - this.trigger({ triggerReason: modes.SignatureHelpTriggerReason.Retrigger }); + this.trigger({ triggerReason: modes.SignatureHelpTriggerReason.ContentChange }); } } private onModelContentChange(): void { if (this.isTriggered) { - this.trigger({ triggerReason: modes.SignatureHelpTriggerReason.Retrigger }); + this.trigger({ triggerReason: modes.SignatureHelpTriggerReason.ContentChange }); } } @@ -474,7 +481,9 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { private getParameterLabelOffsets(signature: modes.SignatureInformation, paramIdx: number): [number, number] { const param = signature.parameters[paramIdx]; - if (Array.isArray(param.label)) { + if (!param) { + return [0, 0]; + } else if (Array.isArray(param.label)) { return param.label; } else { const idx = signature.label.lastIndexOf(param.label); @@ -569,7 +578,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { return ParameterHintsWidget.ID; } - trigger(context: modes.SignatureHelpContext): void { + trigger(context: TriggerContext): void { this.model.trigger(context, 0); } diff --git a/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts b/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts index 99290fb5fe1..8f29af942ac 100644 --- a/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts +++ b/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts @@ -27,4 +27,7 @@ export function provideSignatureHelp(model: ITextModel, position: Position, cont } registerDefaultLanguageCommand('_executeSignatureHelpProvider', (model, position) => - provideSignatureHelp(model, position, { triggerReason: modes.SignatureHelpTriggerReason.Invoke }, CancellationToken.None)); + provideSignatureHelp(model, position, { + triggerReason: modes.SignatureHelpTriggerReason.Invoke, + isRetrigger: false + }, CancellationToken.None)); diff --git a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts index 586e2063108..8ebea45c4e9 100644 --- a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts @@ -93,7 +93,8 @@ suite('ParameterHintsModel', () => { editor.trigger('keyboard', Handler.Type, { text: triggerChar }); } else { assert.strictEqual(invokeCount, 2); - assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.Retrigger); + assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.TriggerCharacter); + assert.ok(context.isRetrigger); assert.strictEqual(context.triggerCharacter, triggerChar); done(); } @@ -149,7 +150,8 @@ suite('ParameterHintsModel', () => { provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelp | Thenable { ++invokeCount; - assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.Retrigger); + assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.TriggerCharacter); + assert.ok(context.isRetrigger); assert.strictEqual(context.triggerCharacter, 'c'); // Give some time to allow for later triggers @@ -185,7 +187,8 @@ suite('ParameterHintsModel', () => { // retrigger after delay for widget to show up setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: 'b' }), 50); } else if (invokeCount === 2) { - assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.Retrigger); + assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.TriggerCharacter); + assert.ok(context.isRetrigger); assert.strictEqual(context.triggerCharacter, 'b'); done(); } else { @@ -272,7 +275,8 @@ suite('ParameterHintsModel', () => { // retrigger after delay for widget to show up setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: retriggerChar }), 50); } else if (invokeCount === 2) { - assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.Retrigger); + assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.TriggerCharacter); + assert.ok(context.isRetrigger); assert.strictEqual(context.triggerCharacter, retriggerChar); done(); } else { diff --git a/src/vs/editor/contrib/referenceSearch/referenceSearch.ts b/src/vs/editor/contrib/referenceSearch/referenceSearch.ts index 5c872b26f76..539d96078e6 100644 --- a/src/vs/editor/contrib/referenceSearch/referenceSearch.ts +++ b/src/vs/editor/contrib/referenceSearch/referenceSearch.ts @@ -60,8 +60,8 @@ export class ReferenceAction extends EditorAction { constructor() { super({ id: 'editor.action.referenceSearch.trigger', - label: nls.localize('references.action.label', "Find All References"), - alias: 'Find All References', + label: nls.localize('references.action.label', "Peek References"), + alias: 'Find All References', // leave the alias? precondition: ContextKeyExpr.and( EditorContextKeys.hasReferenceProvider, PeekContext.notInPeekEditor, diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 75f2cc2296c..2fa6143d301 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -43,10 +43,10 @@ class RenameSkeleton { return this._provider.length > 0; } - async resolveRenameLocation(token: CancellationToken): Promise { + async resolveRenameLocation(token: CancellationToken): Promise { let [provider] = this._provider; - let res: RenameLocation & Rejection; + let res: RenameLocation & Rejection | null | undefined; if (provider.resolveRenameLocation) { res = await provider.resolveRenameLocation(this.model, this.position, token); @@ -116,15 +116,19 @@ class RenameController implements IEditorContribution { this._renameInputVisible = CONTEXT_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); } - public dispose(): void { + dispose(): void { this._renameInputField.dispose(); } - public getId(): string { + getId(): string { return RenameController.ID; } - public async run(token: CancellationToken): Promise { + async run(token: CancellationToken): Promise { + + if (!this.editor.hasModel()) { + return undefined; + } const position = this.editor.getPosition(); const skeleton = new RenameSkeleton(this.editor.getModel(), position); @@ -133,7 +137,7 @@ class RenameController implements IEditorContribution { return undefined; } - let loc: RenameLocation & Rejection; + let loc: RenameLocation & Rejection | null | undefined; try { loc = await skeleton.resolveRenameLocation(token); } catch (e) { @@ -175,6 +179,11 @@ class RenameController implements IEditorContribution { const state = new EditorState(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection | CodeEditorStateFlag.Scroll); const renameOperation = Promise.resolve(skeleton.provideRenameEdits(newNameOrFocusFlag, 0, [], token).then(result => { + + if (!this.editor.hasModel()) { + return undefined; + } + if (result.rejectReason) { if (state.validate(this.editor)) { MessageController.get(this.editor).showMessage(result.rejectReason, this.editor.getPosition()); @@ -187,7 +196,7 @@ class RenameController implements IEditorContribution { return this._bulkEditService.apply(result, { editor: this.editor }).then(result => { // alert if (result.ariaSummary) { - alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc.text, newNameOrFocusFlag, result.ariaSummary)); + alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, newNameOrFocusFlag, result.ariaSummary)); } }); @@ -242,6 +251,9 @@ export class RenameAction extends EditorAction { if (URI.isUri(uri) && Position.isIPosition(pos)) { return editorService.openCodeEditor({ resource: uri }, editorService.getActiveCodeEditor()).then(editor => { + if (!editor) { + return; + } editor.setPosition(pos); editor.invokeWithinContext(accessor => { this.reportTelemetry(accessor, editor); @@ -258,7 +270,7 @@ export class RenameAction extends EditorAction { if (controller) { return Promise.resolve(controller.run(CancellationToken.None)); } - return undefined; + return Promise.resolve(); } } diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index 91fbe9737de..5c60267db08 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -99,14 +99,14 @@ export default class RenameInputField implements IContentWidget, IDisposable { this._inputField.style.fontSize = `${fontInfo.fontSize}px`; } - public getPosition(): IContentWidgetPosition { + public getPosition(): IContentWidgetPosition | null { return this._visible ? { position: this._position, preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] } : null; } - private _currentAcceptInput: () => void = null; - private _currentCancelInput: (focusEditor) => void = null; + private _currentAcceptInput: (() => void) | null = null; + private _currentCancelInput: ((focusEditor) => void) | null = null; public acceptInput(): void { if (this._currentAcceptInput) { @@ -158,7 +158,8 @@ export default class RenameInputField implements IContentWidget, IDisposable { }; let onCursorChanged = () => { - if (!Range.containsPosition(where, this._editor.getPosition())) { + const editorPosition = this._editor.getPosition(); + if (!editorPosition || !Range.containsPosition(where, editorPosition)) { this.cancelInput(true); } }; @@ -185,8 +186,8 @@ export default class RenameInputField implements IContentWidget, IDisposable { setTimeout(() => { this._inputField.focus(); this._inputField.setSelectionRange( - parseInt(this._inputField.getAttribute('selectionStart')), - parseInt(this._inputField.getAttribute('selectionEnd'))); + parseInt(this._inputField.getAttribute('selectionStart')!), + parseInt(this._inputField.getAttribute('selectionEnd')!)); }, 100); } diff --git a/src/vs/editor/contrib/smartSelect/test/tokenSelectionSupport.test.ts b/src/vs/editor/contrib/smartSelect/test/tokenSelectionSupport.test.ts index 633ce503192..bab374e26c7 100644 --- a/src/vs/editor/contrib/smartSelect/test/tokenSelectionSupport.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/tokenSelectionSupport.test.ts @@ -8,7 +8,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { TokenSelectionSupport } from 'vs/editor/contrib/smartSelect/tokenSelectionSupport'; -import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { MockMode, StaticLanguageSelector } from 'vs/editor/test/common/mocks/mockMode'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -56,7 +56,7 @@ suite('TokenSelectionSupport', () => { function assertGetRangesToPosition(text: string[], lineNumber: number, column: number, ranges: Range[]): void { let uri = URI.file('test.js'); - modelService.createModel(text.join('\n'), mode, uri); + modelService.createModel(text.join('\n'), new StaticLanguageSelector(mode.getLanguageIdentifier()), uri); let actual = tokenSelectionSupport.getRangesToPositionSync(uri, new Position(lineNumber, column)); diff --git a/src/vs/editor/contrib/smartSelect/tokenSelectionSupport.ts b/src/vs/editor/contrib/smartSelect/tokenSelectionSupport.ts index 8fca91c771e..04400c56929 100644 --- a/src/vs/editor/contrib/smartSelect/tokenSelectionSupport.ts +++ b/src/vs/editor/contrib/smartSelect/tokenSelectionSupport.ts @@ -7,14 +7,14 @@ import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { Node, build, find } from './tokenTree'; +import { build, find } from './tokenTree'; import { Position } from 'vs/editor/common/core/position'; /** * Interface used to compute a hierachry of logical ranges. */ export interface ILogicalSelectionEntry { - type: string; + type?: string; range: Range; } @@ -45,10 +45,9 @@ export class TokenSelectionSupport { private _doGetRangesToPosition(model: ITextModel, position: Position): Range[] { let tree = build(model); - let node: Node; - let lastRange: Range; + let lastRange: Range | undefined; - node = find(tree, position); + let node = find(tree, position); let ranges: Range[] = []; while (node) { if (!lastRange || !Range.equalsRange(lastRange, node.range)) { diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index fa99073f569..df08a20b3a9 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -173,7 +173,7 @@ export class SnippetController2 implements IEditorContribution { // insertText: `\${1|${after.concat(before).join(',')}|}$0`, // snippetType: 'textmate', sortText: repeat('a', i), - overwriteAfter: first.value.length + range: Range.fromPositions(this._editor.getPosition(), this._editor.getPosition().delta(0, first.value.length)) }; })); } diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 68450c4f919..9928428a60c 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -357,6 +357,10 @@ export class SnippetSession { let firstBeforeText = model.getValueInRange(SnippetSession.adjustSelection(model, editor.getSelection(), overwriteBefore, 0)); let firstAfterText = model.getValueInRange(SnippetSession.adjustSelection(model, editor.getSelection(), 0, overwriteAfter)); + // remember the first non-whitespace column to decide if + // `keepWhitespace` should be overruled for secondary selections + let firstLineFirstNonWhitespace = model.getLineFirstNonWhitespaceColumn(editor.getSelection().positionLineNumber); + // sort selections by their start position but remeber // the original index. that allows you to create correct // offset-based selection logic without changing the @@ -387,8 +391,10 @@ export class SnippetSession { // adjust the template string to match the indentation and // whitespace rules of this insert location (can be different for each cursor) + // happens when being asked for (default) or when this is a secondary + // cursor and the leading whitespace is different const start = snippetSelection.getStartPosition(); - if (adjustWhitespace) { + if (adjustWhitespace || (idx > 0 && firstLineFirstNonWhitespace !== model.getLineFirstNonWhitespaceColumn(selection.positionLineNumber))) { SnippetSession.adjustWhitespace(model, start, snippet); } diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index f78a5daae87..384bcb78205 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -143,17 +143,16 @@ } /** Styles for each row in the list **/ - .monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label::before { - height: 16px; - padding-right: 4px; + height: 100%; + } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon { display: block; height: 16px; width: 16px; - margin-right: 2px; + margin-left: 2px; background-repeat: no-repeat; background-size: 80%; background-position: center; @@ -163,37 +162,44 @@ display: none; } -.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .icon { +.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .icon, +.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .monaco-icon-label.suggest-icon::before { display: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon { background-image: url('Misc_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.method, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.function, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.constructor { background-image: url('Method_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.field { background-image: url('Field_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.event { background-image: url('Event_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.operator { background-image: url('Operator_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.variable { background-image: url('LocalVariable_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.class { background-image: url('Class_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.interface { background-image: url('Interface_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.struct { background-image: url('Structure_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.type-parameter { background-image: url('Template_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.module { background-image: url('Namespace_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.property { background-image: url('Property_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.unit { background-image: url('Ruler_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.constant { background-image: url('Constant_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.value, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.enum { background-image: url('Enumerator_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.enum-member { background-image: url('EnumItem_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.keyword { background-image: url('IntelliSenseKeyword_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.text { background-image: url('String_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.color { background-image: url('ColorPalette_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.file { background-image: url('Document_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.reference { background-image: url('ImportFile_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.snippet { background-image: url('Snippet_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.customcolor { background-image: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.folder { background-image: url('Folder_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.suggest-icon::before { + content: ' '; + background-image: url('Misc_16x.svg'); + background-repeat: no-repeat; + background-position: center; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method::before, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function::before, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before { background-image: url('Method_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before { background-image: url('Field_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before { background-image: url('Event_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before { background-image: url('Operator_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before { background-image: url('LocalVariable_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before { background-image: url('Class_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before { background-image: url('Interface_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before { background-image: url('Structure_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before { background-image: url('Template_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before { background-image: url('Namespace_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before { background-image: url('Property_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before { background-image: url('Ruler_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before { background-image: url('Constant_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value::before, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before { background-image: url('Enumerator_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before { background-image: url('EnumItem_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before { background-image: url('IntelliSenseKeyword_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before { background-image: url('String_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before { background-image: url('ColorPalette_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before { background-image: url('Document_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before { background-image: url('ImportFile_16x_vscode.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before { background-image: url('Snippet_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor::before { background-image: none; } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before { background-image: url('Folder_16x.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.customcolor .colorspan { margin: 0 0 0 0.3em; @@ -284,80 +290,80 @@ background-image: url('./close-dark.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon { background-image: url('Misc_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon::before { background-image: url('Misc_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.method, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.method, -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.function, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.function, -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.constructor, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.constructor { background-image: url('Method_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method::before, +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function::before, +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before { background-image: url('Method_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.field, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.field { background-image: url('Field_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before { background-image: url('Field_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.event, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.event { background-image: url('Event_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before { background-image: url('Event_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.operator, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.operator { background-image: url('Operator_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before { background-image: url('Operator_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.variable, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.variable { background-image: url('LocalVariable_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before { background-image: url('LocalVariable_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.class, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.class { background-image: url('Class_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before { background-image: url('Class_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.interface, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.interface { background-image: url('Interface_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before { background-image: url('Interface_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.struct, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.struct { background-image: url('Structure_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before { background-image: url('Structure_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.type-parameter, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.type-parameter { background-image: url('Template_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before { background-image: url('Template_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.module, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.module { background-image: url('Namespace_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before { background-image: url('Namespace_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.property, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.property { background-image: url('Property_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before { background-image: url('Property_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.unit, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.unit { background-image: url('Ruler_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before { background-image: url('Ruler_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.constant, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.constant { background-image: url('Constant_16x_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before { background-image: url('Constant_16x_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.value, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.value, -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.enum, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.enum { background-image: url('Enumerator_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value::before, +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before { background-image: url('Enumerator_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.enum-member, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.enum-member { background-image: url('EnumItem_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before { background-image: url('EnumItem_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.keyword, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.keyword { background-image: url('IntelliSenseKeyword_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before { background-image: url('IntelliSenseKeyword_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.text, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.text { background-image: url('String_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before { background-image: url('String_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.color, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.color { background-image: url('ColorPalette_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before { background-image: url('ColorPalette_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.file, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.file { background-image: url('Document_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before { background-image: url('Document_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.reference, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.reference { background-image: url('ImportFile_16x_vscode_inverse.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before { background-image: url('ImportFile_16x_vscode_inverse.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.snippet, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.snippet { background-image: url('Snippet_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before { background-image: url('Snippet_inverse_16x.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.customcolor, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.customcolor { background-image: none; } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor::before { background-image: none; } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.folder, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.folder { background-image: url('Folder_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before { background-image: url('Folder_inverse_16x.svg'); } diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index 70274ba7818..f2619157158 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -152,12 +152,16 @@ export function ensureLowerCaseVariants(suggestion: CompletionItem) { } function createSuggestionResolver(provider: CompletionItemProvider, suggestion: CompletionItem, model: ITextModel, position: Position): (token: CancellationToken) => Promise { + let cached: Promise; return (token) => { - if (typeof provider.resolveCompletionItem === 'function') { - return Promise.resolve(provider.resolveCompletionItem(model, position, suggestion, token)).then(value => { assign(suggestion, value); }); - } else { - return Promise.resolve(void 0); + if (!cached) { + if (typeof provider.resolveCompletionItem === 'function') { + cached = Promise.resolve(provider.resolveCompletionItem(model, position, suggestion, token)).then(value => { assign(suggestion, value); }); + } else { + cached = Promise.resolve(void 0); + } } + return cached; }; } diff --git a/src/vs/editor/contrib/suggest/suggestAlternatives.ts b/src/vs/editor/contrib/suggest/suggestAlternatives.ts index bc65e8bb6fe..a7c47391ca5 100644 --- a/src/vs/editor/contrib/suggest/suggestAlternatives.ts +++ b/src/vs/editor/contrib/suggest/suggestAlternatives.ts @@ -14,14 +14,15 @@ export class SuggestAlternatives { static OtherSuggestions = new RawContextKey('hasOtherSuggestions', false); private readonly _ckOtherSuggestions: IContextKey; + private _index: number; private _model: CompletionModel; + private _acceptNext: (selected: ISelectedSuggestion) => any; private _listener: IDisposable; private _ignore: boolean; constructor( private readonly _editor: ICodeEditor, - private readonly _accept: (selected: ISelectedSuggestion) => any, @IContextKeyService contextKeyService: IContextKeyService ) { this._ckOtherSuggestions = SuggestAlternatives.OtherSuggestions.bindTo(contextKeyService); @@ -35,10 +36,11 @@ export class SuggestAlternatives { this._ckOtherSuggestions.reset(); dispose(this._listener); this._model = undefined; + this._acceptNext = undefined; this._ignore = false; } - set({ model, index }: ISelectedSuggestion): void { + set({ model, index }: ISelectedSuggestion, acceptNext: (selected: ISelectedSuggestion) => any): void { // no suggestions -> nothing to do if (model.items.length === 0) { @@ -53,6 +55,7 @@ export class SuggestAlternatives { return; } + this._acceptNext = acceptNext; this._model = model; this._index = index; this._listener = this._editor.onDidChangeCursorPosition(() => { @@ -93,7 +96,7 @@ export class SuggestAlternatives { try { this._ignore = true; this._index = SuggestAlternatives._moveIndex(fwd, this._model, this._index); - this._accept({ index: this._index, item: this._model.items[this._index], model: this._model }); + this._acceptNext({ index: this._index, item: this._model.items[this._index], model: this._model }); } finally { this._ignore = false; } diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 2de72fe8c24..db8e2cb45d5 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -14,7 +14,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution, ScrollType, Handler } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { CompletionItem, CompletionItemProvider, CompletionItemInsertTextRule } from 'vs/editor/common/modes'; +import { CompletionItemProvider, CompletionItemInsertTextRule } from 'vs/editor/common/modes'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { SuggestMemories } from 'vs/editor/contrib/suggest/suggestMemory'; @@ -23,7 +23,6 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ICompletionItem } from './completionModel'; import { Context as SuggestContext, ISuggestionItem } from './suggest'; import { SuggestAlternatives } from './suggestAlternatives'; @@ -101,7 +100,6 @@ export class SuggestController implements IEditorContribution { @IEditorWorkerService editorWorker: IEditorWorkerService, @ICommandService private readonly _commandService: ICommandService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { this._model = new SuggestModel(this._editor, editorWorker); @@ -112,7 +110,7 @@ export class SuggestController implements IEditorContribution { }); this._alternatives = new IdleValue(() => { - let res = new SuggestAlternatives(this._editor, item => this._onDidSelectItem(item, false, false), this._contextKeyService); + let res = new SuggestAlternatives(this._editor, this._contextKeyService); this._toDispose.push(res); return res; }); @@ -221,6 +219,8 @@ export class SuggestController implements IEditorContribution { return; } + const model = this._editor.getModel(); + const modelVersionNow = model.getAlternativeVersionId(); const { suggestion, position } = event.item; const editorColumn = this._editor.getPosition().column; const columnDelta = editorColumn - position.column; @@ -236,7 +236,7 @@ export class SuggestController implements IEditorContribution { } // keep item in memory - this._memory.getValue().memorize(this._editor.getModel(), this._editor.getPosition(), event.item); + this._memory.getValue().memorize(model, this._editor.getPosition(), event.item); let { insertText } = suggestion; if (!(suggestion.insertTextRules & CompletionItemInsertTextRule.InsertAsSnippet)) { @@ -273,24 +273,21 @@ export class SuggestController implements IEditorContribution { } if (keepAlternativeSuggestions) { - this._alternatives.getValue().set(event); + this._alternatives.getValue().set(event, next => { + // this is not so pretty. when inserting the 'next' + // suggestion we undo until we are at the state at + // which we were before inserting the previous suggestion... + while (model.canUndo()) { + if (modelVersionNow !== model.getAlternativeVersionId()) { + model.undo(); + } + this._onDidSelectItem(next, false, false); + break; + } + }); } this._alertCompletionItem(event.item); - SuggestController._onDidSelectTelemetry(this._telemetryService, suggestion); - } - - private static _onDidSelectTelemetry(service: ITelemetryService, item: CompletionItem): void { - /* __GDPR__ - "acceptsuggestion2" : { - "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "multiline" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - service.publicLog('acceptsuggestion2', { - type: item.kind, - multiline: item.insertText.match(/\r|\n/) ? 'TRUE' : 'FALSE' - }); } private _alertCompletionItem({ suggestion }: ICompletionItem): void { diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index c37df11784e..9fd55ac4d09 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -386,6 +386,8 @@ export class SuggestModel implements IDisposable { this._requestToken = new CancellationTokenSource(); + // TODO: Remove this workaround - https://github.com/Microsoft/vscode/issues/61917 + // let wordDistance = Promise.resolve().then(() => WordDistance.create(this._editorWorker, this._editor)); let wordDistance = WordDistance.create(this._editorWorker, this._editor); let items = provideSuggestionItems( diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 45289783912..3dc19b69164 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -37,6 +37,7 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { FileKind } from 'vs/platform/files/common/files'; const expandSuggestionDocsByDefault = false; const maxSuggestionsToShow = 12; @@ -85,6 +86,7 @@ class Renderer implements IListRendererObject.create(null); data.disposables = []; data.root = container; + addClass(data.root, 'show-file-icons'); data.icon = append(container, $('.icon')); data.colorspan = append(data.icon, $('span.colorspan')); @@ -147,33 +150,42 @@ class Renderer implements IListRenderer { diff --git a/src/vs/editor/contrib/wordOperations/test/wordTestUtils.ts b/src/vs/editor/contrib/wordOperations/test/wordTestUtils.ts index ec374b4e2b9..68a6a2f6738 100644 --- a/src/vs/editor/contrib/wordOperations/test/wordTestUtils.ts +++ b/src/vs/editor/contrib/wordOperations/test/wordTestUtils.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Position } from 'vs/editor/common/core/position'; -import { withTestCodeEditor, TestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { TestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; export function deserializePipePositions(text: string): [string, Position[]] { let resultText = ''; diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index 468dd013f28..b39269c30a7 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -4,19 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { ScrollType } from 'vs/editor/common/editorCommon'; -import { ITextModel } from 'vs/editor/common/model'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { Selection } from 'vs/editor/common/core/selection'; -import { registerEditorCommand, ServicesAccessor, EditorCommand, ICommandOptions } from 'vs/editor/browser/editorExtensions'; -import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; -import { WordNavigationType, WordOperations } from 'vs/editor/common/controller/cursorWordOperations'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorCommand, ICommandOptions, ServicesAccessor, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; -import { getMapForWordSeparators, WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier'; import { CursorState } from 'vs/editor/common/controller/cursorCommon'; import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { WordNavigationType, WordOperations } from 'vs/editor/common/controller/cursorWordOperations'; +import { WordCharacterClassifier, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ScrollType } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ITextModel } from 'vs/editor/common/model'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export interface MoveWordOptions extends ICommandOptions { diff --git a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts index 57a41073820..cce3df09f91 100644 --- a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts +++ b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts @@ -2,12 +2,13 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { Position } from 'vs/editor/common/core/position'; -import { DeleteWordPartLeft, DeleteWordPartRight, CursorWordPartLeft, CursorWordPartLeftSelect, CursorWordPartRight, CursorWordPartRightSelect } from 'vs/editor/contrib/wordPartOperations/wordPartOperations'; -import { EditorCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { deserializePipePositions, testRepeatedActionAndExtractPositions, serializePipePositions } from 'vs/editor/contrib/wordOperations/test/wordTestUtils'; +import { EditorCommand } from 'vs/editor/browser/editorExtensions'; +import { Position } from 'vs/editor/common/core/position'; +import { deserializePipePositions, serializePipePositions, testRepeatedActionAndExtractPositions } from 'vs/editor/contrib/wordOperations/test/wordTestUtils'; +import { CursorWordPartLeft, CursorWordPartLeftSelect, CursorWordPartRight, CursorWordPartRightSelect, DeleteWordPartLeft, DeleteWordPartRight } from 'vs/editor/contrib/wordPartOperations/wordPartOperations'; suite('WordPartOperations', () => { const _deleteWordPartLeft = new DeleteWordPartLeft(); diff --git a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts index 44794d95791..0c20a58b790 100644 --- a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts +++ b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { ITextModel } from 'vs/editor/common/model'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { Selection } from 'vs/editor/common/core/selection'; import { registerEditorCommand } from 'vs/editor/browser/editorExtensions'; -import { Range } from 'vs/editor/common/core/range'; import { WordNavigationType, WordPartOperations } from 'vs/editor/common/controller/cursorWordOperations'; import { WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier'; -import { DeleteWordCommand, MoveWordCommand } from '../wordOperations/wordOperations'; import { Position } from 'vs/editor/common/core/position'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ITextModel } from 'vs/editor/common/model'; +import { DeleteWordCommand, MoveWordCommand } from 'vs/editor/contrib/wordOperations/wordOperations'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class DeleteWordPartLeft extends DeleteWordCommand { constructor() { diff --git a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts index 7990d5453dd..a627626bfc0 100644 --- a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts @@ -4,19 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./zoneWidget'; +import * as dom from 'vs/base/browser/dom'; +import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; +import { Color, RGBA } from 'vs/base/common/color'; +import { IdGenerator } from 'vs/base/common/idGenerator'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; -import * as dom from 'vs/base/browser/dom'; -import { Sash, Orientation, IHorizontalSashLayoutProvider, ISashEvent, SashState } from 'vs/base/browser/ui/sash/sash'; -import { Range, IRange } from 'vs/editor/common/core/range'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; -import { Color, RGBA } from 'vs/base/common/color'; import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { IdGenerator } from 'vs/base/common/idGenerator'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; export interface IOptions { showFrame?: boolean; @@ -31,8 +31,8 @@ export interface IOptions { } export interface IStyles { - frameColor?: Color; - arrowColor?: Color; + frameColor?: Color | null; + arrowColor?: Color | null; } const defaultColor = new Color(new RGBA(0, 122, 204)); @@ -98,7 +98,7 @@ export class OverlayWidgetDelegate implements IOverlayWidget { return this._domNode; } - public getPosition(): IOverlayWidgetPosition { + public getPosition(): IOverlayWidgetPosition | null { return null; } } @@ -160,11 +160,11 @@ class Arrow { export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { private _arrow: Arrow; - private _overlayWidget: OverlayWidgetDelegate; + private _overlayWidget: OverlayWidgetDelegate | null; private _resizeSash: Sash; private _positionMarkerId: string[] = []; - protected _viewZone: ViewZoneDelegate; + protected _viewZone: ViewZoneDelegate | null; protected _disposables: IDisposable[] = []; public container: HTMLElement; @@ -202,7 +202,9 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { if (this._viewZone) { this.editor.changeViewZones(accessor => { - accessor.removeZone(this._viewZone.id); + if (this._viewZone) { + accessor.removeZone(this._viewZone.id); + } this._viewZone = null; }); } @@ -214,7 +216,9 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { public create(): void { dom.addClass(this.domNode, 'zone-widget'); - dom.addClass(this.domNode, this.options.className); + if (this.options.className) { + dom.addClass(this.domNode, this.options.className); + } this.container = document.createElement('div'); dom.addClass(this.container, 'zone-widget-container'); @@ -239,12 +243,12 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { } protected _applyStyles(): void { - if (this.container) { + if (this.container && this.options.frameColor) { let frameColor = this.options.frameColor.toString(); this.container.style.borderTopColor = frameColor; this.container.style.borderBottomColor = frameColor; } - if (this._arrow) { + if (this._arrow && this.options.arrowColor) { let arrowColor = this.options.arrowColor.toString(); this._arrow.color = arrowColor; } @@ -277,12 +281,18 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { this._resizeSash.layout(); } - public get position(): Position { + public get position(): Position | undefined { const [id] = this._positionMarkerId; if (!id) { return undefined; } - const range = this.editor.getModel().getDecorationRange(id); + + const model = this.editor.getModel(); + if (!model) { + return undefined; + } + + const range = model.getDecorationRange(id); if (!range) { return undefined; } @@ -305,7 +315,9 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { public hide(): void { if (this._viewZone) { this.editor.changeViewZones(accessor => { - accessor.removeZone(this._viewZone.id); + if (this._viewZone) { + accessor.removeZone(this._viewZone.id); + } }); this._viewZone = null; } @@ -413,9 +425,12 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { this.editor.setSelection(where); } - // Reveal the line above or below the zone widget, to get the zone widget in the viewport - const revealLineNumber = Math.min(this.editor.getModel().getLineCount(), Math.max(1, where.endLineNumber + 1)); - this.revealLine(revealLineNumber); + const model = this.editor.getModel(); + if (model) { + // Reveal the line above or below the zone widget, to get the zone widget in the viewport + const revealLineNumber = Math.min(model.getLineCount(), Math.max(1, where.endLineNumber + 1)); + this.revealLine(revealLineNumber); + } } protected revealLine(lineNumber: number) { @@ -442,10 +457,12 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { } protected _relayout(newHeightInLines: number): void { - if (this._viewZone.heightInLines !== newHeightInLines) { + if (this._viewZone && this._viewZone.heightInLines !== newHeightInLines) { this.editor.changeViewZones(accessor => { - this._viewZone.heightInLines = newHeightInLines; - accessor.layoutZone(this._viewZone.id); + if (this._viewZone) { + this._viewZone.heightInLines = newHeightInLines; + accessor.layoutZone(this._viewZone.id); + } }); } } @@ -460,7 +477,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { this._resizeSash.state = SashState.Disabled; } - let data: { startY: number; heightInLines: number; }; + let data: { startY: number; heightInLines: number; } | undefined; this._disposables.push(this._resizeSash.onDidStart((e: ISashEvent) => { if (this._viewZone) { data = { @@ -492,7 +509,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { } getHorizontalSashTop() { - return parseInt(this.domNode.style.height) - (this._decoratingElementsHeight() / 2); + return (this.domNode.style.height === null ? 0 : parseInt(this.domNode.style.height)) - (this._decoratingElementsHeight() / 2); } getHorizontalSashWidth() { diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index d16ae868270..d4d7ef65ee7 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -7,11 +7,11 @@ import 'vs/editor/browser/controller/coreCommands'; import 'vs/editor/browser/widget/codeEditorWidget'; import 'vs/editor/browser/widget/diffEditorWidget'; import 'vs/editor/browser/widget/diffNavigator'; - import 'vs/editor/contrib/bracketMatching/bracketMatching'; import 'vs/editor/contrib/caretOperations/caretOperations'; import 'vs/editor/contrib/caretOperations/transpose'; import 'vs/editor/contrib/clipboard/clipboard'; +import 'vs/editor/contrib/codeAction/codeActionContributions'; import 'vs/editor/contrib/codelens/codelensController'; import 'vs/editor/contrib/colorPicker/colorDetector'; import 'vs/editor/contrib/comment/comment'; @@ -31,7 +31,6 @@ import 'vs/editor/contrib/linesOperations/linesOperations'; import 'vs/editor/contrib/links/links'; import 'vs/editor/contrib/multicursor/multicursor'; import 'vs/editor/contrib/parameterHints/parameterHints'; -import 'vs/editor/contrib/codeAction/codeActionContributions'; import 'vs/editor/contrib/referenceSearch/referenceSearch'; import 'vs/editor/contrib/rename/rename'; import 'vs/editor/contrib/smartSelect/smartSelect'; diff --git a/src/vs/editor/editor.api.ts b/src/vs/editor/editor.api.ts index 20c5c9d4cb9..4b91fda4d45 100644 --- a/src/vs/editor/editor.api.ts +++ b/src/vs/editor/editor.api.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; +import { EDITOR_DEFAULTS, WrappingIndent } from 'vs/editor/common/config/editorOptions'; import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'; import { createMonacoEditorAPI } from 'vs/editor/standalone/browser/standaloneEditor'; import { createMonacoLanguagesAPI } from 'vs/editor/standalone/browser/standaloneLanguages'; -import { EDITOR_DEFAULTS, WrappingIndent } from 'vs/editor/common/config/editorOptions'; -import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; var global: any = self; diff --git a/src/vs/editor/editor.main.ts b/src/vs/editor/editor.main.ts index 040a988e932..2566ecac65b 100644 --- a/src/vs/editor/editor.main.ts +++ b/src/vs/editor/editor.main.ts @@ -5,12 +5,12 @@ import 'vs/editor/editor.all'; import 'vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp'; -import 'vs/editor/standalone/browser/inspectTokens/inspectTokens'; import 'vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard'; -import 'vs/editor/standalone/browser/quickOpen/quickOutline'; +import 'vs/editor/standalone/browser/inspectTokens/inspectTokens'; import 'vs/editor/standalone/browser/quickOpen/gotoLine'; import 'vs/editor/standalone/browser/quickOpen/quickCommand'; -import 'vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast'; +import 'vs/editor/standalone/browser/quickOpen/quickOutline'; import 'vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch'; +import 'vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast'; export * from 'vs/editor/editor.api'; diff --git a/src/vs/editor/editor.worker.ts b/src/vs/editor/editor.worker.ts index 0eb3c40ebb0..41140026a47 100644 --- a/src/vs/editor/editor.worker.ts +++ b/src/vs/editor/editor.worker.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker'; import { SimpleWorkerServer } from 'vs/base/common/worker/simpleWorker'; +import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker'; let initialized = false; diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts index b69787e9ca2..d3d05ae6d8d 100644 --- a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts +++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts @@ -5,31 +5,31 @@ import 'vs/css!./accessibilityHelp'; import * as nls from 'vs/nls'; +import * as browser from 'vs/base/browser/browser'; +import * as dom from 'vs/base/browser/dom'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { Widget } from 'vs/base/browser/ui/widget'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; -import * as dom from 'vs/base/browser/dom'; -import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; -import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { Widget } from 'vs/base/browser/ui/widget'; -import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { URI } from 'vs/base/common/uri'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { Selection } from 'vs/editor/common/core/selection'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { registerEditorAction, registerEditorContribution, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorWidgetBackground, widgetShadow, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import * as platform from 'vs/base/common/platform'; -import { alert } from 'vs/base/browser/ui/aria/aria'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { URI } from 'vs/base/common/uri'; -import { Selection } from 'vs/editor/common/core/selection'; -import * as browser from 'vs/base/browser/browser'; import { IEditorConstructionOptions } from 'vs/editor/standalone/browser/standaloneCodeEditor'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { contrastBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey('accessibilityHelpWidgetVisible', false); diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index 245b4559fef..e085b2e80ed 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable } from 'vs/base/common/lifecycle'; -import { ITextModel } from 'vs/editor/common/model'; -import { ColorId, MetadataConsts, FontStyle, TokenizationRegistry, ITokenizationSupport } from 'vs/editor/common/modes'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { renderViewLine2 as renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer'; -import { LineTokens, IViewLineTokens } from 'vs/editor/common/core/lineTokens'; -import * as strings from 'vs/base/common/strings'; -import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; -import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { TimeoutTimer } from 'vs/base/common/async'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import * as strings from 'vs/base/common/strings'; +import { IViewLineTokens, LineTokens } from 'vs/editor/common/core/lineTokens'; +import { ITextModel } from 'vs/editor/common/model'; +import { ColorId, FontStyle, ITokenizationSupport, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/modes'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { RenderLineInput, renderViewLine2 as renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; export interface IColorizerOptions { tabSize?: number; @@ -60,7 +60,7 @@ export class Colorizer { } // Send out the event to create the mode - modeService.getOrCreateMode(language); + modeService.triggerMode(language); let tokenizationSupport = TokenizationRegistry.get(language); if (tokenizationSupport) { diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts index cf3c70dc143..722105f2cf7 100644 --- a/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts +++ b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./iPadShowKeyboard'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; export class IPadShowKeyboard implements IEditorContribution { diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts index 9372dbd7e58..7369afaad96 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -5,22 +5,22 @@ import 'vs/css!./inspectTokens'; import * as nls from 'vs/nls'; +import { CharCode } from 'vs/base/common/charCode'; +import { Color } from 'vs/base/common/color'; import { Disposable } from 'vs/base/common/lifecycle'; import { escape } from 'vs/base/common/strings'; +import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; +import { Token } from 'vs/editor/common/core/token'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { registerEditorAction, registerEditorContribution, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditor, ContentWidgetPositionPreference, IContentWidget, IContentWidgetPosition, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { TokenizationRegistry, LanguageIdentifier, FontStyle, StandardTokenType, ITokenizationSupport, IState, TokenMetadata } from 'vs/editor/common/modes'; -import { CharCode } from 'vs/base/common/charCode'; -import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { FontStyle, IState, ITokenizationSupport, LanguageIdentifier, StandardTokenType, TokenMetadata, TokenizationRegistry } from 'vs/editor/common/modes'; import { NULL_STATE, nullTokenize, nullTokenize2 } from 'vs/editor/common/modes/nullMode'; -import { Token } from 'vs/editor/common/core/token'; -import { Color } from 'vs/base/common/color'; -import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; +import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; class InspectTokensController extends Disposable implements IEditorContribution { diff --git a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts index 13c572dc484..89764e22f17 100644 --- a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts +++ b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts @@ -6,15 +6,15 @@ import 'vs/css!./editorQuickOpen'; import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; -import * as editorCommon from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { QuickOpenEditorWidget } from './quickOpenEditorWidget'; -import { Selection } from 'vs/editor/common/core/selection'; -import { registerEditorContribution, IActionOptions, EditorAction } from 'vs/editor/browser/editorExtensions'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { EditorAction, IActionOptions, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { QuickOpenEditorWidget } from 'vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; export interface IQuickOpenControllerOpts { inputAriaLabel: string; diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts index 49e2f9864bc..0a4135b4087 100644 --- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts +++ b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts @@ -5,17 +5,17 @@ import 'vs/css!./gotoLine'; import * as nls from 'vs/nls'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IContext, QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { IAutoFocus, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ICodeEditor, IDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { BaseEditorQuickOpenAction, IDecorator } from './editorQuickOpen'; -import { registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITextModel } from 'vs/editor/common/model'; +import { BaseEditorQuickOpenAction, IDecorator } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; interface ParseResult { diff --git a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts index b3b30c1b95b..0b84763f0c4 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts @@ -4,18 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import * as browser from 'vs/base/browser/browser'; import { onUnexpectedError } from 'vs/base/common/errors'; import { matchesFuzzy } from 'vs/base/common/filters'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IContext, IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { IAutoFocus, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IEditorAction, IEditor } from 'vs/editor/common/editorCommon'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { BaseEditorQuickOpenAction } from './editorQuickOpen'; -import { registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import * as browser from 'vs/base/browser/browser'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { IEditor, IEditorAction } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { BaseEditorQuickOpenAction } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class EditorActionCommandEntry extends QuickOpenEntryGroup { diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts index 1a78bb5fffc..34df6555f29 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Dimension } from 'vs/base/browser/dom'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenWidget } from 'vs/base/parts/quickopen/browser/quickOpenWidget'; import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; -import { attachQuickOpenStyler } from 'vs/platform/theme/common/styler'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; import { foreground } from 'vs/platform/theme/common/colorRegistry'; -import { Dimension } from 'vs/base/browser/dom'; +import { attachQuickOpenStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; export interface IQuickOpenEditorWidgetOptions { inputAriaLabel: string; diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts index 24526f675d5..37f229f1ff2 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts @@ -5,21 +5,21 @@ import 'vs/css!./quickOutline'; import * as nls from 'vs/nls'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { matchesFuzzy } from 'vs/base/common/filters'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as strings from 'vs/base/common/strings'; import { IContext, IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { IAutoFocus, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { DocumentSymbol, DocumentSymbolProviderRegistry, symbolKindToCssClass } from 'vs/editor/common/modes'; -import { BaseEditorQuickOpenAction, IDecorator } from './editorQuickOpen'; import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen'; -import { registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { BaseEditorQuickOpenAction, IDecorator } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { CancellationToken } from 'vs/base/common/cancellation'; let SCOPE_PREFIX = ':'; diff --git a/src/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch.ts b/src/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch.ts index 5934481f798..3dcbb43537c 100644 --- a/src/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch.ts +++ b/src/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IStorageService } from 'vs/platform/storage/common/storage'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IStorageService } from 'vs/platform/storage/common/storage'; export class StandaloneReferencesController extends ReferencesController { diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 810b67b4328..f1e31e1af67 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -3,48 +3,45 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import Severity from 'vs/base/common/severity'; -import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { ICommandService, ICommand, ICommandEvent, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; -import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; -import { IKeybindingEvent, KeybindingSource, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; -import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IWorkspaceContextService, IWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ICodeEditor, IDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { Event, Emitter } from 'vs/base/common/event'; -import { Configuration, DefaultConfigurationModel, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; -import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; -import { ITextModelService, ITextModelContentProvider, ITextEditorModel } from 'vs/editor/common/services/resolverService'; -import { IDisposable, IReference, ImmortalReference, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { KeybindingsRegistry, IKeybindingItem } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions'; -import { Menu } from 'vs/platform/actions/common/menu'; -import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; -import { ResolvedKeybinding, Keybinding, createKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; -import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Keybinding, ResolvedKeybinding, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; +import { IDisposable, IReference, ImmortalReference, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { OS, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { Range } from 'vs/editor/common/core/range'; -import { ITextModel } from 'vs/editor/common/model'; -import { INotificationService, INotification, INotificationHandle, NoOpNotification, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification'; -import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions } from 'vs/platform/dialogs/common/dialogs'; -import { IPosition, Position as Pos } from 'vs/editor/common/core/position'; -import { isEditorConfigurationKey, isDiffEditorConfigurationKey } from 'vs/editor/common/config/commonEditorConfig'; -import { IBulkEditService, IBulkEditOptions, IBulkEditResult } from 'vs/editor/browser/services/bulkEditService'; -import { WorkspaceEdit, isResourceTextEdit, TextEdit } from 'vs/editor/common/modes'; -import { IModelService } from 'vs/editor/common/services/modelService'; +import Severity from 'vs/base/common/severity'; +import { URI } from 'vs/base/common/uri'; +import { ICodeEditor, IDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IBulkEditOptions, IBulkEditResult, IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { isDiffEditorConfigurationKey, isEditorConfigurationKey } from 'vs/editor/common/config/commonEditorConfig'; import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { localize } from 'vs/nls'; +import { IPosition, Position as Pos } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ITextModel } from 'vs/editor/common/model'; +import { TextEdit, WorkspaceEdit, isResourceTextEdit } from 'vs/editor/common/modes'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; +import { CommandsRegistry, ICommand, ICommandEvent, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Configuration, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; +import { IKeybindingEvent, IKeyboardEvent, KeybindingSource } from 'vs/platform/keybinding/common/keybinding'; +import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; +import { IKeybindingItem, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { ILabelService, LabelRules, RegisterFormatterEvent } from 'vs/platform/label/common/label'; +import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification } from 'vs/platform/notification/common/notification'; +import { IProgressRunner, IProgressService } from 'vs/platform/progress/common/progress'; +import { ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export class SimpleModel implements ITextEditorModel { @@ -60,8 +57,8 @@ export class SimpleModel implements ITextEditorModel { return this._onDispose.event; } - public load(): TPromise { - return TPromise.as(this); + public load(): Promise { + return Promise.resolve(this); } public get textEditorModel(): ITextModel { @@ -100,7 +97,7 @@ export class SimpleEditorModelResolverService implements ITextModelService { this.editor = editor; } - public createModelReference(resource: URI): TPromise> { + public createModelReference(resource: URI): Promise> { let model: ITextModel; model = withTypedEditor(this.editor, @@ -109,10 +106,10 @@ export class SimpleEditorModelResolverService implements ITextModelService { ); if (!model) { - return TPromise.as(new ImmortalReference(null)); + return Promise.resolve(new ImmortalReference(null)); } - return TPromise.as(new ImmortalReference(new SimpleModel(model))); + return Promise.resolve(new ImmortalReference(new SimpleModel(model))); } public registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable { @@ -417,8 +414,8 @@ export class SimpleConfigurationService implements IConfigurationService { public inspect(key: string, options: IConfigurationOverrides = {}): { default: C, user: C, - workspace: C, - workspaceFolder: C + workspace?: C, + workspaceFolder?: C value: C, } { return this.configuration().inspect(key, options, null); @@ -479,31 +476,16 @@ export class SimpleResourcePropertiesService implements ITextResourcePropertiesS } } -export class SimpleMenuService implements IMenuService { - - _serviceBrand: any; - - private readonly _commandService: ICommandService; - - constructor(commandService: ICommandService) { - this._commandService = commandService; - } - - public createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu { - return new Menu(id, Promise.resolve(true), this._commandService, contextKeyService); - } -} - export class StandaloneTelemetryService implements ITelemetryService { _serviceBrand: void; public isOptedIn = false; - public publicLog(eventName: string, data?: any): TPromise { - return TPromise.wrap(null); + public publicLog(eventName: string, data?: any): Promise { + return Promise.resolve(null); } - public getTelemetryInfo(): TPromise { + public getTelemetryInfo(): Promise { return null; } } @@ -635,4 +617,8 @@ export class SimpleUriLabelService implements ILabelService { public registerFormatter(selector: string, formatter: LabelRules): IDisposable { throw new Error('Not implemented'); } + + public getHostLabel(): string { + return ''; + } } diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index f623c86781e..385af45f87c 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -3,31 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IModelChangedEvent } from 'vs/editor/common/editorCommon'; -import { ITextModel } from 'vs/editor/common/model'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { StandaloneKeybindingService, applyConfigurationValues } from 'vs/editor/standalone/browser/simpleServices'; -import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; -import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; -import { InternalEditorAction } from 'vs/editor/common/editorAction'; -import { MenuId, MenuRegistry, IMenuItem } from 'vs/platform/actions/common/actions'; -import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import * as aria from 'vs/base/browser/ui/aria/aria'; import * as nls from 'vs/nls'; import * as browser from 'vs/base/browser/browser'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import * as aria from 'vs/base/browser/ui/aria/aria'; +import { Disposable, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { InternalEditorAction } from 'vs/editor/common/editorAction'; +import { IModelChangedEvent } from 'vs/editor/common/editorCommon'; +import { ITextModel } from 'vs/editor/common/model'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { StandaloneKeybindingService, applyConfigurationValues } from 'vs/editor/standalone/browser/simpleServices'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { IMenuItem, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; /** * Description of an action contribution @@ -69,7 +69,7 @@ export interface IActionDescriptor { contextMenuOrder?: number; /** * Method that will be executed when the action is triggered. - * @param editor The editor instance is passed in as a convinience + * @param editor The editor instance is passed in as a convenience */ run(editor: ICodeEditor): void | Promise; } diff --git a/src/vs/editor/standalone/browser/standaloneCodeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneCodeServiceImpl.ts index 58b3a3b7fdb..1e060eaa872 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeServiceImpl.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IResourceInput } from 'vs/platform/editor/common/editor'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; -import { ScrollType } from 'vs/editor/common/editorCommon'; import { windowOpenNoOpener } from 'vs/base/browser/dom'; import { Schemas } from 'vs/base/common/network'; -import { IRange } from 'vs/editor/common/core/range'; -import { ITextModel } from 'vs/editor/common/model'; import { URI } from 'vs/base/common/uri'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; +import { IRange } from 'vs/editor/common/core/range'; +import { ScrollType } from 'vs/editor/common/editorCommon'; +import { ITextModel } from 'vs/editor/common/model'; +import { IResourceInput } from 'vs/platform/editor/common/editor'; export class StandaloneCodeEditorServiceImpl extends CodeEditorServiceImpl { diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index e4e986e24ca..acace41158e 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -4,38 +4,38 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./standalone-tokens'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ICodeEditor, ContentWidgetPositionPreference, OverlayWidgetPositionPreference, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { StandaloneEditor, IStandaloneCodeEditor, StandaloneDiffEditor, IStandaloneDiffEditor, IEditorConstructionOptions, IDiffEditorConstructionOptions } from 'vs/editor/standalone/browser/standaloneCodeEditor'; -import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { IEditorOverrideServices, DynamicStandaloneServices, StaticServices } from 'vs/editor/standalone/browser/standaloneServices'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { OpenerService } from 'vs/editor/browser/services/openerService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; +import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; +import { Token } from 'vs/editor/common/core/token'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { FindMatch, ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; +import * as modes from 'vs/editor/common/modes'; +import { NULL_STATE, nullTokenize } from 'vs/editor/common/modes/nullMode'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from 'vs/editor/common/services/webWorker'; +import * as standaloneEnums from 'vs/editor/common/standalone/standaloneEnums'; import { Colorizer, IColorizerElementOptions, IColorizerOptions } from 'vs/editor/standalone/browser/colorizer'; import { SimpleEditorModelResolverService } from 'vs/editor/standalone/browser/simpleServices'; -import * as modes from 'vs/editor/common/modes'; -import { IWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from 'vs/editor/common/services/webWorker'; -import { IMarkerData, IMarker } from 'vs/platform/markers/common/markers'; -import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; +import { IDiffEditorConstructionOptions, IEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor, StandaloneDiffEditor, StandaloneEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor'; +import { DynamicStandaloneServices, IEditorOverrideServices, StaticServices } from 'vs/editor/standalone/browser/standaloneServices'; +import { IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { NULL_STATE, nullTokenize } from 'vs/editor/common/modes/nullMode'; -import { IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; -import { Token } from 'vs/editor/common/core/token'; -import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo'; -import * as editorOptions from 'vs/editor/common/config/editorOptions'; -import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; -import { ITextModel, OverviewRulerLane, EndOfLinePreference, DefaultEndOfLine, EndOfLineSequence, TrackedRangeStickiness, TextModelResolvedOptions, FindMatch } from 'vs/editor/common/model'; +import { IMarker, IMarkerData } from 'vs/platform/markers/common/markers'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; function withAllStandaloneServices(domElement: HTMLElement, override: IEditorOverrideServices, callback: (services: DynamicStandaloneServices) => T): T { let services = new DynamicStandaloneServices(domElement, override); @@ -135,8 +135,8 @@ export function createDiffNavigator(diffEditor: IStandaloneDiffEditor, opts?: ID return new DiffNavigator(diffEditor, opts); } -function doCreateModel(value: string, mode: Promise, uri?: URI): ITextModel { - return StaticServices.modelService.get().createModel(value, mode, uri); +function doCreateModel(value: string, languageSelection: ILanguageSelection, uri?: URI): ITextModel { + return StaticServices.modelService.get().createModel(value, languageSelection, uri); } /** @@ -155,16 +155,16 @@ export function createModel(value: string, language?: string, uri?: URI): ITextM firstLine = value.substring(0, firstLF); } - return doCreateModel(value, StaticServices.modeService.get().getOrCreateModeByFilepathOrFirstLine(path, firstLine), uri); + return doCreateModel(value, StaticServices.modeService.get().createByFilepathOrFirstLine(path, firstLine), uri); } - return doCreateModel(value, StaticServices.modeService.get().getOrCreateMode(language), uri); + return doCreateModel(value, StaticServices.modeService.get().create(language), uri); } /** * Change the language for a model. */ export function setModelLanguage(model: ITextModel, languageId: string): void { - StaticServices.modelService.get().setMode(model, StaticServices.modeService.get().getOrCreateMode(languageId)); + StaticServices.modelService.get().setMode(model, StaticServices.modeService.get().create(languageId)); } /** @@ -278,7 +278,7 @@ function getSafeTokenizationSupport(language: string): modes.ITokenizationSuppor export function tokenize(text: string, languageId: string): Token[][] { let modeService = StaticServices.modeService.get(); // Needed in order to get the mode registered for subsequent look-ups - modeService.getOrCreateMode(languageId); + modeService.triggerMode(languageId); let tokenizationSupport = getSafeTokenizationSupport(languageId); let lines = text.split(/\r\n|\r|\n/); @@ -295,7 +295,7 @@ export function tokenize(text: string, languageId: string): Token[][] { } /** - * Define a new theme or updte an existing theme. + * Define a new theme or update an existing theme. */ export function defineTheme(themeName: string, themeData: IStandaloneThemeData): void { StaticServices.standaloneThemeService.get().defineTheme(themeName, themeData); @@ -308,33 +308,6 @@ export function setTheme(themeName: string): void { StaticServices.standaloneThemeService.get().setTheme(themeName); } -/** - * @internal - * -------------------------------------------- - * This is repeated here so it can be exported - * because TS inlines const enums - * -------------------------------------------- - */ -enum ScrollType { - Smooth = 0, - Immediate = 1, -} - -/** - * @internal - * -------------------------------------------- - * This is repeated here so it can be exported - * because TS inlines const enums - * -------------------------------------------- - */ -enum RenderLineNumbersType { - Off = 0, - On = 1, - Relative = 2, - Interval = 3, - Custom = 4 -} - /** * @internal */ @@ -346,42 +319,42 @@ export function createMonacoEditorAPI(): typeof monaco.editor { createDiffEditor: createDiffEditor, createDiffNavigator: createDiffNavigator, - createModel: createModel, - setModelLanguage: setModelLanguage, - setModelMarkers: setModelMarkers, - getModelMarkers: getModelMarkers, - getModels: getModels, - getModel: getModel, - onDidCreateModel: onDidCreateModel, - onWillDisposeModel: onWillDisposeModel, - onDidChangeModelLanguage: onDidChangeModelLanguage, + createModel: createModel, + setModelLanguage: setModelLanguage, + setModelMarkers: setModelMarkers, + getModelMarkers: getModelMarkers, + getModels: getModels, + getModel: getModel, + onDidCreateModel: onDidCreateModel, + onWillDisposeModel: onWillDisposeModel, + onDidChangeModelLanguage: onDidChangeModelLanguage, - createWebWorker: createWebWorker, - colorizeElement: colorizeElement, - colorize: colorize, - colorizeModelLine: colorizeModelLine, - tokenize: tokenize, - defineTheme: defineTheme, - setTheme: setTheme, + createWebWorker: createWebWorker, + colorizeElement: colorizeElement, + colorize: colorize, + colorizeModelLine: colorizeModelLine, + tokenize: tokenize, + defineTheme: defineTheme, + setTheme: setTheme, // enums - ScrollbarVisibility: ScrollbarVisibility, - WrappingIndent: editorOptions.WrappingIndent, - OverviewRulerLane: OverviewRulerLane, - EndOfLinePreference: EndOfLinePreference, - DefaultEndOfLine: DefaultEndOfLine, - EndOfLineSequence: EndOfLineSequence, - TrackedRangeStickiness: TrackedRangeStickiness, - CursorChangeReason: CursorChangeReason, - MouseTargetType: MouseTargetType, - TextEditorCursorStyle: editorOptions.TextEditorCursorStyle, - TextEditorCursorBlinkingStyle: editorOptions.TextEditorCursorBlinkingStyle, - ContentWidgetPositionPreference: ContentWidgetPositionPreference, - OverlayWidgetPositionPreference: OverlayWidgetPositionPreference, - RenderMinimap: editorOptions.RenderMinimap, - ScrollType: ScrollType, - RenderLineNumbersType: RenderLineNumbersType, + ScrollbarVisibility: standaloneEnums.ScrollbarVisibility, + WrappingIndent: standaloneEnums.WrappingIndent, + OverviewRulerLane: standaloneEnums.OverviewRulerLane, + EndOfLinePreference: standaloneEnums.EndOfLinePreference, + DefaultEndOfLine: standaloneEnums.DefaultEndOfLine, + EndOfLineSequence: standaloneEnums.EndOfLineSequence, + TrackedRangeStickiness: standaloneEnums.TrackedRangeStickiness, + CursorChangeReason: standaloneEnums.CursorChangeReason, + MouseTargetType: standaloneEnums.MouseTargetType, + TextEditorCursorStyle: standaloneEnums.TextEditorCursorStyle, + TextEditorCursorBlinkingStyle: standaloneEnums.TextEditorCursorBlinkingStyle, + ContentWidgetPositionPreference: standaloneEnums.ContentWidgetPositionPreference, + OverlayWidgetPositionPreference: standaloneEnums.OverlayWidgetPositionPreference, + RenderMinimap: standaloneEnums.RenderMinimap, + ScrollType: standaloneEnums.ScrollType, + RenderLineNumbersType: standaloneEnums.RenderLineNumbersType, // classes InternalEditorOptions: editorOptions.InternalEditorOptions, diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index fdb6caba717..5d3be561d56 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -3,23 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; -import { IMonarchLanguage } from 'vs/editor/standalone/common/monarch/monarchTypes'; -import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; -import { StaticServices } from 'vs/editor/standalone/browser/standaloneServices'; -import * as modes from 'vs/editor/common/modes'; -import { LanguageConfiguration, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { Token, TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; +import * as model from 'vs/editor/common/model'; +import * as modes from 'vs/editor/common/modes'; +import { LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; +import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; +import * as standaloneEnums from 'vs/editor/common/standalone/standaloneEnums'; +import { StaticServices } from 'vs/editor/standalone/browser/standaloneServices'; import { compile } from 'vs/editor/standalone/common/monarch/monarchCompile'; import { createTokenizationSupport } from 'vs/editor/standalone/common/monarch/monarchLexer'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { IMarkerData } from 'vs/platform/markers/common/markers'; -import { Token, TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; +import { IMonarchLanguage } from 'vs/editor/standalone/common/monarch/monarchTypes'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; -import * as model from 'vs/editor/common/model'; +import { IMarkerData } from 'vs/platform/markers/common/markers'; /** * Register information about a new language. @@ -333,7 +334,7 @@ export function registerRenameProvider(languageId: string, provider: modes.Renam } /** - * Register a signature help provider (used by e.g. paremeter hints). + * Register a signature help provider (used by e.g. parameter hints). */ export function registerSignatureHelpProvider(languageId: string, provider: modes.SignatureHelpProvider): IDisposable { return modes.SignatureHelpProviderRegistry.register(languageId, provider); @@ -503,42 +504,44 @@ export interface CodeActionProvider { */ export function createMonacoLanguagesAPI(): typeof monaco.languages { return { - register: register, - getLanguages: getLanguages, - onLanguage: onLanguage, - getEncodedLanguageId: getEncodedLanguageId, + register: register, + getLanguages: getLanguages, + onLanguage: onLanguage, + getEncodedLanguageId: getEncodedLanguageId, // provider methods - setLanguageConfiguration: setLanguageConfiguration, - setTokensProvider: setTokensProvider, - setMonarchTokensProvider: setMonarchTokensProvider, - registerReferenceProvider: registerReferenceProvider, - registerRenameProvider: registerRenameProvider, - registerCompletionItemProvider: registerCompletionItemProvider, - registerSignatureHelpProvider: registerSignatureHelpProvider, - registerHoverProvider: registerHoverProvider, - registerDocumentSymbolProvider: registerDocumentSymbolProvider, - registerDocumentHighlightProvider: registerDocumentHighlightProvider, - registerDefinitionProvider: registerDefinitionProvider, - registerImplementationProvider: registerImplementationProvider, - registerTypeDefinitionProvider: registerTypeDefinitionProvider, - registerCodeLensProvider: registerCodeLensProvider, - registerCodeActionProvider: registerCodeActionProvider, - registerDocumentFormattingEditProvider: registerDocumentFormattingEditProvider, - registerDocumentRangeFormattingEditProvider: registerDocumentRangeFormattingEditProvider, - registerOnTypeFormattingEditProvider: registerOnTypeFormattingEditProvider, - registerLinkProvider: registerLinkProvider, - registerColorProvider: registerColorProvider, - registerFoldingRangeProvider: registerFoldingRangeProvider, + setLanguageConfiguration: setLanguageConfiguration, + setTokensProvider: setTokensProvider, + setMonarchTokensProvider: setMonarchTokensProvider, + registerReferenceProvider: registerReferenceProvider, + registerRenameProvider: registerRenameProvider, + registerCompletionItemProvider: registerCompletionItemProvider, + registerSignatureHelpProvider: registerSignatureHelpProvider, + registerHoverProvider: registerHoverProvider, + registerDocumentSymbolProvider: registerDocumentSymbolProvider, + registerDocumentHighlightProvider: registerDocumentHighlightProvider, + registerDefinitionProvider: registerDefinitionProvider, + registerImplementationProvider: registerImplementationProvider, + registerTypeDefinitionProvider: registerTypeDefinitionProvider, + registerCodeLensProvider: registerCodeLensProvider, + registerCodeActionProvider: registerCodeActionProvider, + registerDocumentFormattingEditProvider: registerDocumentFormattingEditProvider, + registerDocumentRangeFormattingEditProvider: registerDocumentRangeFormattingEditProvider, + registerOnTypeFormattingEditProvider: registerOnTypeFormattingEditProvider, + registerLinkProvider: registerLinkProvider, + registerColorProvider: registerColorProvider, + registerFoldingRangeProvider: registerFoldingRangeProvider, // enums - DocumentHighlightKind: modes.DocumentHighlightKind, - CompletionItemKind: modes.CompletionItemKind, - CompletionItemInsertTextRule: modes.CompletionItemInsertTextRule, - SymbolKind: modes.SymbolKind, - IndentAction: IndentAction, - CompletionTriggerKind: modes.CompletionTriggerKind, + DocumentHighlightKind: standaloneEnums.DocumentHighlightKind, + CompletionItemKind: standaloneEnums.CompletionItemKind, + CompletionItemInsertTextRule: standaloneEnums.CompletionItemInsertTextRule, + SymbolKind: standaloneEnums.SymbolKind, + IndentAction: standaloneEnums.IndentAction, + CompletionTriggerKind: standaloneEnums.CompletionTriggerKind, + SignatureHelpTriggerReason: standaloneEnums.SignatureHelpTriggerReason, + + // classes FoldingRangeKind: modes.FoldingRangeKind, - SignatureHelpTriggerReason: modes.SignatureHelpTriggerReason, }; } diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 6546e1e6053..7b87fd88147 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -4,47 +4,44 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; -import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; -import { createDecorator, IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { MarkerService } from 'vs/platform/markers/common/markerService'; -import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IStorageService, NullStorageService } from 'vs/platform/storage/common/storage'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; -import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; +import { SimpleBulkEditService, SimpleConfigurationService, SimpleDialogService, SimpleNotificationService, SimpleProgressService, SimpleResourceConfigurationService, SimpleResourcePropertiesService, SimpleUriLabelService, SimpleWorkspaceContextService, StandaloneCommandService, StandaloneKeybindingService, StandaloneTelemetryService } from 'vs/editor/standalone/browser/simpleServices'; import { StandaloneCodeEditorServiceImpl } from 'vs/editor/standalone/browser/standaloneCodeServiceImpl'; -import { - SimpleConfigurationService, SimpleResourceConfigurationService, SimpleMenuService, - SimpleProgressService, StandaloneCommandService, StandaloneKeybindingService, SimpleNotificationService, - StandaloneTelemetryService, SimpleWorkspaceContextService, SimpleDialogService, SimpleBulkEditService, SimpleUriLabelService, SimpleResourcePropertiesService -} from 'vs/editor/standalone/browser/simpleServices'; -import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; -import { IMenuService } from 'vs/platform/actions/common/actions'; -import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl'; -import { ILogService, NullLogService } from 'vs/platform/log/common/log'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { IMenuService } from 'vs/platform/actions/common/actions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IListService, ListService } from 'vs/platform/list/browser/listService'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IInstantiationService, ServiceIdentifier, createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ILabelService } from 'vs/platform/label/common/label'; +import { IListService, ListService } from 'vs/platform/list/browser/listService'; +import { ILogService, NullLogService } from 'vs/platform/log/common/log'; +import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IStorageService, NullStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { MenuService } from 'vs/platform/actions/common/menuService'; export interface IEditorOverrideServices { [index: string]: any; @@ -193,7 +190,7 @@ export class DynamicStandaloneServices extends Disposable { ensure(IContextMenuService, () => this._register(new ContextMenuService(domElement, telemetryService, notificationService, contextViewService, keybindingService, themeService))); - ensure(IMenuService, () => new SimpleMenuService(commandService)); + ensure(IMenuService, () => new MenuService(commandService)); ensure(IBulkEditService, () => new SimpleBulkEditService(StaticServices.modelService.get(IModelService))); } diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index f25911aa714..32ef8b20066 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TokenTheme, ITokenThemeRule, generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; -import { IStandaloneThemeService, BuiltinTheme, IStandaloneThemeData, IStandaloneTheme } from 'vs/editor/standalone/common/standaloneThemeService'; -import { vs, vs_dark, hc_black } from 'vs/editor/standalone/common/themes'; import * as dom from 'vs/base/browser/dom'; -import { TokenizationRegistry } from 'vs/editor/common/modes'; import { Color } from 'vs/base/common/color'; -import { Extensions, IColorRegistry, ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; -import { Extensions as ThemingExtensions, IThemingRegistry, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; +import { TokenizationRegistry } from 'vs/editor/common/modes'; +import { ITokenThemeRule, TokenTheme, generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; +import { BuiltinTheme, IStandaloneTheme, IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { hc_black, vs, vs_dark } from 'vs/editor/standalone/common/themes'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ColorIdentifier, Extensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry'; +import { Extensions as ThemingExtensions, ICssStyleCollector, IIconTheme, IThemingRegistry } from 'vs/platform/theme/common/themeService'; const VS_THEME_NAME = 'vs'; const VS_DARK_THEME_NAME = 'vs-dark'; @@ -163,10 +163,12 @@ export class StandaloneThemeServiceImpl implements IStandaloneThemeService { private _styleElement: HTMLStyleElement; private _theme: IStandaloneTheme; private readonly _onThemeChange: Emitter; + private readonly _onIconThemeChange: Emitter; private environment: IEnvironmentService = Object.create(null); constructor() { this._onThemeChange = new Emitter(); + this._onIconThemeChange = new Emitter(); this._knownThemes = new Map(); this._knownThemes.set(VS_THEME_NAME, newBuiltInTheme(VS_THEME_NAME)); @@ -239,4 +241,16 @@ export class StandaloneThemeServiceImpl implements IStandaloneThemeService { return theme.id; } + + public getIconTheme(): IIconTheme { + return { + hasFileIcons: false, + hasFolderIcons: false, + hidesExplorerArrows: false + }; + } + + public get onIconThemeChange(): Event { + return this._onIconThemeChange.event; + } } diff --git a/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts b/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts index 0b59bc960a6..ce3bfc8969f 100644 --- a/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts +++ b/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; class ToggleHighContrast extends EditorAction { diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index d85f7829c9e..35c9e5b1c50 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -9,13 +9,13 @@ */ import { IDisposable } from 'vs/base/common/lifecycle'; -import * as modes from 'vs/editor/common/modes'; -import * as monarchCommon from 'vs/editor/standalone/common/monarch/monarchCommon'; -import { IModeService } from 'vs/editor/common/services/modeService'; import { Token, TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; -import { NULL_STATE, NULL_MODE_ID } from 'vs/editor/common/modes/nullMode'; -import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import * as modes from 'vs/editor/common/modes'; +import { NULL_MODE_ID, NULL_STATE } from 'vs/editor/common/modes/nullMode'; import { TokenTheme } from 'vs/editor/common/modes/supports/tokenization'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import * as monarchCommon from 'vs/editor/standalone/common/monarch/monarchCommon'; +import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; const CACHE_STACK_DEPTH = 5; @@ -787,38 +787,31 @@ class MonarchTokenizer implements modes.ITokenizationSupport { } private _getNestedEmbeddedModeData(mimetypeOrModeId: string): EmbeddedModeData { - let nestedMode = this._locateMode(mimetypeOrModeId); - if (nestedMode) { - let tokenizationSupport = modes.TokenizationRegistry.get(nestedMode.getId()); + let nestedModeId = this._locateMode(mimetypeOrModeId); + if (nestedModeId) { + let tokenizationSupport = modes.TokenizationRegistry.get(nestedModeId); if (tokenizationSupport) { - return new EmbeddedModeData(nestedMode.getId(), tokenizationSupport.getInitialState()); + return new EmbeddedModeData(nestedModeId, tokenizationSupport.getInitialState()); } } - let nestedModeId = nestedMode ? nestedMode.getId() : NULL_MODE_ID; - return new EmbeddedModeData(nestedModeId, NULL_STATE); + return new EmbeddedModeData(nestedModeId || NULL_MODE_ID, NULL_STATE); } - private _locateMode(mimetypeOrModeId: string): modes.IMode { + private _locateMode(mimetypeOrModeId: string): string | null { if (!mimetypeOrModeId || !this._modeService.isRegisteredMode(mimetypeOrModeId)) { return null; } let modeId = this._modeService.getModeId(mimetypeOrModeId); - // Fire mode loading event - this._modeService.getOrCreateMode(modeId); - - let mode = this._modeService.getMode(modeId); - if (mode) { - // Re-emit tokenizationSupport change events from all modes that I ever embedded + if (modeId) { + // Fire mode loading event + this._modeService.triggerMode(modeId); this._embeddedModes[modeId] = true; - return mode; } - this._embeddedModes[modeId] = true; - - return null; + return modeId; } } diff --git a/src/vs/editor/standalone/common/monarch/monarchTypes.ts b/src/vs/editor/standalone/common/monarch/monarchTypes.ts index c254f8158da..34f42d7a5e5 100644 --- a/src/vs/editor/standalone/common/monarch/monarchTypes.ts +++ b/src/vs/editor/standalone/common/monarch/monarchTypes.ts @@ -105,7 +105,7 @@ export interface IExpandedMonarchLanguageAction { */ bracket?: string; /** - * switch to embedded language (useing the mimetype) or get out using "@pop" + * switch to embedded language (using the mimetype) or get out using "@pop" */ nextEmbedded?: string; /** @@ -128,7 +128,7 @@ export interface IMonarchLanguageBracket { */ open: string; /** - * closeing bracket + * closing bracket */ close: string; /** diff --git a/src/vs/editor/standalone/common/standaloneThemeService.ts b/src/vs/editor/standalone/common/standaloneThemeService.ts index 2a4e4f2d5c5..cfbd291da9e 100644 --- a/src/vs/editor/standalone/common/standaloneThemeService.ts +++ b/src/vs/editor/standalone/common/standaloneThemeService.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ITokenThemeRule, TokenTheme } from 'vs/editor/common/modes/supports/tokenization'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { TokenTheme, ITokenThemeRule } from 'vs/editor/common/modes/supports/tokenization'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; export const IStandaloneThemeService = createDecorator('themeService'); diff --git a/src/vs/editor/standalone/common/themes.ts b/src/vs/editor/standalone/common/themes.ts index 345d16748ee..fcf65c490fb 100644 --- a/src/vs/editor/standalone/common/themes.ts +++ b/src/vs/editor/standalone/common/themes.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { editorActiveIndentGuides, editorIndentGuides } from 'vs/editor/common/view/editorColorRegistry'; import { IStandaloneThemeData } from 'vs/editor/standalone/common/standaloneThemeService'; -import { editorBackground, editorForeground, editorSelectionHighlight, editorInactiveSelection } from 'vs/platform/theme/common/colorRegistry'; -import { editorIndentGuides, editorActiveIndentGuides } from 'vs/editor/common/view/editorColorRegistry'; +import { editorBackground, editorForeground, editorInactiveSelection, editorSelectionHighlight } from 'vs/platform/theme/common/colorRegistry'; /* -------------------------------- Begin vs theme -------------------------------- */ export const vs: IStandaloneThemeData = { diff --git a/src/vs/editor/standalone/test/browser/simpleServices.test.ts b/src/vs/editor/standalone/test/browser/simpleServices.test.ts index be3c43cc6ae..7154ac5e4f2 100644 --- a/src/vs/editor/standalone/test/browser/simpleServices.test.ts +++ b/src/vs/editor/standalone/test/browser/simpleServices.test.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { SimpleConfigurationService, SimpleNotificationService, StandaloneCommandService, StandaloneKeybindingService } from 'vs/editor/standalone/browser/simpleServices'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; -import { SimpleConfigurationService, StandaloneKeybindingService, StandaloneCommandService, SimpleNotificationService } from 'vs/editor/standalone/browser/simpleServices'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { KeyCode } from 'vs/base/common/keyCodes'; import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts index e8b3ec85b60..baa5c568f05 100644 --- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -2,16 +2,17 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { TokenizationSupport2Adapter, TokensProvider, ILineTokens, IToken } from 'vs/editor/standalone/browser/standaloneLanguages'; -import { IStandaloneThemeService, IStandaloneThemeData, IStandaloneTheme } from 'vs/editor/standalone/common/standaloneThemeService'; -import { Event } from 'vs/base/common/event'; -import { ITheme, LIGHT } from 'vs/platform/theme/common/themeService'; -import { LanguageIdentifier, LanguageId, IState, MetadataConsts } from 'vs/editor/common/modes'; -import { Token } from 'vs/editor/common/core/token'; -import { TokenTheme } from 'vs/editor/common/modes/supports/tokenization'; -import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { Color } from 'vs/base/common/color'; +import { Emitter } from 'vs/base/common/event'; +import { Token } from 'vs/editor/common/core/token'; +import { IState, LanguageId, LanguageIdentifier, MetadataConsts } from 'vs/editor/common/modes'; +import { TokenTheme } from 'vs/editor/common/modes/supports/tokenization'; +import { ILineTokens, IToken, TokenizationSupport2Adapter, TokensProvider } from 'vs/editor/standalone/browser/standaloneLanguages'; +import { IStandaloneTheme, IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; +import { IIconTheme, ITheme, LIGHT } from 'vs/platform/theme/common/themeService'; suite('TokenizationSupport2Adapter', () => { @@ -56,7 +57,15 @@ suite('TokenizationSupport2Adapter', () => { } }; } - public readonly onThemeChange: Event | null = null; + public getIconTheme(): IIconTheme { + return { + hasFileIcons: false, + hasFolderIcons: false, + hidesExplorerArrows: false + }; + } + public readonly onThemeChange = new Emitter().event; + public readonly onIconThemeChange = new Emitter().event; } class MockState implements IState { diff --git a/src/vs/editor/test/browser/commands/shiftCommand.test.ts b/src/vs/editor/test/browser/commands/shiftCommand.test.ts index bdee04af697..455bab39acc 100644 --- a/src/vs/editor/test/browser/commands/shiftCommand.test.ts +++ b/src/vs/editor/test/browser/commands/shiftCommand.test.ts @@ -2,16 +2,17 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; -import { Selection } from 'vs/editor/common/core/selection'; import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { getEditOperation, testCommand } from 'vs/editor/test/browser/testCommand'; import { withEditorModel } from 'vs/editor/test/common/editorTestUtils'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; -import { LanguageIdentifier } from 'vs/editor/common/modes'; import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules'; /** diff --git a/src/vs/editor/test/browser/commands/sideEditing.test.ts b/src/vs/editor/test/browser/commands/sideEditing.test.ts index 409a53a0e01..0b19c93b13b 100644 --- a/src/vs/editor/test/browser/commands/sideEditing.test.ts +++ b/src/vs/editor/test/browser/commands/sideEditing.test.ts @@ -2,17 +2,18 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; +import { Cursor } from 'vs/editor/common/controller/cursor'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; -import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; -import { Cursor } from 'vs/editor/common/controller/cursor'; +import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; function testCommand(lines: string[], selections: Selection[], edits: IIdentifiedSingleEditOperation[], expectedLines: string[], expectedSelections: Selection[]): void { withTestCodeEditor(lines, {}, (editor, cursor) => { diff --git a/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts b/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts index e85cab7461a..11e6c56b8c0 100644 --- a/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts +++ b/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts @@ -2,11 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { TrimTrailingWhitespaceCommand, trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand'; -import { Selection } from 'vs/editor/common/core/selection'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { getEditOperation } from 'vs/editor/test/browser/testCommand'; import { withEditorModel } from 'vs/editor/test/common/editorTestUtils'; diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index b4df2444ab3..323ccc2bcd0 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -2,27 +2,28 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; +import { CoreEditingCommands, CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { Handler, ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; -import { EndOfLinePreference, ITextModel, EndOfLineSequence } from 'vs/editor/common/model'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { Handler, ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; +import { EndOfLinePreference, EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; +import { IState, ITokenizationSupport, LanguageIdentifier, TokenizationRegistry } from 'vs/editor/common/modes'; import { IndentAction, IndentationRule } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; -import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; -import { LanguageIdentifier, ITokenizationSupport, IState, TokenizationRegistry } from 'vs/editor/common/modes'; -import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { CoreNavigationCommands, CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; -import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; -import { TokenizationResult2 } from 'vs/editor/common/core/token'; -import { createTextModel, IRelaxedTextModelCreationOptions } from 'vs/editor/test/common/editorTestUtils'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; +import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { IRelaxedTextModelCreationOptions, createTextModel } from 'vs/editor/test/common/editorTestUtils'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules'; const H = Handler; @@ -1868,7 +1869,7 @@ suite('Editor Controller - Regression tests', () => { this.timeout(10000); const LINE_CNT = 2000; - let text = []; + let text: string[] = []; for (let i = 0; i < LINE_CNT; i++) { text[i] = 'asd'; } diff --git a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts index 2965da15cb6..dc70dd0240e 100644 --- a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts +++ b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts @@ -2,16 +2,17 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; +import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; import { Cursor } from 'vs/editor/common/controller/cursor'; -import { Position } from 'vs/editor/common/core/position'; -import { TextModel } from 'vs/editor/common/model/textModel'; -import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { CursorMove } from 'vs/editor/common/controller/cursorMoveCommands'; +import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; +import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; suite('Cursor move command test', () => { diff --git a/src/vs/editor/test/browser/controller/imeTester.ts b/src/vs/editor/test/browser/controller/imeTester.ts index a535ed336d1..4ea8935cd20 100644 --- a/src/vs/editor/test/browser/controller/imeTester.ts +++ b/src/vs/editor/test/browser/controller/imeTester.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextAreaInput, ITextAreaInputHost } from 'vs/editor/browser/controller/textAreaInput'; -import { ISimpleModel, TextAreaState, PagedScreenReaderStrategy } from 'vs/editor/browser/controller/textAreaState'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; -import { createFastDomNode } from 'vs/base/browser/fastDomNode'; import * as browser from 'vs/base/browser/browser'; +import { createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { ITextAreaInputHost, TextAreaInput } from 'vs/editor/browser/controller/textAreaInput'; +import { ISimpleModel, PagedScreenReaderStrategy, TextAreaState } from 'vs/editor/browser/controller/textAreaState'; +import { Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { EndOfLinePreference } from 'vs/editor/common/model'; // To run this test, open imeTester.html diff --git a/src/vs/editor/test/browser/controller/textAreaState.test.ts b/src/vs/editor/test/browser/controller/textAreaState.test.ts index 062eb4d1111..1e00f28d632 100644 --- a/src/vs/editor/test/browser/controller/textAreaState.test.ts +++ b/src/vs/editor/test/browser/controller/textAreaState.test.ts @@ -2,12 +2,13 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { TextAreaState, ITextAreaWrapper, PagedScreenReaderStrategy } from 'vs/editor/browser/controller/textAreaState'; -import { Position } from 'vs/editor/common/core/position'; import { Disposable } from 'vs/base/common/lifecycle'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { ITextAreaWrapper, PagedScreenReaderStrategy, TextAreaState } from 'vs/editor/browser/controller/textAreaState'; +import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; +import { TextModel } from 'vs/editor/common/model/textModel'; export class MockTextAreaWrapper extends Disposable implements ITextAreaWrapper { diff --git a/src/vs/editor/test/browser/core/editorState.test.ts b/src/vs/editor/test/browser/core/editorState.test.ts index 8f744e2c421..fff6ae14061 100644 --- a/src/vs/editor/test/browser/core/editorState.test.ts +++ b/src/vs/editor/test/browser/core/editorState.test.ts @@ -5,11 +5,11 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; -import { ITextModel } from 'vs/editor/common/model'; -import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; -import { Selection } from 'vs/editor/common/core/selection'; -import { Position } from 'vs/editor/common/core/position'; +import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ITextModel } from 'vs/editor/common/model'; interface IStubEditorState { model?: { uri?: URI, version?: number }; diff --git a/src/vs/editor/test/browser/editorTestServices.ts b/src/vs/editor/test/browser/editorTestServices.ts index 2796c7a6924..0a1d0b122a4 100644 --- a/src/vs/editor/test/browser/editorTestServices.ts +++ b/src/vs/editor/test/browser/editorTestServices.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IModelDecorationOptions } from 'vs/editor/common/model'; -import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; -import { AbstractCodeEditorService } from 'vs/editor/browser/services/abstractCodeEditorService'; -import { ICommandService, ICommandEvent, CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { AbstractCodeEditorService } from 'vs/editor/browser/services/abstractCodeEditorService'; +import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; +import { IModelDecorationOptions } from 'vs/editor/common/model'; +import { CommandsRegistry, ICommandEvent, ICommandService } from 'vs/platform/commands/common/commands'; import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class TestCodeEditorService extends AbstractCodeEditorService { public lastInput: IResourceInput; diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts index 23f313503b6..feb3a465cf6 100644 --- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts +++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts @@ -2,14 +2,15 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { URI } from 'vs/base/common/uri'; import * as dom from 'vs/base/browser/dom'; +import { URI } from 'vs/base/common/uri'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; -import { TestTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { TestTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; const themeServiceMock = new TestThemeService(); @@ -133,7 +134,7 @@ suite('Decoration Render Options', () => { // unix file path (used as string) let s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet); - s.registerDecorationType('example', { gutterIconPath: '/Users/foo/bar.png' }); + s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/bar.png') }); let sheet = readStyleSheet(styleSheet);//.innerHTML || styleSheet.sheet.toString(); assert( sheet.indexOf('background: url(\'file:///Users/foo/bar.png\') center center no-repeat;') > 0 @@ -142,7 +143,7 @@ suite('Decoration Render Options', () => { // windows file path (used as string) s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet); - s.registerDecorationType('example', { gutterIconPath: 'c:\\files\\miles\\more.png' }); + s.registerDecorationType('example', { gutterIconPath: URI.file('c:\\files\\miles\\more.png') }); sheet = readStyleSheet(styleSheet); // TODO@Alex test fails // assert( @@ -161,7 +162,7 @@ suite('Decoration Render Options', () => { // single quote must always be escaped/encoded s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet); - s.registerDecorationType('example', { gutterIconPath: '/Users/foo/b\'ar.png' }); + s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/b\'ar.png') }); sheet = readStyleSheet(styleSheet); assert( sheet.indexOf('background: url(\'file:///Users/foo/b%27ar.png\') center center no-repeat;') > 0 diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index fb181887f91..4c19449c358 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -2,11 +2,11 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; import * as assert from 'assert'; -import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices'; -import { ICommandService, NullCommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { URI } from 'vs/base/common/uri'; import { OpenerService } from 'vs/editor/browser/services/openerService'; +import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices'; +import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands'; suite('OpenerService', function () { diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index 7422b8f6ae5..d0996a536fc 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -3,28 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; -import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { View } from 'vs/editor/browser/view/viewImpl'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { Cursor } from 'vs/editor/common/controller/cursor'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import { TextModel } from 'vs/editor/common/model/textModel'; -import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; -import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { ITextModel } from 'vs/editor/common/model'; -import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { CodeEditorWidget, ICodeEditorWidgetOptions, } from 'vs/editor/browser/widget/codeEditorWidget'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TestCodeEditorService, TestCommandService } from 'vs/editor/test/browser/editorTestServices'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { View } from 'vs/editor/browser/view/viewImpl'; +import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; +import { TestCodeEditorService, TestCommandService } from 'vs/editor/test/browser/editorTestServices'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; export class TestCodeEditor extends CodeEditorWidget implements editorBrowser.ICodeEditor { @@ -34,13 +34,13 @@ export class TestCodeEditor extends CodeEditorWidget implements editorBrowser.IC } protected _createView(viewModel: ViewModel, cursor: Cursor): [View, boolean] { // Never create a view - return [null, false]; + return [null! as View, false]; } //#endregion //#region Testing utils - public getCursor(): Cursor { - return this._modelData.cursor; + public getCursor(): Cursor | undefined { + return this._modelData ? this._modelData.cursor : undefined; } public registerAndInstantiateContribution(ctor: any): T { let r = this._instantiationService.createInstance(ctor, this); @@ -60,7 +60,7 @@ class TestEditorDomElement { setAttribute(attr: string, value: string): void { } removeAttribute(attr: string): void { } hasAttribute(attr: string): boolean { return false; } - getAttribute(attr: string): string { return undefined; } + getAttribute(attr: string): string | undefined { return undefined; } addEventListener(event: string): void { } removeEventListener(event: string): void { } } @@ -73,7 +73,7 @@ export interface TestCodeEditorCreationOptions extends editorOptions.IEditorOpti serviceCollection?: ServiceCollection; } -export function withTestCodeEditor(text: string | string[], options: TestCodeEditorCreationOptions, callback: (editor: TestCodeEditor, cursor: Cursor) => void): void { +export function withTestCodeEditor(text: string | string[], options: TestCodeEditorCreationOptions, callback: (editor: TestCodeEditor, cursor: Cursor | undefined) => void): void { // create a model if necessary and remember it in order to dispose it. if (!options.model) { if (typeof text === 'string') { diff --git a/src/vs/editor/test/browser/testCommand.ts b/src/vs/editor/test/browser/testCommand.ts index 3848810fda7..7bc2f797403 100644 --- a/src/vs/editor/test/browser/testCommand.ts +++ b/src/vs/editor/test/browser/testCommand.ts @@ -7,10 +7,10 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; export function testCommand( lines: string[], @@ -21,7 +21,10 @@ export function testCommand( expectedSelection: Selection ): void { let model = TextModel.createFromString(lines.join('\n'), undefined, languageIdentifier); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor('', { model: model }, (_editor, cursor) => { + if (!cursor) { + return; + } cursor.setSelections('tests', [selection]); @@ -58,7 +61,7 @@ export function getEditOperation(model: ITextModel, command: editorCommon.IComma trackSelection: (selection: Selection) => { - return null; + return ''; } }; command.getEditOperations(model, editOperationBuilder); diff --git a/src/vs/editor/test/browser/view/minimapFontCreator.ts b/src/vs/editor/test/browser/view/minimapFontCreator.ts index 4a2d0c489b8..afd9b17e328 100644 --- a/src/vs/editor/test/browser/view/minimapFontCreator.ts +++ b/src/vs/editor/test/browser/view/minimapFontCreator.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -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 { RGBA8 } from 'vs/editor/common/core/rgba'; +import { Constants, MinimapCharRenderer } from 'vs/editor/common/view/minimapCharRenderer'; +import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; +import { MinimapCharRendererFactory } from 'vs/editor/test/common/view/minimapCharRendererFactory'; let canvas = document.getElementById('my-canvas'); let ctx = canvas.getContext('2d')!; diff --git a/src/vs/editor/test/browser/view/viewLayer.test.ts b/src/vs/editor/test/browser/view/viewLayer.test.ts index 3d98b04c75b..bffb2f8b18b 100644 --- a/src/vs/editor/test/browser/view/viewLayer.test.ts +++ b/src/vs/editor/test/browser/view/viewLayer.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { RenderedLinesCollection, ILine } from 'vs/editor/browser/view/viewLayer'; +import { ILine, RenderedLinesCollection } from 'vs/editor/browser/view/viewLayer'; class TestLine implements ILine { diff --git a/src/vs/editor/test/common/commentMode.ts b/src/vs/editor/test/common/commentMode.ts index 20f19f488aa..c8291f4ddd8 100644 --- a/src/vs/editor/test/common/commentMode.ts +++ b/src/vs/editor/test/common/commentMode.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { CommentRule } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { CommentRule } from 'vs/editor/common/modes/languageConfiguration'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; export class CommentMode extends MockMode { diff --git a/src/vs/editor/test/common/config/commonEditorConfig.test.ts b/src/vs/editor/test/common/config/commonEditorConfig.test.ts index 20ff796be64..b212af51ab2 100644 --- a/src/vs/editor/test/common/config/commonEditorConfig.test.ts +++ b/src/vs/editor/test/common/config/commonEditorConfig.test.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { AccessibilitySupport } from 'vs/base/common/platform'; +import { IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; +import { IEditorHoverOptions } from 'vs/editor/common/config/editorOptions'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; -import { IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; -import { AccessibilitySupport } from 'vs/base/common/platform'; -import { IEditorHoverOptions } from 'vs/editor/common/config/editorOptions'; suite('Common Editor Config', () => { test('Zoom Level', () => { diff --git a/src/vs/editor/test/common/core/characterClassifier.test.ts b/src/vs/editor/test/common/core/characterClassifier.test.ts index 7c5c4eef615..de9effc7444 100644 --- a/src/vs/editor/test/common/core/characterClassifier.test.ts +++ b/src/vs/editor/test/common/core/characterClassifier.test.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; import { CharCode } from 'vs/base/common/charCode'; +import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; suite('CharacterClassifier', () => { diff --git a/src/vs/editor/test/common/core/lineTokens.test.ts b/src/vs/editor/test/common/core/lineTokens.test.ts index c3b4fc73905..d3a9924fb88 100644 --- a/src/vs/editor/test/common/core/lineTokens.test.ts +++ b/src/vs/editor/test/common/core/lineTokens.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { LineTokens, IViewLineTokens } from 'vs/editor/common/core/lineTokens'; +import { IViewLineTokens, LineTokens } from 'vs/editor/common/core/lineTokens'; import { MetadataConsts } from 'vs/editor/common/modes'; suite('LineTokens', () => { diff --git a/src/vs/editor/test/common/core/range.test.ts b/src/vs/editor/test/common/core/range.test.ts index 4e4996b4666..1d2d627cb32 100644 --- a/src/vs/editor/test/common/core/range.test.ts +++ b/src/vs/editor/test/common/core/range.test.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; suite('Editor Core - Range', () => { test('empty range', () => { diff --git a/src/vs/editor/test/common/core/viewLineToken.ts b/src/vs/editor/test/common/core/viewLineToken.ts index 51e1f351f86..340157110ae 100644 --- a/src/vs/editor/test/common/core/viewLineToken.ts +++ b/src/vs/editor/test/common/core/viewLineToken.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ColorId, TokenMetadata } from 'vs/editor/common/modes'; import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; +import { ColorId, TokenMetadata } from 'vs/editor/common/modes'; /** * A token on a line. diff --git a/src/vs/editor/test/common/diff/diffComputer.test.ts b/src/vs/editor/test/common/diff/diffComputer.test.ts index daa4cec055a..a9812a63c35 100644 --- a/src/vs/editor/test/common/diff/diffComputer.test.ts +++ b/src/vs/editor/test/common/diff/diffComputer.test.ts @@ -53,7 +53,7 @@ function assertDiff(originalLines: string[], modifiedLines: string[], expectedCh }); let changes = diffComputer.computeDiff(); - let extracted = []; + let extracted: IChange[] = []; for (let i = 0; i < changes.length; i++) { extracted.push(extractLineChangeRepresentation(changes[i], (i < expectedChanges.length ? expectedChanges[i] : null))); } diff --git a/src/vs/editor/test/common/editorTestUtils.ts b/src/vs/editor/test/common/editorTestUtils.ts index 6d800762f3b..6fae62d5463 100644 --- a/src/vs/editor/test/common/editorTestUtils.ts +++ b/src/vs/editor/test/common/editorTestUtils.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextModel } from 'vs/editor/common/model/textModel'; -import { DefaultEndOfLine, ITextModelCreationOptions } from 'vs/editor/common/model'; -import { LanguageIdentifier } from 'vs/editor/common/modes'; import { URI } from 'vs/base/common/uri'; +import { DefaultEndOfLine, ITextModelCreationOptions } from 'vs/editor/common/model'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; export function withEditorModel(text: string[], callback: (model: TextModel) => void): void { let model = TextModel.createFromString(text.join('\n')); diff --git a/src/vs/editor/test/common/mocks/mockMode.ts b/src/vs/editor/test/common/mocks/mockMode.ts index 7d5ce73d972..bf24cc35ceb 100644 --- a/src/vs/editor/test/common/mocks/mockMode.ts +++ b/src/vs/editor/test/common/mocks/mockMode.ts @@ -3,8 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IMode, LanguageIdentifier } from 'vs/editor/common/modes'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; export class MockMode extends Disposable implements IMode { private _languageIdentifier: LanguageIdentifier; @@ -22,3 +24,9 @@ export class MockMode extends Disposable implements IMode { return this._languageIdentifier; } } + +export class StaticLanguageSelector implements ILanguageSelection { + readonly onDidChange: Event = Event.None; + constructor(public readonly languageIdentifier: LanguageIdentifier) { } + public dispose(): void { } +} diff --git a/src/vs/editor/test/common/mocks/testConfiguration.ts b/src/vs/editor/test/common/mocks/testConfiguration.ts index 619b2d75639..7d389bc8bea 100644 --- a/src/vs/editor/test/common/mocks/testConfiguration.ts +++ b/src/vs/editor/test/common/mocks/testConfiguration.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { AccessibilitySupport } from 'vs/base/common/platform'; import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo'; -import { AccessibilitySupport } from 'vs/base/common/platform'; +import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; export class TestConfiguration extends CommonEditorConfiguration { diff --git a/src/vs/editor/test/common/model/benchmark/benchmarkUtils.ts b/src/vs/editor/test/common/model/benchmark/benchmarkUtils.ts index e9649551bd1..0be0937e1e8 100644 --- a/src/vs/editor/test/common/model/benchmark/benchmarkUtils.ts +++ b/src/vs/editor/test/common/model/benchmark/benchmarkUtils.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITextBufferBuilder, ITextBufferFactory, ITextBuffer, DefaultEndOfLine } from 'vs/editor/common/model'; +import { DefaultEndOfLine, ITextBuffer, ITextBufferBuilder, ITextBufferFactory } from 'vs/editor/common/model'; import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; export function doBenchmark(id: string, ts: T[], fn: (t: T) => void) { diff --git a/src/vs/editor/test/common/model/benchmark/modelbuilder.benchmark.ts b/src/vs/editor/test/common/model/benchmark/modelbuilder.benchmark.ts index 1c82c58ab65..a01339eb263 100644 --- a/src/vs/editor/test/common/model/benchmark/modelbuilder.benchmark.ts +++ b/src/vs/editor/test/common/model/benchmark/modelbuilder.benchmark.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; import { ITextBufferBuilder } from 'vs/editor/common/model'; -import { generateRandomChunkWithLF } from 'vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils'; +import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; import { doBenchmark } from 'vs/editor/test/common/model/benchmark/benchmarkUtils'; +import { generateRandomChunkWithLF } from 'vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils'; let pieceTreeTextBufferBuilder = new PieceTreeTextBufferBuilder(); let chunks: string[] = []; diff --git a/src/vs/editor/test/common/model/benchmark/operations.benchmark.ts b/src/vs/editor/test/common/model/benchmark/operations.benchmark.ts index 405f0f26ec6..7d67644a302 100644 --- a/src/vs/editor/test/common/model/benchmark/operations.benchmark.ts +++ b/src/vs/editor/test/common/model/benchmark/operations.benchmark.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITextBufferBuilder, EndOfLinePreference } from 'vs/editor/common/model'; +import { Range } from 'vs/editor/common/core/range'; +import { EndOfLinePreference, ITextBufferBuilder } from 'vs/editor/common/model'; import { BenchmarkSuite } from 'vs/editor/test/common/model/benchmark/benchmarkUtils'; import { generateRandomChunkWithLF, generateRandomEdits, generateSequentialInserts, getRandomInt } from 'vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils'; -import { Range } from 'vs/editor/common/core/range'; let fileSizes = [1, 1000, 64 * 1000, 32 * 1000 * 1000]; let editTypes = [ diff --git a/src/vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts b/src/vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts index 54535caafb8..ab86d7e0a81 100644 --- a/src/vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts +++ b/src/vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { ITextBufferBuilder } from 'vs/editor/common/model'; -import { generateRandomReplaces, generateRandomChunkWithLF } from 'vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils'; import { BenchmarkSuite } from 'vs/editor/test/common/model/benchmark/benchmarkUtils'; +import { generateRandomChunkWithLF, generateRandomReplaces } from 'vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils'; let fileSizes = [1, 1000, 64 * 1000, 32 * 1000 * 1000]; diff --git a/src/vs/editor/test/common/model/editableTextModel.test.ts b/src/vs/editor/test/common/model/editableTextModel.test.ts index 4106487c8be..056fd47c1a8 100644 --- a/src/vs/editor/test/common/model/editableTextModel.test.ts +++ b/src/vs/editor/test/common/model/editableTextModel.test.ts @@ -5,11 +5,11 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; -import { EndOfLineSequence, IIdentifiedSingleEditOperation, EndOfLinePreference } from 'vs/editor/common/model'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; -import { assertSyncedModels, testApplyEditsWithSyncedModels } from 'vs/editor/test/common/model/editableTextModelTestUtils'; +import { TextModel } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import { assertSyncedModels, testApplyEditsWithSyncedModels } from 'vs/editor/test/common/model/editableTextModelTestUtils'; function createEditableTextModelFromString(text: string): TextModel { return new TextModel(text, TextModel.DEFAULT_CREATION_OPTIONS, null); diff --git a/src/vs/editor/test/common/model/editableTextModelAuto.test.ts b/src/vs/editor/test/common/model/editableTextModelAuto.test.ts index d0b4ab0e7ec..13cfd1ef8ad 100644 --- a/src/vs/editor/test/common/model/editableTextModelAuto.test.ts +++ b/src/vs/editor/test/common/model/editableTextModelAuto.test.ts @@ -2,11 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from 'vs/base/common/charCode'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { testApplyEditsWithSyncedModels } from 'vs/editor/test/common/model/editableTextModelTestUtils'; -import { CharCode } from 'vs/base/common/charCode'; const GENERATE_TESTS = false; diff --git a/src/vs/editor/test/common/model/editableTextModelTestUtils.ts b/src/vs/editor/test/common/model/editableTextModelTestUtils.ts index c4345047f37..02fba238d80 100644 --- a/src/vs/editor/test/common/model/editableTextModelTestUtils.ts +++ b/src/vs/editor/test/common/model/editableTextModelTestUtils.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TextModel } from 'vs/editor/common/model/textModel'; -import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; import { Position } from 'vs/editor/common/core/position'; +import { EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; +import { TextModel } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; -import { EndOfLinePreference, IIdentifiedSingleEditOperation, EndOfLineSequence } from 'vs/editor/common/model'; export function testApplyEditsWithSyncedModels(original: string[], edits: IIdentifiedSingleEditOperation[], expected: string[], inputEditsAreInvalid: boolean = false): void { let originalStr = original.join('\n'); diff --git a/src/vs/editor/test/common/model/intervalTree.test.ts b/src/vs/editor/test/common/model/intervalTree.test.ts index 91a64c90503..4f67baf706a 100644 --- a/src/vs/editor/test/common/model/intervalTree.test.ts +++ b/src/vs/editor/test/common/model/intervalTree.test.ts @@ -2,9 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { IntervalTree, IntervalNode, getNodeColor, NodeColor, SENTINEL, intervalCompare, setNodeStickiness, nodeAcceptEdit } from 'vs/editor/common/model/intervalTree'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IntervalNode, IntervalTree, NodeColor, SENTINEL, getNodeColor, intervalCompare, nodeAcceptEdit, setNodeStickiness } from 'vs/editor/common/model/intervalTree'; const GENERATE_TESTS = false; let TEST_COUNT = GENERATE_TESTS ? 10000 : 0; diff --git a/src/vs/editor/test/common/model/linesTextBuffer/linesTextBuffer.test.ts b/src/vs/editor/test/common/model/linesTextBuffer/linesTextBuffer.test.ts index 555fde77fda..83866beea5f 100644 --- a/src/vs/editor/test/common/model/linesTextBuffer/linesTextBuffer.test.ts +++ b/src/vs/editor/test/common/model/linesTextBuffer/linesTextBuffer.test.ts @@ -5,9 +5,9 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; -import { PieceTreeTextBuffer, IValidatedEditOperation } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer'; -import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { DefaultEndOfLine } from 'vs/editor/common/model'; +import { IValidatedEditOperation, PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer'; +import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; suite('PieceTreeTextBuffer._getInverseEdits', () => { diff --git a/src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test.ts b/src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test.ts index 6cc725cdbca..dafa53c0771 100644 --- a/src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test.ts +++ b/src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test.ts @@ -2,11 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { DefaultEndOfLine } from 'vs/editor/common/model'; -import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; import * as strings from 'vs/base/common/strings'; +import { DefaultEndOfLine } from 'vs/editor/common/model'; import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer'; +import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; export function testTextBufferFactory(text: string, eol: string, mightContainNonBasicASCII: boolean, mightContainRTL: boolean): void { const textBuffer = createTextBufferFactory(text).create(DefaultEndOfLine.LF); diff --git a/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts b/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts index 87f188e1c9b..155dd7fbe4f 100644 --- a/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts +++ b/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from 'vs/base/common/charCode'; -import { IIdentifiedSingleEditOperation, DefaultEndOfLine, ITextBufferBuilder, ITextBuffer } from 'vs/editor/common/model'; import { Range } from 'vs/editor/common/core/range'; +import { DefaultEndOfLine, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferBuilder } from 'vs/editor/common/model'; export function getRandomInt(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1)) + min; @@ -32,7 +32,7 @@ export function getRandomString(minLength: number, maxLength: number): string { } export function generateRandomEdits(chunks: string[], editCnt: number): IIdentifiedSingleEditOperation[] { - let lines = []; + let lines: string[] = []; for (let i = 0; i < chunks.length; i++) { let newLines = chunks[i].split(/\r\n|\r|\n/); if (lines.length === 0) { @@ -65,7 +65,7 @@ export function generateRandomEdits(chunks: string[], editCnt: number): IIdentif } export function generateSequentialInserts(chunks: string[], editCnt: number): IIdentifiedSingleEditOperation[] { - let lines = []; + let lines: string[] = []; for (let i = 0; i < chunks.length; i++) { let newLines = chunks[i].split(/\r\n|\r|\n/); if (lines.length === 0) { @@ -100,7 +100,7 @@ export function generateSequentialInserts(chunks: string[], editCnt: number): II } export function generateRandomReplaces(chunks: string[], editCnt: number, searchStringLen: number, replaceStringLen: number): IIdentifiedSingleEditOperation[] { - let lines = []; + let lines: string[] = []; for (let i = 0; i < chunks.length; i++) { let newLines = chunks[i].split(/\r\n|\r|\n/); if (lines.length === 0) { diff --git a/src/vs/editor/test/common/model/model.line.test.ts b/src/vs/editor/test/common/model/model.line.test.ts index d329463bcd1..e1016de828d 100644 --- a/src/vs/editor/test/common/model/model.line.test.ts +++ b/src/vs/editor/test/common/model/model.line.test.ts @@ -2,12 +2,13 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; -import { LanguageIdentifier, MetadataConsts } from 'vs/editor/common/modes'; import { Range } from 'vs/editor/common/core/range'; -import { ViewLineToken, ViewLineTokenFactory } from 'vs/editor/test/common/core/viewLineToken'; import { TextModel } from 'vs/editor/common/model/textModel'; +import { LanguageIdentifier, MetadataConsts } from 'vs/editor/common/modes'; +import { ViewLineToken, ViewLineTokenFactory } from 'vs/editor/test/common/core/viewLineToken'; interface ILineEdit { startColumn: number; 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 24281106b4e..ccdd2f0ba08 100644 --- a/src/vs/editor/test/common/model/model.modes.test.ts +++ b/src/vs/editor/test/common/model/model.modes.test.ts @@ -2,15 +2,16 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { IDisposable } from 'vs/base/common/lifecycle'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; import { TextModel } from 'vs/editor/common/model/textModel'; 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'; // --------- utils diff --git a/src/vs/editor/test/common/model/model.test.ts b/src/vs/editor/test/common/model/model.test.ts index cacd40b88b7..168cb4007fa 100644 --- a/src/vs/editor/test/common/model/model.test.ts +++ b/src/vs/editor/test/common/model/model.test.ts @@ -2,21 +2,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; +import { Disposable, dispose } from 'vs/base/common/lifecycle'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { - ModelRawContentChangedEvent, ModelRawFlush, ModelRawLineChanged, - ModelRawLinesDeleted, ModelRawLinesInserted -} from 'vs/editor/common/model/textModelEvents'; -import { TextModel } from 'vs/editor/common/model/textModel'; -import { LanguageIdentifier, TokenizationRegistry, IState, MetadataConsts } from 'vs/editor/common/modes'; -import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { ModelRawContentChangedEvent, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from 'vs/editor/common/model/textModelEvents'; +import { IState, LanguageIdentifier, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/modes'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; -import { dispose, Disposable } from 'vs/base/common/lifecycle'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; // --------- utils @@ -464,4 +462,30 @@ suite('Editor Model - Words', () => { assert.deepEqual(model.getWordAtPosition(new Position(1, 6)), { word: 'xx', startColumn: 4, endColumn: 6 }); assert.deepEqual(model.getWordAtPosition(new Position(1, 7)), { word: 'ab', startColumn: 7, endColumn: 9 }); }); + + test('issue #61296: VS code freezes when editing CSS file with emoji', () => { + const MODE_ID = new LanguageIdentifier('testMode', 4); + + const mode = new class extends MockMode { + constructor() { + super(MODE_ID); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]*(?=[^,{;]*[,{]))|(([@#.!])?[\w-?]+%?|[@#!.])/g + })); + } + }; + disposables.push(mode); + + const thisModel = TextModel.createFromString('.🐷-a-b', undefined, MODE_ID); + disposables.push(thisModel); + + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 1)), { word: '.', startColumn: 1, endColumn: 2 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 2)), { word: '.', startColumn: 1, endColumn: 2 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 3)), null); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 4)), { word: '-a-b', startColumn: 4, endColumn: 8 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 5)), { word: '-a-b', startColumn: 4, endColumn: 8 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 6)), { word: '-a-b', startColumn: 4, endColumn: 8 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 7)), { word: '-a-b', startColumn: 4, endColumn: 8 }); + assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 8)), { word: '-a-b', startColumn: 4, endColumn: 8 }); + }); }); diff --git a/src/vs/editor/test/common/model/modelDecorations.test.ts b/src/vs/editor/test/common/model/modelDecorations.test.ts index 0038e48730a..2fba0dad772 100644 --- a/src/vs/editor/test/common/model/modelDecorations.test.ts +++ b/src/vs/editor/test/common/model/modelDecorations.test.ts @@ -2,11 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { IModelDeltaDecoration, TrackedRangeStickiness, EndOfLineSequence } from 'vs/editor/common/model'; +import { EndOfLineSequence, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; // --------- utils @@ -49,7 +50,7 @@ function addDecoration(model: TextModel, startLineNumber: number, startColumn: n } function lineHasDecorations(model: TextModel, lineNumber: number, decorations: { start: number; end: number; className: string; }[]) { - let lineDecorations = []; + let lineDecorations: Array<{ start: number; end: number; className: string; }> = []; let decs = model.getLineDecorations(lineNumber); for (let i = 0, len = decs.length; i < len; i++) { lineDecorations.push({ diff --git a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts index 384be50f0e8..4a0bac5eeb1 100644 --- a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts +++ b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts @@ -2,18 +2,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; +import { WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; import { DefaultEndOfLine } from 'vs/editor/common/model'; import { PieceTreeBase } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; -import { SENTINEL, NodeColor, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer'; +import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; +import { NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { ITextSnapshot } from 'vs/platform/files/common/files'; import { SearchData } from 'vs/editor/common/model/textModelSearch'; -import { WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { ITextSnapshot } from 'vs/platform/files/common/files'; const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n'; @@ -1515,7 +1516,7 @@ suite('random is unsupervised', () => { test('random chunks', function () { this.timeout(500000); - let chunks = []; + let chunks: string[] = []; for (let i = 0; i < 5; i++) { chunks.push(randomStr(1000)); } @@ -1550,7 +1551,7 @@ suite('random is unsupervised', () => { test('random chunks 2', function () { this.timeout(500000); - let chunks = []; + let chunks: string[] = []; chunks.push(randomStr(1000)); let pieceTable = createTextBuffer(chunks, false); diff --git a/src/vs/editor/test/common/model/textModel.test.ts b/src/vs/editor/test/common/model/textModel.test.ts index 935001a2737..2f8f8e4b161 100644 --- a/src/vs/editor/test/common/model/textModel.test.ts +++ b/src/vs/editor/test/common/model/textModel.test.ts @@ -2,11 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; +import { UTF8_BOM_CHARACTER } from 'vs/base/common/strings'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel'; -import { UTF8_BOM_CHARACTER } from 'vs/base/common/strings'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; function testGuessIndentation(defaultInsertSpaces: boolean, defaultTabSize: number, expectedInsertSpaces: boolean, expectedTabSize: number, text: string[], msg?: string): void { diff --git a/src/vs/editor/test/common/model/textModelSearch.test.ts b/src/vs/editor/test/common/model/textModelSearch.test.ts index c6c22689be8..f1d13c181e1 100644 --- a/src/vs/editor/test/common/model/textModelSearch.test.ts +++ b/src/vs/editor/test/common/model/textModelSearch.test.ts @@ -2,13 +2,14 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { Position } from 'vs/editor/common/core/position'; -import { FindMatch, EndOfLineSequence } from 'vs/editor/common/model'; -import { Range } from 'vs/editor/common/core/range'; -import { TextModel } from 'vs/editor/common/model/textModel'; -import { TextModelSearch, SearchParams, SearchData, isMultilineRegexSource } from 'vs/editor/common/model/textModelSearch'; import { getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { EndOfLineSequence, FindMatch } from 'vs/editor/common/model'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { SearchData, SearchParams, TextModelSearch, isMultilineRegexSource } from 'vs/editor/common/model/textModelSearch'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; // --------- Find diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts index 9350a8278db..d9c0cd42afe 100644 --- a/src/vs/editor/test/common/model/textModelWithTokens.test.ts +++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts @@ -2,18 +2,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { TextModel } from 'vs/editor/common/model/textModel'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { ViewLineToken } from 'vs/editor/test/common/core/viewLineToken'; -import { ITokenizationSupport, TokenizationRegistry, LanguageId, LanguageIdentifier, MetadataConsts } from 'vs/editor/common/modes'; -import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; -import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; import { IFoundBracket } from 'vs/editor/common/model'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { ITokenizationSupport, LanguageId, LanguageIdentifier, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/modes'; +import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; -import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { ViewLineToken } from 'vs/editor/test/common/core/viewLineToken'; suite('TextModelWithTokens', () => { diff --git a/src/vs/editor/test/common/modes/languageConfiguration.test.ts b/src/vs/editor/test/common/modes/languageConfiguration.test.ts index 512e6c1e653..e898568a727 100644 --- a/src/vs/editor/test/common/modes/languageConfiguration.test.ts +++ b/src/vs/editor/test/common/modes/languageConfiguration.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { StandardTokenType } from 'vs/editor/common/modes'; +import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; suite('StandardAutoClosingPairConditional', () => { diff --git a/src/vs/editor/test/common/modes/supports/characterPair.test.ts b/src/vs/editor/test/common/modes/supports/characterPair.test.ts index 286c59b6954..ba9aa15f503 100644 --- a/src/vs/editor/test/common/modes/supports/characterPair.test.ts +++ b/src/vs/editor/test/common/modes/supports/characterPair.test.ts @@ -2,10 +2,11 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; +import { StandardTokenType } from 'vs/editor/common/modes'; import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils'; -import { StandardTokenType } from 'vs/editor/common/modes'; suite('CharacterPairSupport', () => { diff --git a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts index cf7c692d411..bdd639ff6e2 100644 --- a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts +++ b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts @@ -2,11 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; -import { createFakeScopedLineTokens, TokenText } from 'vs/editor/test/common/modesTestUtils'; -import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; import { LanguageIdentifier, StandardTokenType } from 'vs/editor/common/modes'; +import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; +import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; +import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils'; const fakeLanguageIdentifier = new LanguageIdentifier('test', 3); diff --git a/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts b/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts index 4cb4968aa0c..a1fc97b8228 100644 --- a/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts +++ b/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts @@ -2,9 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { BracketsUtils } from 'vs/editor/common/modes/supports/richEditBrackets'; import { Range } from 'vs/editor/common/core/range'; +import { BracketsUtils } from 'vs/editor/common/modes/supports/richEditBrackets'; suite('richEditBrackets', () => { diff --git a/src/vs/editor/test/common/modes/supports/tokenization.test.ts b/src/vs/editor/test/common/modes/supports/tokenization.test.ts index 7241b63363b..4a0c09f5a79 100644 --- a/src/vs/editor/test/common/modes/supports/tokenization.test.ts +++ b/src/vs/editor/test/common/modes/supports/tokenization.test.ts @@ -2,9 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { strcmp, parseTokenTheme, TokenTheme, ParsedTokenThemeRule, ColorMap, ExternalThemeTrieElement, ThemeTrieElementRule } from 'vs/editor/common/modes/supports/tokenization'; import { FontStyle } from 'vs/editor/common/modes'; +import { ColorMap, ExternalThemeTrieElement, ParsedTokenThemeRule, ThemeTrieElementRule, TokenTheme, parseTokenTheme, strcmp } from 'vs/editor/common/modes/supports/tokenization'; suite('Token theme matching', () => { diff --git a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts index 674b18bd869..21e04e6d703 100644 --- a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts +++ b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts @@ -2,12 +2,13 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { TokenizationRegistry, IState, LanguageIdentifier, ColorId, FontStyle, MetadataConsts } from 'vs/editor/common/modes'; -import { tokenizeToString, tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; -import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { ColorId, FontStyle, IState, LanguageIdentifier, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/modes'; +import { tokenizeLineToHTML, tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { ViewLineToken, ViewLineTokens } from 'vs/editor/test/common/core/viewLineToken'; +import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; suite('Editor Modes - textToHtmlTokenizer', () => { function toStr(pieces: { className: string; text: string }[]): string { diff --git a/src/vs/editor/test/common/modesTestUtils.ts b/src/vs/editor/test/common/modesTestUtils.ts index eb447ccd306..c4328288d3b 100644 --- a/src/vs/editor/test/common/modesTestUtils.ts +++ b/src/vs/editor/test/common/modesTestUtils.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { LineTokens } from 'vs/editor/common/core/lineTokens'; -import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/modes/supports'; -import { StandardTokenType, MetadataConsts } from 'vs/editor/common/modes'; +import { MetadataConsts, StandardTokenType } from 'vs/editor/common/modes'; +import { ScopedLineTokens, createScopedLineTokens } from 'vs/editor/common/modes/supports'; export interface TokenText { text: string; diff --git a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts index 64d485afde7..0d7ef7e4f86 100644 --- a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts +++ b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts @@ -2,9 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { EditorSimpleWorkerImpl, ICommonModel } from 'vs/editor/common/services/editorSimpleWorker'; import { Range } from 'vs/editor/common/core/range'; +import { EditorSimpleWorkerImpl, ICommonModel } from 'vs/editor/common/services/editorSimpleWorker'; suite('EditorSimpleWorker', () => { diff --git a/src/vs/editor/test/common/services/languagesRegistry.test.ts b/src/vs/editor/test/common/services/languagesRegistry.test.ts index 6db5eac003a..aa72a87b982 100644 --- a/src/vs/editor/test/common/services/languagesRegistry.test.ts +++ b/src/vs/editor/test/common/services/languagesRegistry.test.ts @@ -2,9 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry'; import { URI } from 'vs/base/common/uri'; +import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry'; suite('LanguagesRegistry', () => { diff --git a/src/vs/editor/test/common/services/modelService.test.ts b/src/vs/editor/test/common/services/modelService.test.ts index 38c8bdaf3a6..56a7575d4fb 100644 --- a/src/vs/editor/test/common/services/modelService.test.ts +++ b/src/vs/editor/test/common/services/modelService.test.ts @@ -2,19 +2,20 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; -import { URI } from 'vs/base/common/uri'; +import { CharCode } from 'vs/base/common/charCode'; import * as platform from 'vs/base/common/platform'; -import { DefaultEndOfLine } from 'vs/editor/common/model'; -import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel'; +import { URI } from 'vs/base/common/uri'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; -import { CharCode } from 'vs/base/common/charCode'; import { createStringBuilder } from 'vs/editor/common/core/stringBuilder'; +import { DefaultEndOfLine } from 'vs/editor/common/model'; +import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; const GENERATE_TESTS = false; diff --git a/src/vs/editor/test/common/standalone/standaloneBase.test.ts b/src/vs/editor/test/common/standalone/standaloneBase.test.ts deleted file mode 100644 index c28d7b164ba..00000000000 --- a/src/vs/editor/test/common/standalone/standaloneBase.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; -import { KeyCode as StandaloneKeyCode } from 'vs/editor/common/standalone/standaloneBase'; -import { KeyCode as RuntimeKeyCode } from 'vs/base/common/keyCodes'; - -suite('KeyCode', () => { - test('is exported correctly in standalone editor', () => { - - function assertKeyCode(standalone: StandaloneKeyCode, runtime: RuntimeKeyCode): void { - assert.equal(standalone, runtime); - } - - assertKeyCode(StandaloneKeyCode.Unknown, RuntimeKeyCode.Unknown); - assertKeyCode(StandaloneKeyCode.Backspace, RuntimeKeyCode.Backspace); - assertKeyCode(StandaloneKeyCode.Tab, RuntimeKeyCode.Tab); - assertKeyCode(StandaloneKeyCode.Enter, RuntimeKeyCode.Enter); - assertKeyCode(StandaloneKeyCode.Shift, RuntimeKeyCode.Shift); - assertKeyCode(StandaloneKeyCode.Ctrl, RuntimeKeyCode.Ctrl); - assertKeyCode(StandaloneKeyCode.Alt, RuntimeKeyCode.Alt); - assertKeyCode(StandaloneKeyCode.PauseBreak, RuntimeKeyCode.PauseBreak); - assertKeyCode(StandaloneKeyCode.CapsLock, RuntimeKeyCode.CapsLock); - assertKeyCode(StandaloneKeyCode.Escape, RuntimeKeyCode.Escape); - assertKeyCode(StandaloneKeyCode.Space, RuntimeKeyCode.Space); - assertKeyCode(StandaloneKeyCode.PageUp, RuntimeKeyCode.PageUp); - assertKeyCode(StandaloneKeyCode.PageDown, RuntimeKeyCode.PageDown); - assertKeyCode(StandaloneKeyCode.End, RuntimeKeyCode.End); - assertKeyCode(StandaloneKeyCode.Home, RuntimeKeyCode.Home); - assertKeyCode(StandaloneKeyCode.LeftArrow, RuntimeKeyCode.LeftArrow); - assertKeyCode(StandaloneKeyCode.UpArrow, RuntimeKeyCode.UpArrow); - assertKeyCode(StandaloneKeyCode.RightArrow, RuntimeKeyCode.RightArrow); - assertKeyCode(StandaloneKeyCode.DownArrow, RuntimeKeyCode.DownArrow); - assertKeyCode(StandaloneKeyCode.Insert, RuntimeKeyCode.Insert); - assertKeyCode(StandaloneKeyCode.Delete, RuntimeKeyCode.Delete); - assertKeyCode(StandaloneKeyCode.KEY_0, RuntimeKeyCode.KEY_0); - assertKeyCode(StandaloneKeyCode.KEY_1, RuntimeKeyCode.KEY_1); - assertKeyCode(StandaloneKeyCode.KEY_2, RuntimeKeyCode.KEY_2); - assertKeyCode(StandaloneKeyCode.KEY_3, RuntimeKeyCode.KEY_3); - assertKeyCode(StandaloneKeyCode.KEY_4, RuntimeKeyCode.KEY_4); - assertKeyCode(StandaloneKeyCode.KEY_5, RuntimeKeyCode.KEY_5); - assertKeyCode(StandaloneKeyCode.KEY_6, RuntimeKeyCode.KEY_6); - assertKeyCode(StandaloneKeyCode.KEY_7, RuntimeKeyCode.KEY_7); - assertKeyCode(StandaloneKeyCode.KEY_8, RuntimeKeyCode.KEY_8); - assertKeyCode(StandaloneKeyCode.KEY_9, RuntimeKeyCode.KEY_9); - assertKeyCode(StandaloneKeyCode.KEY_A, RuntimeKeyCode.KEY_A); - assertKeyCode(StandaloneKeyCode.KEY_B, RuntimeKeyCode.KEY_B); - assertKeyCode(StandaloneKeyCode.KEY_C, RuntimeKeyCode.KEY_C); - assertKeyCode(StandaloneKeyCode.KEY_D, RuntimeKeyCode.KEY_D); - assertKeyCode(StandaloneKeyCode.KEY_E, RuntimeKeyCode.KEY_E); - assertKeyCode(StandaloneKeyCode.KEY_F, RuntimeKeyCode.KEY_F); - assertKeyCode(StandaloneKeyCode.KEY_G, RuntimeKeyCode.KEY_G); - assertKeyCode(StandaloneKeyCode.KEY_H, RuntimeKeyCode.KEY_H); - assertKeyCode(StandaloneKeyCode.KEY_I, RuntimeKeyCode.KEY_I); - assertKeyCode(StandaloneKeyCode.KEY_J, RuntimeKeyCode.KEY_J); - assertKeyCode(StandaloneKeyCode.KEY_K, RuntimeKeyCode.KEY_K); - assertKeyCode(StandaloneKeyCode.KEY_L, RuntimeKeyCode.KEY_L); - assertKeyCode(StandaloneKeyCode.KEY_M, RuntimeKeyCode.KEY_M); - assertKeyCode(StandaloneKeyCode.KEY_N, RuntimeKeyCode.KEY_N); - assertKeyCode(StandaloneKeyCode.KEY_O, RuntimeKeyCode.KEY_O); - assertKeyCode(StandaloneKeyCode.KEY_P, RuntimeKeyCode.KEY_P); - assertKeyCode(StandaloneKeyCode.KEY_Q, RuntimeKeyCode.KEY_Q); - assertKeyCode(StandaloneKeyCode.KEY_R, RuntimeKeyCode.KEY_R); - assertKeyCode(StandaloneKeyCode.KEY_S, RuntimeKeyCode.KEY_S); - assertKeyCode(StandaloneKeyCode.KEY_T, RuntimeKeyCode.KEY_T); - assertKeyCode(StandaloneKeyCode.KEY_U, RuntimeKeyCode.KEY_U); - assertKeyCode(StandaloneKeyCode.KEY_V, RuntimeKeyCode.KEY_V); - assertKeyCode(StandaloneKeyCode.KEY_W, RuntimeKeyCode.KEY_W); - assertKeyCode(StandaloneKeyCode.KEY_X, RuntimeKeyCode.KEY_X); - assertKeyCode(StandaloneKeyCode.KEY_Y, RuntimeKeyCode.KEY_Y); - assertKeyCode(StandaloneKeyCode.KEY_Z, RuntimeKeyCode.KEY_Z); - assertKeyCode(StandaloneKeyCode.Meta, RuntimeKeyCode.Meta); - assertKeyCode(StandaloneKeyCode.ContextMenu, RuntimeKeyCode.ContextMenu); - assertKeyCode(StandaloneKeyCode.F1, RuntimeKeyCode.F1); - assertKeyCode(StandaloneKeyCode.F2, RuntimeKeyCode.F2); - assertKeyCode(StandaloneKeyCode.F3, RuntimeKeyCode.F3); - assertKeyCode(StandaloneKeyCode.F4, RuntimeKeyCode.F4); - assertKeyCode(StandaloneKeyCode.F5, RuntimeKeyCode.F5); - assertKeyCode(StandaloneKeyCode.F6, RuntimeKeyCode.F6); - assertKeyCode(StandaloneKeyCode.F7, RuntimeKeyCode.F7); - assertKeyCode(StandaloneKeyCode.F8, RuntimeKeyCode.F8); - assertKeyCode(StandaloneKeyCode.F9, RuntimeKeyCode.F9); - assertKeyCode(StandaloneKeyCode.F10, RuntimeKeyCode.F10); - assertKeyCode(StandaloneKeyCode.F11, RuntimeKeyCode.F11); - assertKeyCode(StandaloneKeyCode.F12, RuntimeKeyCode.F12); - assertKeyCode(StandaloneKeyCode.F13, RuntimeKeyCode.F13); - assertKeyCode(StandaloneKeyCode.F14, RuntimeKeyCode.F14); - assertKeyCode(StandaloneKeyCode.F15, RuntimeKeyCode.F15); - assertKeyCode(StandaloneKeyCode.F16, RuntimeKeyCode.F16); - assertKeyCode(StandaloneKeyCode.F17, RuntimeKeyCode.F17); - assertKeyCode(StandaloneKeyCode.F18, RuntimeKeyCode.F18); - assertKeyCode(StandaloneKeyCode.F19, RuntimeKeyCode.F19); - assertKeyCode(StandaloneKeyCode.NumLock, RuntimeKeyCode.NumLock); - assertKeyCode(StandaloneKeyCode.ScrollLock, RuntimeKeyCode.ScrollLock); - assertKeyCode(StandaloneKeyCode.US_SEMICOLON, RuntimeKeyCode.US_SEMICOLON); - assertKeyCode(StandaloneKeyCode.US_EQUAL, RuntimeKeyCode.US_EQUAL); - assertKeyCode(StandaloneKeyCode.US_COMMA, RuntimeKeyCode.US_COMMA); - assertKeyCode(StandaloneKeyCode.US_MINUS, RuntimeKeyCode.US_MINUS); - assertKeyCode(StandaloneKeyCode.US_DOT, RuntimeKeyCode.US_DOT); - assertKeyCode(StandaloneKeyCode.US_SLASH, RuntimeKeyCode.US_SLASH); - assertKeyCode(StandaloneKeyCode.US_BACKTICK, RuntimeKeyCode.US_BACKTICK); - assertKeyCode(StandaloneKeyCode.US_OPEN_SQUARE_BRACKET, RuntimeKeyCode.US_OPEN_SQUARE_BRACKET); - assertKeyCode(StandaloneKeyCode.US_BACKSLASH, RuntimeKeyCode.US_BACKSLASH); - assertKeyCode(StandaloneKeyCode.US_CLOSE_SQUARE_BRACKET, RuntimeKeyCode.US_CLOSE_SQUARE_BRACKET); - assertKeyCode(StandaloneKeyCode.US_QUOTE, RuntimeKeyCode.US_QUOTE); - assertKeyCode(StandaloneKeyCode.OEM_8, RuntimeKeyCode.OEM_8); - assertKeyCode(StandaloneKeyCode.OEM_102, RuntimeKeyCode.OEM_102); - assertKeyCode(StandaloneKeyCode.NUMPAD_0, RuntimeKeyCode.NUMPAD_0); - assertKeyCode(StandaloneKeyCode.NUMPAD_1, RuntimeKeyCode.NUMPAD_1); - assertKeyCode(StandaloneKeyCode.NUMPAD_2, RuntimeKeyCode.NUMPAD_2); - assertKeyCode(StandaloneKeyCode.NUMPAD_3, RuntimeKeyCode.NUMPAD_3); - assertKeyCode(StandaloneKeyCode.NUMPAD_4, RuntimeKeyCode.NUMPAD_4); - assertKeyCode(StandaloneKeyCode.NUMPAD_5, RuntimeKeyCode.NUMPAD_5); - assertKeyCode(StandaloneKeyCode.NUMPAD_6, RuntimeKeyCode.NUMPAD_6); - assertKeyCode(StandaloneKeyCode.NUMPAD_7, RuntimeKeyCode.NUMPAD_7); - assertKeyCode(StandaloneKeyCode.NUMPAD_8, RuntimeKeyCode.NUMPAD_8); - assertKeyCode(StandaloneKeyCode.NUMPAD_9, RuntimeKeyCode.NUMPAD_9); - assertKeyCode(StandaloneKeyCode.NUMPAD_MULTIPLY, RuntimeKeyCode.NUMPAD_MULTIPLY); - assertKeyCode(StandaloneKeyCode.NUMPAD_ADD, RuntimeKeyCode.NUMPAD_ADD); - assertKeyCode(StandaloneKeyCode.NUMPAD_SEPARATOR, RuntimeKeyCode.NUMPAD_SEPARATOR); - assertKeyCode(StandaloneKeyCode.NUMPAD_SUBTRACT, RuntimeKeyCode.NUMPAD_SUBTRACT); - assertKeyCode(StandaloneKeyCode.NUMPAD_DECIMAL, RuntimeKeyCode.NUMPAD_DECIMAL); - assertKeyCode(StandaloneKeyCode.NUMPAD_DIVIDE, RuntimeKeyCode.NUMPAD_DIVIDE); - assertKeyCode(StandaloneKeyCode.KEY_IN_COMPOSITION, RuntimeKeyCode.KEY_IN_COMPOSITION); - assertKeyCode(StandaloneKeyCode.ABNT_C1, RuntimeKeyCode.ABNT_C1); - assertKeyCode(StandaloneKeyCode.ABNT_C2, RuntimeKeyCode.ABNT_C2); - assertKeyCode(StandaloneKeyCode.MAX_VALUE, RuntimeKeyCode.MAX_VALUE); - }); -}); diff --git a/src/vs/editor/test/common/view/minimapCharRenderer.test.ts b/src/vs/editor/test/common/view/minimapCharRenderer.test.ts index b5db5f5e5dd..f13b962a4d3 100644 --- a/src/vs/editor/test/common/view/minimapCharRenderer.test.ts +++ b/src/vs/editor/test/common/view/minimapCharRenderer.test.ts @@ -2,11 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + 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 { RGBA8 } from 'vs/editor/common/core/rgba'; +import { Constants } from 'vs/editor/common/view/minimapCharRenderer'; +import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; +import { MinimapCharRendererFactory } from 'vs/editor/test/common/view/minimapCharRendererFactory'; suite('MinimapCharRenderer', () => { diff --git a/src/vs/editor/test/common/view/overviewZoneManager.test.ts b/src/vs/editor/test/common/view/overviewZoneManager.test.ts index 13e55414e44..39c104fbb81 100644 --- a/src/vs/editor/test/common/view/overviewZoneManager.test.ts +++ b/src/vs/editor/test/common/view/overviewZoneManager.test.ts @@ -2,8 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { OverviewZoneManager, ColorZone, OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; +import { ColorZone, OverviewRulerZone, OverviewZoneManager } from 'vs/editor/common/view/overviewZoneManager'; suite('Editor View - OverviewZoneManager', () => { diff --git a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts index c967f2703ec..e35df432fcd 100644 --- a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts +++ b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts @@ -2,8 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { RenderMinimap, EditorLayoutInfo, EditorLayoutProvider, IEditorLayoutProviderOpts } from 'vs/editor/common/config/editorOptions'; +import { EditorLayoutInfo, EditorLayoutProvider, IEditorLayoutProviderOpts, RenderMinimap } from 'vs/editor/common/config/editorOptions'; suite('Editor ViewLayout - EditorLayoutProvider', () => { diff --git a/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts b/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts index d9457095116..d40db3db77a 100644 --- a/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts +++ b/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts @@ -2,9 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { DecorationSegment, LineDecorationsNormalizer, LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { Range } from 'vs/editor/common/core/range'; +import { DecorationSegment, LineDecoration, LineDecorationsNormalizer } from 'vs/editor/common/viewLayout/lineDecorations'; import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; suite('Editor ViewLayout - ViewLineParts', () => { diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts index 5706522c4b6..5216cba2740 100644 --- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -2,15 +2,16 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { renderViewLine2 as renderViewLine, RenderLineInput, CharacterMapping } from 'vs/editor/common/viewLayout/viewLineRenderer'; -import { ViewLineToken, ViewLineTokens } from 'vs/editor/test/common/core/viewLineToken'; import { CharCode } from 'vs/base/common/charCode'; +import * as strings from 'vs/base/common/strings'; +import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { MetadataConsts } from 'vs/editor/common/modes'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; +import { CharacterMapping, RenderLineInput, renderViewLine2 as renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; -import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; -import * as strings from 'vs/base/common/strings'; +import { ViewLineToken, ViewLineTokens } from 'vs/editor/test/common/core/viewLineToken'; function createViewLineTokens(viewLineTokens: ViewLineToken[]): IViewLineTokens { return new ViewLineTokens(viewLineTokens); diff --git a/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts b/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts index cb21209c5fe..11cc42efb8d 100644 --- a/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts +++ b/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts @@ -2,9 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { PrefixSumComputer, PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer'; import { toUint32Array } from 'vs/editor/common/core/uint'; +import { PrefixSumComputer, PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer'; suite('Editor ViewModel - PrefixSumComputer', () => { diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index 6a022329bdd..7e581361cfb 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -2,22 +2,23 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; -import { CharacterHardWrappingLineMapping, CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; -import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { ILineMapping, SplitLine, SplitLinesCollection, ISimpleModel } from 'vs/editor/common/viewModel/splitLinesCollection'; -import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; import { toUint32Array } from 'vs/editor/common/core/uint'; +import { EndOfLinePreference } from 'vs/editor/common/model'; +import { TextModel } from 'vs/editor/common/model/textModel'; 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 { IDisposable } from 'vs/base/common/lifecycle'; +import { CharacterHardWrappingLineMapperFactory, CharacterHardWrappingLineMapping } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; +import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; +import { ILineMapping, ISimpleModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; -import { EndOfLinePreference } from 'vs/editor/common/model'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; suite('Editor ViewModel - SplitLinesCollection', () => { test('SplitLine', () => { diff --git a/src/vs/editor/test/common/viewModel/testViewModel.ts b/src/vs/editor/test/common/viewModel/testViewModel.ts index d76e87a33c4..75b429dd095 100644 --- a/src/vs/editor/test/common/viewModel/testViewModel.ts +++ b/src/vs/editor/test/common/viewModel/testViewModel.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextModel } from 'vs/editor/common/model/textModel'; -import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; -import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; export function testViewModel(text: string[], options: IEditorOptions, callback: (viewModel: ViewModel, model: TextModel) => void): void { const EDITOR_ID = 1; diff --git a/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts b/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts index 17295c1ec43..9aab952c372 100644 --- a/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts +++ b/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts @@ -2,11 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; -import { Range } from 'vs/editor/common/core/range'; -import { testViewModel } from 'vs/editor/test/common/viewModel/testViewModel'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { Range } from 'vs/editor/common/core/range'; import { InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; +import { testViewModel } from 'vs/editor/test/common/viewModel/testViewModel'; suite('ViewModelDecorations', () => { test('getDecorationsViewportData', () => { diff --git a/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts b/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts index 3c6336a2369..141afb3c455 100644 --- a/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts +++ b/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts @@ -2,10 +2,11 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; -import { testViewModel } from 'vs/editor/test/common/viewModel/testViewModel'; import { EndOfLineSequence } from 'vs/editor/common/model'; +import { testViewModel } from 'vs/editor/test/common/viewModel/testViewModel'; suite('ViewModel', () => { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index f9a7697a49f..ef910f3855d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -25,19 +25,19 @@ declare namespace monaco { dispose(): void; } + export enum MarkerTag { - Unnecessary = 1, + Unnecessary = 1 } export enum MarkerSeverity { Hint = 1, Info = 2, Warning = 4, - Error = 8, + Error = 8 } - export class Promise { constructor(executor: (resolve: (value: T | PromiseLike) => void, reject: (reason: any) => void) => void); @@ -182,7 +182,7 @@ declare namespace monaco { fragment?: string; }): Uri; /** - * Creates a string presentation for this Uri. It's guardeed that calling + * Creates a string presentation for this Uri. It's guaranteed that calling * `Uri.parse` with the result of this function creates an Uri which is equal * to this Uri. * @@ -385,7 +385,6 @@ declare namespace monaco { */ MAX_VALUE = 112 } - export class KeyMod { static readonly CtrlCmd: number; static readonly Shift: number; @@ -932,7 +931,7 @@ declare namespace monaco.editor { export function tokenize(text: string, languageId: string): Token[][]; /** - * Define a new theme or updte an existing theme. + * Define a new theme or update an existing theme. */ export function defineTheme(themeName: string, themeData: IStandaloneThemeData): void; @@ -1037,7 +1036,7 @@ declare namespace monaco.editor { contextMenuOrder?: number; /** * Method that will be executed when the action is triggered. - * @param editor The editor instance is passed in as a convinience + * @param editor The editor instance is passed in as a convenience */ run(editor: ICodeEditor): void | Promise; } @@ -1297,7 +1296,7 @@ declare namespace monaco.editor { */ readonly id: string; /** - * Identifier for a decoration's owener. + * Identifier for a decoration's owner. */ readonly ownerId: number; /** @@ -1568,13 +1567,13 @@ declare namespace monaco.editor { */ validatePosition(position: IPosition): Position; /** - * Advances the given position by the given offest (negative offsets are also accepted) + * Advances the given position by the given offset (negative offsets are also accepted) * and returns it as a new valid position. * * If the offset and position are such that their combination goes beyond the beginning or * end of the model, throws an exception. * - * If the ofsset is such that the new position would be in the middle of a multi-byte + * If the offset is such that the new position would be in the middle of a multi-byte * line terminator, throws an exception. */ modifyPosition(position: IPosition, offset: number): Position; @@ -1669,7 +1668,7 @@ declare namespace monaco.editor { */ getWordUntilPosition(position: IPosition): IWordAtPosition; /** - * Perform a minimum ammount of operations, in order to transform the decorations + * Perform a minimum amount of operations, in order to transform the decorations * identified by `oldDecorations` to the decorations described by `newDecorations` * and returns the new identifiers associated with the resulting decorations. * @@ -1709,7 +1708,7 @@ declare namespace monaco.editor { */ getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; /** - * Gets all the deocorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. + * Gets all the decorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. * So for now it returns all the decorations on the same line as `range`. * @param range The range to search in * @param ownerId If set, it will ignore decorations belonging to other owners. @@ -1754,7 +1753,7 @@ declare namespace monaco.editor { /** * Push edit operations, basically editing the model. This is the preferred way * of editing the model. The edit operations will land on the undo stack. - * @param beforeCursorState The cursor state before the edit operaions. This cursor state will be returned when `undo` or `redo` are invoked. + * @param beforeCursorState The cursor state before the edit operations. This cursor state will be returned when `undo` or `redo` are invoked. * @param editOperations The edit operations. * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. * @return The cursor state returned by the `cursorStateComputer`. @@ -1838,7 +1837,7 @@ declare namespace monaco.editor { * @param selection The selection to track. * @param trackPreviousOnEmpty If set, and the selection is empty, indicates whether the selection * should clamp to the previous or the next character. - * @return A unique identifer. + * @return A unique identifier. */ trackSelection(selection: Selection, trackPreviousOnEmpty?: boolean): string; } @@ -1871,7 +1870,7 @@ declare namespace monaco.editor { getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void; /** * Compute the cursor state after the edit operations were applied. - * @param model The model the commad has executed on. + * @param model The model the command has executed on. * @param helper A helper to get inverse edit operations and to get previously tracked selections. * @return The cursor state after the command executed. */ @@ -1999,7 +1998,7 @@ declare namespace monaco.editor { */ export type IEditorViewState = ICodeEditorViewState | IDiffEditorViewState; - export const enum ScrollType { + export enum ScrollType { Smooth = 0, Immediate = 1 } @@ -2676,6 +2675,11 @@ declare namespace monaco.editor { * Defaults to false. */ mouseWheelZoom?: boolean; + /** + * Enable smooth caret animation. + * Defaults to false. + */ + cursorSmoothCaretAnimation?: boolean; /** * Control the cursor style, either 'block' or 'line'. * Defaults to 'line'. @@ -3206,7 +3210,7 @@ declare namespace monaco.editor { readonly wordWrapBreakObtrusiveCharacters: string; } - export const enum RenderLineNumbersType { + export enum RenderLineNumbersType { Off = 0, On = 1, Relative = 2, @@ -3229,6 +3233,7 @@ declare namespace monaco.editor { readonly overviewRulerBorder: boolean; readonly cursorBlinking: TextEditorCursorBlinkingStyle; readonly mouseWheelZoom: boolean; + readonly cursorSmoothCaretAnimation: boolean; readonly cursorStyle: TextEditorCursorStyle; readonly cursorWidth: number; readonly hideCursorInOverviewRuler: boolean; @@ -3972,7 +3977,7 @@ declare namespace monaco.editor { */ executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean; /** - * Execute multiple (concommitent) commands on the editor. + * Execute multiple (concomitant) commands on the editor. * @param source The source of the call. * @param command The commands to execute */ @@ -4013,7 +4018,7 @@ declare namespace monaco.editor { addContentWidget(widget: IContentWidget): void; /** * Layout/Reposition a content widget. This is a ping to the editor to call widget.getPosition() - * and update appropiately. + * and update appropriately. */ layoutContentWidget(widget: IContentWidget): void; /** @@ -4026,7 +4031,7 @@ declare namespace monaco.editor { addOverlayWidget(widget: IOverlayWidget): void; /** * Layout/Reposition an overlay widget. This is a ping to the editor to call widget.getPosition() - * and update appropiately. + * and update appropriately. */ layoutOverlayWidget(widget: IOverlayWidget): void; /** @@ -4059,7 +4064,7 @@ declare namespace monaco.editor { * The result position takes scrolling into account and is relative to the top left corner of the editor. * Explanation 1: the results of this method will change for the same `position` if the user scrolls the editor. * Explanation 2: the results of this method will not change if the container of the editor gets repositioned. - * Warning: the results of this method are innacurate for positions that are outside the current editor viewport. + * Warning: the results of this method are inaccurate for positions that are outside the current editor viewport. */ getScrolledVisiblePosition(position: IPosition): { top: number; @@ -4292,7 +4297,7 @@ declare namespace monaco.languages { export function registerRenameProvider(languageId: string, provider: RenameProvider): IDisposable; /** - * Register a signature help provider (used by e.g. paremeter hints). + * Register a signature help provider (used by e.g. parameter hints). */ export function registerSignatureHelpProvider(languageId: string, provider: SignatureHelpProvider): IDisposable; @@ -4477,7 +4482,7 @@ declare namespace monaco.languages { */ export interface IndentationRule { /** - * If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches). + * If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches). */ decreaseIndentPattern: RegExp; /** @@ -4510,7 +4515,7 @@ declare namespace monaco.languages { */ export interface FoldingRules { /** - * Used by the indentation based strategy to decide wheter empty lines belong to the previous or the next block. + * Used by the indentation based strategy to decide whether empty lines belong to the previous or the next block. * A language adheres to the off-side rule if blocks in that language are expressed by their indentation. * See [wikipedia](https://en.wikipedia.org/wiki/Off-side_rule) for more information. * If not set, `false` is used and empty lines belong to the previous block. @@ -4922,12 +4927,13 @@ declare namespace monaco.languages { export enum SignatureHelpTriggerReason { Invoke = 1, TriggerCharacter = 2, - Retrigger = 3 + ContentChange = 3 } export interface SignatureHelpContext { - triggerReason: SignatureHelpTriggerReason; - triggerCharacter?: string; + readonly triggerReason: SignatureHelpTriggerReason; + readonly triggerCharacter?: string; + readonly isRetrigger: boolean; } /** @@ -5354,7 +5360,7 @@ declare namespace monaco.languages { } export interface WorkspaceEdit { - edits: Array; + edits?: Array; } export interface Rejection { @@ -5495,7 +5501,7 @@ declare namespace monaco.languages { */ bracket?: string; /** - * switch to embedded language (useing the mimetype) or get out using "@pop" + * switch to embedded language (using the mimetype) or get out using "@pop" */ nextEmbedded?: string; /** @@ -5515,7 +5521,7 @@ declare namespace monaco.languages { */ open: string; /** - * closeing bracket + * closing bracket */ close: string; /** @@ -5543,3 +5549,5 @@ declare namespace monaco.worker { } } + +//dtsv=2 \ No newline at end of file diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 9d981e148c2..90b6e588e71 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -10,7 +10,7 @@ import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry' import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; export interface ILocalizedString { @@ -58,49 +58,44 @@ export function isISubmenuItem(item: IMenuItem | ISubmenuItem): item is ISubmenu return (item as ISubmenuItem).submenu !== undefined; } -export class MenuId { - - private static ID = 1; - - static readonly EditorTitle = new MenuId(); - static readonly EditorTitleContext = new MenuId(); - static readonly EditorContext = new MenuId(); - static readonly EmptyEditorGroupContext = new MenuId(); - static readonly ExplorerContext = new MenuId(); - static readonly OpenEditorsContext = new MenuId(); - static readonly ProblemsPanelContext = new MenuId(); - static readonly DebugVariablesContext = new MenuId(); - static readonly DebugWatchContext = new MenuId(); - static readonly DebugCallStackContext = new MenuId(); - static readonly DebugBreakpointsContext = new MenuId(); - static readonly DebugConsoleContext = new MenuId(); - static readonly SCMTitle = new MenuId(); - static readonly SCMSourceControl = new MenuId(); - static readonly SCMResourceGroupContext = new MenuId(); - static readonly SCMResourceContext = new MenuId(); - static readonly SCMChangeContext = new MenuId(); - static readonly CommandPalette = new MenuId(); - static readonly ViewTitle = new MenuId(); - static readonly ViewItemContext = new MenuId(); - static readonly TouchBarContext = new MenuId(); - static readonly SearchContext = new MenuId(); - static readonly MenubarFileMenu = new MenuId(); - static readonly MenubarEditMenu = new MenuId(); - static readonly MenubarRecentMenu = new MenuId(); - static readonly MenubarSelectionMenu = new MenuId(); - static readonly MenubarViewMenu = new MenuId(); - static readonly MenubarAppearanceMenu = new MenuId(); - static readonly MenubarLayoutMenu = new MenuId(); - static readonly MenubarGoMenu = new MenuId(); - static readonly MenubarSwitchEditorMenu = new MenuId(); - static readonly MenubarSwitchGroupMenu = new MenuId(); - static readonly MenubarDebugMenu = new MenuId(); - static readonly MenubarNewBreakpointMenu = new MenuId(); - static readonly MenubarPreferencesMenu = new MenuId(); - static readonly MenubarHelpMenu = new MenuId(); - static readonly MenubarTerminalMenu = new MenuId(); - - readonly id: string = String(MenuId.ID++); +export const enum MenuId { + CommandPalette, + DebugBreakpointsContext, + DebugCallStackContext, + DebugConsoleContext, + DebugVariablesContext, + DebugWatchContext, + EditorContext, + EditorTitle, + EditorTitleContext, + EmptyEditorGroupContext, + ExplorerContext, + MenubarAppearanceMenu, + MenubarDebugMenu, + MenubarEditMenu, + MenubarFileMenu, + MenubarGoMenu, + MenubarHelpMenu, + MenubarLayoutMenu, + MenubarNewBreakpointMenu, + MenubarPreferencesMenu, + MenubarRecentMenu, + MenubarSelectionMenu, + MenubarSwitchEditorMenu, + MenubarSwitchGroupMenu, + MenubarTerminalMenu, + MenubarViewMenu, + OpenEditorsContext, + ProblemsPanelContext, + SCMChangeContext, + SCMResourceContext, + SCMResourceGroupContext, + SCMSourceControl, + SCMTitle, + SearchContext, + TouchBarContext, + ViewItemContext, + ViewTitle, } export interface IMenuActionOptions { @@ -128,6 +123,7 @@ export interface IMenuRegistry { getCommands(): ICommandsMap; appendMenuItem(menu: MenuId, item: IMenuItem | ISubmenuItem): IDisposable; getMenuItems(loc: MenuId): (IMenuItem | ISubmenuItem)[]; + onDidChangeMenu: Event; } export interface ICommandsMap { @@ -136,9 +132,11 @@ export interface ICommandsMap { export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry { - private _commands: { [id: string]: ICommandAction } = Object.create(null); + private readonly _commands: { [id: string]: ICommandAction } = Object.create(null); + private readonly _menuItems: { [loc: string]: (IMenuItem | ISubmenuItem)[] } = Object.create(null); + private readonly _onDidChangeMenu = new Emitter(); - private _menuItems: { [loc: string]: (IMenuItem | ISubmenuItem)[] } = Object.create(null); + readonly onDidChangeMenu: Event = this._onDidChangeMenu.event; addCommand(command: ICommandAction): boolean { const old = this._commands[command.id]; @@ -158,27 +156,29 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry { return result; } - appendMenuItem({ id }: MenuId, item: IMenuItem | ISubmenuItem): IDisposable { + appendMenuItem(id: MenuId, item: IMenuItem | ISubmenuItem): IDisposable { let array = this._menuItems[id]; if (!array) { this._menuItems[id] = array = [item]; } else { array.push(item); } + this._onDidChangeMenu.fire(id); return { - dispose() { + dispose: () => { const idx = array.indexOf(item); if (idx >= 0) { array.splice(idx, 1); + this._onDidChangeMenu.fire(id); } } }; } - getMenuItems({ id }: MenuId): (IMenuItem | ISubmenuItem)[] { + getMenuItems(id: MenuId): (IMenuItem | ISubmenuItem)[] { const result = this._menuItems[id] || []; - if (id === MenuId.CommandPalette.id) { + if (id === MenuId.CommandPalette) { // CommandPalette is special because it shows // all commands by default this._appendImplicitItems(result); diff --git a/src/vs/platform/actions/common/menu.ts b/src/vs/platform/actions/common/menu.ts index d90656347f8..ec4b4a4ae67 100644 --- a/src/vs/platform/actions/common/menu.ts +++ b/src/vs/platform/actions/common/menu.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import { Event, Emitter, filterEvent, debounceEvent } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MenuId, MenuRegistry, MenuItemAction, IMenu, IMenuItem, IMenuActionOptions, ISubmenuItem, SubmenuItemAction, isIMenuItem } from 'vs/platform/actions/common/actions'; @@ -13,59 +13,74 @@ type MenuItemGroup = [string, (IMenuItem | ISubmenuItem)[]]; export class Menu implements IMenu { - private _menuGroups: MenuItemGroup[] = []; - private _disposables: IDisposable[] = []; - private _onDidChange = new Emitter(); + private readonly _onDidChange = new Emitter(); + private readonly _disposables: IDisposable[] = []; + + private _menuGroups: MenuItemGroup[]; + private _contextKeys: Set; constructor( - id: MenuId, - startupSignal: Thenable, + private readonly _id: MenuId, @ICommandService private readonly _commandService: ICommandService, @IContextKeyService private readonly _contextKeyService: IContextKeyService ) { - startupSignal.then(_ => { - const menuItems = MenuRegistry.getMenuItems(id); - const keysFilter = new Set(); + this._build(); - let group: MenuItemGroup; - menuItems.sort(Menu._compareMenuItems); + // rebuild this menu whenever the menu registry reports an + // event for this MenuId + debounceEvent( + filterEvent(MenuRegistry.onDidChangeMenu, menuId => menuId === this._id), + () => { }, + 100 + )(this._build, this, this._disposables); - for (let item of menuItems) { - // group by groupId - const groupName = item.group; - if (!group || group[0] !== groupName) { - group = [groupName, []]; - this._menuGroups.push(group); - } - group[1].push(item); + // when context keys change we need to change if the menu also + // has changed + this._contextKeyService.onDidChangeContext(event => { + if (event.affectsSome(this._contextKeys)) { + this._onDidChange.fire(); + } + }, this, this._disposables); + } - // keep keys for eventing - Menu._fillInKbExprKeys(item.when, keysFilter); + private _build(): void { - // keep precondition keys for event if applicable - if (isIMenuItem(item) && item.command.precondition) { - Menu._fillInKbExprKeys(item.command.precondition, keysFilter); - } + // reset + this._menuGroups = []; + this._contextKeys = new Set(); - // keep toggled keys for event if applicable - if (isIMenuItem(item) && item.command.toggled) { - Menu._fillInKbExprKeys(item.command.toggled, keysFilter); - } + const menuItems = MenuRegistry.getMenuItems(this._id); + + let group: MenuItemGroup | undefined; + menuItems.sort(Menu._compareMenuItems); + + for (let item of menuItems) { + // group by groupId + const groupName = item.group; + if (!group || group[0] !== groupName) { + group = [groupName || '', []]; + this._menuGroups.push(group); + } + group![1].push(item); + + // keep keys for eventing + Menu._fillInKbExprKeys(item.when, this._contextKeys); + + // keep precondition keys for event if applicable + if (isIMenuItem(item) && item.command.precondition) { + Menu._fillInKbExprKeys(item.command.precondition, this._contextKeys); } - // subscribe to context changes - this._disposables.push(this._contextKeyService.onDidChangeContext(event => { - if (event.affectsSome(keysFilter)) { - this._onDidChange.fire(); - } - })); - - this._onDidChange.fire(this); - }); + // keep toggled keys for event if applicable + if (isIMenuItem(item) && item.command.toggled) { + Menu._fillInKbExprKeys(item.command.toggled, this._contextKeys); + } + } + this._onDidChange.fire(this); } dispose() { - this._disposables = dispose(this._disposables); + dispose(this._disposables); this._onDidChange.dispose(); } @@ -79,7 +94,7 @@ export class Menu implements IMenu { const [id, items] = group; const activeActions: (MenuItemAction | SubmenuItemAction)[] = []; for (const item of items) { - if (this._contextKeyService.contextMatchesRules(item.when)) { + if (this._contextKeyService.contextMatchesRules(item.when || null)) { const action = isIMenuItem(item) ? new MenuItemAction(item.command, item.alt, options, this._contextKeyService, this._commandService) : new SubmenuItemAction(item); activeActions.push(action); } @@ -91,7 +106,7 @@ export class Menu implements IMenu { return result; } - private static _fillInKbExprKeys(exp: ContextKeyExpr, set: Set): void { + private static _fillInKbExprKeys(exp: ContextKeyExpr | undefined, set: Set): void { if (exp) { for (let key of exp.keys()) { set.add(key); diff --git a/src/vs/workbench/services/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts similarity index 75% rename from src/vs/workbench/services/actions/common/menuService.ts rename to src/vs/platform/actions/common/menuService.ts index 9bdef6d4cdd..e5013e1854e 100644 --- a/src/vs/workbench/services/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -6,7 +6,6 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { Menu } from 'vs/platform/actions/common/menu'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ICommandService } from 'vs/platform/commands/common/commands'; export class MenuService implements IMenuService { @@ -14,13 +13,12 @@ export class MenuService implements IMenuService { _serviceBrand: any; constructor( - @IExtensionService private readonly _extensionService: IExtensionService, @ICommandService private readonly _commandService: ICommandService ) { // } createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu { - return new Menu(id, this._extensionService.whenInstalledExtensionsRegistered(), this._commandService, contextKeyService); + return new Menu(id, this._commandService, contextKeyService); } } diff --git a/src/vs/workbench/services/actions/test/common/menuService.test.ts b/src/vs/platform/actions/test/common/menuService.test.ts similarity index 94% rename from src/vs/workbench/services/actions/test/common/menuService.test.ts rename to src/vs/platform/actions/test/common/menuService.test.ts index 27d45ad36d3..28ec720b8c3 100644 --- a/src/vs/workbench/services/actions/test/common/menuService.test.ts +++ b/src/vs/platform/actions/test/common/menuService.test.ts @@ -2,18 +2,16 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { MenuRegistry, MenuId, isIMenuItem } from 'vs/platform/actions/common/actions'; -import { MenuService } from 'vs/workbench/services/actions/common/menuService'; +import { MenuService } from 'vs/platform/actions/common/menuService'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { NullCommandService } from 'vs/platform/commands/common/commands'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; -import { TestExtensionService } from 'vs/workbench/test/workbenchTestServices'; // --- service instances -const extensionService = new TestExtensionService(); - const contextKeyService = new class extends MockContextKeyService { contextMatchesRules() { return true; @@ -29,8 +27,8 @@ suite('MenuService', function () { let testMenuId: MenuId; setup(function () { - menuService = new MenuService(extensionService, NullCommandService); - testMenuId = new MenuId(); + menuService = new MenuService(NullCommandService); + testMenuId = Math.PI; disposables = []; }); diff --git a/src/vs/platform/backup/common/backup.ts b/src/vs/platform/backup/common/backup.ts index 201e8f42948..e1e5392e73a 100644 --- a/src/vs/platform/backup/common/backup.ts +++ b/src/vs/platform/backup/common/backup.ts @@ -21,6 +21,7 @@ export const IBackupMainService = createDecorator('backupMai export interface IEmptyWindowBackupInfo { backupFolder: string; + remoteAuthority?: string; } export interface IBackupMainService { diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index 4b647b93479..362719331e4 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -139,12 +139,13 @@ export class BackupMainService implements IBackupMainService { public registerEmptyWindowBackupSync(backupInfo: IEmptyWindowBackupInfo): string { let backupFolder = backupInfo.backupFolder; + let remoteAuthority = backupInfo.remoteAuthority; // Generate a new folder if this is a new empty workspace if (!backupFolder) { backupFolder = this.getRandomEmptyWindowId(); } if (!this.emptyWorkspaces.some(w => isEqual(w.backupFolder, backupFolder, !platform.isLinux))) { - this.emptyWorkspaces.push({ backupFolder }); + this.emptyWorkspaces.push({ backupFolder, remoteAuthority }); this.saveSync(); } return this.getBackupPath(backupFolder); diff --git a/src/vs/platform/commands/common/commands.ts b/src/vs/platform/commands/common/commands.ts index 7e115e47485..f2a0df759e1 100644 --- a/src/vs/platform/commands/common/commands.ts +++ b/src/vs/platform/commands/common/commands.ts @@ -6,7 +6,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { TypeConstraint, validateConstraints } from 'vs/base/common/types'; import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { LinkedList } from 'vs/base/common/linkedList'; export const ICommandService = createDecorator('commandService'); @@ -42,6 +42,7 @@ export interface ICommandHandlerDescription { } export interface ICommandRegistry { + onDidRegisterCommand: Event; registerCommand(id: string, command: ICommandHandler): IDisposable; registerCommand(command: ICommand): IDisposable; registerCommandAlias(oldId: string, newId: string): IDisposable; @@ -51,7 +52,10 @@ export interface ICommandRegistry { export const CommandsRegistry: ICommandRegistry = new class implements ICommandRegistry { - private _commands = new Map>(); + private readonly _commands = new Map>(); + + private readonly _onDidRegisterCommand = new Emitter(); + readonly onDidRegisterCommand: Event = this._onDidRegisterCommand.event; registerCommand(idOrCommand: string | ICommand, handler?: ICommandHandler): IDisposable { @@ -90,12 +94,17 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR let removeFn = commands.unshift(idOrCommand); - return toDisposable(() => { + let ret = toDisposable(() => { removeFn(); if (this._commands.get(id).isEmpty()) { this._commands.delete(id); } }); + + // tell the world about this command + this._onDidRegisterCommand.fire(id); + + return ret; } registerCommandAlias(oldId: string, newId: string): IDisposable { diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 8dbe9b65055..2bd8874037b 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -89,8 +89,8 @@ export interface IConfigurationService { inspect(key: string, overrides?: IConfigurationOverrides): { default: T, user: T, - workspace: T, - workspaceFolder: T, + workspace?: T, + workspaceFolder?: T, memory?: T, value: T, }; diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 78ac8b14af8..b332c2b18b3 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -249,8 +249,8 @@ export class ConfigurationModelParser { currentParent = previousParents.pop(); }, onLiteralValue: onValue, - onError: (error: json.ParseErrorCode) => { - parseErrors.push({ error: error }); + onError: (error: json.ParseErrorCode, offset: number, length: number) => { + parseErrors.push({ error, offset, length }); } }; if (content) { @@ -320,8 +320,8 @@ export class Configuration { inspect(key: string, overrides: IConfigurationOverrides, workspace: Workspace): { default: C, user: C, - workspace: C, - workspaceFolder: C + workspace?: C, + workspaceFolder?: C memory?: C value: C, } { @@ -344,7 +344,7 @@ export class Configuration { workspace: string[]; workspaceFolder: string[]; } { - const folderConfigurationModel = this.getFolderConfigurationModelForResource(null, workspace); + const folderConfigurationModel = this.getFolderConfigurationModelForResource(undefined, workspace); return { default: this._defaultConfiguration.freeze().keys, user: this._userConfiguration.freeze().keys, @@ -447,7 +447,7 @@ export class Configuration { return folderConsolidatedConfiguration; } - private getFolderConfigurationModelForResource(resource: URI, workspace: Workspace): ConfigurationModel { + private getFolderConfigurationModelForResource(resource: URI | undefined, workspace: Workspace): ConfigurationModel | null { if (workspace && resource) { const root = workspace.getFolder(resource); if (root) { diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/node/configurationService.ts index aaeceb09110..ff137c9d897 100644 --- a/src/vs/platform/configuration/node/configurationService.ts +++ b/src/vs/platform/configuration/node/configurationService.ts @@ -67,8 +67,8 @@ export class ConfigurationService extends Disposable implements IConfigurationSe inspect(key: string): { default: T, user: T, - workspace: T, - workspaceFolder: T + workspace?: T, + workspaceFolder?: T value: T } { return this.configuration.inspect(key, {}, null); @@ -89,7 +89,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe } private onDidChangeUserConfiguration(): void { - let changedKeys = []; + let changedKeys: string[] = []; const { added, updated, removed } = compare(this._configuration.user, this.userConfiguration.configurationModel); changedKeys = [...added, ...updated, ...removed]; if (changedKeys.length) { diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index fa5b0393498..236e4b6afa1 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Emitter, Event, debounceEvent } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; -import { IContextKey, IContext, IContextKeyServiceTarget, IContextKeyService, SET_CONTEXT_COMMAND_ID, ContextKeyExpr, IContextKeyChangeEvent, IReadableSet } from 'vs/platform/contextkey/common/contextkey'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { Event, Emitter, debounceEvent } from 'vs/base/common/event'; import { keys } from 'vs/base/common/map'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextKeyExpr, IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, SET_CONTEXT_COMMAND_ID } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context'; @@ -295,7 +295,7 @@ export abstract class AbstractContextKeyService implements IContextKeyService { } } - public getContext(target: IContextKeyServiceTarget): IContext { + public getContext(target: IContextKeyServiceTarget | null): IContext { if (this._isDisposed) { return NullContext.INSTANCE; } diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index f17884e4a14..aa9d6eb637c 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const enum ContextKeyExprType { Defined = 1, @@ -581,7 +581,7 @@ export interface IContextKeyService { getContextKeyValue(key: string): T | undefined; createScoped(target?: IContextKeyServiceTarget): IContextKeyService; - getContext(target: IContextKeyServiceTarget): IContext; + getContext(target: IContextKeyServiceTarget | null): IContext; } export const SET_CONTEXT_COMMAND_ID = 'setContext'; diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index e77179c4bec..12b0e88181c 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -7,7 +7,7 @@ import 'vs/css!./contextMenuHandler'; import { combinedDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { ActionRunner, IAction, IRunEvent } from 'vs/base/common/actions'; +import { ActionRunner, IRunEvent } from 'vs/base/common/actions'; import { Menu } from 'vs/base/browser/ui/menu/menu'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -50,58 +50,57 @@ export class ContextMenuHandler { } showContextMenu(delegate: IContextMenuDelegate): void { - delegate.getActions().then((actions: IAction[]) => { - if (!actions.length) { - return; // Don't render an empty context menu - } + const actions = delegate.getActions(); + if (!actions.length) { + return; // Don't render an empty context menu + } - this.focusToReturn = document.activeElement as HTMLElement; + this.focusToReturn = document.activeElement as HTMLElement; - this.contextViewService.showContextView({ - getAnchor: () => delegate.getAnchor(), - canRelayout: false, + this.contextViewService.showContextView({ + getAnchor: () => delegate.getAnchor(), + canRelayout: false, - render: (container) => { - this.menuContainerElement = container; + render: (container) => { + this.menuContainerElement = container; - let className = delegate.getMenuClassName ? delegate.getMenuClassName() : ''; + let className = delegate.getMenuClassName ? delegate.getMenuClassName() : ''; - if (className) { - container.className += ' ' + className; - } - - const menuDisposables: IDisposable[] = []; - - const actionRunner = delegate.actionRunner || new ActionRunner(); - actionRunner.onDidBeforeRun(this.onActionRun, this, menuDisposables); - actionRunner.onDidRun(this.onDidActionRun, this, menuDisposables); - - const menu = new Menu(container, actions, { - actionItemProvider: delegate.getActionItem, - context: delegate.getActionsContext ? delegate.getActionsContext() : null, - actionRunner, - getKeyBinding: delegate.getKeyBinding ? delegate.getKeyBinding : action => this.keybindingService.lookupKeybinding(action.id) - }); - - menuDisposables.push(attachMenuStyler(menu, this.themeService)); - - menu.onDidCancel(() => this.contextViewService.hideContextView(true), null, menuDisposables); - menu.onDidBlur(() => this.contextViewService.hideContextView(true), null, menuDisposables); - domEvent(window, EventType.BLUR)(() => { this.contextViewService.hideContextView(true); }, null, menuDisposables); - - menu.focus(!!delegate.autoSelectFirstItem); - - return combinedDisposable([...menuDisposables, menu]); - }, - - onHide: (didCancel?: boolean) => { - if (delegate.onHide) { - delegate.onHide(didCancel); - } - - this.menuContainerElement = null; + if (className) { + container.className += ' ' + className; } - }); + + const menuDisposables: IDisposable[] = []; + + const actionRunner = delegate.actionRunner || new ActionRunner(); + actionRunner.onDidBeforeRun(this.onActionRun, this, menuDisposables); + actionRunner.onDidRun(this.onDidActionRun, this, menuDisposables); + + const menu = new Menu(container, actions, { + actionItemProvider: delegate.getActionItem, + context: delegate.getActionsContext ? delegate.getActionsContext() : null, + actionRunner, + getKeyBinding: delegate.getKeyBinding ? delegate.getKeyBinding : action => this.keybindingService.lookupKeybinding(action.id) + }); + + menuDisposables.push(attachMenuStyler(menu, this.themeService)); + + menu.onDidCancel(() => this.contextViewService.hideContextView(true), null, menuDisposables); + menu.onDidBlur(() => this.contextViewService.hideContextView(true), null, menuDisposables); + domEvent(window, EventType.BLUR)(() => { this.contextViewService.hideContextView(true); }, null, menuDisposables); + + menu.focus(!!delegate.autoSelectFirstItem); + + return combinedDisposable([...menuDisposables, menu]); + }, + + onHide: (didCancel?: boolean) => { + if (delegate.onHide) { + delegate.onHide(didCancel); + } + + this.menuContainerElement = null; + } }); } @@ -152,4 +151,4 @@ export class ContextMenuHandler { dispose(): void { this.setContainer(null); } -} \ No newline at end of file +} diff --git a/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts b/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts index e16dc49b2e2..94cba280d74 100644 --- a/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts @@ -85,10 +85,10 @@ export class DiagnosticsService implements IDiagnosticsService { getPerformanceInfo(info: IMainProcessInfo): Promise { return listProcesses(info.mainPID).then(rootProcess => { - const workspaceInfoMessages = []; + const workspaceInfoMessages: string[] = []; // Workspace Stats - const workspaceStatPromises = []; + const workspaceStatPromises: Promise[] = []; if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0)) { info.windows.forEach(window => { if (window.folderURIs.length === 0) { @@ -173,7 +173,7 @@ export class DiagnosticsService implements IDiagnosticsService { console.log(this.formatProcessList(info, rootProcess)); // Workspace Stats - const workspaceStatPromises = []; + const workspaceStatPromises: Promise[] = []; if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0)) { console.log(''); console.log('Workspace Stats: '); diff --git a/src/vs/platform/dialogs/node/dialogIpc.ts b/src/vs/platform/dialogs/node/dialogIpc.ts index 035f714fdbf..07be979e3b3 100644 --- a/src/vs/platform/dialogs/node/dialogIpc.ts +++ b/src/vs/platform/dialogs/node/dialogIpc.ts @@ -25,8 +25,8 @@ export class DialogChannel implements IDialogChannel { call(command: string, args?: any[]): Thenable { switch (command) { - case 'show': return this.dialogService.show(args[0], args[1], args[2]); - case 'confirm': return this.dialogService.confirm(args[0]); + case 'show': return this.dialogService.show(args![0], args![1], args![2]); + case 'confirm': return this.dialogService.confirm(args![0]); } return TPromise.wrapError(new Error('invalid command')); } diff --git a/src/vs/platform/dialogs/node/dialogService.ts b/src/vs/platform/dialogs/node/dialogService.ts index 4304af55184..cf04c7608e7 100644 --- a/src/vs/platform/dialogs/node/dialogService.ts +++ b/src/vs/platform/dialogs/node/dialogService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as readline from 'readline'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { localize } from 'vs/nls'; @@ -14,8 +13,8 @@ export class CommandLineDialogService implements IDialogService { _serviceBrand: any; - show(severity: Severity, message: string, options: string[]): TPromise { - const promise = new TPromise((c, e) => { + show(severity: Severity, message: string, options: string[]): Promise { + const promise = new Promise((c, e) => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, @@ -58,8 +57,8 @@ export class CommandLineDialogService implements IDialogService { return -1; } - confirm(confirmation: IConfirmation): TPromise { - return this.show(Severity.Info, confirmation.message, [confirmation.primaryButton, confirmation.secondaryButton || localize('cancel', "Cancel")]).then(index => { + confirm(confirmation: IConfirmation): Promise { + return this.show(Severity.Info, confirmation.message, [confirmation.primaryButton || localize('ok', "Ok"), confirmation.secondaryButton || localize('cancel', "Cancel")]).then(index => { return { confirmed: index === 0 } as IConfirmationResult; diff --git a/src/vs/platform/driver/electron-browser/driver.ts b/src/vs/platform/driver/electron-browser/driver.ts index f09c8b4b9ec..7847ac043a0 100644 --- a/src/vs/platform/driver/electron-browser/driver.ts +++ b/src/vs/platform/driver/electron-browser/driver.ts @@ -22,7 +22,7 @@ function serializeElement(element: Element, recursive: boolean): IElement { attributes[attr.name] = attr.value; } - const children = []; + const children: IElement[] = []; if (recursive) { for (let i = 0; i < element.children.length; i++) { @@ -119,7 +119,7 @@ class WindowDriver implements IWindowDriver { const element = document.querySelector(selector); if (element !== document.activeElement) { - const chain = []; + const chain: string[] = []; let el = document.activeElement; while (el) { diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index f308e1471f4..ff392b24669 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -109,7 +109,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { const resolvedKeybinding = new USLayoutResolvedKeybinding(noModifiedKeybinding, OS); const keyCode = resolvedKeybinding.getElectronAccelerator(); - const modifiers = []; + const modifiers: string[] = []; if (keybinding.ctrlKey) { modifiers.push('ctrl'); diff --git a/src/vs/platform/driver/node/driver.ts b/src/vs/platform/driver/node/driver.ts index d8e213518b4..9ac264e01f4 100644 --- a/src/vs/platform/driver/node/driver.ts +++ b/src/vs/platform/driver/node/driver.ts @@ -86,7 +86,7 @@ export class DriverChannel implements IDriverChannel { case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1], arg[2]); } - return undefined; + throw new Error(`Call not found: ${command}`); } } @@ -178,7 +178,7 @@ export class WindowDriverRegistryChannel implements IWindowDriverRegistryChannel case 'reloadWindowDriver': return this.registry.reloadWindowDriver(arg); } - return undefined; + throw new Error(`Call not found: ${command}`); } } @@ -243,7 +243,7 @@ export class WindowDriverChannel implements IWindowDriverChannel { case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1]); } - return undefined; + throw new Error(`Call not found: ${command}`); } } diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index c6015a9bb53..d2478b8bc2d 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -35,7 +35,6 @@ export interface ParsedArgs { 'trace-options'?: string; log?: string; logExtensionHostCommunication?: boolean; - logStorage?: boolean; 'extensions-dir'?: string; 'builtin-extensions-dir'?: string; extensionDevelopmentPath?: string; @@ -69,6 +68,7 @@ export interface ParsedArgs { 'upload-logs'?: string; 'driver'?: string; 'driver-verbose'?: boolean; + remote?: string; } export const IEnvironmentService = createDecorator('environmentService'); @@ -103,6 +103,8 @@ export interface IEnvironmentService { settingsSearchBuildId: number; settingsSearchUrl: string; + workspaceStorageHome: string; + backupHome: string; backupWorkspacesPath: string; @@ -119,7 +121,6 @@ export interface IEnvironmentService { debugSearch: IDebugParams; logExtensionHostCommunication: boolean; - logStorage: boolean; isBuilt: boolean; wait: boolean; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 3618a0b25c8..f61de15990b 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -20,6 +20,7 @@ const options: minimist.Opts = { 'extensions-dir', 'folder-uri', 'file-uri', + 'remote', 'extensionDevelopmentPath', 'extensionTestsPath', 'install-extension', @@ -54,7 +55,6 @@ const options: minimist.Opts = { 'prof-startup', 'verbose', 'logExtensionHostCommunication', - 'logStorage', 'disable-extensions', 'list-extensions', 'show-versions', diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index ab1f9329432..64320564d5b 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -109,6 +109,9 @@ export class EnvironmentService implements IEnvironmentService { @memoize get appSettingsPath(): string { return path.join(this.appSettingsHome, 'settings.json'); } + @memoize + get workspaceStorageHome(): string { return path.join(this.appSettingsHome, 'workspaceStorage'); } + @memoize get settingsSearchBuildId(): number { return product.settingsSearchBuildId; } @@ -208,7 +211,6 @@ export class EnvironmentService implements IEnvironmentService { get wait(): boolean { return this._args.wait; } get logExtensionHostCommunication(): boolean { return this._args.logExtensionHostCommunication; } - get logStorage(): boolean { return this._args.logStorage; } get performance(): boolean { return this._args.performance; } get status(): boolean { return this._args.status; } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 7e7e490f0d7..79d170ada8a 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -300,6 +300,9 @@ export interface DidUninstallExtensionEvent { error?: string; } +export const INSTALL_ERROR_MALICIOUS = 'malicious'; +export const INSTALL_ERROR_INCOMPATIBLE = 'incompatible'; + export interface IExtensionManagementService { _serviceBrand: any; @@ -330,9 +333,9 @@ export interface IExtensionManagementServer { export interface IExtensionManagementServerService { _serviceBrand: any; - readonly extensionManagementServers: IExtensionManagementServer[]; - getLocalExtensionManagementServer(): IExtensionManagementServer; - getExtensionManagementServer(location: URI): IExtensionManagementServer; + readonly localExtensionManagementServer: IExtensionManagementServer | null; + readonly otherExtensionManagementServer: IExtensionManagementServer | null; + getExtensionManagementServer(location: URI): IExtensionManagementServer | null; } export const enum EnablementState { diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index 6930c70087b..240c532f9a2 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -628,8 +628,8 @@ export class ExtensionGalleryService implements IExtensionGalleryService { .withFilter(FilterType.ExtensionName, ...extensionNames); return this.queryGallery(query, token).then(result => { - const dependencies = []; - const ids = []; + const dependencies: IGalleryExtension[] = []; + const ids: string[] = []; for (let index = 0; index < result.galleryExtensions.length; index++) { const rawExtension = result.galleryExtensions[index]; diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 42dfd9148b2..afaee154cc8 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -19,12 +19,14 @@ import { StatisticType, IExtensionIdentifier, IReportedExtension, - InstallOperation + InstallOperation, + INSTALL_ERROR_MALICIOUS, + INSTALL_ERROR_INCOMPATIBLE } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localizeManifest } from '../common/extensionNls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { Limiter, always, createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; +import { Limiter, always, createCancelablePromise, CancelablePromise, Queue } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import * as semver from 'semver'; import { URI } from 'vs/base/common/uri'; @@ -48,7 +50,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled'; -const INSTALL_ERROR_INCOMPATIBLE = 'incompatible'; const INSTALL_ERROR_DOWNLOADING = 'downloading'; const INSTALL_ERROR_VALIDATING = 'validating'; const INSTALL_ERROR_GALLERY = 'gallery'; @@ -56,7 +57,6 @@ const INSTALL_ERROR_LOCAL = 'local'; const INSTALL_ERROR_EXTRACTING = 'extracting'; const INSTALL_ERROR_RENAMING = 'renaming'; const INSTALL_ERROR_DELETING = 'deleting'; -const INSTALL_ERROR_MALICIOUS = 'malicious'; const ERROR_UNKNOWN = 'unknown'; export class ExtensionManagementError extends Error { @@ -114,7 +114,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private systemExtensionsPath: string; private extensionsPath: string; private uninstalledPath: string; - private uninstalledFileLimiter: Limiter; + private uninstalledFileLimiter: Queue; private reportedExtensions: Promise | undefined; private lastReportTimestamp = 0; private readonly installingExtensions: Map> = new Map>(); @@ -146,7 +146,7 @@ export class ExtensionManagementService extends Disposable implements IExtension this.systemExtensionsPath = environmentService.builtinExtensionsPath; this.extensionsPath = environmentService.extensionsPath; this.uninstalledPath = path.join(this.extensionsPath, '.obsolete'); - this.uninstalledFileLimiter = new Limiter(1); + this.uninstalledFileLimiter = new Queue(); this.manifestCache = this._register(new ExtensionsManifestCache(environmentService, this)); this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService)); @@ -207,7 +207,7 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(manifest => { const identifier = { id: getLocalExtensionIdFromManifest(manifest) }; if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode)) { - return Promise.reject(new Error(nls.localize('incompatible', "Unable to install Extension '{0}' as it is not compatible with Code '{1}'.", identifier.id, pkg.version))); + return Promise.reject(new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", identifier.id, pkg.version))); } return this.removeIfExists(identifier.id) .then( @@ -229,7 +229,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } return null; }), - e => Promise.reject(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", manifest.displayName || manifest.name)))); + e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); }); }); }); @@ -330,6 +330,9 @@ export class ExtensionManagementService extends Disposable implements IExtension this.logService.error(`Failed to install extension:`, extension.identifier.id, error ? error.message : errorCode); this._onDidInstallExtension.fire({ identifier, gallery: extension, operation, error: errorCode }); this.reportTelemetry(this.getTelemetryEvent(operation), telemetryData, new Date().getTime() - startTime, error); + if (error instanceof Error) { + error.name = errorCode; + } errorCallback(error); }); @@ -404,7 +407,7 @@ export class ExtensionManagementService extends Disposable implements IExtension }, error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING))); } else { - return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the depending extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE)); + return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE)); } }, error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY))); @@ -655,7 +658,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return []; } const packedExtensions = installed.filter(i => extension.manifest.extensionPack.some(id => areSameExtensions({ id }, i.galleryIdentifier))); - const packOfPackedExtensions = []; + const packOfPackedExtensions: ILocalExtension[] = []; for (const packedExtension of packedExtensions) { packOfPackedExtensions.push(...this.getAllPackExtensionsToUninstall(packedExtension, installed, checked)); } @@ -758,7 +761,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } private scanExtension(folderName: string, root: string, type: LocalExtensionType): Promise { - if (type === LocalExtensionType.User && folderName.indexOf('.') === 0) { // Do not consider user exension folder starting with `.` + if (type === LocalExtensionType.User && folderName.indexOf('.') === 0) { // Do not consider user extension folder starting with `.` return Promise.resolve(null); } const extensionPath = path.join(root, folderName); @@ -827,7 +830,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private filterUninstalled(...ids: string[]): Promise { return this.withUninstalledExtensions(allUninstalled => { - const uninstalled = []; + const uninstalled: string[] = []; for (const id of ids) { if (!!allUninstalled[id]) { uninstalled.push(id); diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 514c81e594e..748ba2a9072 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -46,10 +46,15 @@ export interface IFileService { onDidChangeFileSystemProviderRegistrations: Event; /** - * Registeres a file system provider for a certain scheme. + * Registers a file system provider for a certain scheme. */ registerProvider(scheme: string, provider: IFileSystemProvider): IDisposable; + /** + * Tries to activate a provider with the given scheme. + */ + activateProvider(scheme: string): Thenable; + /** * Checks if this file service can handle the given resource. */ @@ -69,13 +74,13 @@ export interface IFileService { resolveFile(resource: URI, options?: IResolveFileOptions): TPromise; /** - * Same as resolveFile but supports resolving mulitple resources in parallel. + * Same as resolveFile but supports resolving multiple resources in parallel. * If one of the resolve targets fails to resolve returns a fake IFileStat instead of making the whole call fail. */ resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): TPromise; /** - *Finds out if a file identified by the resource exists. + * Finds out if a file identified by the resource exists. */ existsFile(resource: URI): TPromise; diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index fdce961c3aa..209ed98e2b5 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -240,7 +240,7 @@ export class HistoryMainService implements IHistoryMainService { // Add currently files to open to the beginning if any if (currentFiles) { - files.unshift(...currentFiles.map(f => f.fileUri)); + files.unshift(...arrays.coalesce(currentFiles.map(f => f.fileUri))); } // Clear those dupes @@ -374,7 +374,8 @@ export class HistoryMainService implements IHistoryMainService { let description; let args; if (isSingleFolderWorkspaceIdentifier(workspace)) { - description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), this.labelService.getUriLabel(dirname(workspace))); + const parentFolder = dirname(workspace); + description = parentFolder ? nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), this.labelService.getUriLabel(parentFolder)) : getBaseLabel(workspace); args = `--folder-uri "${workspace.toString()}"`; } else { description = nls.localize('codeWorkspace', "Code Workspace"); diff --git a/src/vs/platform/instantiation/common/descriptors.ts b/src/vs/platform/instantiation/common/descriptors.ts index 9ad8ad5b482..658330c23cb 100644 --- a/src/vs/platform/instantiation/common/descriptors.ts +++ b/src/vs/platform/instantiation/common/descriptors.ts @@ -5,15 +5,16 @@ import * as instantiation from './instantiation'; - export class SyncDescriptor { readonly ctor: any; readonly staticArguments: any[]; + readonly supportsDelayedInstantiation: boolean; - constructor(ctor: new (...args: any[]) => T, ..._staticArguments: any[]) { + constructor(ctor: new (...args: any[]) => T, staticArguments: any[] = [], supportsDelayedInstantiation: boolean = false) { this.ctor = ctor; - this.staticArguments = _staticArguments; + this.staticArguments = staticArguments; + this.supportsDelayedInstantiation = supportsDelayedInstantiation; } } @@ -74,7 +75,7 @@ export interface CreateSyncFunc { (ctor: instantiation.IConstructorSignature8, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8): SyncDescriptor0; } export const createSyncDescriptor: CreateSyncFunc = (ctor: any, ...staticArguments: any[]): any => { - return new SyncDescriptor(ctor, ...staticArguments); + return new SyncDescriptor(ctor, staticArguments); }; export interface SyncDescriptor0 { diff --git a/src/vs/platform/instantiation/common/extensions.ts b/src/vs/platform/instantiation/common/extensions.ts index 5ff44b2f291..5ecd792c518 100644 --- a/src/vs/platform/instantiation/common/extensions.ts +++ b/src/vs/platform/instantiation/common/extensions.ts @@ -14,8 +14,8 @@ export interface IServiceContribution { const _registry: IServiceContribution[] = []; -export function registerSingleton(id: ServiceIdentifier, ctor: IConstructorSignature0): void { - _registry.push({ id, descriptor: new SyncDescriptor(ctor) }); +export function registerSingleton(id: ServiceIdentifier, ctor: IConstructorSignature0, supportsDelayedInstantiation?: boolean): void { + _registry.push({ id, descriptor: new SyncDescriptor(ctor, [], supportsDelayedInstantiation) }); } export function getServices(): IServiceContribution[] { diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index 2a8a4fe604d..b1b28925e47 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -186,7 +186,7 @@ export class InstantiationService implements IInstantiationService { for (let { data } of roots) { // create instance and overwrite the service collections - const instance = this._createServiceInstanceWithOwner(data.id, data.desc.ctor, data.desc.staticArguments, data._trace); + const instance = this._createServiceInstanceWithOwner(data.id, data.desc.ctor, data.desc.staticArguments, false, data._trace); this._setServiceInstance(data.id, instance); graph.removeNode(data); } @@ -195,17 +195,17 @@ export class InstantiationService implements IInstantiationService { return this._getServiceInstanceOrDescriptor(id); } - private _createServiceInstanceWithOwner(id: ServiceIdentifier, ctor: any, args: any[] = [], _trace: Trace): T { + private _createServiceInstanceWithOwner(id: ServiceIdentifier, ctor: any, args: any[] = [], supportsDelayedInstantiation: boolean, _trace: Trace): T { if (this._services.get(id) instanceof SyncDescriptor) { - return this._createServiceInstance(ctor, args, _trace); + return this._createServiceInstance(ctor, args, supportsDelayedInstantiation, _trace); } else if (this._parent) { - return this._parent._createServiceInstanceWithOwner(id, ctor, args, _trace); + return this._parent._createServiceInstanceWithOwner(id, ctor, args, supportsDelayedInstantiation, _trace); } else { throw new Error('illegalState - creating UNKNOWN service instance'); } } - protected _createServiceInstance(ctor: any, args: any[] = [], _trace: Trace): T { + protected _createServiceInstance(ctor: any, args: any[] = [], supportsDelayedInstantiation: boolean, _trace: Trace): T { return this._createInstance(ctor, args, _trace); } } diff --git a/src/vs/platform/instantiation/node/instantiationService.ts b/src/vs/platform/instantiation/node/instantiationService.ts index a1e1a5ad7ad..c888491b365 100644 --- a/src/vs/platform/instantiation/node/instantiationService.ts +++ b/src/vs/platform/instantiation/node/instantiationService.ts @@ -16,8 +16,12 @@ export class InstantiationService extends BaseInstantiationService { return new InstantiationService(services, this._strict, this); } - protected _createServiceInstance(ctor: any, args: any[] = [], _trace): T { - return InstantiationService._newIdleProxyService(() => super._createServiceInstance(ctor, args, _trace)); + protected _createServiceInstance(ctor: any, args: any[] = [], supportsDelayedInstantiation: boolean, _trace): T { + if (supportsDelayedInstantiation) { + return InstantiationService._newIdleProxyService(() => super._createServiceInstance(ctor, args, supportsDelayedInstantiation, _trace)); + } else { + return super._createServiceInstance(ctor, args, supportsDelayedInstantiation, _trace); + } } private static _newIdleProxyService(executor: () => T): T { diff --git a/src/vs/platform/integrity/common/integrity.ts b/src/vs/platform/integrity/common/integrity.ts index 113020c25d3..e326da2bac3 100644 --- a/src/vs/platform/integrity/common/integrity.ts +++ b/src/vs/platform/integrity/common/integrity.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IIntegrityService = createDecorator('integrityService'); diff --git a/src/vs/platform/integrity/node/integrityServiceImpl.ts b/src/vs/platform/integrity/node/integrityServiceImpl.ts index 94d46ea2906..670230c6d4e 100644 --- a/src/vs/platform/integrity/node/integrityServiceImpl.ts +++ b/src/vs/platform/integrity/node/integrityServiceImpl.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as fs from 'fs'; import * as crypto from 'crypto'; -import { IIntegrityService, IntegrityTestResult, ChecksumPair } from 'vs/platform/integrity/common/integrity'; -import product from 'vs/platform/node/product'; -import { URI } from 'vs/base/common/uri'; +import * as fs from 'fs'; import Severity from 'vs/base/common/severity'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { URI } from 'vs/base/common/uri'; +import { ChecksumPair, IIntegrityService, IntegrityTestResult } from 'vs/platform/integrity/common/integrity'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import product from 'vs/platform/node/product'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; interface IStorageData { dontShowPrompt: boolean; diff --git a/src/vs/platform/issue/common/issue.ts b/src/vs/platform/issue/common/issue.ts index 62734a94f86..774a737636e 100644 --- a/src/vs/platform/issue/common/issue.ts +++ b/src/vs/platform/issue/common/issue.ts @@ -3,9 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; export const IIssueService = createDecorator('issueService'); @@ -43,9 +41,20 @@ export interface IssueReporterStyles extends WindowStyles { sliderActiveColor: string; } +export interface IssueReporterExtensionData { + name: string; + publisher: string; + version: string; + id: string; + isTheme: boolean; + displayName: string | undefined; + repositoryUrl: string | undefined; + bugsUrl: string | undefined; +} + export interface IssueReporterData extends WindowData { styles: IssueReporterStyles; - enabledExtensions: ILocalExtension[]; + enabledExtensions: IssueReporterExtensionData[]; issueType?: IssueType; } @@ -78,6 +87,6 @@ export interface ProcessExplorerData extends WindowData { export interface IIssueService { _serviceBrand: any; - openReporter(data: IssueReporterData): TPromise; - openProcessExplorer(data: ProcessExplorerData): TPromise; + openReporter(data: IssueReporterData): Promise; + openProcessExplorer(data: ProcessExplorerData): Promise; } diff --git a/src/vs/platform/issue/electron-main/issueService.ts b/src/vs/platform/issue/electron-main/issueService.ts index 1cb9f623661..a63f2ed1984 100644 --- a/src/vs/platform/issue/electron-main/issueService.ts +++ b/src/vs/platform/issue/electron-main/issueService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise, Promise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import * as objects from 'vs/base/common/objects'; import { parseArgs } from 'vs/platform/environment/node/argv'; @@ -15,14 +14,15 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowState } from 'vs/platform/windows/electron-main/windows'; const DEFAULT_BACKGROUND_COLOR = '#1E1E1E'; export class IssueService implements IIssueService { _serviceBrand: any; - _issueWindow: BrowserWindow; + _issueWindow: BrowserWindow | null; _issueParentWindow: BrowserWindow; - _processExplorerWindow: BrowserWindow; + _processExplorerWindow: BrowserWindow | null; constructor( private machineId: string, @@ -73,102 +73,102 @@ export class IssueService implements IIssueService { } - openReporter(data: IssueReporterData): TPromise { - this._issueParentWindow = BrowserWindow.getFocusedWindow(); - const position = this.getWindowPosition(this._issueParentWindow, 700, 800); - if (!this._issueWindow) { - this._issueWindow = new BrowserWindow({ - width: position.width, - height: position.height, - minWidth: 300, - minHeight: 200, - x: position.x, - y: position.y, - title: localize('issueReporter', "Issue Reporter"), - backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR - }); + openReporter(data: IssueReporterData): Promise { + return new Promise(_ => { + this._issueParentWindow = BrowserWindow.getFocusedWindow(); + const position = this.getWindowPosition(this._issueParentWindow, 700, 800); + if (!this._issueWindow) { + this._issueWindow = new BrowserWindow({ + width: position.width, + height: position.height, + minWidth: 300, + minHeight: 200, + x: position.x, + y: position.y, + title: localize('issueReporter', "Issue Reporter"), + backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR + }); - this._issueWindow.setMenuBarVisibility(false); // workaround for now, until a menu is implemented + this._issueWindow.setMenuBarVisibility(false); // workaround for now, until a menu is implemented - // Modified when testing UI - const features: IssueReporterFeatures = {}; + // Modified when testing UI + const features: IssueReporterFeatures = {}; - this.logService.trace('issueService#openReporter: opening issue reporter'); - this._issueWindow.loadURL(this.getIssueReporterPath(data, features)); + this.logService.trace('issueService#openReporter: opening issue reporter'); + this._issueWindow.loadURL(this.getIssueReporterPath(data, features)); - this._issueWindow.on('close', () => this._issueWindow = null); + this._issueWindow.on('close', () => this._issueWindow = null); - this._issueParentWindow.on('closed', () => { - if (this._issueWindow) { - this._issueWindow.close(); - this._issueWindow = null; - } - }); - } - - this._issueWindow.focus(); - - return TPromise.as(null); - } - - openProcessExplorer(data: ProcessExplorerData): TPromise { - // Create as singleton - if (!this._processExplorerWindow) { - const parentWindow = BrowserWindow.getFocusedWindow(); - const position = this.getWindowPosition(parentWindow, 800, 300); - this._processExplorerWindow = new BrowserWindow({ - skipTaskbar: true, - resizable: true, - width: position.width, - height: position.height, - minWidth: 300, - minHeight: 200, - x: position.x, - y: position.y, - backgroundColor: data.styles.backgroundColor, - title: localize('processExplorer', "Process Explorer") - }); - - this._processExplorerWindow.setMenuBarVisibility(false); - - const windowConfiguration = { - appRoot: this.environmentService.appRoot, - nodeCachedDataDir: this.environmentService.nodeCachedDataDir, - windowId: this._processExplorerWindow.id, - userEnv: this.userEnv, - machineId: this.machineId, - data - }; - - const environment = parseArgs(process.argv); - const config = objects.assign(environment, windowConfiguration); - for (let key in config) { - if (config[key] === void 0 || config[key] === null || config[key] === '') { - delete config[key]; // only send over properties that have a true value - } + this._issueParentWindow.on('closed', () => { + if (this._issueWindow) { + this._issueWindow.close(); + this._issueWindow = null; + } + }); } - this._processExplorerWindow.loadURL(`${require.toUrl('vs/code/electron-browser/processExplorer/processExplorer.html')}?config=${encodeURIComponent(JSON.stringify(config))}`); - - this._processExplorerWindow.on('close', () => this._processExplorerWindow = void 0); - - parentWindow.on('close', () => { - if (this._processExplorerWindow) { - this._processExplorerWindow.close(); - this._processExplorerWindow = null; - } - }); - } - - // Focus - this._processExplorerWindow.focus(); - - return TPromise.as(null); + this._issueWindow.focus(); + }); } - private getWindowPosition(parentWindow: BrowserWindow, defaultWidth: number, defaultHeight: number) { + openProcessExplorer(data: ProcessExplorerData): Promise { + return new Promise(_ => { + // Create as singleton + if (!this._processExplorerWindow) { + const parentWindow = BrowserWindow.getFocusedWindow(); + const position = this.getWindowPosition(parentWindow, 800, 300); + this._processExplorerWindow = new BrowserWindow({ + skipTaskbar: true, + resizable: true, + width: position.width, + height: position.height, + minWidth: 300, + minHeight: 200, + x: position.x, + y: position.y, + backgroundColor: data.styles.backgroundColor, + title: localize('processExplorer', "Process Explorer") + }); + + this._processExplorerWindow.setMenuBarVisibility(false); + + const windowConfiguration = { + appRoot: this.environmentService.appRoot, + nodeCachedDataDir: this.environmentService.nodeCachedDataDir, + windowId: this._processExplorerWindow.id, + userEnv: this.userEnv, + machineId: this.machineId, + data + }; + + const environment = parseArgs(process.argv); + const config = objects.assign(environment, windowConfiguration); + for (let key in config) { + if (config[key] === void 0 || config[key] === null || config[key] === '') { + delete config[key]; // only send over properties that have a true value + } + } + + this._processExplorerWindow.loadURL(`${require.toUrl('vs/code/electron-browser/processExplorer/processExplorer.html')}?config=${encodeURIComponent(JSON.stringify(config))}`); + + this._processExplorerWindow.on('close', () => this._processExplorerWindow = null); + + parentWindow.on('close', () => { + if (this._processExplorerWindow) { + this._processExplorerWindow.close(); + this._processExplorerWindow = null; + } + }); + } + + // Focus + this._processExplorerWindow.focus(); + }); + } + + private getWindowPosition(parentWindow: BrowserWindow, defaultWidth: number, defaultHeight: number): IWindowState { // We want the new window to open on the same display that the parent is in - let displayToUse: Electron.Display; + let displayToUse: Electron.Display | undefined; const displays = screen.getAllDisplays(); // Single Display @@ -196,16 +196,14 @@ export class IssueService implements IIssueService { } } - let state = { + const state: IWindowState = { width: defaultWidth, - height: defaultHeight, - x: undefined, - y: undefined + height: defaultHeight }; const displayBounds = displayToUse.bounds; - state.x = displayBounds.x + (displayBounds.width / 2) - (state.width / 2); - state.y = displayBounds.y + (displayBounds.height / 2) - (state.height / 2); + state.x = displayBounds.x + (displayBounds.width / 2) - (state.width! / 2); + state.y = displayBounds.y + (displayBounds.height / 2) - (state.height! / 2); if (displayBounds.width > 0 && displayBounds.height > 0 /* Linux X11 sessions sometimes report wrong display bounds */) { if (state.x < displayBounds.x) { @@ -224,11 +222,11 @@ export class IssueService implements IIssueService { state.y = displayBounds.y; // prevent window from falling out of the screen to the bottom } - if (state.width > displayBounds.width) { + if (state.width! > displayBounds.width) { state.width = displayBounds.width; // prevent window from exceeding display bounds width } - if (state.height > displayBounds.height) { + if (state.height! > displayBounds.height) { state.height = displayBounds.height; // prevent window from exceeding display bounds height } } @@ -236,7 +234,7 @@ export class IssueService implements IIssueService { return state; } - private getSystemInformation(): TPromise { + private getSystemInformation(): Promise { return new Promise((resolve, reject) => { this.launchService.getMainProcessInfo().then(info => { resolve(this.diagnosticsService.getSystemInfo(info)); @@ -244,7 +242,7 @@ export class IssueService implements IIssueService { }); } - private getPerformanceInfo(): TPromise { + private getPerformanceInfo(): Promise { return new Promise((resolve, reject) => { this.launchService.getMainProcessInfo().then(info => { this.diagnosticsService.getPerformanceInfo(info) @@ -259,7 +257,11 @@ export class IssueService implements IIssueService { }); } - private getIssueReporterPath(data: IssueReporterData, features: IssueReporterFeatures) { + private getIssueReporterPath(data: IssueReporterData, features: IssueReporterFeatures): string { + if (!this._issueWindow) { + throw new Error('Issue window has been disposed'); + } + const windowConfiguration = { appRoot: this.environmentService.appRoot, nodeCachedDataDir: this.environmentService.nodeCachedDataDir, diff --git a/src/vs/platform/issue/node/issueIpc.ts b/src/vs/platform/issue/node/issueIpc.ts index 9692c67935b..604c527ac29 100644 --- a/src/vs/platform/issue/node/issueIpc.ts +++ b/src/vs/platform/issue/node/issueIpc.ts @@ -3,15 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { IIssueService, IssueReporterData, ProcessExplorerData } from '../common/issue'; import { Event } from 'vs/base/common/event'; export interface IIssueChannel extends IChannel { - call(command: 'openIssueReporter', arg: IssueReporterData): TPromise; - call(command: 'getStatusInfo'): TPromise; - call(command: string, arg?: any): TPromise; + call(command: 'openIssueReporter', arg: IssueReporterData): Promise; + call(command: 'getStatusInfo'): Promise; + call(command: string, arg?: any): Promise; } export class IssueChannel implements IIssueChannel { @@ -19,17 +18,18 @@ export class IssueChannel implements IIssueChannel { constructor(private service: IIssueService) { } listen(event: string): Event { - throw new Error('No event found'); + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): TPromise { + call(command: string, arg?: any): Promise { switch (command) { case 'openIssueReporter': return this.service.openReporter(arg); case 'openProcessExplorer': return this.service.openProcessExplorer(arg); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -39,11 +39,11 @@ export class IssueChannelClient implements IIssueService { constructor(private channel: IIssueChannel) { } - openReporter(data: IssueReporterData): TPromise { + openReporter(data: IssueReporterData): Promise { return this.channel.call('openIssueReporter', data); } - openProcessExplorer(data: ProcessExplorerData): TPromise { + openProcessExplorer(data: ProcessExplorerData): Promise { return this.channel.call('openProcessExplorer', data); } } \ No newline at end of file diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index 8744626c339..78d71449bec 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -5,18 +5,18 @@ import * as nls from 'vs/nls'; import * as arrays from 'vs/base/common/arrays'; -import { ResolvedKeybinding, Keybinding, KeyCode } from 'vs/base/common/keyCodes'; -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { KeybindingResolver, IResolveResult } from 'vs/platform/keybinding/common/keybindingResolver'; -import { IKeybindingEvent, IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; -import { IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { IntervalTimer } from 'vs/base/common/async'; +import { Emitter, Event } from 'vs/base/common/event'; +import { KeyCode, Keybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; +import { IKeybindingEvent, IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { IResolveResult, KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; interface CurrentChord { keypress: string; diff --git a/src/vs/platform/keybinding/common/keybinding.ts b/src/vs/platform/keybinding/common/keybinding.ts index f27e1ea58d5..078cea23ede 100644 --- a/src/vs/platform/keybinding/common/keybinding.ts +++ b/src/vs/platform/keybinding/common/keybinding.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResolvedKeybinding, Keybinding, KeyCode } from 'vs/base/common/keyCodes'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Event } from 'vs/base/common/event'; +import { KeyCode, Keybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IResolveResult } from 'vs/platform/keybinding/common/keybindingResolver'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; -import { Event } from 'vs/base/common/event'; export interface IUserFriendlyKeybinding { key: string; diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 3aa1abb7517..e09852b56b9 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { isFalsyOrEmpty } from 'vs/base/common/arrays'; -import { ContextKeyExpr, IContext, ContextKeyAndExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; -import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { MenuRegistry } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; +import { ContextKeyAndExpr, ContextKeyExpr, IContext } from 'vs/platform/contextkey/common/contextkey'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; export interface IResolveResult { enterChord: boolean; diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index 42e32af9215..5f9e5191b54 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SimpleKeybinding, KeyCode, KeybindingType, createKeybinding, Keybinding } from 'vs/base/common/keyCodes'; +import { KeyCode, Keybinding, KeybindingType, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; import { OS, OperatingSystem } from 'vs/base/common/platform'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { CommandsRegistry, ICommandHandler, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; export interface IKeybindingItem { diff --git a/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts b/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts index c1ab03d4762..173456244ac 100644 --- a/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts +++ b/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CharCode } from 'vs/base/common/charCode'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { CharCode } from 'vs/base/common/charCode'; export class ResolvedKeybindingItem { _resolvedKeybindingItemBrand: void; diff --git a/src/vs/platform/keybinding/common/usLayoutResolvedKeybinding.ts b/src/vs/platform/keybinding/common/usLayoutResolvedKeybinding.ts index ec7a41766d2..e55a2961797 100644 --- a/src/vs/platform/keybinding/common/usLayoutResolvedKeybinding.ts +++ b/src/vs/platform/keybinding/common/usLayoutResolvedKeybinding.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResolvedKeybinding, ResolvedKeybindingPart, KeyCode, KeyCodeUtils, Keybinding, KeybindingType, SimpleKeybinding } from 'vs/base/common/keyCodes'; -import { UILabelProvider, AriaLabelProvider, ElectronAcceleratorLabelProvider, UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; +import { KeyCode, KeyCodeUtils, Keybinding, KeybindingType, ResolvedKeybinding, ResolvedKeybindingPart, SimpleKeybinding } from 'vs/base/common/keyCodes'; +import { AriaLabelProvider, ElectronAcceleratorLabelProvider, UILabelProvider, UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; import { OperatingSystem } from 'vs/base/common/platform'; /** diff --git a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index 80306936a52..d75b1cbdec5 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ResolvedKeybinding, KeyCode, KeyMod, KeyChord, Keybinding, createKeybinding, createSimpleKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; -import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; -import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; +import { KeyChord, KeyCode, KeyMod, Keybinding, ResolvedKeybinding, SimpleKeybinding, createKeybinding, createSimpleKeybinding } from 'vs/base/common/keyCodes'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { OS } from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; -import { IContext, ContextKeyExpr, IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; -import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; -import { OS } from 'vs/base/common/platform'; +import { ContextKeyExpr, IContext, IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; +import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; +import { INotification, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification } from 'vs/platform/notification/common/notification'; +import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { INotificationService, NoOpNotification, INotification, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification'; function createContext(ctx: any) { return { diff --git a/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts b/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts index 68719a1af5e..a0ded747d81 100644 --- a/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { KeyCode, KeyMod, KeyChord, createKeybinding } from 'vs/base/common/keyCodes'; -import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; +import { KeyChord, KeyCode, KeyMod, createKeybinding } from 'vs/base/common/keyCodes'; import { OperatingSystem } from 'vs/base/common/platform'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; suite('KeybindingLabels', () => { diff --git a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts index 50b1090cb31..39970c89803 100644 --- a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { KeyCode, KeyMod, KeyChord, createKeybinding, KeybindingType, SimpleKeybinding } from 'vs/base/common/keyCodes'; -import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; +import { KeyChord, KeyCode, KeyMod, KeybindingType, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; +import { OS } from 'vs/base/common/platform'; import { ContextKeyAndExpr, ContextKeyExpr, IContext } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { OS } from 'vs/base/common/platform'; function createContext(ctx: any) { return { diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index 05678445f22..99893ecbff8 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResolvedKeybinding, Keybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; import { Event } from 'vs/base/common/event'; -import { IKeybindingService, IKeybindingEvent, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; -import { IContextKey, IContextKeyService, IContextKeyServiceTarget, ContextKeyExpr, IContextKeyChangeEvent } from 'vs/platform/contextkey/common/contextkey'; -import { IResolveResult } from 'vs/platform/keybinding/common/keybindingResolver'; -import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; +import { Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; import { OS } from 'vs/base/common/platform'; +import { ContextKeyExpr, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; +import { IKeybindingEvent, IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { IResolveResult } from 'vs/platform/keybinding/common/keybindingResolver'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; class MockKeybindingContextKey implements IContextKey { private _defaultValue: T | undefined; diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index cc27d136362..abe748d84f5 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -18,6 +18,8 @@ import { localize } from 'vs/nls'; import { isParent } from 'vs/platform/files/common/files'; import { basename, dirname, join } from 'vs/base/common/paths'; import { Schemas } from 'vs/base/common/network'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; export interface RegisterFormatterEvent { selector: string; @@ -33,6 +35,7 @@ export interface ILabelService { */ getUriLabel(resource: URI, options?: { relative?: boolean, noPrefix?: boolean }): string; getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string; + getHostLabel(): string; registerFormatter(selector: string, formatter: LabelRules): IDisposable; onDidRegisterFormatter: Event; } @@ -66,7 +69,8 @@ export class LabelService implements ILabelService { constructor( @IEnvironmentService private environmentService: IEnvironmentService, - @IWorkspaceContextService private contextService: IWorkspaceContextService + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IWindowService private windowService: IWindowService ) { } get onDidRegisterFormatter(): Event { @@ -155,6 +159,19 @@ export class LabelService implements ILabelService { return localize('workspaceName', "{0} (Workspace)", workspaceName); } + getHostLabel(): string { + if (this.windowService) { + const authority = this.windowService.getConfiguration().remoteAuthority; + if (authority) { + const formatter = this.findFormatter(URI.from({ scheme: REMOTE_HOST_SCHEME, authority })); + if (formatter && formatter.workspace) { + return formatter.workspace.suffix; + } + } + } + return ''; + } + registerFormatter(selector: string, formatter: LabelRules): IDisposable { this.formatters[selector] = formatter; this._onDidRegisterFormatter.fire({ selector, formatter }); diff --git a/src/vs/platform/label/test/label.test.ts b/src/vs/platform/label/test/label.test.ts index 3e282c2374d..542e7563960 100644 --- a/src/vs/platform/label/test/label.test.ts +++ b/src/vs/platform/label/test/label.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { LabelService } from 'vs/platform/label/common/label'; -import { TestEnvironmentService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { TestEnvironmentService, TestContextService, TestWindowService } from 'vs/workbench/test/workbenchTestServices'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { URI } from 'vs/base/common/uri'; import { nativeSep } from 'vs/base/common/paths'; @@ -16,7 +16,7 @@ suite('URI Label', () => { let labelService: LabelService; setup(() => { - labelService = new LabelService(TestEnvironmentService, new TestContextService()); + labelService = new LabelService(TestEnvironmentService, new TestContextService(), new TestWindowService()); }); test('file scheme', function () { diff --git a/src/vs/platform/launch/electron-main/launchService.ts b/src/vs/platform/launch/electron-main/launchService.ts index dca64fbada1..e7981250608 100644 --- a/src/vs/platform/launch/electron-main/launchService.ts +++ b/src/vs/platform/launch/electron-main/launchService.ts @@ -231,7 +231,7 @@ export class LaunchService implements ILaunchService { // If the other instance is waiting to be killed, we hook up a window listener if one window // is being used and only then resolve the startup promise which will kill this second instance. // In addition, we poll for the wait marker file to be deleted to return. - if (args.wait && usedWindows.length === 1 && usedWindows[0]) { + if (args.wait && args.waitMarkerFilePath && usedWindows.length === 1 && usedWindows[0]) { return Promise.race([ this.windowsMainService.waitForWindowCloseOrLoad(usedWindows[0].id), whenDeleted(args.waitMarkerFilePath) diff --git a/src/vs/platform/lifecycle/common/lifecycle.ts b/src/vs/platform/lifecycle/common/lifecycle.ts index 86d0f01117d..17108e70ffb 100644 --- a/src/vs/platform/lifecycle/common/lifecycle.ts +++ b/src/vs/platform/lifecycle/common/lifecycle.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { isThenable } from 'vs/base/common/async'; @@ -149,9 +148,9 @@ export const NullLifecycleService: ILifecycleService = { }; // Shared veto handling across main and renderer -export function handleVetos(vetos: (boolean | Thenable)[], onError: (error: Error) => void): TPromise { +export function handleVetos(vetos: (boolean | Thenable)[], onError: (error: Error) => void): Promise { if (vetos.length === 0) { - return TPromise.as(false); + return Promise.resolve(false); } const promises: Thenable[] = []; @@ -161,7 +160,7 @@ export function handleVetos(vetos: (boolean | Thenable)[], onError: (er // veto, done if (valueOrPromise === true) { - return TPromise.as(true); + return Promise.resolve(true); } if (isThenable(valueOrPromise)) { @@ -176,5 +175,5 @@ export function handleVetos(vetos: (boolean | Thenable)[], onError: (er } } - return TPromise.join(promises).then(() => lazyValue); + return Promise.all(promises).then(() => lazyValue); } diff --git a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts index 2662c2cd45e..fb0b87eef59 100644 --- a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ILifecycleService, WillShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, handleVetos, LifecyclePhaseToString, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -102,7 +101,7 @@ export class LifecycleService extends Disposable implements ILifecycleService { }); } - private handleWillShutdown(reason: ShutdownReason): TPromise { + private handleWillShutdown(reason: ShutdownReason): Promise { const vetos: (boolean | Thenable)[] = []; this._onWillShutdown.fire({ @@ -130,7 +129,7 @@ export class LifecycleService extends Disposable implements ILifecycleService { reason }); - return TPromise.join(joiners).then(() => void 0, err => { + return Promise.all(joiners).then(() => void 0, err => { this.notificationService.error(toErrorMessage(err)); onUnexpectedError(err); }); diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts index 67423115764..1a1fd15d414 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts @@ -100,8 +100,8 @@ export class LifecycleService extends Disposable implements ILifecycleService { private static readonly QUIT_FROM_RESTART_MARKER = 'quit.from.restart'; // use a marker to find out if the session was restarted private windowToCloseRequest: { [windowId: string]: boolean } = Object.create(null); - private pendingQuitPromise: TPromise; - private pendingQuitPromiseComplete: TValueCallback; + private pendingQuitPromise: TPromise | null; + private pendingQuitPromiseComplete: TValueCallback | null; private oneTimeListenerTokenGenerator = 0; private windowCounter = 0; @@ -399,7 +399,10 @@ export class LifecycleService extends Disposable implements ILifecycleService { // Code starts it will set it back to the installation directory again. try { if (isWindows) { - process.chdir(process.env['VSCODE_CWD']); + const vscodeCwd = process.env['VSCODE_CWD']; + if (vscodeCwd) { + process.chdir(vscodeCwd); + } } } catch (err) { this.logService.error(err); diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index abb88e40e0f..29615cd9eb9 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -598,6 +598,10 @@ export class ObjectTreeResourceNavigator extends Disposable { const focus = this.tree.getFocus(); this.tree.setSelection(focus, e.browserEvent); + if (!e.browserEvent) { + return; + } + const isMouseEvent = e.browserEvent && e.browserEvent instanceof MouseEvent; if (!isMouseEvent) { diff --git a/src/vs/platform/localizations/node/localizations.ts b/src/vs/platform/localizations/node/localizations.ts index d08a4100a2f..ded2a3cdf03 100644 --- a/src/vs/platform/localizations/node/localizations.ts +++ b/src/vs/platform/localizations/node/localizations.ts @@ -9,7 +9,7 @@ import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier } fr import { Disposable } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TPromise } from 'vs/base/common/winjs.base'; -import { Limiter } from 'vs/base/common/async'; +import { Queue } from 'vs/base/common/async'; import { areSameExtensions, getGalleryExtensionIdFromLocal, getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; import { isValidLocalization, ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; @@ -100,7 +100,7 @@ class LanguagePacksCache extends Disposable { private languagePacks: { [language: string]: ILanguagePack } = {}; private languagePacksFilePath: string; - private languagePacksFileLimiter: Limiter; + private languagePacksFileLimiter: Queue; constructor( @IEnvironmentService environmentService: IEnvironmentService, @@ -108,7 +108,7 @@ class LanguagePacksCache extends Disposable { ) { super(); this.languagePacksFilePath = posix.join(environmentService.userDataPath, 'languagepacks.json'); - this.languagePacksFileLimiter = new Limiter(1); + this.languagePacksFileLimiter = new Queue(); } getLanguagePacks(): TPromise<{ [language: string]: ILanguagePack }> { diff --git a/src/vs/platform/localizations/node/localizationsIpc.ts b/src/vs/platform/localizations/node/localizationsIpc.ts index 863bb3f8095..a6347943ce8 100644 --- a/src/vs/platform/localizations/node/localizationsIpc.ts +++ b/src/vs/platform/localizations/node/localizationsIpc.ts @@ -29,14 +29,15 @@ export class LocalizationsChannel implements ILocalizationsChannel { case 'onDidLanguagesChange': return this.onDidLanguagesChange; } - throw new Error('No event found'); + throw new Error(`Event not found: ${event}`); } call(command: string, arg?: any): Thenable { switch (command) { case 'getLanguageIds': return this.service.getLanguageIds(arg); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } diff --git a/src/vs/platform/log/node/logIpc.ts b/src/vs/platform/log/node/logIpc.ts index ca2cb87d67c..2aed50e35b2 100644 --- a/src/vs/platform/log/node/logIpc.ts +++ b/src/vs/platform/log/node/logIpc.ts @@ -29,14 +29,15 @@ export class LogLevelSetterChannel implements ILogLevelSetterChannel { case 'onDidChangeLogLevel': return this.onDidChangeLogLevel; } - throw new Error('No event found'); + throw new Error(`Event not found: ${event}`); } call(command: string, arg?: any): TPromise { switch (command) { case 'setLevel': this.service.setLevel(arg); return TPromise.as(null); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } diff --git a/src/vs/platform/markers/common/markerService.ts b/src/vs/platform/markers/common/markerService.ts index 929a53ebebd..a3fd07f7719 100644 --- a/src/vs/platform/markers/common/markerService.ts +++ b/src/vs/platform/markers/common/markerService.ts @@ -17,7 +17,7 @@ interface MapMap { namespace MapMap { - export function get(map: MapMap, key1: string, key2: string): V { + export function get(map: MapMap, key1: string, key2: string): V | undefined { if (map[key1]) { return map[key1][key2]; } @@ -50,7 +50,7 @@ class MarkerStats implements MarkerStatistics { warnings: number = 0; unknowns: number = 0; - private _data: { [resource: string]: MarkerStatistics } = Object.create(null); + private _data?: { [resource: string]: MarkerStatistics } = Object.create(null); private _service: IMarkerService; private _subscription: IDisposable; @@ -65,6 +65,10 @@ class MarkerStats implements MarkerStatistics { } private _update(resources: URI[]): void { + if (!this._data) { + return; + } + for (const resource of resources) { const key = resource.toString(); const oldStats = this._data[key]; @@ -144,7 +148,7 @@ export class MarkerService implements IMarkerService { remove(owner: string, resources: URI[]): void { if (!isFalsyOrEmpty(resources)) { for (const resource of resources) { - this.changeOne(owner, resource, undefined); + this.changeOne(owner, resource, []); } } } @@ -177,7 +181,7 @@ export class MarkerService implements IMarkerService { } } - private static _toMarker(owner: string, resource: URI, data: IMarkerData): IMarker { + private static _toMarker(owner: string, resource: URI, data: IMarkerData): IMarker | undefined { let { code, severity, message, source, @@ -191,7 +195,6 @@ export class MarkerService implements IMarkerService { } // santize data - code = code || null; startLineNumber = startLineNumber > 0 ? startLineNumber : 1; startColumn = startColumn > 0 ? startColumn : 1; endLineNumber = endLineNumber >= startLineNumber ? endLineNumber : startLineNumber; @@ -200,7 +203,7 @@ export class MarkerService implements IMarkerService { return { resource, owner, - code, + code: code || undefined, severity, message, source, @@ -221,13 +224,16 @@ export class MarkerService implements IMarkerService { if (map) { delete this._byOwner[owner]; for (const resource in map) { - // remeber what we remove - const [first] = MapMap.get(this._byResource, resource, owner); - if (first) { - changes.push(first.resource); + const entry = MapMap.get(this._byResource, resource, owner); + if (entry) { + // remeber what we remove + const [first] = entry; + if (first) { + changes.push(first.resource); + } + // actual remove + MapMap.remove(this._byResource, resource, owner); } - // actual remove - MapMap.remove(this._byResource, resource, owner); } } @@ -308,9 +314,9 @@ export class MarkerService implements IMarkerService { } else { // of one resource OR owner - const map: { [key: string]: IMarker[] } = owner + const map: { [key: string]: IMarker[] } | undefined = owner ? this._byOwner[owner] - : this._byResource[resource.toString()]; + : resource ? this._byResource[resource.toString()] : undefined; if (!map) { return []; diff --git a/src/vs/platform/markers/common/markers.ts b/src/vs/platform/markers/common/markers.ts index 511ae3053ca..dd3cc805444 100644 --- a/src/vs/platform/markers/common/markers.ts +++ b/src/vs/platform/markers/common/markers.ts @@ -37,7 +37,7 @@ export interface IRelatedInformation { endColumn: number; } -export enum MarkerTag { +export const enum MarkerTag { Unnecessary = 1, } diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index af8317230f0..1677b3e90b0 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -112,7 +112,7 @@ export class Menubar { // Help Menu Items if (product.twitterUrl) { - this.fallbackMenuHandlers['workbench.action.openRequestFeatureUrl'] = () => this.openUrl(product.twitterUrl, 'openTwitterUrl'); + this.fallbackMenuHandlers['workbench.action.openTwitterUrl'] = () => this.openUrl(product.twitterUrl, 'openTwitterUrl'); } if (product.requestFeatureUrl) { @@ -124,7 +124,7 @@ export class Menubar { } if (product.licenseUrl) { - this.addFallbackHandlers['workbench.action.openLicenseUrl'] = () => { + this.fallbackMenuHandlers['workbench.action.openLicenseUrl'] = () => { if (language) { const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; this.openUrl(`${product.licenseUrl}${queryArgChar}lang=${language}`, 'openLicenseUrl'); @@ -177,6 +177,10 @@ export class Menubar { } private get currentEnableNativeTabs(): boolean { + if (!isMacintosh) { + return false; + } + let enableNativeTabs = this.configurationService.getValue('window.nativeTabs'); if (typeof enableNativeTabs !== 'boolean') { enableNativeTabs = false; diff --git a/src/vs/platform/menubar/node/menubarIpc.ts b/src/vs/platform/menubar/node/menubarIpc.ts index 52eaa99666b..7d6b0c4d844 100644 --- a/src/vs/platform/menubar/node/menubarIpc.ts +++ b/src/vs/platform/menubar/node/menubarIpc.ts @@ -17,14 +17,15 @@ export class MenubarChannel implements IMenubarChannel { constructor(private service: IMenubarService) { } listen(event: string, arg?: any): Event { - throw new Error('No events'); + throw new Error(`Event not found: ${event}`); } call(command: string, arg?: any): TPromise { switch (command) { case 'updateMenubar': return this.service.updateMenubar(arg[0], arg[1]); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } diff --git a/src/vs/platform/progress/common/progress.ts b/src/vs/platform/progress/common/progress.ts index 9fb299f9a21..99144374abb 100644 --- a/src/vs/platform/progress/common/progress.ts +++ b/src/vs/platform/progress/common/progress.ts @@ -25,6 +25,36 @@ export interface IProgressService { showWhile(promise: Thenable, delay?: number): Thenable; } +export const enum ProgressLocation { + Explorer = 1, + Scm = 3, + Extensions = 5, + Window = 10, + Notification = 15 +} + +export interface IProgressOptions { + location: ProgressLocation | string; + title?: string; + source?: string; + total?: number; + cancellable?: boolean; +} + +export interface IProgressStep { + message?: string; + increment?: number; +} + +export const IProgressService2 = createDecorator('progressService2'); + +export interface IProgressService2 { + + _serviceBrand: any; + + withProgress

, R=any>(options: IProgressOptions, task: (progress: IProgress) => P, onDidCancel?: () => void): P; +} + export interface IProgressRunner { total(value: number): void; worked(value: number): void; diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts index 20e13c85c36..e273c0918a2 100644 --- a/src/vs/platform/quickinput/common/quickInput.ts +++ b/src/vs/platform/quickinput/common/quickInput.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { URI } from 'vs/base/common/uri'; @@ -76,7 +75,7 @@ export interface IPickOptions { /** * an optional property for the item to focus initially. */ - activeItem?: TPromise | T; + activeItem?: Promise | T; onKeyMods?: (keyMods: IKeyMods) => void; onDidFocus?: (entry: T) => void; @@ -231,14 +230,14 @@ export interface IQuickInputService { /** * Opens the quick input box for selecting items and returns a promise with the user selected item(s) if any. */ - pick(picks: TPromise[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: true }, token?: CancellationToken): TPromise; - pick(picks: TPromise[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: false }, token?: CancellationToken): TPromise; - pick(picks: TPromise[]> | QuickPickInput[], options?: Omit, 'canPickMany'>, token?: CancellationToken): TPromise; + pick(picks: Thenable[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: true }, token?: CancellationToken): Promise; + pick(picks: Thenable[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: false }, token?: CancellationToken): Promise; + pick(picks: Thenable[]> | QuickPickInput[], options?: Omit, 'canPickMany'>, token?: CancellationToken): Promise; /** * Opens the quick input box for text input and returns a promise with the user typed value if any. */ - input(options?: IInputOptions, token?: CancellationToken): TPromise; + input(options?: IInputOptions, token?: CancellationToken): Promise; backButton: IQuickInputButton; @@ -251,9 +250,9 @@ export interface IQuickInputService { navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void; - accept(): TPromise; + accept(): Promise; - back(): TPromise; + back(): Promise; - cancel(): TPromise; + cancel(): Promise; } diff --git a/src/vs/platform/remote/common/remoteHosts.ts b/src/vs/platform/remote/common/remoteHosts.ts new file mode 100644 index 00000000000..29e16a54967 --- /dev/null +++ b/src/vs/platform/remote/common/remoteHosts.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; + +export const REMOTE_HOST_SCHEME = 'vscode-remote'; + +export function getRemoteAuthority(uri: URI) { + return uri.scheme === REMOTE_HOST_SCHEME ? uri.authority : void 0; +} \ No newline at end of file diff --git a/src/vs/platform/request/electron-browser/requestService.ts b/src/vs/platform/request/electron-browser/requestService.ts index 8c68dfe743c..34030b8cdba 100644 --- a/src/vs/platform/request/electron-browser/requestService.ts +++ b/src/vs/platform/request/electron-browser/requestService.ts @@ -24,7 +24,7 @@ export const xhrRequest: IRequestFunction = (options: IRequestOptions, token: Ca const xhr = new XMLHttpRequest(); return new Promise((resolve, reject) => { - xhr.open(options.type || 'GET', options.url, true, options.user, options.password); + xhr.open(options.type || 'GET', options.url || '', true, options.user, options.password); setRequestHeaders(xhr, options); xhr.responseType = 'arraybuffer'; diff --git a/src/vs/platform/request/node/request.ts b/src/vs/platform/request/node/request.ts index e7a0a7abe94..28a8ca3416b 100644 --- a/src/vs/platform/request/node/request.ts +++ b/src/vs/platform/request/node/request.ts @@ -47,6 +47,17 @@ Registry.as(Extensions.Configuration) type: ['null', 'string'], default: null, description: localize('proxyAuthorization', "The value to send as the 'Proxy-Authorization' header for every network request.") + }, + 'http.systemProxy': { + type: 'string', + enum: ['off', 'on', 'force'], + enumDescriptions: [ + localize('systemProxyOff', "Do not use system proxy configuration."), + localize('systemProxyOn', "Use system proxy configuration if not specified in the request options."), + localize('systemProxyForce', "Always use system proxy configuration."), + ], + default: 'off', + description: localize('systemProxy', "Experimental setting: Use the system proxy configuration.") } } }); diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index fe394ac1555..d8e9bfdde46 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -20,9 +20,9 @@ export class RequestService implements IRequestService { _serviceBrand: any; - private proxyUrl: string; + private proxyUrl?: string; private strictSSL: boolean; - private authorization: string; + private authorization?: string; private disposables: IDisposable[] = []; constructor( @@ -35,7 +35,7 @@ export class RequestService implements IRequestService { private configure(config: IHTTPConfiguration) { this.proxyUrl = config.http && config.http.proxy; - this.strictSSL = config.http && config.http.proxyStrictSSL; + this.strictSSL = !!(config.http && config.http.proxyStrictSSL); this.authorization = config.http && config.http.proxyAuthorization; } @@ -43,7 +43,7 @@ export class RequestService implements IRequestService { this.logService.trace('RequestService#request', options.url); const { proxyUrl, strictSSL } = this; - const agentPromise = options.agent ? Promise.resolve(options.agent) : Promise.resolve(getProxyAgent(options.url, { proxyUrl, strictSSL })); + const agentPromise = options.agent ? Promise.resolve(options.agent) : Promise.resolve(getProxyAgent(options.url || '', { proxyUrl, strictSSL })); return agentPromise.then(agent => { options.agent = agent; diff --git a/src/vs/platform/search/common/replace.ts b/src/vs/platform/search/common/replace.ts index d5100b6fc7d..564c66da7a4 100644 --- a/src/vs/platform/search/common/replace.ts +++ b/src/vs/platform/search/common/replace.ts @@ -21,14 +21,18 @@ export class ReplacePattern { let parseParameters: boolean; if (typeof arg2 === 'boolean') { parseParameters = arg2; + this._regExp = arg3; + } else { searchPatternInfo = arg2; - parseParameters = searchPatternInfo.isRegExp; + parseParameters = !!searchPatternInfo.isRegExp; + this._regExp = strings.createRegExp(searchPatternInfo.pattern, !!searchPatternInfo.isRegExp, { matchCase: searchPatternInfo.isCaseSensitive, wholeWord: searchPatternInfo.isWordMatch, multiline: searchPatternInfo.isMultiline, global: false }); } + if (parseParameters) { this.parseReplaceString(replaceString); } - this._regExp = arg3 ? arg3 : strings.createRegExp(searchPatternInfo.pattern, searchPatternInfo.isRegExp, { matchCase: searchPatternInfo.isCaseSensitive, wholeWord: searchPatternInfo.isWordMatch, multiline: searchPatternInfo.isMultiline, global: false }); + if (this._regExp.global) { this._regExp = strings.createRegExp(this._regExp.source, true, { matchCase: !this._regExp.ignoreCase, wholeWord: false, multiline: this._regExp.multiline, global: false }); } @@ -50,7 +54,7 @@ export class ReplacePattern { * Returns the replace string for the first match in the given text. * If text has no matches then returns null. */ - public getReplaceString(text: string): string { + public getReplaceString(text: string): string | null { this._regExp.lastIndex = 0; let match = this._regExp.exec(text); if (match) { @@ -63,6 +67,7 @@ export class ReplacePattern { } return this.pattern; } + return null; } diff --git a/src/vs/platform/search/common/search.ts b/src/vs/platform/search/common/search.ts index 0bf434f5477..122cbd62efb 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -3,17 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { mapArrayOrNot } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as paths from 'vs/base/common/paths'; -import { URI as uri, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { getNLines } from 'vs/base/common/strings'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { Promise } from 'vs/base/common/winjs.base'; import { IFilesConfiguration } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { getNLines } from 'vs/base/common/strings'; export const VIEW_ID = 'workbench.view.search'; @@ -25,9 +26,10 @@ export const ISearchService = createDecorator('searchService'); */ export interface ISearchService { _serviceBrand: any; - search(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise; - extendQuery(query: ISearchQuery): void; - clearCache(cacheKey: string): TPromise; + textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise; + fileSearch(query: IFileQuery, token?: CancellationToken): Promise; + extendQuery(query: ITextQuery | IFileQuery): void; + clearCache(cacheKey: string): Promise; registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable; } @@ -56,63 +58,75 @@ export const enum SearchProviderType { } export interface ISearchResultProvider { - search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise; - clearCache(cacheKey: string): TPromise; + textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): Promise; + fileSearch(query: IFileQuery, token?: CancellationToken): Promise; + clearCache(cacheKey: string): Promise; } -export interface IFolderQuery { +export interface IFolderQuery { folder: U; excludePattern?: glob.IExpression; includePattern?: glob.IExpression; fileEncoding?: string; disregardIgnoreFiles?: boolean; disregardGlobalIgnoreFiles?: boolean; + ignoreSymlinks?: boolean; } -export interface ICommonQueryOptions { +export interface ICommonQueryProps { + /** For telemetry - indicates what is triggering the source */ + _reason?: string; + + folderQueries?: IFolderQuery[]; + includePattern?: glob.IExpression; + excludePattern?: glob.IExpression; extraFileResources?: U[]; - filePattern?: string; // file search only - fileEncoding?: string; + + useRipgrep?: boolean; maxResults?: number; + usingSearchPaths?: boolean; +} + +export interface IFileQueryProps extends ICommonQueryProps { + type: QueryType.File; + filePattern?: string; + + // TODO: Remove this! + disregardExcludeSettings?: boolean; + /** * If true no results will be returned. Instead `limitHit` will indicate if at least one result exists or not. - * * Currently does not work with queries including a 'siblings clause'. */ exists?: boolean; sortByScore?: boolean; cacheKey?: string; - useRipgrep?: boolean; - disregardIgnoreFiles?: boolean; - disregardGlobalIgnoreFiles?: boolean; - disregardExcludeSettings?: boolean; - ignoreSymlinks?: boolean; - maxFileSize?: number; +} + +export interface ITextQueryProps extends ICommonQueryProps { + type: QueryType.Text; + contentPattern: IPatternInfo; + previewOptions?: ITextSearchPreviewOptions; + maxFileSize?: number; + usePCRE2?: boolean; + afterContext?: number; + beforeContext?: number; } -export interface IQueryOptions extends ICommonQueryOptions { - excludePattern?: string; - includePattern?: string; -} +export type IFileQuery = IFileQueryProps; +export type IRawFileQuery = IFileQueryProps; +export type ITextQuery = ITextQueryProps; +export type IRawTextQuery = ITextQueryProps; -export interface ISearchQueryProps extends ICommonQueryOptions { - type: QueryType; - - excludePattern?: glob.IExpression; - includePattern?: glob.IExpression; - contentPattern?: IPatternInfo; - folderQueries?: IFolderQuery[]; - usingSearchPaths?: boolean; -} - -export type ISearchQuery = ISearchQueryProps; -export type IRawSearchQuery = ISearchQueryProps; +export type IRawQuery = IRawTextQuery | IRawFileQuery; +export type ISearchQuery = ITextQuery | IFileQuery; export const enum QueryType { File = 1, Text = 2 } + /* __GDPR__FRAGMENT__ "IPatternInfo" : { "pattern" : { "classification": "CustomerContent", "purpose": "FeatureInsight" }, @@ -134,9 +148,13 @@ export interface IPatternInfo { isSmartCase?: boolean; } -export interface IFileMatch { +export interface IExtendedExtensionSearchOptions { + usePCRE2?: boolean; +} + +export interface IFileMatch { resource?: U; - matches?: ITextSearchResult[]; + results?: ITextSearchResult[]; } export type IRawFileMatch2 = IFileMatch; @@ -155,15 +173,27 @@ export interface ISearchRange { export interface ITextSearchResultPreview { text: string; - match: ISearchRange; + matches: ISearchRange | ISearchRange[]; } -export interface ITextSearchResult { - uri?: uri; - range: ISearchRange; +export interface ITextSearchMatch { + uri?: URI; + ranges: ISearchRange | ISearchRange[]; preview: ITextSearchResultPreview; } +export interface ITextSearchContext { + uri?: URI; + text: string; + lineNumber: number; +} + +export type ITextSearchResult = ITextSearchMatch | ITextSearchContext; + +export function resultIsMatch(result: ITextSearchResult): result is ITextSearchMatch { + return !!(result).preview; +} + export interface IProgress { total?: number; worked?: number; @@ -226,38 +256,41 @@ export interface IFileIndexProviderStats { } export class FileMatch implements IFileMatch { - public matches: ITextSearchResult[] = []; - constructor(public resource: uri) { + public results: ITextSearchResult[] = []; + constructor(public resource: URI) { // empty } } -export class TextSearchResult implements ITextSearchResult { - range: ISearchRange; +export class TextSearchMatch implements ITextSearchMatch { + ranges: ISearchRange | ISearchRange[]; preview: ITextSearchResultPreview; - constructor(text: string, range: ISearchRange, previewOptions?: ITextSearchPreviewOptions) { - this.range = range; - if (previewOptions) { + constructor(text: string, range: ISearchRange | ISearchRange[], previewOptions?: ITextSearchPreviewOptions) { + this.ranges = range; + + if (previewOptions && previewOptions.matchLines === 1 && !Array.isArray(range)) { + // 1 line preview requested text = getNLines(text, previewOptions.matchLines); const leadingChars = Math.floor(previewOptions.charsPerLine / 5); - const endColumnByTrimmedLines = (range.startLineNumber + previewOptions.matchLines - 1) === range.endLineNumber ? // if single line... - range.endColumn : - previewOptions.charsPerLine; - - // This doesn't handle all previewOptions correctly const previewStart = Math.max(range.startColumn - leadingChars, 0); - const endByCharsPerLine = previewOptions.charsPerLine + previewStart; - const trimmedEndOfMatchRangeInPreview = Math.min(endByCharsPerLine, endColumnByTrimmedLines - previewStart); + const previewText = text.substring(previewStart, previewOptions.charsPerLine + previewStart); + + const endColInPreview = (range.endLineNumber - range.startLineNumber + 1) <= previewOptions.matchLines ? + Math.min(previewText.length, range.endColumn - previewStart) : // if number of match lines will not be trimmed by previewOptions + previewText.length; // if number of lines is trimmed this.preview = { - text: text.substring(previewStart, endByCharsPerLine), - match: new OneLineRange(0, range.startColumn - previewStart, trimmedEndOfMatchRangeInPreview) + text: previewText, + matches: new OneLineRange(0, range.startColumn - previewStart, endColInPreview) }; } else { + const firstMatchLine = Array.isArray(range) ? range[0].startLineNumber : range.startLineNumber; + + // n line, no preview requested, or multiple matches in the preview this.preview = { - text: text, - match: new OneLineRange(0, range.startColumn, range.endColumn) + text, + matches: mapArrayOrNot(range, r => new SearchRange(r.startLineNumber - firstMatchLine, r.startColumn, r.endLineNumber - firstMatchLine, r.endColumn)) }; } } @@ -286,6 +319,7 @@ export class OneLineRange extends SearchRange { export interface ISearchConfigurationProperties { exclude: glob.IExpression; useRipgrep: boolean; + useLegacySearch: boolean; /** * Use ignore file for file search. */ @@ -297,6 +331,7 @@ export interface ISearchConfigurationProperties { location: 'sidebar' | 'panel'; useReplacePreview: boolean; showLineNumbers: boolean; + usePCRE2: boolean; } export interface ISearchConfiguration extends IFilesConfiguration { @@ -326,18 +361,18 @@ export function getExcludes(configuration: ISearchConfiguration): glob.IExpressi return allExcludes; } -export function pathIncludedInQuery(query: ISearchQuery, fsPath: string): boolean { - if (query.excludePattern && glob.match(query.excludePattern, fsPath)) { +export function pathIncludedInQuery(queryProps: ICommonQueryProps, fsPath: string): boolean { + if (queryProps.excludePattern && glob.match(queryProps.excludePattern, fsPath)) { return false; } - if (query.includePattern && !glob.match(query.includePattern, fsPath)) { + if (queryProps.includePattern && !glob.match(queryProps.includePattern, fsPath)) { return false; } // If searchPaths are being used, the extra file must be in a subfolder and match the pattern, if present - if (query.usingSearchPaths) { - return !!query.folderQueries && query.folderQueries.every(fq => { + if (queryProps.usingSearchPaths) { + return !!queryProps.folderQueries && queryProps.folderQueries.every(fq => { const searchPath = fq.folder.fsPath; if (paths.isEqualOrParent(fsPath, searchPath)) { return !fq.includePattern || !!glob.match(fq.includePattern, fsPath); @@ -349,3 +384,32 @@ export function pathIncludedInQuery(query: ISearchQuery, fsPath: string): boolea return true; } + +export enum SearchErrorCode { + unknownEncoding = 1, + regexParseError, + globParseError, + invalidLiteral, + rgProcessError, + other +} + +export class SearchError extends Error { + constructor(message: string, readonly code?: SearchErrorCode) { + super(message); + } +} + +export function deserializeSearchError(errorMsg: string): SearchError { + try { + const details = JSON.parse(errorMsg); + return new SearchError(details.message, details.code); + } catch (e) { + return new SearchError(errorMsg, SearchErrorCode.other); + } +} + +export function serializeSearchError(searchError: SearchError): Error { + const details = { message: searchError.message, code: searchError.code }; + return new Error(JSON.stringify(details)); +} diff --git a/src/vs/platform/search/test/common/search.test.ts b/src/vs/platform/search/test/common/search.test.ts index 25b89a956d7..147c0cfc65b 100644 --- a/src/vs/platform/search/test/common/search.test.ts +++ b/src/vs/platform/search/test/common/search.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ITextSearchPreviewOptions, OneLineRange, TextSearchResult, SearchRange } from 'vs/platform/search/common/search'; +import { ITextSearchPreviewOptions, OneLineRange, TextSearchMatch, SearchRange } from 'vs/platform/search/common/search'; suite('TextSearchResult', () => { @@ -12,58 +12,58 @@ suite('TextSearchResult', () => { charsPerLine: 100 }; - function assertPreviewRangeText(text: string, result: TextSearchResult): void { + function assertPreviewRangeText(text: string, result: TextSearchMatch): void { assert.equal( - result.preview.text.substring(result.preview.match.startColumn, result.preview.match.endColumn), + result.preview.text.substring((result.preview.matches).startColumn, (result.preview.matches).endColumn), text); } test('empty without preview options', () => { const range = new OneLineRange(5, 0, 0); - const result = new TextSearchResult('', range); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('', range); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('', result); }); test('empty with preview options', () => { const range = new OneLineRange(5, 0, 0); - const result = new TextSearchResult('', range, previewOptions1); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('', range, previewOptions1); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('', result); }); test('short without preview options', () => { const range = new OneLineRange(5, 4, 7); - const result = new TextSearchResult('foo bar', range); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('foo bar', range); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('bar', result); }); test('short with preview options', () => { const range = new OneLineRange(5, 4, 7); - const result = new TextSearchResult('foo bar', range, previewOptions1); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('foo bar', range, previewOptions1); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('bar', result); }); test('leading', () => { const range = new OneLineRange(5, 25, 28); - const result = new TextSearchResult('long text very long text foo', range, previewOptions1); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('long text very long text foo', range, previewOptions1); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('foo', result); }); test('trailing', () => { const range = new OneLineRange(5, 0, 3); - const result = new TextSearchResult('foo long text very long text long text very long text long text very long text long text very long text long text very long text', range, previewOptions1); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('foo long text very long text long text very long text long text very long text long text very long text long text very long text', range, previewOptions1); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('foo', result); }); test('middle', () => { const range = new OneLineRange(5, 30, 33); - const result = new TextSearchResult('long text very long text long foo text very long text long text very long text long text very long text long text very long text', range, previewOptions1); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('long text very long text long foo text very long text long text very long text long text very long text long text very long text', range, previewOptions1); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('foo', result); }); @@ -74,8 +74,8 @@ suite('TextSearchResult', () => { }; const range = new OneLineRange(0, 4, 7); - const result = new TextSearchResult('foo bar', range, previewOptions); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('foo bar', range, previewOptions); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('b', result); }); @@ -86,8 +86,8 @@ suite('TextSearchResult', () => { }; const range = new SearchRange(5, 4, 6, 3); - const result = new TextSearchResult('foo bar\nfoo bar', range, previewOptions); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('foo bar\nfoo bar', range, previewOptions); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('bar', result); }); diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index fb6e3fd3a0c..b45a0009b7a 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -30,6 +30,7 @@ export interface IStorageService { * The scope argument allows to define the scope of the storage * operation to either the current workspace only or all workspaces. */ + get(key: string, scope: StorageScope, fallbackValue: string): string; get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined; /** @@ -40,6 +41,7 @@ export interface IStorageService { * The scope argument allows to define the scope of the storage * operation to either the current workspace only or all workspaces. */ + getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined; /** @@ -50,7 +52,8 @@ export interface IStorageService { * The scope argument allows to define the scope of the storage * operation to either the current workspace only or all workspaces. */ - getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; + getInteger(key: string, scope: StorageScope, fallbackValue: number): number; + getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; /** * Store a string value under the given key to storage. The value will @@ -88,27 +91,30 @@ export interface IWorkspaceStorageChangeEvent { scope: StorageScope; } -export const NullStorageService: IStorageService = { - _serviceBrand: undefined, +export const NullStorageService: IStorageService = new class implements IStorageService { + _serviceBrand = undefined; - onDidChangeStorage: Event.None, - onWillSaveState: Event.None, + onDidChangeStorage = Event.None; + onWillSaveState = Event.None; + get(key: string, scope: StorageScope, fallbackValue: string): string; get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { return fallbackValue; - }, + } + getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { return fallbackValue; - }, + } + getInteger(key: string, scope: StorageScope, fallbackValue: number): number; getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { return fallbackValue; - }, + } store(key: string, value: any, scope: StorageScope): Promise { return Promise.resolve(); - }, + } remove(key: string, scope: StorageScope): Promise { return Promise.resolve(); diff --git a/src/vs/platform/storage/common/storageLegacyMigration.ts b/src/vs/platform/storage/common/storageLegacyMigration.ts index 3c840e9ee09..a5234694280 100644 --- a/src/vs/platform/storage/common/storageLegacyMigration.ts +++ b/src/vs/platform/storage/common/storageLegacyMigration.ts @@ -6,7 +6,6 @@ import { StorageLegacyService, IStorageLegacy } from 'vs/platform/storage/common/storageLegacyService'; import { endsWith, startsWith, rtrim } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; /** * We currently store local storage with the following format: @@ -31,6 +30,7 @@ import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; const EMPTY_WORKSPACE_PREFIX = `${StorageLegacyService.COMMON_PREFIX}workspace/empty:`; const MULTI_ROOT_WORKSPACE_PREFIX = `${StorageLegacyService.COMMON_PREFIX}workspace/root:`; +const NO_WORKSPACE_PREFIX = 'storage://workspace/__$noWorkspace__'; export type StorageObject = { [key: string]: string }; @@ -39,6 +39,7 @@ export interface IParsedStorage { multiRoot: Map; folder: Map; empty: Map; + noWorkspace: StorageObject; } /** @@ -46,6 +47,7 @@ export interface IParsedStorage { */ export function parseStorage(storage: IStorageLegacy): IParsedStorage { const globalStorage = new Map(); + const noWorkspaceStorage: StorageObject = Object.create(null); const folderWorkspacesStorage = new Map(); const emptyWorkspacesStorage = new Map(); const multiRootWorkspacesStorage = new Map(); @@ -57,9 +59,18 @@ export function parseStorage(storage: IStorageLegacy): IParsedStorage { // Workspace Storage (storage://workspace/) if (startsWith(key, StorageLegacyService.WORKSPACE_PREFIX)) { + // No Workspace key is for extension development windows + if (key.indexOf('__$noWorkspace__') > 0) { + + // storage://workspace/__$noWorkspace__someKey => someKey + const noWorkspaceStorageKey = key.substr(NO_WORKSPACE_PREFIX.length); + + noWorkspaceStorage[noWorkspaceStorageKey] = storage.getItem(key); + } + // We are looking for key: storage://workspace//workspaceIdentifier to be able to find all folder // paths that are known to the storage. is the only way how to parse all folder paths known in storage. - if (endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { + else if (endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { // storage://workspace//workspaceIdentifier => / let workspace = key.substring(StorageLegacyService.WORKSPACE_PREFIX.length, key.length - StorageLegacyService.WORKSPACE_IDENTIFIER.length); @@ -164,35 +175,7 @@ export function parseStorage(storage: IStorageLegacy): IParsedStorage { global: globalStorage, multiRoot: multiRootWorkspacesStorage, folder: folderWorkspacesStorage, - empty: emptyWorkspacesStorage + empty: emptyWorkspacesStorage, + noWorkspace: noWorkspaceStorage }; -} - -export function migrateStorageToMultiRootWorkspace(fromWorkspaceId: string, toWorkspace: IWorkspaceIdentifier, storage: IStorageLegacy): string { - const parsed = parseStorage(storage); - - const newWorkspaceId = URI.from({ path: toWorkspace.id, scheme: 'root' }).toString(); - - // Find in which location the workspace storage is to be migrated from - let storageForWorkspace: StorageObject; - if (parsed.multiRoot.has(fromWorkspaceId)) { - storageForWorkspace = parsed.multiRoot.get(fromWorkspaceId); - } else if (parsed.empty.has(fromWorkspaceId)) { - storageForWorkspace = parsed.empty.get(fromWorkspaceId); - } else if (parsed.folder.has(fromWorkspaceId)) { - storageForWorkspace = parsed.folder.get(fromWorkspaceId); - } - - // Migrate existing storage to new workspace id - if (storageForWorkspace) { - Object.keys(storageForWorkspace).forEach(key => { - if (key === StorageLegacyService.WORKSPACE_IDENTIFIER) { - return; // make sure to never migrate the workspace identifier - } - - storage.setItem(`${StorageLegacyService.WORKSPACE_PREFIX}${newWorkspaceId}/${key}`, storageForWorkspace[key]); - }); - } - - return newWorkspaceId; } \ No newline at end of file diff --git a/src/vs/platform/storage/common/storageLegacyService.ts b/src/vs/platform/storage/common/storageLegacyService.ts index bdb709fc682..7ed240b8bc2 100644 --- a/src/vs/platform/storage/common/storageLegacyService.ts +++ b/src/vs/platform/storage/common/storageLegacyService.ts @@ -12,9 +12,9 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' // Browser localStorage interface export interface IStorageLegacy { length: number; - key(index: number): string; + key(index: number): string | null; setItem(key: string, value: any): void; - getItem(key: string): string; + getItem(key: string): string | null; removeItem(key: string): void; } @@ -94,7 +94,7 @@ export class StorageLegacyService implements IStorageLegacyService { private _globalStorage: IStorageLegacy; private workspaceKey: string; - private _workspaceId: string; + private _workspaceId: string | undefined; constructor( globalStorage: IStorageLegacy, @@ -108,11 +108,11 @@ export class StorageLegacyService implements IStorageLegacyService { this.setWorkspaceId(workspaceId, legacyWorkspaceId); } - get workspaceId(): string { + get workspaceId(): string | undefined { return this._workspaceId; } - setWorkspaceId(workspaceId: string, legacyWorkspaceId?: number): void { + setWorkspaceId(workspaceId: string | undefined, legacyWorkspaceId?: number): void { this._workspaceId = workspaceId; // Calculate workspace storage key @@ -122,6 +122,13 @@ export class StorageLegacyService implements IStorageLegacyService { // which is only possible if a id property is provided that we can check on if (types.isNumber(legacyWorkspaceId)) { this.cleanupWorkspaceScope(legacyWorkspaceId); + } else { + // ensure that we always store a workspace identifier because this key + // is used to migrate data out as needed + const workspaceIdentifier = this.getInteger(StorageLegacyService.WORKSPACE_IDENTIFIER, StorageLegacyScope.WORKSPACE); + if (!workspaceIdentifier) { + this.store(StorageLegacyService.WORKSPACE_IDENTIFIER, 42, StorageLegacyScope.WORKSPACE); + } } } @@ -163,7 +170,7 @@ export class StorageLegacyService implements IStorageLegacyService { for (let i = 0; i < length; i++) { const key = this._workspaceStorage.key(i); - if (key.indexOf(StorageLegacyService.WORKSPACE_PREFIX) < 0) { + if (!key || key.indexOf(StorageLegacyService.WORKSPACE_PREFIX) < 0) { continue; // ignore stored things that don't belong to storage service or are defined globally } @@ -214,7 +221,7 @@ export class StorageLegacyService implements IStorageLegacyService { return value; } - getInteger(key: string, scope = StorageLegacyScope.GLOBAL, defaultValue?: number): number { + getInteger(key: string, scope = StorageLegacyScope.GLOBAL, defaultValue: number = 0): number { const value = this.get(key, scope, defaultValue); if (types.isUndefinedOrNull(value)) { @@ -224,7 +231,7 @@ export class StorageLegacyService implements IStorageLegacyService { return parseInt(value, 10); } - getBoolean(key: string, scope = StorageLegacyScope.GLOBAL, defaultValue?: boolean): boolean { + getBoolean(key: string, scope = StorageLegacyScope.GLOBAL, defaultValue: boolean = false): boolean { const value = this.get(key, scope, defaultValue); if (types.isUndefinedOrNull(value)) { @@ -266,7 +273,7 @@ export class InMemoryLocalStorage implements IStorageLegacy { return Object.keys(this.store).length; } - key(index: number): string { + key(index: number): string | null { const keys = Object.keys(this.store); if (keys.length > index) { return keys[index]; @@ -279,7 +286,7 @@ export class InMemoryLocalStorage implements IStorageLegacy { this.store[key] = value.toString(); } - getItem(key: string): string { + getItem(key: string): string | null { const item = this.store[key]; if (!types.isUndefinedOrNull(item)) { return item; diff --git a/src/vs/platform/storage/electron-browser/storageService.ts b/src/vs/platform/storage/node/storageService.ts similarity index 53% rename from src/vs/platform/storage/electron-browser/storageService.ts rename to src/vs/platform/storage/node/storageService.ts index b8d862456c6..c9f5966a917 100644 --- a/src/vs/platform/storage/electron-browser/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -3,24 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { ILogService } from 'vs/platform/log/common/log'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { Storage, IStorageLoggingOptions } from 'vs/base/node/storage'; +import { Storage, IStorageLoggingOptions, NullStorage, IStorage } from 'vs/base/node/storage'; import { IStorageLegacyService, StorageLegacyScope } from 'vs/platform/storage/common/storageLegacyService'; -import { addDisposableListener } from 'vs/base/browser/dom'; import { startsWith } from 'vs/base/common/strings'; import { Action } from 'vs/base/common/actions'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { localize } from 'vs/nls'; -import { mark } from 'vs/base/common/performance'; +import { mark, getDuration } from 'vs/base/common/performance'; +import { join, basename } from 'path'; +import { copy } from 'vs/base/node/pfs'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class StorageService extends Disposable implements IStorageService { _serviceBrand: any; - private static IN_MEMORY_PATH = ':memory:'; + static IN_MEMORY_PATH = ':memory:'; private _onDidChangeStorage: Emitter = this._register(new Emitter()); get onDidChangeStorage(): Event { return this._onDidChangeStorage.event; } @@ -28,10 +30,14 @@ export class StorageService extends Disposable implements IStorageService { private _onWillSaveState: Emitter = this._register(new Emitter()); get onWillSaveState(): Event { return this._onWillSaveState.event; } + private _hasErrors = false; + get hasErrors(): boolean { return this._hasErrors; } + private bufferedStorageErrors: (string | Error)[] = []; private _onStorageError: Emitter = this._register(new Emitter()); get onStorageError(): Event { if (Array.isArray(this.bufferedStorageErrors)) { + // todo@ben cleanup after a while if (this.bufferedStorageErrors.length > 0) { const bufferedStorageErrors = this.bufferedStorageErrors; setTimeout(() => { @@ -45,22 +51,31 @@ export class StorageService extends Disposable implements IStorageService { return this._onStorageError.event; } - private globalStorage: Storage; - private workspaceStorage: Storage; + private globalStorage: IStorage; + private globalStorageWorkspacePath: string; + + private workspaceStoragePath: string; + private workspaceStorage: IStorage; + private workspaceStorageListener: IDisposable; + + private loggingOptions: IStorageLoggingOptions; constructor( - workspaceDBPath: string, + workspaceStoragePath: string, + disableGlobalStorage: boolean, @ILogService logService: ILogService, @IEnvironmentService environmentService: IEnvironmentService ) { super(); - const loggingOptions: IStorageLoggingOptions = { - info: environmentService.verbose || environmentService.logStorage, - infoLogger: msg => logService.info(msg), - errorLogger: error => { + this.loggingOptions = { + trace: logService.getLevel() === LogLevel.Trace, + logTrace: msg => logService.trace(msg), + logError: error => { logService.error(error); + this._hasErrors = true; + if (Array.isArray(this.bufferedStorageErrors)) { this.bufferedStorageErrors.push(error); } else { @@ -69,17 +84,23 @@ export class StorageService extends Disposable implements IStorageService { } }; - const useInMemoryStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests + this.globalStorageWorkspacePath = workspaceStoragePath === StorageService.IN_MEMORY_PATH ? StorageService.IN_MEMORY_PATH : StorageService.IN_MEMORY_PATH; + this.globalStorage = disableGlobalStorage ? new NullStorage() : new Storage({ path: this.globalStorageWorkspacePath, logging: this.loggingOptions }); + this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL))); - this.globalStorage = new Storage({ path: useInMemoryStorage ? StorageService.IN_MEMORY_PATH : StorageService.IN_MEMORY_PATH, logging: loggingOptions }); - this.workspaceStorage = new Storage({ path: useInMemoryStorage ? StorageService.IN_MEMORY_PATH : workspaceDBPath, logging: loggingOptions }); - - this.registerListeners(); + this.createWorkspaceStorage(workspaceStoragePath); } - private registerListeners(): void { - this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL))); - this._register(this.workspaceStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.WORKSPACE))); + private createWorkspaceStorage(workspaceStoragePath: string): void { + + // Dispose old (if any) + this.workspaceStorage = dispose(this.workspaceStorage); + this.workspaceStorageListener = dispose(this.workspaceStorageListener); + + // Create new + this.workspaceStoragePath = workspaceStoragePath; + this.workspaceStorage = new Storage({ path: workspaceStoragePath, logging: this.loggingOptions }); + this.workspaceStorageListener = this.workspaceStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.WORKSPACE)); } private handleDidChangeStorage(key: string, scope: StorageScope): void { @@ -87,24 +108,32 @@ export class StorageService extends Disposable implements IStorageService { } init(): Promise { - mark('willInitGlobalStorage'); mark('willInitWorkspaceStorage'); + return this.workspaceStorage.init().then(() => { + mark('didInitWorkspaceStorage'); - return Promise.all([ - this.globalStorage.init().then(() => mark('didInitGlobalStorage')), - this.workspaceStorage.init().then(() => mark('didInitWorkspaceStorage')) - ]).then(() => void 0); + mark('willInitGlobalStorage'); + return this.globalStorage.init().then(() => { + mark('didInitGlobalStorage'); + }); + }); } - get(key: string, scope: StorageScope, fallbackValue?: any): string { + get(key: string, scope: StorageScope, fallbackValue: string): string; + get(key: string, scope: StorageScope): string | undefined; + get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { return this.getStorage(scope).get(key, fallbackValue); } - getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean { + getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; + getBoolean(key: string, scope: StorageScope): boolean | undefined; + getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { return this.getStorage(scope).getBoolean(key, fallbackValue); } - getInteger(key: string, scope: StorageScope, fallbackValue?: number): number { + getInteger(key: string, scope: StorageScope, fallbackValue: number): number; + getInteger(key: string, scope: StorageScope): number | undefined; + getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { return this.getStorage(scope).getInteger(key, fallbackValue); } @@ -128,7 +157,7 @@ export class StorageService extends Disposable implements IStorageService { ]).then(() => void 0); } - private getStorage(scope: StorageScope): Storage { + private getStorage(scope: StorageScope): IStorage { return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; } @@ -155,39 +184,68 @@ export class StorageService extends Disposable implements IStorageService { } }; - const globalItems = Object.create(null); - const globalItemsParsed = Object.create(null); + const globalItems = new Map(); + const globalItemsParsed = new Map(); result[0].forEach((value, key) => { - globalItems[key] = value; - globalItemsParsed[key] = safeParse(value); + globalItems.set(key, value); + globalItemsParsed.set(key, safeParse(value)); }); - const workspaceItems = Object.create(null); - const workspaceItemsParsed = Object.create(null); + const workspaceItems = new Map(); + const workspaceItemsParsed = new Map(); result[1].forEach((value, key) => { - workspaceItems[key] = value; - workspaceItemsParsed[key] = safeParse(value); + workspaceItems.set(key, value); + workspaceItemsParsed.set(key, safeParse(value)); }); - console.group(`Storage: Global (check: ${result[2]})`); - console.table(globalItems); + console.group(`Storage: Global (integrity: ${result[2]}, load: ${getDuration('willInitGlobalStorage', 'didInitGlobalStorage')}, path: ${this.globalStorageWorkspacePath})`); + let globalValues = []; + globalItems.forEach((value, key) => { + globalValues.push({ key, value }); + }); + console.table(globalValues); console.groupEnd(); console.log(globalItemsParsed); - console.group(`Storage: Workspace (check: ${result[3]})`); - console.table(workspaceItems); + console.group(`Storage: Workspace (integrity: ${result[3]}, load: ${getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage')}, path: ${this.workspaceStoragePath})`); + let workspaceValues = []; + workspaceItems.forEach((value, key) => { + workspaceValues.push({ key, value }); + }); + console.table(workspaceValues); console.groupEnd(); console.log(workspaceItemsParsed); }); } + + migrate(toWorkspaceStorageFolder: string): Thenable { + if (this.workspaceStoragePath === StorageService.IN_MEMORY_PATH) { + return Promise.resolve(); // no migration needed if running in memory + } + + // Compute new workspace storage path based on workspace identifier + const newWorkspaceStoragePath = join(toWorkspaceStorageFolder, basename(this.workspaceStoragePath)); + if (this.workspaceStoragePath === newWorkspaceStoragePath) { + return Promise.resolve(); // guard against migrating to same path + } + + // Close workspace DB to be able to copy + return this.workspaceStorage.close().then(() => { + return copy(this.workspaceStoragePath, newWorkspaceStoragePath).then(() => { + this.createWorkspaceStorage(newWorkspaceStoragePath); + + return this.workspaceStorage.init(); + }); + }); + } } export class LogStorageAction extends Action { static readonly ID = 'workbench.action.logStorage'; - static LABEL = localize('logStorage', "Log Storage"); + static LABEL = localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"); constructor( id: string, @@ -215,14 +273,18 @@ export class DelegatingStorageService extends Disposable implements IStorageServ get onWillSaveState(): Event { return this._onWillSaveState.event; } private closed: boolean; + private useLegacyWorkspaceStorage: boolean; constructor( - @IStorageService private storageService: StorageService, - @IStorageLegacyService private storageLegacyService: IStorageLegacyService, - @ILogService private logService: ILogService + private storageService: IStorageService, + private storageLegacyService: IStorageLegacyService, + private logService: ILogService, + configurationService: IConfigurationService ) { super(); + this.useLegacyWorkspaceStorage = configurationService.inspect('workbench.enableLegacyStorage').value === true; + this.registerListeners(); } @@ -231,54 +293,42 @@ export class DelegatingStorageService extends Disposable implements IStorageServ this._register(this.storageService.onWillSaveState(() => this._onWillSaveState.fire())); const globalKeyMarker = 'storage://global/'; - this._register(addDisposableListener(window, 'storage', (e: StorageEvent) => { + + window.addEventListener('storage', e => { if (startsWith(e.key, globalKeyMarker)) { const key = e.key.substr(globalKeyMarker.length); this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL }); } - })); + }); } get storage(): StorageService { - return this.storageService; + return this.storageService as StorageService; } - get(key: string, scope: StorageScope, fallbackValue?: any): string { - const localStorageValue = this.storageLegacyService.get(key, this.convertScope(scope), fallbackValue); - const dbValue = this.storageService.get(key, scope, localStorageValue); + get(key: string, scope: StorageScope, fallbackValue?: string): string { + if (scope === StorageScope.WORKSPACE && !this.useLegacyWorkspaceStorage) { + return this.storageService.get(key, scope, fallbackValue); + } - return this.assertAndGet(key, scope, dbValue, localStorageValue); + return this.storageLegacyService.get(key, this.convertScope(scope), fallbackValue); } getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean { - const localStorageValue = this.storageLegacyService.getBoolean(key, this.convertScope(scope), fallbackValue); - const dbValue = this.storageService.getBoolean(key, scope, localStorageValue); + if (scope === StorageScope.WORKSPACE && !this.useLegacyWorkspaceStorage) { + return this.storageService.getBoolean(key, scope, fallbackValue); + } - return this.assertAndGet(key, scope, dbValue, localStorageValue); + return this.storageLegacyService.getBoolean(key, this.convertScope(scope), fallbackValue); } getInteger(key: string, scope: StorageScope, fallbackValue?: number): number { - const localStorageValue = this.storageLegacyService.getInteger(key, this.convertScope(scope), fallbackValue); - const dbValue = this.storageService.getInteger(key, scope, localStorageValue); - - return this.assertAndGet(key, scope, dbValue, localStorageValue); - } - - private assertAndGet(key: string, scope: StorageScope, dbValue: any, localStorageValue: any): any { - if (scope === StorageScope.WORKSPACE) { - this.assertStorageValue(key, scope, dbValue, localStorageValue); - - return dbValue; + if (scope === StorageScope.WORKSPACE && !this.useLegacyWorkspaceStorage) { + return this.storageService.getInteger(key, scope, fallbackValue); } - return localStorageValue; - } - - private assertStorageValue(key: string, scope: StorageScope, dbValue: any, localStorageValue: any): void { - if (dbValue !== localStorageValue) { - this.logService.error(`Unexpected storage value (key: ${key}, scope: ${scope === StorageScope.GLOBAL ? 'global' : 'workspace'}), actual: ${dbValue}, expected: ${localStorageValue}`); - } + return this.storageLegacyService.getInteger(key, this.convertScope(scope), fallbackValue); } store(key: string, value: any, scope: StorageScope): void { @@ -306,7 +356,7 @@ export class DelegatingStorageService extends Disposable implements IStorageServ } close(): Promise { - const promise = this.storageService.close(); + const promise = this.storage.close(); this.closed = true; diff --git a/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts b/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts index 73feeedbe5d..9090fffa434 100644 --- a/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts +++ b/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { StorageLegacyScope, StorageLegacyService } from 'vs/platform/storage/common/storageLegacyService'; -import { parseStorage, migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/storageLegacyMigration'; +import { parseStorage } from 'vs/platform/storage/common/storageLegacyMigration'; import { URI } from 'vs/base/common/uri'; import { startsWith } from 'vs/base/common/strings'; @@ -154,96 +154,4 @@ suite('Storage Migration', () => { return service; } - - test('Migrate Storage (folder (Unix) => multi root)', () => { - const workspaceToMigrateFrom = URI.file('/some/folder/folder1').toString(); - createService(workspaceToMigrateFrom); - - const workspaceToMigrateTo = URI.from({ path: '1500007676869', scheme: 'root' }).toString(); - - migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '1500007676869', configPath: null }, storage); - - const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo); - const parsed = parseStorage(storage); - - assert.equal(parsed.empty.size, 0); - assert.equal(parsed.folder.size, 1); - assert.equal(parsed.multiRoot.size, 1); - - const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo); - assert.equal(Object.keys(migratedStorage).length, 4); - assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE)); - }); - - test('Migrate Storage (folder (Windows) => multi root)', () => { - const workspaceToMigrateFrom = URI.file('c:\\some\\folder\\folder1').toString(); - createService(workspaceToMigrateFrom); - - const workspaceToMigrateTo = URI.from({ path: '1500007676869', scheme: 'root' }).toString(); - - migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '1500007676869', configPath: null }, storage); - - const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo); - const parsed = parseStorage(storage); - - assert.equal(parsed.empty.size, 0); - assert.equal(parsed.folder.size, 1); - assert.equal(parsed.multiRoot.size, 1); - - const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo); - assert.equal(Object.keys(migratedStorage).length, 4); - assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE)); - }); - - test('Migrate Storage (folder (Windows UNC) => multi root)', () => { - const workspaceToMigrateFrom = 'file://localhost/c%3A/some/folder/folder1'; - createService(workspaceToMigrateFrom); - - const workspaceToMigrateTo = URI.from({ path: '1500007676869', scheme: 'root' }).toString(); - - migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '1500007676869', configPath: null }, storage); - - const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo); - const parsed = parseStorage(storage); - - assert.equal(parsed.empty.size, 0); - assert.equal(parsed.folder.size, 1); - assert.equal(parsed.multiRoot.size, 1); - - const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo); - assert.equal(Object.keys(migratedStorage).length, 4); - assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE)); - }); - - test('Migrate Storage (empty => multi root)', () => { - const workspaceToMigrateFrom = URI.from({ path: '1500007676869', scheme: 'empty' }).toString(); - createService(workspaceToMigrateFrom); - - const workspaceToMigrateTo = URI.from({ path: '2500007676869', scheme: 'root' }).toString(); - - migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '2500007676869', configPath: null }, storage); - - const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo); - const parsed = parseStorage(storage); - - assert.equal(parsed.empty.size, 1); - assert.equal(parsed.folder.size, 0); - assert.equal(parsed.multiRoot.size, 1); - - const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo); - assert.equal(Object.keys(migratedStorage).length, 4); - assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE)); - assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE)); - }); }); \ No newline at end of file diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts deleted file mode 100644 index 960884afd3f..00000000000 --- a/src/vs/platform/storage/test/common/storageService.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { StorageScope } from 'vs/platform/storage/common/storage'; -// tslint:disable-next-line:import-patterns -import { TestStorageService } from 'vs/workbench/test/workbenchTestServices'; - -suite('StorageService', () => { - - test('Remove Data', () => { - let storage = new TestStorageService(); - storage.store('Monaco.IDE.Core.Storage.Test.remove', 'foobar', StorageScope.GLOBAL); - assert.strictEqual('foobar', storage.get('Monaco.IDE.Core.Storage.Test.remove', StorageScope.GLOBAL)); - - storage.remove('Monaco.IDE.Core.Storage.Test.remove', StorageScope.GLOBAL); - assert.ok(!storage.get('Monaco.IDE.Core.Storage.Test.remove', StorageScope.GLOBAL)); - }); - - test('Get Data, Integer, Boolean', () => { - let storage = new TestStorageService(); - - assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL, 'foobar'), 'foobar'); - assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL, ''), ''); - assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL, 5), 5); - assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL, 0), 0); - assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL, true), true); - assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL, false), false); - - storage.store('Monaco.IDE.Core.Storage.Test.get', 'foobar', StorageScope.GLOBAL); - assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL), 'foobar'); - - storage.store('Monaco.IDE.Core.Storage.Test.get', '', StorageScope.GLOBAL); - assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL), ''); - - storage.store('Monaco.IDE.Core.Storage.Test.getInteger', 5, StorageScope.GLOBAL); - assert.strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL), 5); - - storage.store('Monaco.IDE.Core.Storage.Test.getInteger', 0, StorageScope.GLOBAL); - assert.strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL), 0); - - storage.store('Monaco.IDE.Core.Storage.Test.getBoolean', true, StorageScope.GLOBAL); - assert.strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL), true); - - storage.store('Monaco.IDE.Core.Storage.Test.getBoolean', false, StorageScope.GLOBAL); - assert.strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL), false); - - assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getDefault', StorageScope.GLOBAL, 'getDefault'), 'getDefault'); - assert.strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getIntegerDefault', StorageScope.GLOBAL, 5), 5); - assert.strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBooleanDefault', StorageScope.GLOBAL, true), true); - }); -}); \ No newline at end of file diff --git a/src/vs/platform/storage/test/node/storageService.test.ts b/src/vs/platform/storage/test/node/storageService.test.ts new file mode 100644 index 00000000000..455dfc72e4a --- /dev/null +++ b/src/vs/platform/storage/test/node/storageService.test.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { strictEqual, ok, equal } from 'assert'; +import { StorageScope } from 'vs/platform/storage/common/storage'; +import { TestStorageService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; +import { StorageService } from 'vs/platform/storage/node/storageService'; +import { generateUuid } from 'vs/base/common/uuid'; +import { join } from 'path'; +import { tmpdir } from 'os'; +import { mkdirp, del } from 'vs/base/node/pfs'; +import { NullLogService } from 'vs/platform/log/common/log'; + +suite('StorageService', () => { + + test('Remove Data (global, in-memory)', () => { + removeData(StorageScope.GLOBAL); + }); + + test('Remove Data (workspace, in-memory)', () => { + removeData(StorageScope.WORKSPACE); + }); + + function removeData(scope: StorageScope): void { + const storage = new TestStorageService(); + + storage.store('Monaco.IDE.Core.Storage.Test.remove', 'foobar', scope); + strictEqual('foobar', storage.get('Monaco.IDE.Core.Storage.Test.remove', scope)); + + storage.remove('Monaco.IDE.Core.Storage.Test.remove', scope); + ok(!storage.get('Monaco.IDE.Core.Storage.Test.remove', scope)); + } + + test('Get Data, Integer, Boolean (global, in-memory)', () => { + storeData(StorageScope.GLOBAL); + }); + + test('Get Data, Integer, Boolean (workspace, in-memory)', () => { + storeData(StorageScope.WORKSPACE); + }); + + function storeData(scope: StorageScope): void { + const storage = new TestStorageService(); + + strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', scope, 'foobar'), 'foobar'); + strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', scope, ''), ''); + strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', scope, 5), 5); + strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', scope, 0), 0); + strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', scope, true), true); + strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', scope, false), false); + + storage.store('Monaco.IDE.Core.Storage.Test.get', 'foobar', scope); + strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', scope), 'foobar'); + + storage.store('Monaco.IDE.Core.Storage.Test.get', '', scope); + strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', scope), ''); + + storage.store('Monaco.IDE.Core.Storage.Test.getInteger', 5, scope); + strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', scope), 5); + + storage.store('Monaco.IDE.Core.Storage.Test.getInteger', 0, scope); + strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', scope), 0); + + storage.store('Monaco.IDE.Core.Storage.Test.getBoolean', true, scope); + strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', scope), true); + + storage.store('Monaco.IDE.Core.Storage.Test.getBoolean', false, scope); + strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', scope), false); + + strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getDefault', scope, 'getDefault'), 'getDefault'); + strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getIntegerDefault', scope, 5), 5); + strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBooleanDefault', scope, true), true); + } + + function uniqueStorageDir(): string { + const id = generateUuid(); + + return join(tmpdir(), 'vsctests', id, 'storage2', id); + } + + test('Migrate Data', async () => { + const storageDir = uniqueStorageDir(); + await mkdirp(storageDir); + + const storage = new StorageService(join(storageDir, 'storage.db'), false, new NullLogService(), TestEnvironmentService); + await storage.init(); + + storage.store('bar', 'foo', StorageScope.WORKSPACE); + storage.store('barNumber', 55, StorageScope.WORKSPACE); + storage.store('barBoolean', true, StorageScope.GLOBAL); + + const newStorageDir = uniqueStorageDir(); + await mkdirp(newStorageDir); + + await storage.migrate(newStorageDir); + + equal(storage.get('bar', StorageScope.WORKSPACE), 'foo'); + equal(storage.getInteger('barNumber', StorageScope.WORKSPACE), 55); + equal(storage.getBoolean('barBoolean', StorageScope.GLOBAL), true); + + await storage.close(); + await del(newStorageDir, tmpdir()); + await del(storageDir, tmpdir()); + }); +}); \ No newline at end of file diff --git a/src/vs/platform/telemetry/common/telemetry.ts b/src/vs/platform/telemetry/common/telemetry.ts index 0c25b3e5877..159ad58dabe 100644 --- a/src/vs/platform/telemetry/common/telemetry.ts +++ b/src/vs/platform/telemetry/common/telemetry.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const ITelemetryService = createDecorator('telemetryService'); @@ -28,9 +27,9 @@ export interface ITelemetryService { * Sends a telemetry event that has been privacy approved. * Do not call this unless you have been given approval. */ - publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): TPromise; + publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Thenable; - getTelemetryInfo(): TPromise; + getTelemetryInfo(): Thenable; isOptedIn: boolean; } diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index 3d8af6dab79..7abf8d3d4e3 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -10,14 +10,13 @@ import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils' import { optional } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { cloneAndChange, mixin } from 'vs/base/common/objects'; import { Registry } from 'vs/platform/registry/common/platform'; export interface ITelemetryServiceConfig { appender: ITelemetryAppender; - commonProperties?: TPromise<{ [name: string]: any }>; + commonProperties?: Thenable<{ [name: string]: any }>; piiPaths?: string[]; } @@ -29,7 +28,7 @@ export class TelemetryService implements ITelemetryService { _serviceBrand: any; private _appender: ITelemetryAppender; - private _commonProperties: TPromise<{ [name: string]: any; }>; + private _commonProperties: Thenable<{ [name: string]: any; }>; private _piiPaths: string[]; private _userOptIn: boolean; @@ -41,7 +40,7 @@ export class TelemetryService implements ITelemetryService { @optional(IConfigurationService) private _configurationService: IConfigurationService ) { this._appender = config.appender; - this._commonProperties = config.commonProperties || TPromise.as({}); + this._commonProperties = config.commonProperties || Promise.resolve({}); this._piiPaths = config.piiPaths || []; this._userOptIn = true; @@ -73,7 +72,7 @@ export class TelemetryService implements ITelemetryService { return this._userOptIn; } - getTelemetryInfo(): TPromise { + getTelemetryInfo(): Thenable { return this._commonProperties.then(values => { // well known properties let sessionId = values['sessionID']; @@ -88,10 +87,10 @@ export class TelemetryService implements ITelemetryService { this._disposables = dispose(this._disposables); } - publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): TPromise { + publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Thenable { // don't send events when the user is optout if (!this._userOptIn) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } return this._commonProperties.then(values => { @@ -169,4 +168,4 @@ Registry.as(Extensions.Configuration).registerConfigurat 'tags': ['usesOnlineServices'] } } -}); \ No newline at end of file +}); diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index dcd0abacbed..22b6ed96274 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable } from 'vs/base/common/lifecycle'; import { guessMimeTypes } from 'vs/base/common/mime'; import * as paths from 'vs/base/common/paths'; @@ -16,11 +15,11 @@ import { ILogService } from 'vs/platform/log/common/log'; export const NullTelemetryService = new class implements ITelemetryService { _serviceBrand: undefined; publicLog(eventName: string, data?: ITelemetryData) { - return TPromise.wrap(void 0); + return Promise.resolve(void 0); } isOptedIn: true; - getTelemetryInfo(): TPromise { - return TPromise.wrap({ + getTelemetryInfo(): Promise { + return Promise.resolve({ instanceId: 'someValue.instanceId', sessionId: 'someValue.sessionId', machineId: 'someValue.machineId' @@ -30,17 +29,17 @@ export const NullTelemetryService = new class implements ITelemetryService { export interface ITelemetryAppender { log(eventName: string, data: any): void; - dispose(): TPromise; + dispose(): Thenable; } export function combinedAppender(...appenders: ITelemetryAppender[]): ITelemetryAppender { return { log: (e, d) => appenders.forEach(a => a.log(e, d)), - dispose: () => TPromise.join(appenders.map(a => a.dispose())) + dispose: () => Promise.all(appenders.map(a => a.dispose())) }; } -export const NullAppender: ITelemetryAppender = { log: () => null, dispose: () => TPromise.as(null) }; +export const NullAppender: ITelemetryAppender = { log: () => null, dispose: () => Promise.resolve(null) }; export class LogAppender implements ITelemetryAppender { @@ -48,8 +47,8 @@ export class LogAppender implements ITelemetryAppender { private commonPropertiesRegex = /^sessionID$|^version$|^timestamp$|^commitHash$|^common\./; constructor(@ILogService private readonly _logService: ILogService) { } - dispose(): TPromise { - return TPromise.as(undefined); + dispose(): Promise { + return Promise.resolve(undefined); } log(eventName: string, data: any): void { @@ -66,19 +65,21 @@ export class LogAppender implements ITelemetryAppender { /* __GDPR__FRAGMENT__ "URIDescriptor" : { "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "scheme": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "path": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ export interface URIDescriptor { mimeType?: string; + scheme?: string; ext?: string; path?: string; } export function telemetryURIDescriptor(uri: URI, hashPath: (path: string) => string): URIDescriptor { const fsPath = uri && uri.fsPath; - return fsPath ? { mimeType: guessMimeTypes(fsPath).join(', '), ext: paths.extname(fsPath), path: hashPath(fsPath) } : {}; + return fsPath ? { mimeType: guessMimeTypes(fsPath).join(', '), scheme: uri.scheme, ext: paths.extname(fsPath), path: hashPath(fsPath) } : {}; } /** @@ -134,6 +135,7 @@ const configurationValueWhitelist = [ 'editor.overviewRulerLanes', 'editor.overviewRulerBorder', 'editor.cursorBlinking', + 'editor.cursorSmoothCaretAnimation', 'editor.cursorStyle', 'editor.mouseWheelZoom', 'editor.fontLigatures', @@ -157,6 +159,7 @@ const configurationValueWhitelist = [ 'breadcrumbs.enabled', 'breadcrumbs.filePath', 'breadcrumbs.symbolPath', + 'breadcrumbs.symbolSortOrder', 'breadcrumbs.useQuickPick', 'explorer.openEditors.visible', 'extensions.autoUpdate', diff --git a/src/vs/platform/telemetry/node/appInsightsAppender.ts b/src/vs/platform/telemetry/node/appInsightsAppender.ts index 857443a4f7d..96440af93ec 100644 --- a/src/vs/platform/telemetry/node/appInsightsAppender.ts +++ b/src/vs/platform/telemetry/node/appInsightsAppender.ts @@ -6,7 +6,6 @@ import * as appInsights from 'applicationinsights'; import { isObject } from 'vs/base/common/types'; import { safeStringify, mixin } from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { ILogService } from 'vs/platform/log/common/log'; @@ -144,9 +143,9 @@ export class AppInsightsAppender implements ITelemetryAppender { }); } - dispose(): TPromise { + dispose(): Promise { if (this._aiClient) { - return new TPromise(resolve => { + return new Promise(resolve => { this._aiClient.flush({ callback: () => { // all data flushed @@ -158,4 +157,4 @@ export class AppInsightsAppender implements ITelemetryAppender { } return undefined; } -} \ No newline at end of file +} diff --git a/src/vs/platform/telemetry/node/commonProperties.ts b/src/vs/platform/telemetry/node/commonProperties.ts index d02cd24f2de..57330663627 100644 --- a/src/vs/platform/telemetry/node/commonProperties.ts +++ b/src/vs/platform/telemetry/node/commonProperties.ts @@ -5,11 +5,10 @@ import * as Platform from 'vs/base/common/platform'; import * as os from 'os'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as uuid from 'vs/base/common/uuid'; import { readFile } from 'vs/base/node/pfs'; -export function resolveCommonProperties(commit: string, version: string, machineId: string, installSourcePath: string): TPromise<{ [name: string]: string; }> { +export function resolveCommonProperties(commit: string, version: string, machineId: string, installSourcePath: string): Promise<{ [name: string]: string; }> { const result: { [name: string]: string; } = Object.create(null); // __GDPR__COMMON__ "common.machineId" : { "endPoint": "MacAddressHash", "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" } result['common.machineId'] = machineId; @@ -58,4 +57,4 @@ export function resolveCommonProperties(commit: string, version: string, machine }, error => { return result; }); -} \ No newline at end of file +} diff --git a/src/vs/platform/telemetry/node/telemetryIpc.ts b/src/vs/platform/telemetry/node/telemetryIpc.ts index 9acd46151c0..7952f069535 100644 --- a/src/vs/platform/telemetry/node/telemetryIpc.ts +++ b/src/vs/platform/telemetry/node/telemetryIpc.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { Event } from 'vs/base/common/event'; @@ -23,12 +22,12 @@ export class TelemetryAppenderChannel implements ITelemetryAppenderChannel { constructor(private appender: ITelemetryAppender) { } listen(event: string, arg?: any): Event { - throw new Error('No events'); + throw new Error(`Event not found: ${event}`); } call(command: string, { eventName, data }: ITelemetryLog): Thenable { this.appender.log(eventName, data); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -38,12 +37,12 @@ export class TelemetryAppenderClient implements ITelemetryAppender { log(eventName: string, data?: any): any { this.channel.call('log', { eventName, data }) - .then(null, err => `Failed to log telemetry: ${console.warn(err)}`); + .then(undefined, err => `Failed to log telemetry: ${console.warn(err)}`); - return TPromise.as(null); + return Promise.resolve(null); } dispose(): any { // TODO } -} \ No newline at end of file +} diff --git a/src/vs/platform/telemetry/node/telemetryNodeUtils.ts b/src/vs/platform/telemetry/node/telemetryNodeUtils.ts index 808c678c574..6c42c1e752f 100644 --- a/src/vs/platform/telemetry/node/telemetryNodeUtils.ts +++ b/src/vs/platform/telemetry/node/telemetryNodeUtils.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; + import { URI } from 'vs/base/common/uri'; import product from 'vs/platform/node/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -export function addGAParameters(telemetryService: ITelemetryService, environmentService: IEnvironmentService, uri: URI, origin: string, experiment = '1'): TPromise { +export function addGAParameters(telemetryService: ITelemetryService, environmentService: IEnvironmentService, uri: URI, origin: string, experiment = '1'): Thenable { if (environmentService.isBuilt && !environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { if (uri.scheme === 'https' && uri.authority === 'code.visualstudio.com') { return telemetryService.getTelemetryInfo() @@ -18,5 +18,5 @@ export function addGAParameters(telemetryService: ITelemetryService, environment }); } } - return TPromise.as(uri); + return Promise.resolve(uri); } diff --git a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts index 5f722afccb4..e870150ac11 100644 --- a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts @@ -3,19 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import * as uuid from 'vs/base/common/uuid'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -export function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string, version: string, machineId: string, installSourcePath: string): TPromise<{ [name: string]: string }> { +export const lastSessionDateStorageKey = 'telemetry.lastSessionDate'; + +export function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string, version: string, machineId: string, installSourcePath: string): Promise<{ [name: string]: string }> { return resolveCommonProperties(commit, version, machineId, installSourcePath).then(result => { // __GDPR__COMMON__ "common.version.shell" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.version.shell'] = process.versions && (process).versions['electron']; // __GDPR__COMMON__ "common.version.renderer" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.version.renderer'] = process.versions && (process).versions['chrome']; - const lastSessionDate = storageService.get('telemetry.lastSessionDate', StorageScope.GLOBAL); + const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); storageService.store('telemetry.lastSessionDate', new Date().toUTCString(), StorageScope.GLOBAL); // __GDPR__COMMON__ "common.firstSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } @@ -47,4 +48,4 @@ function getOrCreateFirstSessionDate(storageService: IStorageService): string { storageService.store(key, firstSessionDate, StorageScope.GLOBAL); return firstSessionDate; -} \ No newline at end of file +} diff --git a/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts b/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts index 36776bf11f3..9b127e465e4 100644 --- a/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts @@ -9,7 +9,7 @@ import * as fs from 'fs'; import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/node/workbenchCommonProperties'; import { getRandomTestPath, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { StorageService } from 'vs/platform/storage/electron-browser/storageService'; +import { StorageService } from 'vs/platform/storage/node/storageService'; import { NullLogService } from 'vs/platform/log/common/log'; import { del } from 'vs/base/node/extfs'; import { mkdirp } from 'vs/base/node/pfs'; @@ -24,7 +24,7 @@ suite('Telemetry - common properties', function () { let nestStorage2Service: IStorageService; setup(() => { - nestStorage2Service = new StorageService(':memory:', new NullLogService(), TestEnvironmentService); + nestStorage2Service = new StorageService(':memory:', false, new NullLogService(), TestEnvironmentService); }); teardown(done => { diff --git a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts index 8ae67b193be..3272a7f9ccc 100644 --- a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { NullAppender, ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import * as Errors from 'vs/base/common/errors'; import * as sinon from 'sinon'; import { getConfigurationValue } from 'vs/platform/configuration/common/configuration'; +import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; class TestTelemetryAppender implements ITelemetryAppender { @@ -30,9 +30,9 @@ class TestTelemetryAppender implements ITelemetryAppender { return this.events.length; } - public dispose(): TPromise { + public dispose(): Promise { this.isDisposed = true; - return TPromise.as(null); + return Promise.resolve(null); } } @@ -139,7 +139,7 @@ suite('TelemetryService', () => { let testAppender = new TestTelemetryAppender(); let service = new TelemetryService({ appender: testAppender, - commonProperties: TPromise.as({ foo: 'JA!', get bar() { return Math.random(); } }) + commonProperties: Promise.resolve({ foo: 'JA!', get bar() { return Math.random(); } }) }, undefined); return service.publicLog('testEvent').then(_ => { @@ -157,7 +157,7 @@ suite('TelemetryService', () => { let testAppender = new TestTelemetryAppender(); let service = new TelemetryService({ appender: testAppender, - commonProperties: TPromise.as({ foo: 'JA!', get bar() { return Math.random(); } }) + commonProperties: Promise.resolve({ foo: 'JA!', get bar() { return Math.random(); } }) }, undefined); return service.publicLog('testEvent', { hightower: 'xl', price: 8000 }).then(_ => { @@ -176,7 +176,7 @@ suite('TelemetryService', () => { test('TelemetryInfo comes from properties', function () { let service = new TelemetryService({ appender: NullAppender, - commonProperties: TPromise.as({ + commonProperties: Promise.resolve({ sessionID: 'one', ['common.instanceId']: 'two', ['common.machineId']: 'three', @@ -204,14 +204,29 @@ suite('TelemetryService', () => { }); })); - test('Error events', sinon.test(function (this: any) { + class JoinableTelemetryService extends TelemetryService { + + private readonly promises: Thenable[] = []; + + join(): Promise { + return Promise.all(this.promises); + } + + publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Thenable { + let p = super.publicLog(eventName, data, anonymizeFilePaths); + this.promises.push(p); + return p; + } + } + + test('Error events', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); try { let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); @@ -223,6 +238,8 @@ suite('TelemetryService', () => { Errors.onUnexpectedError(e); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); + assert.equal(testAppender.getEventsCount(), 1); assert.equal(testAppender.events[0].eventName, 'UnhandledError'); assert.equal(testAppender.events[0].data.msg, 'This is a test.'); @@ -263,17 +280,18 @@ suite('TelemetryService', () => { // } // })); - test('Handle global errors', sinon.test(function (this: any) { + test('Handle global errors', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let testError = new Error('test'); (window.onerror)('Error Message', 'file.js', 2, 42, testError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.alwaysCalledWithExactly('Error Message', 'file.js', 2, 42, testError), true); assert.equal(errorStub.callCount, 1); @@ -290,12 +308,12 @@ suite('TelemetryService', () => { service.dispose(); })); - test('Error Telemetry removes PII from filename with spaces', sinon.test(function (this: any) { + test('Error Telemetry removes PII from filename with spaces', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let personInfoWithSpaces = settings.personalInfo.slice(0, 2) + ' ' + settings.personalInfo.slice(2); @@ -303,6 +321,7 @@ suite('TelemetryService', () => { dangerousFilenameError.stack = settings.stack; (window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo.replace(settings.personalInfo, personInfoWithSpaces) + '/test.js', 2, 42, dangerousFilenameError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo.replace(settings.personalInfo, personInfoWithSpaces)), -1); @@ -313,47 +332,51 @@ suite('TelemetryService', () => { })); test('Uncaught Error Telemetry removes PII from filename', sinon.test(function (this: any) { + let clock = this.clock; let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousFilenameError: any = new Error('dangerousFilename'); dangerousFilenameError.stack = settings.stack; (window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo + '/test.js', 2, 42, dangerousFilenameError); - this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + return service.join().then(() => { + assert.equal(errorStub.callCount, 1); + assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1); - assert.equal(errorStub.callCount, 1); - assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1); + dangerousFilenameError = new Error('dangerousFilename'); + dangerousFilenameError.stack = settings.stack; + (window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo + '/test.js', 2, 42, dangerousFilenameError); + clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + return service.join(); + }).then(() => { + assert.equal(errorStub.callCount, 2); + assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1); + assert.equal(testAppender.events[0].data.file, settings.importantInfo + '/test.js'); - dangerousFilenameError = new Error('dangerousFilename'); - dangerousFilenameError.stack = settings.stack; - (window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo + '/test.js', 2, 42, dangerousFilenameError); - this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); - - assert.equal(errorStub.callCount, 2); - assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1); - assert.equal(testAppender.events[0].data.file, settings.importantInfo + '/test.js'); - - errorTelemetry.dispose(); - service.dispose(); + errorTelemetry.dispose(); + service.dispose(); + }); })); - test('Unexpected Error Telemetry removes PII', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithoutImportantInfoError: any = new Error(settings.dangerousPathWithoutImportantInfo); dangerousPathWithoutImportantInfoError.stack = settings.stack; Errors.onUnexpectedError(dangerousPathWithoutImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1); assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1); @@ -371,18 +394,19 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithoutImportantInfoError: any = new Error('dangerousPathWithoutImportantInfo'); dangerousPathWithoutImportantInfoError.stack = settings.stack; (window.onerror)(settings.dangerousPathWithoutImportantInfo, 'test.js', 2, 42, dangerousPathWithoutImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); // Test that no file information remains, esp. personal info @@ -397,7 +421,7 @@ suite('TelemetryService', () => { service.dispose(); })); - test('Unexpected Error Telemetry removes PII but preserves Code file path', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -405,7 +429,7 @@ suite('TelemetryService', () => { try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error(settings.dangerousPathWithImportantInfo); @@ -414,6 +438,7 @@ suite('TelemetryService', () => { // Test that important information remains but personal info does not Errors.onUnexpectedError(dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1); assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1); @@ -432,18 +457,19 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error('dangerousPathWithImportantInfo'); dangerousPathWithImportantInfoError.stack = settings.stack; (window.onerror)(settings.dangerousPathWithImportantInfo, 'test.js', 2, 42, dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); // Test that important information remains but personal info does not @@ -460,7 +486,7 @@ suite('TelemetryService', () => { service.dispose(); })); - test('Unexpected Error Telemetry removes PII but preserves Code file path with node modules', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII but preserves Code file path with node modules', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -468,7 +494,7 @@ suite('TelemetryService', () => { try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error(settings.dangerousPathWithImportantInfo); @@ -477,6 +503,7 @@ suite('TelemetryService', () => { Errors.onUnexpectedError(dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModuleAsarPathToRetain), -1); assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModulePathToRetain), -1); @@ -491,18 +518,19 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error('dangerousPathWithImportantInfo'); dangerousPathWithImportantInfoError.stack = settings.stack; (window.onerror)(settings.dangerousPathWithImportantInfo, 'test.js', 2, 42, dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); @@ -516,7 +544,7 @@ suite('TelemetryService', () => { })); - test('Unexpected Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -524,7 +552,7 @@ suite('TelemetryService', () => { try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender, piiPaths: [settings.personalInfo + '/resources/app/'] }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender, piiPaths: [settings.personalInfo + '/resources/app/'] }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error(settings.dangerousPathWithImportantInfo); @@ -533,6 +561,7 @@ suite('TelemetryService', () => { // Test that important information remains but personal info does not Errors.onUnexpectedError(dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1); assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1); @@ -551,18 +580,19 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender, piiPaths: [settings.personalInfo + '/resources/app/'] }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender, piiPaths: [settings.personalInfo + '/resources/app/'] }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error('dangerousPathWithImportantInfo'); dangerousPathWithImportantInfoError.stack = settings.stack; (window.onerror)(settings.dangerousPathWithImportantInfo, 'test.js', 2, 42, dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); // Test that important information remains but personal info does not @@ -579,7 +609,7 @@ suite('TelemetryService', () => { service.dispose(); })); - test('Unexpected Error Telemetry removes PII but preserves Missing Model error message', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII but preserves Missing Model error message', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -587,7 +617,7 @@ suite('TelemetryService', () => { try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let missingModelError: any = new Error(settings.missingModelMessage); @@ -597,6 +627,7 @@ suite('TelemetryService', () => { // error message does (Received model events for missing model) Errors.onUnexpectedError(missingModelError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.missingModelPrefix), -1); assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1); @@ -614,18 +645,19 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII but preserves Missing Model error message', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII but preserves Missing Model error message', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let missingModelError: any = new Error('missingModelMessage'); missingModelError.stack = settings.stack; (window.onerror)(settings.missingModelMessage, 'test.js', 2, 42, missingModelError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); // Test that no file information remains, but this particular @@ -643,7 +675,7 @@ suite('TelemetryService', () => { service.dispose(); })); - test('Unexpected Error Telemetry removes PII but preserves No Such File error message', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII but preserves No Such File error message', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -651,7 +683,7 @@ suite('TelemetryService', () => { try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let noSuchFileError: any = new Error(settings.noSuchFileMessage); @@ -661,6 +693,7 @@ suite('TelemetryService', () => { // error message does (ENOENT: no such file or directory) Errors.onUnexpectedError(noSuchFileError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.noSuchFilePrefix), -1); assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1); @@ -678,7 +711,7 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII but preserves No Such File error message', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII but preserves No Such File error message', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -687,13 +720,14 @@ suite('TelemetryService', () => { window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let noSuchFileError: any = new Error('noSuchFileMessage'); noSuchFileError.stack = settings.stack; (window.onerror)(settings.noSuchFileMessage, 'test.js', 2, 42, noSuchFileError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); // Test that no file information remains, but this particular @@ -770,4 +804,4 @@ suite('TelemetryService', () => { service.dispose(); }); -}); \ No newline at end of file +}); diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index a719d8647dd..21ea865016c 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -47,7 +47,7 @@ export interface ITheme { readonly type: ThemeType; /** - * Resolves the color of the given color identifer. If the theme does not + * Resolves the color of the given color identifier. If the theme does not * specify the color, the default color is returned unless useDefault is set to false. * @param color the id of the color * @param useDefault specifies if the default color should be used. If not set, the default is used. @@ -55,12 +55,18 @@ export interface ITheme { getColor(color: ColorIdentifier, useDefault?: boolean): Color | null; /** - * Returns wheter the theme defines a value for the color. If not, that means the + * Returns whether the theme defines a value for the color. If not, that means the * default color will be used. */ defines(color: ColorIdentifier): boolean; } +export interface IIconTheme { + readonly hasFileIcons: boolean; + readonly hasFolderIcons: boolean; + readonly hidesExplorerArrows: boolean; +} + export interface ICssStyleCollector { addRule(rule: string): void; } @@ -74,10 +80,11 @@ export interface IThemeService { getTheme(): ITheme; - /** - * Register a theming participant that is invoked after every theme change. - */ - onThemeChange: Event; + readonly onThemeChange: Event; + + getIconTheme(): IIconTheme; + + readonly onIconThemeChange: Event; } @@ -130,4 +137,4 @@ platform.Registry.add(Extensions.ThemingContribution, themingRegistry); export function registerThemingParticipant(participant: IThemingParticipant): IDisposable { return themingRegistry.onThemeChange(participant); -} \ No newline at end of file +} diff --git a/src/vs/platform/theme/test/common/testThemeService.ts b/src/vs/platform/theme/test/common/testThemeService.ts index f5e9c13a49e..08207173349 100644 --- a/src/vs/platform/theme/test/common/testThemeService.ts +++ b/src/vs/platform/theme/test/common/testThemeService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { IThemeService, ITheme, DARK } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ITheme, DARK, IIconTheme } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; export class TestTheme implements ITheme { @@ -25,14 +25,23 @@ export class TestTheme implements ITheme { } } +export class TestIconTheme implements IIconTheme { + hasFileIcons = false; + hasFolderIcons = false; + hidesExplorerArrows = false; +} + export class TestThemeService implements IThemeService { _serviceBrand: any; _theme: ITheme; + _iconTheme: IIconTheme; _onThemeChange = new Emitter(); + _onIconThemeChange = new Emitter(); - constructor(theme = new TestTheme()) { + constructor(theme = new TestTheme(), iconTheme = new TestIconTheme()) { this._theme = theme; + this._iconTheme = iconTheme; } getTheme(): ITheme { @@ -51,4 +60,12 @@ export class TestThemeService implements IThemeService { public get onThemeChange(): Event { return this._onThemeChange.event; } + + getIconTheme(): IIconTheme { + return this._iconTheme; + } + + public get onIconThemeChange(): Event { + return this._onIconThemeChange.event; + } } diff --git a/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts b/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts index 8be585ab3a7..b9bcc8db83e 100644 --- a/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts +++ b/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts @@ -35,7 +35,7 @@ interface DescriptionDiff { export const forceColorLoad = [editorMarkerNavigationError, overviewRulerModifiedForeground, STATUS_BAR_DEBUGGING_BACKGROUND, debugExceptionWidgetBackground, debugToolBarBackground, buttonBackground, embeddedEditorBackground]; -export const experimental = []; // 'settings.modifiedItemForeground', 'editorUnnecessary.foreground' ]; +export const experimental: string[] = []; // 'settings.modifiedItemForeground', 'editorUnnecessary.foreground' ]; suite('Color Registry', function () { diff --git a/src/vs/platform/update/common/update.ts b/src/vs/platform/update/common/update.ts index 676598c4d1d..b734fb12f3f 100644 --- a/src/vs/platform/update/common/update.ts +++ b/src/vs/platform/update/common/update.ts @@ -48,7 +48,8 @@ export const enum StateType { export const enum UpdateType { Setup, - Archive + Archive, + Snap } export type Uninitialized = { type: StateType.Uninitialized }; diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index b150f2daeb1..ec1ad60c73f 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -74,12 +74,12 @@ export abstract class AbstractUpdateService implements IUpdateService { this.setState(State.Idle(this.getUpdateType())); // Start checking for updates after 30 seconds - this.scheduleCheckForUpdates(30 * 1000).then(null, err => this.logService.error(err)); + this.scheduleCheckForUpdates(30 * 1000).then(undefined, err => this.logService.error(err)); } - private getProductQuality(): string { + private getProductQuality(): string | undefined { const quality = this.configurationService.getValue('update.channel'); - return quality === 'none' ? null : product.quality; + return quality === 'none' ? undefined : product.quality; } private scheduleCheckForUpdates(delay = 60 * 60 * 1000): Thenable { @@ -95,7 +95,7 @@ export abstract class AbstractUpdateService implements IUpdateService { this.logService.trace('update#checkForUpdates, state = ', this.state.type); if (this.state.type !== StateType.Idle) { - return TPromise.as(null); + return TPromise.as(void 0); } return this.throttler.queue(() => TPromise.as(this.doCheckForUpdates(context))); @@ -105,35 +105,35 @@ export abstract class AbstractUpdateService implements IUpdateService { this.logService.trace('update#downloadUpdate, state = ', this.state.type); if (this.state.type !== StateType.AvailableForDownload) { - return TPromise.as(null); + return TPromise.as(void 0); } return this.doDownloadUpdate(this.state); } protected doDownloadUpdate(state: AvailableForDownload): TPromise { - return TPromise.as(null); + return TPromise.as(void 0); } applyUpdate(): TPromise { this.logService.trace('update#applyUpdate, state = ', this.state.type); if (this.state.type !== StateType.Downloaded) { - return TPromise.as(null); + return TPromise.as(void 0); } return this.doApplyUpdate(); } protected doApplyUpdate(): TPromise { - return TPromise.as(null); + return TPromise.as(void 0); } quitAndInstall(): TPromise { this.logService.trace('update#quitAndInstall, state = ', this.state.type); if (this.state.type !== StateType.Ready) { - return TPromise.as(null); + return TPromise.as(void 0); } this.logService.trace('update#quitAndInstall(): before lifecycle quit()'); @@ -148,7 +148,7 @@ export abstract class AbstractUpdateService implements IUpdateService { this.doQuitAndInstall(); }); - return TPromise.as(null); + return TPromise.as(void 0); } isLatestVersion(): TPromise { diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index f418bf32d71..99923f036fe 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -107,7 +107,9 @@ export class DarwinUpdateService extends AbstractUpdateService { // we workaround this issue by forcing an explicit flush of the storage data. // see also https://github.com/Microsoft/vscode/issues/172 this.logService.trace('update#quitAndInstall(): calling flushStorageData()'); - electron.session.defaultSession.flushStorageData(); + if (electron.session.defaultSession) { + electron.session.defaultSession.flushStorageData(); + } this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); electron.autoUpdater.quitAndInstall(); diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index 44bef28ea45..05963efcf62 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -16,6 +16,9 @@ import { asJson } from 'vs/base/node/request'; import { TPromise } from 'vs/base/common/winjs.base'; import { shell } from 'electron'; import { CancellationToken } from 'vs/base/common/cancellation'; +import * as path from 'path'; +import { spawn } from 'child_process'; +import { realpath } from 'fs'; export class LinuxUpdateService extends AbstractUpdateService { @@ -43,33 +46,58 @@ export class LinuxUpdateService extends AbstractUpdateService { this.setState(State.CheckingForUpdates(context)); - this.requestService.request({ url: this.url }, CancellationToken.None) - .then(asJson) - .then(update => { - if (!update || !update.url || !update.version || !update.productVersion) { + if (process.env.SNAP && process.env.SNAP_REVISION) { + this.checkForSnapUpdate(); + } else { + this.requestService.request({ url: this.url }, CancellationToken.None) + .then(asJson) + .then(update => { + if (!update || !update.url || !update.version || !update.productVersion) { + /* __GDPR__ + "update:notAvailable" : { + "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); + + this.setState(State.Idle(UpdateType.Archive)); + } else { + this.setState(State.AvailableForDownload(update)); + } + }) + .then(null, err => { + this.logService.error(err); + /* __GDPR__ - "update:notAvailable" : { - "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } + "update:notAvailable" : { + "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } */ this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); + this.setState(State.Idle(UpdateType.Archive, err.message || err)); + }); + } + } - this.setState(State.Idle(UpdateType.Archive)); - } else { - this.setState(State.AvailableForDownload(update)); - } - }) - .then(null, err => { - this.logService.error(err); + private checkForSnapUpdate(): void { + // If the application was installed as a snap, updates happen in the + // background automatically, we just need to check to see if an update + // has already happened. + realpath(`/snap/${product.applicationName}/current`, (err, resolvedCurrentSnapPath) => { + if (err) { + this.logService.error('update#checkForSnapUpdate(): Could not get realpath of application.'); + return; + } - /* __GDPR__ - "update:notAvailable" : { - "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); - this.setState(State.Idle(UpdateType.Archive, err.message || err)); - }); + const currentRevision = path.basename(resolvedCurrentSnapPath); + + if (process.env.SNAP_REVISION !== currentRevision) { + // TODO@joao: snap + this.setState(State.Ready({ version: '', productVersion: '' })); + } else { + this.setState(State.Idle(UpdateType.Archive)); + } + }); } protected doDownloadUpdate(state: AvailableForDownload): TPromise { @@ -77,11 +105,28 @@ export class LinuxUpdateService extends AbstractUpdateService { // installed and the website download page is more useful than the tarball generally. if (product.downloadUrl && product.downloadUrl.length > 0) { shell.openExternal(product.downloadUrl); - } else { + } else if (state.update.url) { shell.openExternal(state.update.url); } this.setState(State.Idle(UpdateType.Archive)); - return TPromise.as(null); + return TPromise.as(void 0); + } + + protected doQuitAndInstall(): void { + this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); + + const snap = process.env.SNAP; + + // TODO@joao what to do? + if (!snap) { + return; + } + + // Allow 3 seconds for VS Code to close + spawn('bash', ['-c', path.join(snap, `usr/share/${product.applicationName}/snapUpdate.sh`)], { + detached: true, + stdio: ['ignore', 'ignore', 'ignore'] + }); } } diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts new file mode 100644 index 00000000000..682926213aa --- /dev/null +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -0,0 +1,217 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter, fromNodeEventEmitter, filterEvent, debounceEvent } from 'vs/base/common/event'; +import { Throttler, timeout } from 'vs/base/common/async'; +import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import product from 'vs/platform/node/product'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ILogService } from 'vs/platform/log/common/log'; +import * as path from 'path'; +import { realpath, watch } from 'fs'; +import { spawn } from 'child_process'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +abstract class AbstractUpdateService2 implements IUpdateService { + + _serviceBrand: any; + + private _state: State = State.Uninitialized; + private throttler: Throttler = new Throttler(); + + private _onStateChange = new Emitter(); + get onStateChange(): Event { return this._onStateChange.event; } + + get state(): State { + return this._state; + } + + protected setState(state: State): void { + this.logService.info('update#setState', state.type); + this._state = state; + this._onStateChange.fire(state); + } + + constructor( + @ILifecycleService private lifecycleService: ILifecycleService, + @IEnvironmentService environmentService: IEnvironmentService, + @ILogService protected logService: ILogService, + ) { + if (environmentService.disableUpdates) { + this.logService.info('update#ctor - updates are disabled'); + return; + } + + this.setState(State.Idle(this.getUpdateType())); + + // Start checking for updates after 30 seconds + this.scheduleCheckForUpdates(30 * 1000).then(undefined, err => this.logService.error(err)); + } + + private scheduleCheckForUpdates(delay = 60 * 60 * 1000): Thenable { + return timeout(delay) + .then(() => this.checkForUpdates(null)) + .then(() => { + // Check again after 1 hour + return this.scheduleCheckForUpdates(60 * 60 * 1000); + }); + } + + checkForUpdates(context: any): TPromise { + this.logService.trace('update#checkForUpdates, state = ', this.state.type); + + if (this.state.type !== StateType.Idle) { + return TPromise.as(void 0); + } + + return this.throttler.queue(() => TPromise.as(this.doCheckForUpdates(context))); + } + + downloadUpdate(): TPromise { + this.logService.trace('update#downloadUpdate, state = ', this.state.type); + + if (this.state.type !== StateType.AvailableForDownload) { + return TPromise.as(void 0); + } + + return this.doDownloadUpdate(this.state); + } + + protected doDownloadUpdate(state: AvailableForDownload): TPromise { + return TPromise.as(void 0); + } + + applyUpdate(): TPromise { + this.logService.trace('update#applyUpdate, state = ', this.state.type); + + if (this.state.type !== StateType.Downloaded) { + return TPromise.as(void 0); + } + + return this.doApplyUpdate(); + } + + protected doApplyUpdate(): TPromise { + return TPromise.as(void 0); + } + + quitAndInstall(): TPromise { + this.logService.trace('update#quitAndInstall, state = ', this.state.type); + + if (this.state.type !== StateType.Ready) { + return TPromise.as(void 0); + } + + this.logService.trace('update#quitAndInstall(): before lifecycle quit()'); + + this.lifecycleService.quit(true /* from update */).then(vetod => { + this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`); + if (vetod) { + return; + } + + this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); + this.doQuitAndInstall(); + }); + + return TPromise.as(void 0); + } + + + protected getUpdateType(): UpdateType { + return UpdateType.Snap; + } + + protected doQuitAndInstall(): void { + // noop + } + + abstract isLatestVersion(): TPromise; + protected abstract doCheckForUpdates(context: any): void; +} + +export class SnapUpdateService extends AbstractUpdateService2 { + + _serviceBrand: any; + + constructor( + @ILifecycleService lifecycleService: ILifecycleService, + @IEnvironmentService environmentService: IEnvironmentService, + @ILogService logService: ILogService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + super(lifecycleService, environmentService, logService); + + const watcher = watch(path.dirname(process.env.SNAP)); + const onChange = fromNodeEventEmitter(watcher, 'change', (_, fileName: string) => fileName); + const onCurrentChange = filterEvent(onChange, n => n === 'current'); + const onDebouncedCurrentChange = debounceEvent(onCurrentChange, (_, e) => e, 2000); + const listener = onDebouncedCurrentChange(this.checkForUpdates, this); + + lifecycleService.onShutdown(() => { + listener.dispose(); + watcher.close(); + }); + } + + protected doCheckForUpdates(context: any): void { + this.setState(State.CheckingForUpdates(context)); + + this.isUpdateAvailable().then(result => { + if (result) { + this.setState(State.Ready({ version: 'something', productVersion: 'someting' })); + } else { + /* __GDPR__ + "update:notAvailable" : { + "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); + + this.setState(State.Idle(UpdateType.Snap)); + } + }, err => { + this.logService.error(err); + + /* __GDPR__ + "update:notAvailable" : { + "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); + this.setState(State.Idle(UpdateType.Snap, err.message || err)); + }); + } + + protected doQuitAndInstall(): void { + this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); + + // Allow 3 seconds for VS Code to close + spawn('bash', ['-c', path.join(process.env.SNAP, `usr/share/${product.applicationName}/snapUpdate.sh`)], { + detached: true, + stdio: ['ignore', 'ignore', 'ignore'] + }); + } + + private isUpdateAvailable(): TPromise { + return new TPromise((c, e) => { + realpath(`/snap/${product.applicationName}/current`, (err, resolvedCurrentSnapPath) => { + if (err) { return e(err); } + + const currentRevision = path.basename(resolvedCurrentSnapPath); + c(process.env.SNAP_REVISION !== currentRevision); + }); + }); + } + + isLatestVersion(): TPromise { + return this.isUpdateAvailable().then(null, err => { + this.logService.error('update#checkForSnapUpdate(): Could not get realpath of application.'); + return undefined; + }); + } +} diff --git a/src/vs/platform/update/node/updateIpc.ts b/src/vs/platform/update/node/updateIpc.ts index 5581cf09f2e..24b7d3c731b 100644 --- a/src/vs/platform/update/node/updateIpc.ts +++ b/src/vs/platform/update/node/updateIpc.ts @@ -30,7 +30,7 @@ export class UpdateChannel implements IUpdateChannel { case 'onStateChange': return this.service.onStateChange; } - throw new Error('No event found'); + throw new Error(`Event not found: ${event}`); } call(command: string, arg?: any): TPromise { @@ -42,7 +42,8 @@ export class UpdateChannel implements IUpdateChannel { case '_getInitialState': return TPromise.as(this.service.state); case 'isLatestVersion': return this.service.isLatestVersion(); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } diff --git a/src/vs/platform/url/common/url.ts b/src/vs/platform/url/common/url.ts index 0ef98b6115a..f5813089729 100644 --- a/src/vs/platform/url/common/url.ts +++ b/src/vs/platform/url/common/url.ts @@ -6,18 +6,16 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; -export const ID = 'urlService'; -export const IURLService = createDecorator(ID); +export const IURLService = createDecorator('urlService'); export interface IURLHandler { - handleURL(uri: URI): TPromise; + handleURL(uri: URI): Thenable; } export interface IURLService { _serviceBrand: any; - open(url: URI): TPromise; + open(url: URI): Thenable; registerHandler(handler: IURLHandler): IDisposable; } diff --git a/src/vs/platform/url/common/urlService.ts b/src/vs/platform/url/common/urlService.ts index 1f3b94ce267..d70094077b3 100644 --- a/src/vs/platform/url/common/urlService.ts +++ b/src/vs/platform/url/common/urlService.ts @@ -6,7 +6,6 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { URI } from 'vs/base/common/uri'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { first } from 'vs/base/common/async'; declare module Array { @@ -19,7 +18,7 @@ export class URLService implements IURLService { private handlers = new Set(); - open(uri: URI): TPromise { + open(uri: URI): Thenable { const handlers = Array.from(this.handlers); return first(handlers.map(h => () => h.handleURL(uri)), undefined, false); } @@ -36,11 +35,11 @@ export class RelayURLService extends URLService implements IURLHandler { super(); } - open(uri: URI): TPromise { + open(uri: URI): Thenable { return this.urlService.open(uri); } - handleURL(uri: URI): TPromise { + handleURL(uri: URI): Thenable { return super.open(uri); } -} \ No newline at end of file +} diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts index f48fb62c570..233f7c2abc8 100644 --- a/src/vs/platform/url/electron-main/electronUrlListener.ts +++ b/src/vs/platform/url/electron-main/electronUrlListener.ts @@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; import { ReadyState } from 'vs/platform/windows/common/windows'; +import { isWindows } from 'vs/base/common/platform'; function uriFromRawUrl(url: string): URI | null { try { @@ -36,9 +37,15 @@ export class ElectronURLListener { ]; const buffer = rawBuffer.map(uriFromRawUrl).filter(uri => !!uri); - const flush = () => buffer.forEach(uri => urlService.open(uri)); + const flush = () => buffer.forEach(uri => { + if (uri) { + urlService.open(uri); + } + }); - app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, ['--open-url', '--']); + if (isWindows) { + app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, ['--open-url', '--']); + } const onOpenElectronUrl = mapEvent( fromNodeEventEmitter(app, 'open-url', (event: Electron.Event, url: string) => ({ event, url })), diff --git a/src/vs/platform/url/node/urlIpc.ts b/src/vs/platform/url/node/urlIpc.ts index 2721d7a0ab1..c723fc94adc 100644 --- a/src/vs/platform/url/node/urlIpc.ts +++ b/src/vs/platform/url/node/urlIpc.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -20,14 +19,15 @@ export class URLServiceChannel implements IURLServiceChannel { constructor(private service: IURLService) { } listen(event: string, arg?: any): Event { - throw new Error('No events'); + throw new Error(`Event not found: ${event}`); } call(command: string, arg?: any): Thenable { switch (command) { case 'open': return this.service.open(URI.revive(arg)); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -37,8 +37,8 @@ export class URLServiceChannelClient implements IURLService { constructor(private channel: IChannel) { } - open(url: URI): TPromise { - return TPromise.wrap(this.channel.call('open', url.toJSON())); + open(url: URI): Thenable { + return this.channel.call('open', url.toJSON()); } registerHandler(handler: IURLHandler): IDisposable { @@ -56,14 +56,15 @@ export class URLHandlerChannel implements IURLHandlerChannel { constructor(private handler: IURLHandler) { } listen(event: string, arg?: any): Event { - throw new Error('No events'); + throw new Error(`Event not found: ${event}`); } call(command: string, arg?: any): Thenable { switch (command) { case 'handleURL': return this.handler.handleURL(URI.revive(arg)); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -71,7 +72,7 @@ export class URLHandlerChannelClient implements IURLHandler { constructor(private channel: IChannel) { } - handleURL(uri: URI): TPromise { - return TPromise.wrap(this.channel.call('handleURL', uri.toJSON())); + handleURL(uri: URI): Thenable { + return this.channel.call('handleURL', uri.toJSON()); } -} \ No newline at end of file +} diff --git a/src/vs/platform/widget/common/contextScopedWidget.ts b/src/vs/platform/widget/common/contextScopedWidget.ts index a1d619c9e00..65ee493f125 100644 --- a/src/vs/platform/widget/common/contextScopedWidget.ts +++ b/src/vs/platform/widget/common/contextScopedWidget.ts @@ -13,7 +13,7 @@ export function createWidgetScopedContextKeyService(contextKeyService: IContextK return contextKeyService.createScoped(widget.target); } -export function getContextScopedWidget(contextKeyService: IContextKeyService, contextKey: string): T { +export function getContextScopedWidget(contextKeyService: IContextKeyService, contextKey: string): T | undefined { return contextKeyService.getContext(document.activeElement).getValue(contextKey); } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index facb0567866..f6562f8587a 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -83,6 +83,7 @@ export interface SaveDialogOptions { } export interface INewWindowOptions { + remoteAuthority?: string; } export interface IDevToolsOptions { @@ -167,6 +168,7 @@ export interface IWindowsService { startCrashReporter(config: CrashReporterStartOptions): TPromise; openAboutDialog(): TPromise; + resolveProxy(windowId: number, url: string): Promise; } export const IWindowService = createDecorator('windowService'); @@ -214,6 +216,7 @@ export interface IWindowService { showMessageBox(options: MessageBoxOptions): TPromise; showSaveDialog(options: SaveDialogOptions): TPromise; showOpenDialog(options: OpenDialogOptions): TPromise; + resolveProxy(url: string): Promise; } export type MenuBarVisibility = 'default' | 'visible' | 'toggle' | 'hidden'; @@ -234,8 +237,10 @@ export interface IWindowSettings { menuBarVisibility: MenuBarVisibility; newWindowDimensions: 'default' | 'inherit' | 'maximized' | 'fullscreen'; nativeTabs: boolean; + nativeFullScreen: boolean; enableMenuBarMnemonics: boolean; closeWhenEmpty: boolean; + smoothScrollingWorkaround: boolean; clickThroughInactive: boolean; } @@ -341,6 +346,8 @@ export interface IWindowConfiguration extends ParsedArgs { workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; + remoteAuthority?: string; + zoomLevel?: number; fullscreen?: boolean; maximized?: boolean; @@ -379,7 +386,7 @@ export class ActiveWindowManager implements IDisposable { .then(id => (typeof this._activeWindowId === 'undefined') && this.setActiveWindow(id)); } - private setActiveWindow(windowId: number) { + private setActiveWindow(windowId: number | undefined) { if (this.firstActiveWindowIdPromise) { this.firstActiveWindowIdPromise = null; } diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index db3b2a0ca98..55be4cb76e9 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -163,4 +163,8 @@ export class WindowService implements IWindowService { updateTouchBar(items: ISerializableCommandAction[][]): TPromise { return this.windowsService.updateTouchBar(this.windowId, items); } + + resolveProxy(url: string): Promise { + return this.windowsService.resolveProxy(this.windowId, url); + } } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 83972284042..0f68cac7271 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -38,6 +38,8 @@ export interface ICodeWindow { openedWorkspace: IWorkspaceIdentifier; backupPath: string; + remoteAuthority: string; + isExtensionDevelopmentHost: boolean; isExtensionTestHost: boolean; @@ -60,6 +62,7 @@ export interface ICodeWindow { sendWhenReady(channel: string, ...args: any[]): void; toggleFullScreen(): void; + isFullScreen(): boolean; hasHiddenTitleBarStyle(): boolean; setRepresentedFilename(name: string): void; getRepresentedFilename(): string; diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 441ac8f9baf..4b58461d7ef 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -136,7 +136,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable if (codeWindow) { const contents = codeWindow.win.webContents; - if (isMacintosh && codeWindow.hasHiddenTitleBarStyle() && !codeWindow.win.isFullScreen() && !contents.isDevToolsOpened()) { + if (isMacintosh && codeWindow.hasHiddenTitleBarStyle() && !codeWindow.isFullScreen() && !contents.isDevToolsOpened()) { contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647 } else { contents.toggleDevTools(); @@ -564,6 +564,19 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return TPromise.wrap(true); } + resolveProxy(windowId: number, url: string): Promise { + return new Promise(resolve => { + const codeWindow = this.windowsMainService.getWindowById(windowId); + if (codeWindow) { + codeWindow.win.webContents.session.resolveProxy(url, proxy => { + resolve(proxy); + }); + } else { + resolve(); + } + }); + } + dispose(): void { this.disposables = dispose(this.disposables); } diff --git a/src/vs/platform/windows/node/windowsIpc.ts b/src/vs/platform/windows/node/windowsIpc.ts index 69af2c4132e..580df050d5f 100644 --- a/src/vs/platform/windows/node/windowsIpc.ts +++ b/src/vs/platform/windows/node/windowsIpc.ts @@ -29,12 +29,12 @@ export interface IWindowsChannel extends IChannel { call(command: 'showMessageBox', arg: [number, MessageBoxOptions]): Thenable; call(command: 'showSaveDialog', arg: [number, SaveDialogOptions]): Thenable; call(command: 'showOpenDialog', arg: [number, OpenDialogOptions]): Thenable; - call(command: 'reloadWindow', arg: [number, ParsedArgs]): Thenable; - call(command: 'openDevTools', arg: [number, IDevToolsOptions]): Thenable; + call(command: 'reloadWindow', arg: [number, ParsedArgs | undefined]): Thenable; + call(command: 'openDevTools', arg: [number, IDevToolsOptions | undefined]): Thenable; call(command: 'toggleDevTools', arg: number): Thenable; call(command: 'closeWorkspace', arg: number): Thenable; call(command: 'enterWorkspace', arg: [number, string]): Thenable; - call(command: 'createAndEnterWorkspace', arg: [number, IWorkspaceFolderCreationData[], string]): Thenable; + call(command: 'createAndEnterWorkspace', arg: [number, IWorkspaceFolderCreationData[] | undefined, string | undefined]): Thenable; call(command: 'saveAndEnterWorkspace', arg: [number, string]): Thenable; call(command: 'toggleFullScreen', arg: number): Thenable; call(command: 'setRepresentedFilename', arg: [number, string]): Thenable; @@ -59,8 +59,8 @@ export interface IWindowsChannel extends IChannel { call(command: 'onWindowTitleDoubleClick', arg: number): Thenable; call(command: 'setDocumentEdited', arg: [number, boolean]): Thenable; call(command: 'quit'): Thenable; - call(command: 'openWindow', arg: [number, URI[], { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }]): Thenable; - call(command: 'openNewWindow', arg: INewWindowOptions): Thenable; + call(command: 'openWindow', arg: [number, URI[], { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs } | undefined]): Thenable; + call(command: 'openNewWindow', arg?: INewWindowOptions): Thenable; call(command: 'showWindow', arg: number): Thenable; call(command: 'getWindows'): Thenable<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>; call(command: 'getWindowCount'): Thenable; @@ -73,6 +73,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'openExternal', arg: string): Thenable; call(command: 'startCrashReporter', arg: CrashReporterStartOptions): Thenable; call(command: 'openAboutDialog'): Thenable; + call(command: 'resolveProxy', arg: [number, string]): Thenable; } export class WindowsChannel implements IWindowsChannel { @@ -103,7 +104,7 @@ export class WindowsChannel implements IWindowsChannel { case 'onRecentlyOpenedChange': return this.onRecentlyOpenedChange; } - throw new Error('No event found'); + throw new Error(`Event not found: ${event}`); } call(command: string, arg?: any): Thenable { @@ -122,7 +123,7 @@ export class WindowsChannel implements IWindowsChannel { case 'enterWorkspace': return this.service.enterWorkspace(arg[0], arg[1]); case 'createAndEnterWorkspace': { const rawFolders: IWorkspaceFolderCreationData[] = arg[1]; - let folders: IWorkspaceFolderCreationData[]; + let folders: IWorkspaceFolderCreationData[] | undefined = undefined; if (Array.isArray(rawFolders)) { folders = rawFolders.map(rawFolder => { return { @@ -178,8 +179,10 @@ export class WindowsChannel implements IWindowsChannel { case 'openExternal': return this.service.openExternal(arg); case 'startCrashReporter': return this.service.startCrashReporter(arg); case 'openAboutDialog': return this.service.openAboutDialog(); + case 'resolveProxy': return this.service.resolveProxy(arg[0], arg[1]); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -404,4 +407,8 @@ export class WindowsChannelClient implements IWindowsService { openAboutDialog(): TPromise { return TPromise.wrap(this.channel.call('openAboutDialog')); } + + resolveProxy(windowId: number, url: string): Promise { + return Promise.resolve(this.channel.call('resolveProxy', [windowId, url])); + } } diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index c40ac0382ab..321c66641fa 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -145,8 +145,7 @@ export class Workspace implements IWorkspace { constructor( private _id: string, folders: WorkspaceFolder[] = [], - private _configuration: URI | null = null, - private _ctime?: number + private _configuration: URI | null = null ) { this.folders = folders; } @@ -154,7 +153,6 @@ export class Workspace implements IWorkspace { update(workspace: Workspace) { this._id = workspace.id; this._configuration = workspace.configuration; - this._ctime = workspace.ctime; this.folders = workspace.folders; } @@ -171,10 +169,6 @@ export class Workspace implements IWorkspace { return this._id; } - get ctime(): number | undefined { - return this._ctime; - } - get configuration(): URI | null { return this._configuration; } diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index b6238f5eea4..04cc86b69fc 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -85,9 +85,9 @@ export interface IWorkspacesMainService extends IWorkspacesService { createWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier; - resolveWorkspace(path: string): TPromise; + resolveWorkspace(path: string): TPromise; - resolveWorkspaceSync(path: string): IResolvedWorkspace; + resolveWorkspaceSync(path: string): IResolvedWorkspace | null; isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean; diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index 83e0506d604..2d204490b89 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -9,7 +9,7 @@ import { isParent } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { extname, join, dirname, isAbsolute, resolve } from 'path'; import { mkdirp, writeFile, readFile } from 'vs/base/node/pfs'; -import { readFileSync, existsSync, mkdirSync } from 'fs'; +import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { delSync, readdirSync, writeFileAndFlushSync } from 'vs/base/node/extfs'; import { Event, Emitter } from 'vs/base/common/event'; @@ -19,44 +19,38 @@ import { coalesce } from 'vs/base/common/arrays'; import { createHash } from 'crypto'; import * as json from 'vs/base/common/json'; import * as jsonEdit from 'vs/base/common/jsonEdit'; -import { applyEdit } from 'vs/base/common/jsonFormatter'; import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +import { Disposable } from 'vs/base/common/lifecycle'; export interface IStoredWorkspace { folders: IStoredWorkspaceFolder[]; } -export class WorkspacesMainService implements IWorkspacesMainService { +export class WorkspacesMainService extends Disposable implements IWorkspacesMainService { _serviceBrand: any; - protected workspacesHome: string; + private workspacesHome: string; - private readonly _onWorkspaceSaved: Emitter; - private readonly _onUntitledWorkspaceDeleted: Emitter; + private readonly _onWorkspaceSaved = this._register(new Emitter()); + get onWorkspaceSaved(): Event { return this._onWorkspaceSaved.event; } + + private readonly _onUntitledWorkspaceDeleted = this._register(new Emitter()); + get onUntitledWorkspaceDeleted(): Event { return this._onUntitledWorkspaceDeleted.event; } constructor( @IEnvironmentService private environmentService: IEnvironmentService, @ILogService private logService: ILogService ) { + super(); + this.workspacesHome = environmentService.workspacesHome; - - this._onWorkspaceSaved = new Emitter(); - this._onUntitledWorkspaceDeleted = new Emitter(); } - get onWorkspaceSaved(): Event { - return this._onWorkspaceSaved.event; - } - - get onUntitledWorkspaceDeleted(): Event { - return this._onUntitledWorkspaceDeleted.event; - } - - resolveWorkspace(path: string): TPromise { + resolveWorkspace(path: string): TPromise { if (!this.isWorkspacePath(path)) { return TPromise.as(null); // does not look like a valid workspace config file } @@ -64,7 +58,7 @@ export class WorkspacesMainService implements IWorkspacesMainService { return readFile(path, 'utf8').then(contents => this.doResolveWorkspace(path, contents)); } - resolveWorkspaceSync(path: string): IResolvedWorkspace { + resolveWorkspaceSync(path: string): IResolvedWorkspace | null { if (!this.isWorkspacePath(path)) { return null; // does not look like a valid workspace config file } @@ -83,7 +77,7 @@ export class WorkspacesMainService implements IWorkspacesMainService { return this.isInsideWorkspacesHome(path) || extname(path) === `.${WORKSPACE_EXTENSION}`; } - private doResolveWorkspace(path: string, contents: string): IResolvedWorkspace { + private doResolveWorkspace(path: string, contents: string): IResolvedWorkspace | null { try { const workspace = this.doParseStoredWorkspace(path, contents); @@ -231,7 +225,7 @@ export class WorkspacesMainService implements IWorkspacesMainService { let newRawWorkspaceContents = rawWorkspaceContents; const edits = jsonEdit.setProperty(rawWorkspaceContents, ['folders'], storedWorkspace.folders, { insertSpaces: false, tabSize: 4, eol: (isLinux || isMacintosh) ? '\n' : '\r\n' }); edits.forEach(edit => { - newRawWorkspaceContents = applyEdit(rawWorkspaceContents, edit); + newRawWorkspaceContents = jsonEdit.applyEdit(rawWorkspaceContents, edit); }); return writeFile(targetConfigPath, newRawWorkspaceContents).then(() => { @@ -262,7 +256,15 @@ export class WorkspacesMainService implements IWorkspacesMainService { private doDeleteUntitledWorkspaceSync(configPath: string): void { try { + + // Delete Workspace delSync(dirname(configPath)); + + // Mark Workspace Storage to be deleted + const workspaceStoragePath = join(this.environmentService.workspaceStorageHome, this.getWorkspaceId(configPath)); + if (existsSync(workspaceStoragePath)) { + writeFileSync(join(workspaceStoragePath, 'obsolete'), ''); + } } catch (error) { this.logService.warn(`Unable to delete untitled workspace ${configPath} (${error}).`); } diff --git a/src/vs/platform/workspaces/node/workspacesIpc.ts b/src/vs/platform/workspaces/node/workspacesIpc.ts index bdfc6d7b05f..2b9bd3bf815 100644 --- a/src/vs/platform/workspaces/node/workspacesIpc.ts +++ b/src/vs/platform/workspaces/node/workspacesIpc.ts @@ -19,14 +19,14 @@ export class WorkspacesChannel implements IWorkspacesChannel { constructor(private service: IWorkspacesMainService) { } listen(event: string, arg?: any): Event { - throw new Error('No events'); + throw new Error(`Event not found: ${event}`); } call(command: string, arg?: any): Thenable { switch (command) { case 'createWorkspace': { const rawFolders: IWorkspaceFolderCreationData[] = arg; - let folders: IWorkspaceFolderCreationData[]; + let folders: IWorkspaceFolderCreationData[] | undefined = undefined; if (Array.isArray(rawFolders)) { folders = rawFolders.map(rawFolder => { return { @@ -40,7 +40,7 @@ export class WorkspacesChannel implements IWorkspacesChannel { } } - return void 0; + throw new Error(`Call not found: ${command}`); } } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index f6a51fd4673..40c420b6649 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -29,7 +29,7 @@ declare module 'vscode' { command: string; /** - * A tooltip for for command, when represented in the UI. + * A tooltip for the command, when represented in the UI. */ tooltip?: string; @@ -928,12 +928,12 @@ declare module 'vscode' { overviewRulerColor?: string | ThemeColor; /** - * Defines the rendering options of the attachment that is inserted before the decorated text + * Defines the rendering options of the attachment that is inserted before the decorated text. */ before?: ThemableDecorationAttachmentRenderOptions; /** - * Defines the rendering options of the attachment that is inserted after the decorated text + * Defines the rendering options of the attachment that is inserted after the decorated text. */ after?: ThemableDecorationAttachmentRenderOptions; } @@ -1046,12 +1046,12 @@ declare module 'vscode' { export interface ThemableDecorationInstanceRenderOptions { /** - * Defines the rendering options of the attachment that is inserted before the decorated text + * Defines the rendering options of the attachment that is inserted before the decorated text. */ before?: ThemableDecorationAttachmentRenderOptions; /** - * Defines the rendering options of the attachment that is inserted after the decorated text + * Defines the rendering options of the attachment that is inserted after the decorated text. */ after?: ThemableDecorationAttachmentRenderOptions; } @@ -1343,6 +1343,12 @@ declare module 'vscode' { * * The resulting string can be safely used with [Uri.parse](#Uri.parse). * * The resulting string shall *not* be used for display purposes. * + * *Note* that the implementation will encode _aggressive_ which often leads to unexpected, + * but not incorrect, results. For instance, colons are encoded to `%3A` which might be unexpected + * in file-uri. Also `&` and `=` will be encoded which might be unexpected for http-uris. For stability + * reasons this cannot be changed anymore. If you suffer from too aggressive encoding you should use + * the `skipEncoding`-argument: `uri.toString(true)`. + * * @param skipEncoding Do not percentage-encode the result, defaults to `false`. Note that * the `#` and `?` characters occurring in the path will always be encoded. * @returns A string representation of this Uri. @@ -1467,7 +1473,7 @@ declare module 'vscode' { event: Event; /** - * Notify all subscribers of the [event](EventEmitter#event). Failure + * Notify all subscribers of the [event](#EventEmitter.event). Failure * of one or more listener will not fail this function call. * * @param data The event object. @@ -1584,6 +1590,11 @@ declare module 'vscode' { * @see [QuickPickOptions.canPickMany](#QuickPickOptions.canPickMany) */ picked?: boolean; + + /** + * Always show this item. + */ + alwaysShow?: boolean; } /** @@ -2044,7 +2055,7 @@ declare module 'vscode' { * A code action represents a change that can be performed in code, e.g. to fix a problem or * to refactor code. * - * A CodeAction must set either [`edit`](CodeAction#edit) and/or a [`command`](CodeAction#command). If both are supplied, the `edit` is applied first, then the command is executed. + * A CodeAction must set either [`edit`](#CodeAction.edit) and/or a [`command`](#CodeAction.command). If both are supplied, the `edit` is applied first, then the command is executed. */ export class CodeAction { @@ -3163,7 +3174,7 @@ declare module 'vscode' { * given [edit](#CompletionItem.textEdit) is used. * * When selecting a completion item in the editor its defined or synthesized text edit will be applied - * to *all* cursors/selections whereas [additionalTextEdits](CompletionItem.additionalTextEdits) will be + * to *all* cursors/selections whereas [additionalTextEdits](#CompletionItem.additionalTextEdits) will be * applied as provided. * * @see [CompletionItemProvider.provideCompletionItems](#CompletionItemProvider.provideCompletionItems) @@ -5553,7 +5564,7 @@ declare module 'vscode' { * is no longer visible. * * Normally the webview panel's html context is created when the panel becomes visible - * and destroyed when it is is hidden. Extensions that have complex state + * and destroyed when it is hidden. Extensions that have complex state * or UI can set the `retainContextWhenHidden` to make VS Code keep the webview * context around, even when the webview moves to a background tab. When a webview using * `retainContextWhenHidden` becomes hidden, its scripts and other dynamic content are suspended. @@ -6477,7 +6488,7 @@ declare module 'vscode' { readonly visible: boolean; /** - * Event that is fired when [visibility](TreeView.visible) has changed + * Event that is fired when [visibility](#TreeView.visible) has changed */ readonly onDidChangeVisibility: Event; @@ -6485,13 +6496,15 @@ declare module 'vscode' { * Reveals the given element in the tree view. * If the tree view is not visible then the tree view is shown and element is revealed. * - * By default revealed element is selected and not focused. + * By default revealed element is selected. * In order to not to select, set the option `select` to `false`. * In order to focus, set the option `focus` to `true`. + * In order to expand the revealed element, set the option `expand` to `true`. To expand recursively set `expand` to the number of levels to expand. + * **NOTE:** You can expand only to 3 levels maximum. * * **NOTE:** [TreeDataProvider](#TreeDataProvider) is required to implement [getParent](#TreeDataProvider.getParent) method to access this API. */ - reveal(element: T, options?: { select?: boolean, focus?: boolean }): Thenable; + reveal(element: T, options?: { select?: boolean, focus?: boolean, expand?: boolean | number }): Thenable; } /** @@ -7751,7 +7764,7 @@ declare module 'vscode' { * Register a formatting provider for a document range. * * *Note:* A document range provider is also a [document formatter](#DocumentFormattingEditProvider) - * which means there is no need to [register](registerDocumentFormattingEditProvider) a document + * which means there is no need to [register](#languages.registerDocumentFormattingEditProvider) a document * formatter when also registering a range provider. * * Multiple providers can be registered for a language. In that case providers are sorted diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7382f0dd54c..77bd0282fe3 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -3,7 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// This is the place for API experiments and proposals. +/** + * This is the place for API experiments and proposals. + * These API are NOT stable and subject to change. They are only available in the Insiders + * distribution and CANNOT be used in published extensions. + * + * To test these API in local environment: + * - Use Insiders release of VS Code. + * - Add `"enableProposedApi": true` to your package.json. + * - Copy this file to your project. + */ declare module 'vscode' { @@ -181,6 +190,16 @@ declare module 'vscode' { * See the vscode setting `"files.encoding"` */ encoding?: string; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; } /** @@ -214,7 +233,7 @@ declare module 'vscode' { /** * The maximum number of results to be returned. */ - maxResults: number; + maxResults?: number; } /** @@ -225,39 +244,62 @@ declare module 'vscode' { /** * A preview of the text result. */ - export interface TextSearchResultPreview { + export interface TextSearchMatchPreview { /** - * The matching line of text, or a portion of the matching line that contains the match. - * For now, this can only be a single line. + * The matching lines of text, or a portion of the matching line that contains the match. */ text: string; /** * The Range within `text` corresponding to the text of the match. + * The number of matches must match the TextSearchMatch's range property. */ - match: Range; + matches: Range | Range[]; } /** * A match from a text search */ - export interface TextSearchResult { + export interface TextSearchMatch { /** * The uri for the matching document. */ uri: Uri; /** - * The range of the match within the document. + * The range of the match within the document, or multiple ranges for multiple matches. */ - range: Range; + ranges: Range | Range[]; /** - * A preview of the text result. + * A preview of the text match. */ - preview: TextSearchResultPreview; + preview: TextSearchMatchPreview; } + /** + * A line of context surrounding a TextSearchMatch. + */ + export interface TextSearchContext { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * One line of text. + * previewOptions.charsPerLine applies to this + */ + text: string; + + /** + * The line number of this line of context. + */ + lineNumber: number; + } + + export type TextSearchResult = TextSearchMatch | TextSearchContext; + /** * A FileIndexProvider provides a list of files in the given folder. VS Code will filter that list for searching with quickopen or from other extensions. * @@ -365,6 +407,16 @@ declare module 'vscode' { * Options to specify the size of the result text preview. */ previewOptions?: TextSearchPreviewOptions; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; } export namespace workspace { @@ -573,7 +625,7 @@ declare module 'vscode' { /** * A Debug Adapter Tracker is a means to track the communication between VS Code and a Debug Adapter. */ - export interface IDebugAdapterTracker { + export interface DebugAdapterTracker { // VS Code -> Debug Adapter startDebugAdapter?(): void; toDebugAdapter?(message: any): void; @@ -617,7 +669,7 @@ declare module 'vscode' { * @param config The resolved debug configuration. * @param token A cancellation token. */ - provideDebugAdapterTracker?(session: DebugSession, folder: WorkspaceFolder | undefined, config: DebugConfiguration, token?: CancellationToken): ProviderResult; + provideDebugAdapterTracker?(session: DebugSession, folder: WorkspaceFolder | undefined, config: DebugConfiguration, token?: CancellationToken): ProviderResult; /** * Deprecated, use DebugConfigurationProvider.provideDebugAdapter instead. @@ -724,6 +776,21 @@ declare module 'vscode' { //#endregion + //#region Joao: SCM Input Box + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * Controls whether the input box is visible (default is `true`). + */ + visible: boolean; + } + + //#endregion + //#region Comments /** * Comments provider related APIs are still in early stages, they may be changed significantly during our API experiments. @@ -869,7 +936,7 @@ declare module 'vscode' { replyToCommentThread(document: TextDocument, range: Range, commentThread: CommentThread, text: string, token: CancellationToken): Promise; /** - * Called when a user edits the comment body to the be new text text. + * Called when a user edits the comment body to the be new text. */ editComment?(document: TextDocument, comment: Comment, text: string, token: CancellationToken): Promise; @@ -1079,12 +1146,9 @@ declare module 'vscode' { TriggerCharacter = 2, /** - * Signature help was retriggered. - * - * Retriggers occur when the signature help is already active and can be caused by typing a trigger character - * or by a cursor move. + * Signature help was triggered by the cursor moving or by the document content changing. */ - Retrigger = 3, + ContentChange = 3, } /** @@ -1100,9 +1164,18 @@ declare module 'vscode' { /** * Character that caused signature help to be requested. * - * This is `undefined` for manual triggers or retriggers for a cursor move. + * This is `undefined` when signature help is not triggered by typing, such as when invoking signature help + * or when moving the cursor. */ readonly triggerCharacter?: string; + + /** + * Whether or not signature help was previously showing when triggered. + * + * Retriggers occur when the signature help is already active and can be caused by typing a trigger character + * or by a cursor move. + */ + readonly isRetrigger: boolean; } export interface SignatureHelpProvider { @@ -1132,13 +1205,72 @@ declare module 'vscode' { } //#endregion - //#region #59232 + //#region Tree View + + /** + * Options for creating a [TreeView](#TreeView] + */ + export interface TreeViewOptions { - export interface QuickPickItem { /** - * Show this item always + * A data provider that provides tree data. */ - alwaysShow?: boolean; + treeDataProvider: TreeDataProvider; + + /** + * Whether to show collapse all action or not. + */ + showCollapseAll?: boolean; + } + + namespace window { + + export function createTreeView(viewId: string, options: TreeViewOptions): TreeView; + + } + + /** + * Label describing the [Tree item](#TreeItem) + */ + export interface TreeItemLabel { + + /** + * A human-readable string describing the [Tree item](#TreeItem). + */ + label: string; + + /** + * Ranges in the label to highlight. A range is defined as a tuple of two number where the + * first is the inclusive start index and the second the exclusive end index + */ + highlights?: [number, number][]; + + } + + export class TreeItem2 extends TreeItem { + /** + * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). + */ + label?: string | TreeItemLabel | /* for compilation */ any; + + /** + * @param label Label describing this item + * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) + */ + constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); } //#endregion + + //#region Task + /** + * Controls how the task is presented in the UI. + */ + export interface TaskPresentationOptions { + /** + * Controls whether the terminal is cleared before executing the task. + */ + clear?: boolean; + } + //#endregion + } diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 3ddad042b08..296f5b46f77 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -111,7 +111,7 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { container = this.viewContainersRegistry.get(EXPLORER); } const registeredViews = ViewsRegistry.getViews(container); - const viewIds = []; + const viewIds: string[] = []; const viewDescriptors = coalesce(entry.value.map((item, index) => { // validate if (viewIds.indexOf(item.id) !== -1) { diff --git a/src/vs/workbench/api/electron-browser/extHostCustomers.ts b/src/vs/workbench/api/electron-browser/extHostCustomers.ts index 621c2775759..1fe9bd1c540 100644 --- a/src/vs/workbench/api/electron-browser/extHostCustomers.ts +++ b/src/vs/workbench/api/electron-browser/extHostCustomers.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from 'vs/base/common/lifecycle'; -import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; export type IExtHostNamedCustomer = [ProxyIdentifier, IExtHostCustomerCtor]; diff --git a/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts b/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts index 14c3f9bb644..6497256226e 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { clipboard } from 'electron'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { MainContext, MainThreadClipboardShape } from '../node/extHost.protocol'; diff --git a/src/vs/workbench/api/electron-browser/mainThreadComments.ts b/src/vs/workbench/api/electron-browser/mainThreadComments.ts index b3187147fd6..137265bad71 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadComments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadComments.ts @@ -5,7 +5,6 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditor, isCodeEditor, isDiffEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import * as modes from 'vs/editor/common/modes'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { keys } from 'vs/base/common/map'; @@ -16,7 +15,7 @@ import { ICommentService } from 'vs/workbench/parts/comments/electron-browser/co import { COMMENTS_PANEL_ID } from 'vs/workbench/parts/comments/electron-browser/commentsPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { URI } from 'vs/base/common/uri'; -import { ReviewController } from 'vs/workbench/parts/comments/electron-browser/commentsEditorContribution'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @extHostNamedCustomer(MainContext.MainThreadComments) export class MainThreadComments extends Disposable implements MainThreadCommentsShape { @@ -26,70 +25,17 @@ export class MainThreadComments extends Disposable implements MainThreadComments private _workspaceProviders = new Map(); private _firstSessionStart: boolean; - private _visibleModels: { [key /** editor widget id */: string]: string /** model id */ }; constructor( extHostContext: IExtHostContext, @IEditorService private _editorService: IEditorService, @ICommentService private _commentService: ICommentService, @IPanelService private _panelService: IPanelService, - @ICodeEditorService private _codeEditorService: ICodeEditorService + @ITelemetryService private _telemetryService: ITelemetryService ) { super(); this._disposables = []; this._firstSessionStart = true; - this._visibleModels = {}; this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments); - this._disposables.push(this._editorService.onDidVisibleEditorsChange(e => { - const editors = this.getFocusedEditors(); - const visibleEditors = this.getVisibleEditors(); - - const _visibleEditors = {}; - visibleEditors.forEach(editor => { - if (!editor.hasModel()) { - return; // we need a model - } - const id = editor.getId(); - const model = editor.getModel(); - if (editors.filter(ed => ed.getId() === id).length > 0) { - // it's an active editor, we are going to update this editor's comments anyways - } else { - if (this._visibleModels[id]) { - // it's the same active editor, but we may want to check if the model is still the same - let modelId = model.getModeId(); - if (modelId !== this._visibleModels[id]) { - editors.push(editor); - } - } else { - // update - editors.push(editor); - } - } - - _visibleEditors[id] = model.getModeId(); - }); - - this._visibleModels = _visibleEditors; - - if (!editors || !editors.length) { - return; - } - - editors.forEach(editor => { - const controller = ReviewController.get(editor); - if (!controller) { - return; - } - - if (!editor.getModel()) { - return; - } - - const outerEditorURI = editor.getModel().uri; - this.provideDocumentComments(outerEditorURI).then(commentInfos => { - this._commentService.setDocumentComments(outerEditorURI, commentInfos.filter(info => info !== null)); - }); - }); - })); } $registerDocumentCommentProvider(handle: number): void { @@ -118,7 +64,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments ); } - $registerWorkspaceCommentProvider(handle: number): void { + $registerWorkspaceCommentProvider(handle: number, extensionId: string): void { this._workspaceProviders.set(handle, undefined); this._panelService.setPanelEnablement(COMMENTS_PANEL_ID, true); if (this._firstSessionStart) { @@ -130,6 +76,15 @@ export class MainThreadComments extends Disposable implements MainThreadComments this._commentService.setWorkspaceComments(handle, commentThreads); } }); + + /* __GDPR__ + "comments:registerWorkspaceCommentProvider" : { + "extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this._telemetryService.publicLog('comments:registerWorkspaceCommentProvider', { + extensionId: extensionId + }); } $unregisterDocumentCommentProvider(handle: number): void { @@ -151,7 +106,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments } getVisibleEditors(): ICodeEditor[] { - let ret = []; + let ret: ICodeEditor[] = []; this._editorService.visibleControls.forEach(control => { if (isCodeEditor(control.getControl())) { @@ -167,27 +122,6 @@ export class MainThreadComments extends Disposable implements MainThreadComments return ret; } - getFocusedEditors(): ICodeEditor[] { - let activeControl = this._editorService.activeControl; - if (activeControl) { - if (isCodeEditor(activeControl.getControl())) { - return [this._editorService.activeControl.getControl() as ICodeEditor]; - } - - if (isDiffEditor(activeControl.getControl())) { - let diffEditor = activeControl.getControl() as IDiffEditor; - return [diffEditor.getOriginalEditor(), diffEditor.getModifiedEditor()]; - } - } - - let editor = this._codeEditorService.getFocusedCodeEditor(); - - if (editor) { - return [editor]; - } - return []; - } - async provideWorkspaceComments(): Promise { const result: modes.CommentThread[] = []; for (const handle of keys(this._workspaceProviders)) { @@ -210,6 +144,5 @@ export class MainThreadComments extends Disposable implements MainThreadComments this._workspaceProviders.clear(); this._documentProviders.forEach(value => dispose(value)); this._documentProviders.clear(); - this._visibleModels = {}; } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts index 55bd73db626..1656cb5c4de 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; @@ -45,7 +44,7 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape { return this.writeConfiguration(target, key, undefined, resource); } - private writeConfiguration(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise { + private writeConfiguration(target: ConfigurationTarget, key: string, value: any, resource: URI): Promise { target = target !== null && target !== undefined ? target : this.deriveConfigurationTarget(key, resource); return this.configurationService.updateValue(key, value, { resource }, target, true); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index 6df4312e6f6..992a0a037a5 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -6,7 +6,6 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { URI as uri } from 'vs/base/common/uri'; import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, ITerminalSettings, IDebugAdapter, IDebugAdapterProvider, IDebugSession } from 'vs/workbench/parts/debug/common/debug'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto @@ -16,8 +15,6 @@ import severity from 'vs/base/common/severity'; import { AbstractDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { convertToVSCPaths, convertToDAPaths, stringToUri, uriToString } from 'vs/workbench/parts/debug/common/debugUtils'; -import { deepClone } from 'vs/base/common/objects'; - @extHostNamedCustomer(MainContext.MainThreadDebugService) export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterProvider { @@ -27,6 +24,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb private _breakpointEventsActive: boolean; private _debugAdapters: Map; private _debugAdaptersHandleCounter = 1; + private _debugConfigurationProviders: Map; constructor( extHostContext: IExtHostContext, @@ -48,7 +46,8 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb this._proxy.$acceptDebugSessionActiveChanged(this.getSessionDto(session)); })); - this._debugAdapters = new Map(); + this._debugAdapters = new Map(); + this._debugConfigurationProviders = new Map(); } public dispose(): void { @@ -64,22 +63,21 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return da; } - substituteVariables(folder: IWorkspaceFolder, config: IConfig): TPromise { - return TPromise.wrap(this._proxy.$substituteVariables(folder ? folder.uri : undefined, config)); + substituteVariables(folder: IWorkspaceFolder, config: IConfig): Promise { + return Promise.resolve(this._proxy.$substituteVariables(folder ? folder.uri : undefined, config)); } - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { - return TPromise.wrap(this._proxy.$runInTerminal(args, config)); + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { + return Promise.resolve(this._proxy.$runInTerminal(args, config)); } - // RPC methods (MainThreadDebugServiceShape) public $registerDebugTypes(debugTypes: string[]) { this._toDispose.push(this.debugService.getConfigurationManager().registerDebugAdapterProvider(debugTypes, this)); } - public $startBreakpointEvents(): Thenable { + public $startBreakpointEvents(): void { if (!this._breakpointEventsActive) { this._breakpointEventsActive = true; @@ -114,8 +112,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb }); } } - - return TPromise.wrap(undefined); } public $registerBreakpoints(DTOs: (ISourceMultiBreakpointDto | IFunctionBreakpointDto)[]): Thenable { @@ -133,7 +129,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb logMessage: l.logMessage } ); - this.debugService.addBreakpoints(uri.revive(dto.uri), rawbps); + this.debugService.addBreakpoints(uri.revive(dto.uri), rawbps, 'extension'); } else if (dto.type === 'function') { this.debugService.addFunctionBreakpoint(dto.functionName, dto.id); } @@ -156,27 +152,31 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb }; if (hasProvide) { provider.provideDebugConfigurations = (folder) => { - return TPromise.wrap(this._proxy.$provideDebugConfigurations(handle, folder)); + return Promise.resolve(this._proxy.$provideDebugConfigurations(handle, folder)); }; } if (hasResolve) { provider.resolveDebugConfiguration = (folder, config) => { - return TPromise.wrap(this._proxy.$resolveDebugConfiguration(handle, folder, config)); + return Promise.resolve(this._proxy.$resolveDebugConfiguration(handle, folder, config)); }; } if (hasProvideDebugAdapter) { provider.provideDebugAdapter = (session, folder, config) => { - return TPromise.wrap(this._proxy.$provideDebugAdapter(handle, this.getSessionDto(session), folder, config)); + return Promise.resolve(this._proxy.$provideDebugAdapter(handle, this.getSessionDto(session), folder, config)); }; } - this.debugService.getConfigurationManager().registerDebugConfigurationProvider(handle, provider); + this._debugConfigurationProviders.set(handle, provider); + this._toDispose.push(this.debugService.getConfigurationManager().registerDebugConfigurationProvider(provider)); - return TPromise.wrap(undefined); + return Promise.resolve(undefined); } - public $unregisterDebugConfigurationProvider(handle: number): Thenable { - this.debugService.getConfigurationManager().unregisterDebugConfigurationProvider(handle); - return TPromise.wrap(undefined); + public $unregisterDebugConfigurationProvider(handle: number): void { + const provider = this._debugConfigurationProviders.get(handle); + if (provider) { + this._debugConfigurationProviders.delete(handle); + this.debugService.getConfigurationManager().unregisterDebugConfigurationProvider(provider); + } } public $startDebugging(_folderUri: uri | undefined, nameOrConfiguration: string | IConfig): Thenable { @@ -185,7 +185,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return this.debugService.startDebugging(launch, nameOrConfiguration).then(success => { return success; }, err => { - return TPromise.wrapError(new Error(err && err.message ? err.message : 'cannot start debugging')); + return Promise.reject(new Error(err && err.message ? err.message : 'cannot start debugging')); }); } @@ -196,27 +196,24 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb if (response && response.success) { return response.body; } else { - return TPromise.wrapError(new Error(response ? response.message : 'custom request failed')); + return Promise.reject(new Error(response ? response.message : 'custom request failed')); } }); } - return TPromise.wrapError(new Error('debug session not found')); + return Promise.reject(new Error('debug session not found')); } - public $appendDebugConsole(value: string): Thenable { + public $appendDebugConsole(value: string): void { // Use warning as severity to get the orange color for messages coming from the debug extension const session = this.debugService.getViewModel().focusedSession; if (session) { session.appendToRepl(value, severity.Warning); } - return TPromise.wrap(undefined); } public $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage) { - convertToVSCPaths(message, source => uriToString(source)); - - this._debugAdapters.get(handle).acceptMessage(message); + this._debugAdapters.get(handle).acceptMessage(convertToVSCPaths(message, source => uriToString(source))); } public $acceptDAError(handle: number, name: string, message: string, stack: string) { @@ -288,21 +285,16 @@ class ExtensionHostDebugAdapter extends AbstractDebugAdapter { this._onExit.fire(code); } - public startSession(): TPromise { - return TPromise.wrap(this._proxy.$startDASession(this._handle, this._sessionDto, this.folder ? this.folder.uri : undefined, this.config)); + public startSession(): Promise { + return Promise.resolve(this._proxy.$startDASession(this._handle, this._sessionDto, this.folder ? this.folder.uri : undefined, this.config)); } public sendMessage(message: DebugProtocol.ProtocolMessage): void { - // since we modify Source.paths in the message in place, we need to make a copy of it (see #61129) - const msg = deepClone(message); - - convertToDAPaths(msg, source => stringToUri(source)); - - this._proxy.$sendDAMessage(this._handle, msg); + this._proxy.$sendDAMessage(this._handle, convertToDAPaths(message, source => stringToUri(source))); } - public stopSession(): TPromise { - return TPromise.wrap(this._proxy.$stopDASession(this._handle)); + public stopSession(): Promise { + return Promise.resolve(this._proxy.$stopDASession(this._handle)); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocumentContentProviders.ts b/src/vs/workbench/api/electron-browser/mainThreadDocumentContentProviders.ts index c42d2894288..ef57f28a65a 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocumentContentProviders.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocumentContentProviders.ts @@ -46,8 +46,8 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon return this._proxy.$provideTextDocumentContent(handle, uri).then(value => { if (typeof value === 'string') { const firstLineText = value.substr(0, 1 + value.search(/\r?\n/)); - const mode = this._modeService.getOrCreateModeByFilepathOrFirstLine(uri.fsPath, firstLineText); - return this._modelService.createModel(value, mode, uri); + const languageSelection = this._modeService.createByFilepathOrFirstLine(uri.fsPath, firstLineText); + return this._modelService.createModel(value, languageSelection, uri); } return undefined; }); diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts b/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts index 2d08da89f92..901457479b8 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI, UriComponents } from 'vs/base/common/uri'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { IModelService, shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; -import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; -import { TextFileModelChangeEvent, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IFileService } from 'vs/platform/files/common/files'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { ExtHostContext, MainThreadDocumentsShape, ExtHostDocumentsShape, IExtHostContext } from '../node/extHost.protocol'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors'; -import { ITextEditorModel } from 'vs/workbench/common/editor'; -import { ITextModel } from 'vs/editor/common/model'; +import { IDisposable, IReference, dispose } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { ITextModel } from 'vs/editor/common/model'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModelService, shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors'; +import { ExtHostContext, ExtHostDocumentsShape, IExtHostContext, MainThreadDocumentsShape } from 'vs/workbench/api/node/extHost.protocol'; +import { ITextEditorModel } from 'vs/workbench/common/editor'; +import { ITextFileService, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; export class BoundModelReferenceCollection { diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts index 6ba27704f66..cbbe4d79dcb 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts @@ -3,48 +3,32 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IModelService, shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; -import { ITextModel } from 'vs/editor/common/model'; -import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; +import { values } from 'vs/base/common/map'; +import { URI } from 'vs/base/common/uri'; +import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ExtHostContext, ExtHostDocumentsAndEditorsShape, IModelAddedData, ITextEditorAddData, IDocumentsAndEditorsDelta, IExtHostContext, MainContext } from '../node/extHost.protocol'; -import { MainThreadTextEditor } from './mainThreadEditor'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; -import { EditorViewColumn, editorGroupToViewColumn } from 'vs/workbench/api/shared/editor'; -import { IEditor as IWorkbenchEditor } from 'vs/workbench/common/editor'; +import { IEditor } from 'vs/editor/common/editorCommon'; +import { ITextModel } from 'vs/editor/common/model'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModelService, shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IFileService } from 'vs/platform/files/common/files'; import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { MainThreadDocuments } from 'vs/workbench/api/electron-browser/mainThreadDocuments'; +import { MainThreadTextEditor } from 'vs/workbench/api/electron-browser/mainThreadEditor'; import { MainThreadTextEditors } from 'vs/workbench/api/electron-browser/mainThreadEditors'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { IFileService } from 'vs/platform/files/common/files'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { isDiffEditor, ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { URI } from 'vs/base/common/uri'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { ExtHostContext, ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IExtHostContext, IModelAddedData, ITextEditorAddData, MainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { EditorViewColumn, editorGroupToViewColumn } from 'vs/workbench/api/shared/editor'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; -import { IEditor } from 'vs/editor/common/editorCommon'; - -namespace mapset { - - export function setValues(set: Set): T[] { - // return Array.from(set); - let ret: T[] = []; - set.forEach(v => ret.push(v)); - return ret; - } - - export function mapValues(map: Map): T[] { - // return Array.from(map.values()); - let ret: T[] = []; - map.forEach(v => ret.push(v)); - return ret; - } -} +import { IEditor as IWorkbenchEditor } from 'vs/workbench/common/editor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; namespace delta { @@ -127,8 +111,8 @@ class DocumentAndEditorState { static compute(before: DocumentAndEditorState, after: DocumentAndEditorState): DocumentAndEditorStateDelta { if (!before) { return new DocumentAndEditorStateDelta( - [], mapset.setValues(after.documents), - [], mapset.mapValues(after.textEditors), + [], values(after.documents), + [], values(after.textEditors), undefined, after.activeEditor ); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadEditor.ts b/src/vs/workbench/api/electron-browser/mainThreadEditor.ts index 2b7e39dd957..38ab78b31c8 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadEditor.ts @@ -3,19 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IEditor } from 'vs/workbench/common/editor'; -import { IModelService } from 'vs/editor/common/services/modelService'; +import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { Selection, ISelection } from 'vs/editor/common/core/selection'; -import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; -import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes'; -import { TextEditorCursorStyle, cursorStyleToString, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; -import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions, IEditorPropertiesChangeData } from 'vs/workbench/api/node/extHost.protocol'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { ITextModel, ISingleEditOperation, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModelUpdateOptions } from 'vs/editor/common/model'; +import { RenderLineNumbersType, TextEditorCursorStyle, cursorStyleToString } from 'vs/editor/common/config/editorOptions'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { ISelection, Selection } from 'vs/editor/common/core/selection'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { EndOfLineSequence, IIdentifiedSingleEditOperation, ISingleEditOperation, ITextModel, ITextModelUpdateOptions } from 'vs/editor/common/model'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; +import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, IUndoStopOptions, TextEditorRevealType } from 'vs/workbench/api/node/extHost.protocol'; +import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes'; +import { IEditor } from 'vs/workbench/common/editor'; export interface IFocusTracker { onGainedFocus(): void; diff --git a/src/vs/workbench/api/electron-browser/mainThreadEditors.ts b/src/vs/workbench/api/electron-browser/mainThreadEditors.ts index a752838a86b..8e7ba2e02d9 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadEditors.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { localize } from 'vs/nls'; import { disposed } from 'vs/base/common/errors'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -16,15 +14,16 @@ import { IRange } from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; import { IDecorationOptions, IDecorationRenderOptions, ILineChange } from 'vs/editor/common/editorCommon'; import { ISingleEditOperation } from 'vs/editor/common/model'; -import { EditorViewColumn, viewColumnToEditorGroup, editorGroupToViewColumn } from 'vs/workbench/api/shared/editor'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IApplyEditsOptions, ITextEditorConfigurationUpdate, IUndoStopOptions, TextEditorRevealType, WorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol'; +import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors'; +import { MainThreadTextEditor } from 'vs/workbench/api/electron-browser/mainThreadEditor'; +import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType, WorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol'; +import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/shared/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; -import { ExtHostContext, ExtHostEditorsShape, IExtHostContext, ITextDocumentShowOptions, ITextEditorPositionData, MainThreadTextEditorsShape } from '../node/extHost.protocol'; -import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors'; -import { MainThreadTextEditor } from './mainThreadEditor'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; export class MainThreadTextEditors implements MainThreadTextEditorsShape { diff --git a/src/vs/workbench/api/electron-browser/mainThreadErrors.ts b/src/vs/workbench/api/electron-browser/mainThreadErrors.ts index a08149401b5..f1546f94123 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadErrors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadErrors.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { SerializedError, onUnexpectedError } from 'vs/base/common/errors'; -import { MainThreadErrorsShape, MainContext } from '../node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { MainContext, MainThreadErrorsShape } from 'vs/workbench/api/node/extHost.protocol'; @extHostNamedCustomer(MainContext.MainThreadErrors) export class MainThreadErrors implements MainThreadErrorsShape { diff --git a/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts b/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts index 0d04805cb2a..da5059d81a3 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import Severity from 'vs/base/common/severity'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { MainThreadExtensionServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol'; -import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService'; -import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { SerializedError } from 'vs/base/common/errors'; +import Severity from 'vs/base/common/severity'; +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { IExtHostContext, MainContext, MainThreadExtensionServiceShape } from 'vs/workbench/api/node/extHost.protocol'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService'; @extHostNamedCustomer(MainContext.MainThreadExtensionService) export class MainThreadExtensionService implements MainThreadExtensionServiceShape { diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index 8dabd0a4b49..a6d00ce513f 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -51,7 +51,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { class RemoteFileSystemProvider implements IFileSystemProvider { private readonly _onDidChange = new Emitter(); - private readonly _registrations: IDisposable[]; + private readonly _registration: IDisposable; readonly onDidChangeFile: Event = this._onDidChange.event; readonly capabilities: FileSystemProviderCapabilities; @@ -64,11 +64,11 @@ class RemoteFileSystemProvider implements IFileSystemProvider { private readonly _proxy: ExtHostFileSystemShape ) { this.capabilities = capabilities; - this._registrations = [fileService.registerProvider(scheme, this)]; + this._registration = fileService.registerProvider(scheme, this); } dispose(): void { - dispose(this._registrations); + this._registration.dispose(); this._onDidChange.dispose(); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts b/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts index 0666cca9860..e3efb95b75c 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts @@ -137,4 +137,4 @@ export class MainThreadHeapService { } -registerSingleton(IHeapService, HeapService); +registerSingleton(IHeapService, HeapService, true); diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts index 77d0885224a..6ea6e3b3524 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts @@ -33,12 +33,11 @@ export class MainThreadLanguages implements MainThreadLanguagesShape { if (!model) { return Promise.reject(new Error('Invalid uri')); } - return this._modeService.getOrCreateMode(languageId).then(mode => { - if (mode.getId() !== languageId) { - return Promise.reject(new Error(`Unknown language id: ${languageId}`)); - } - this._modelService.setMode(model, mode); - return undefined; - }); + const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); + if (!languageIdentifier || languageIdentifier.language !== languageId) { + return Promise.reject(new Error(`Unknown language id: ${languageId}`)); + } + this._modelService.setMode(model, this._modeService.create(languageId)); + return Promise.resolve(undefined); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts b/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts index 711a957ef7f..256f62fb16d 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Registry } from 'vs/platform/registry/common/platform'; import { IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry } from 'vs/workbench/parts/output/common/output'; import { IPartService } from 'vs/workbench/services/part/common/partService'; @@ -11,7 +10,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { MainThreadOutputServiceShape, MainContext, IExtHostContext, ExtHostOutputServiceShape, ExtHostContext } from '../node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { UriComponents, URI } from 'vs/base/common/uri'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { anyEvent } from 'vs/base/common/event'; @extHostNamedCustomer(MainContext.MainThreadOutputService) @@ -46,15 +45,11 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut setVisibleChannel(); } - public dispose(): void { - super.dispose(); - // Leave all the existing channels intact (e.g. might help with troubleshooting) - } - public $register(label: string, log: boolean, file?: UriComponents): Thenable { const id = 'extension-output-#' + (MainThreadOutputService._idPool++); Registry.as(Extensions.OutputChannels).registerChannel({ id, label, file: file ? URI.revive(file) : null, log }); - return TPromise.as(id); + this._register(toDisposable(() => this.$dispose(id))); + return Promise.resolve(id); } public $append(channelId: string, value: string): Thenable { @@ -92,7 +87,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut public $close(channelId: string): Thenable { const panel = this._panelService.getActivePanel(); if (panel && panel.getId() === OUTPUT_PANEL_ID && channelId === this._outputService.getActiveChannel().id) { - return this._partService.setPanelHidden(true); + this._partService.setPanelHidden(true); } return undefined; diff --git a/src/vs/workbench/api/electron-browser/mainThreadProgress.ts b/src/vs/workbench/api/electron-browser/mainThreadProgress.ts index 876fb35088e..3ce2237f606 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadProgress.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadProgress.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IProgress } from 'vs/platform/progress/common/progress'; +import { IProgress, IProgressService2, IProgressStep, IProgressOptions } from 'vs/platform/progress/common/progress'; import { MainThreadProgressShape, MainContext, IExtHostContext, ExtHostProgressShape, ExtHostContext } from '../node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; -import { IProgressService2, IProgressStep, IProgressOptions } from 'vs/workbench/services/progress/common/progress'; @extHostNamedCustomer(MainContext.MainThreadProgress) export class MainThreadProgress implements MainThreadProgressShape { diff --git a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts index 871cd1ce3f5..0c15505bd43 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { IPickOptions, IInputOptions, IQuickInputService, IQuickInput } from 'vs/platform/quickinput/common/quickInput'; import { InputBoxOptions } from 'vscode'; import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItems, MainContext, IExtHostContext, TransferQuickInput, TransferQuickInputButton } from 'vs/workbench/api/node/extHost.protocol'; @@ -21,10 +20,10 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { private _proxy: ExtHostQuickOpenShape; private _quickInputService: IQuickInputService; - private _doSetItems: (items: TransferQuickPickItems[]) => any; - private _doSetError: (error: Error) => any; - private _contents: TPromise; - private _token: number = 0; + private _items: Record = {}; constructor( extHostContext: IExtHostContext, @@ -37,21 +36,9 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { public dispose(): void { } - $show(options: IPickOptions, token: CancellationToken): Thenable { - const myToken = ++this._token; - - this._contents = new TPromise((c, e) => { - this._doSetItems = (items) => { - if (myToken === this._token) { - c(items); - } - }; - - this._doSetError = (error) => { - if (myToken === this._token) { - e(error); - } - }; + $show(instance: number, options: IPickOptions, token: CancellationToken): Thenable { + const contents = new Promise((resolve, reject) => { + this._items[instance] = { resolve, reject }; }); options = { @@ -64,14 +51,14 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { }; if (options.canPickMany) { - return this._quickInputService.pick(this._contents, options as { canPickMany: true }, token).then(items => { + return this._quickInputService.pick(contents, options as { canPickMany: true }, token).then(items => { if (items) { return items.map(item => item.handle); } return undefined; }); } else { - return this._quickInputService.pick(this._contents, options, token).then(item => { + return this._quickInputService.pick(contents, options, token).then(item => { if (item) { return item.handle; } @@ -80,16 +67,18 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } } - $setItems(items: TransferQuickPickItems[]): Thenable { - if (this._doSetItems) { - this._doSetItems(items); + $setItems(instance: number, items: TransferQuickPickItems[]): Thenable { + if (this._items[instance]) { + this._items[instance].resolve(items); + delete this._items[instance]; } return undefined; } - $setError(error: Error): Thenable { - if (this._doSetError) { - this._doSetError(error); + $setError(instance: number, error: Error): Thenable { + if (this._items[instance]) { + this._items[instance].reject(error); + delete this._items[instance]; } return undefined; } @@ -210,7 +199,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { input[param] = params[param]; } } - return TPromise.as(undefined); + return Promise.resolve(undefined); } $dispose(sessionId: number): Thenable { @@ -219,6 +208,6 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { session.input.dispose(); this.sessions.delete(sessionId); } - return TPromise.as(undefined); + return Promise.resolve(undefined); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts index b1e2f934120..3602f7f3cb5 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts @@ -395,6 +395,16 @@ export class MainThreadSCM implements MainThreadSCMShape { repository.input.placeholder = placeholder; } + $setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void { + const repository = this._repositories[sourceControlHandle]; + + if (!repository) { + return; + } + + repository.input.visible = visible; + } + $setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void { const repository = this._repositories[sourceControlHandle]; diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index a3363290ea8..2e66dbf1806 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -22,7 +22,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IProgressService2, ProgressLocation } from 'vs/workbench/services/progress/common/progress'; +import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress'; import { localize } from 'vs/nls'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/api/electron-browser/mainThreadSearch.ts b/src/vs/workbench/api/electron-browser/mainThreadSearch.ts index f5e2f7996e9..9728cc8359e 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSearch.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSearch.ts @@ -8,7 +8,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IFileMatch, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, QueryType, SearchProviderType } from 'vs/platform/search/common/search'; +import { IFileMatch, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchResultProvider, ISearchService, QueryType, SearchProviderType, ITextQuery, IFileQuery } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostSearchShape, IExtHostContext, MainContext, MainThreadSearchShape } from '../node/extHost.protocol'; @@ -68,7 +68,7 @@ class SearchOperation { private static _idPool = 0; constructor( - readonly progress: (match: IFileMatch) => any, + readonly progress?: (match: IFileMatch) => any, readonly id: number = ++SearchOperation._idPool, readonly matches = new Map() ) { @@ -78,12 +78,14 @@ class SearchOperation { addMatch(match: IFileMatch): void { if (this.matches.has(match.resource.toString())) { // Merge with previous IFileMatches - this.matches.get(match.resource.toString()).matches.push(...match.matches); + this.matches.get(match.resource.toString()).results.push(...match.results); } else { this.matches.set(match.resource.toString(), match); } - this.progress(match); + if (this.progress) { + this.progress(match); + } } } @@ -106,7 +108,15 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { dispose(this._registrations); } - search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): TPromise { + fileSearch(query: IFileQuery, token: CancellationToken = CancellationToken.None): TPromise { + return this.doSearch(query, null, token); + } + + textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): TPromise { + return this.doSearch(query, onProgress, token); + } + + doSearch(query: ITextQuery | IFileQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): TPromise { if (isFalsyOrEmpty(query.folderQueries)) { return TPromise.as(undefined); } @@ -116,7 +126,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { const searchP = query.type === QueryType.File ? this._proxy.$provideFileSearchResults(this._handle, search.id, query, token) - : this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, query, token); + : this._proxy.$provideTextSearchResults(this._handle, search.id, query, token); return TPromise.wrap(searchP).then((result: ISearchCompleteStats) => { this._searches.delete(search.id); @@ -139,10 +149,10 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { const searchOp = this._searches.get(session); dataOrUri.forEach(result => { - if ((result).matches) { + if ((result).results) { searchOp.addMatch({ resource: URI.revive((result).resource), - matches: (result).matches + results: (result).results }); } else { searchOp.addMatch({ diff --git a/src/vs/workbench/api/electron-browser/mainThreadTask.ts b/src/vs/workbench/api/electron-browser/mainThreadTask.ts index d25c79c05ef..8d827f0bacb 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTask.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTask.ts @@ -8,10 +8,10 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import * as Objects from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import { IStringDictionary } from 'vs/base/common/collections'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -20,9 +20,11 @@ import { PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, ExtensionTaskSource, RevealKind, PanelKind } from 'vs/workbench/parts/tasks/common/tasks'; -import { TaskDefinition } from 'vs/workbench/parts/tasks/node/tasks'; -import { ITaskService, TaskFilter } from 'vs/workbench/parts/tasks/common/taskService'; +import { ResolveSet, ResolvedVariables } from 'vs/workbench/parts/tasks/common/taskSystem'; +import { ITaskService, TaskFilter, ITaskProvider } from 'vs/workbench/parts/tasks/common/taskService'; + +import { TaskDefinition } from 'vs/workbench/parts/tasks/node/tasks'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; @@ -91,7 +93,7 @@ namespace TaskPresentationOptionsDTO { } export function to(value: TaskPresentationOptionsDTO): PresentationOptions { if (value === void 0 || value === null) { - return { reveal: RevealKind.Always, echo: true, focus: false, panel: PanelKind.Shared, showReuseMessage: true }; + return { reveal: RevealKind.Always, echo: true, focus: false, panel: PanelKind.Shared, showReuseMessage: true, clear: false }; } return Objects.assign(Object.create(null), value); } @@ -362,7 +364,7 @@ export class MainThreadTask implements MainThreadTaskShape { private _extHostContext: IExtHostContext; private _proxy: ExtHostTaskShape; - private _activeHandles: { [handle: number]: boolean; }; + private _providers: Map; constructor( extHostContext: IExtHostContext, @@ -370,7 +372,7 @@ export class MainThreadTask implements MainThreadTaskShape { @IWorkspaceContextService private readonly _workspaceContextServer: IWorkspaceContextService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTask); - this._activeHandles = Object.create(null); + this._providers = new Map(); this._taskService.onDidStateChange((event: TaskEvent) => { let task = event.__task; if (event.kind === TaskEventKind.Start) { @@ -386,16 +388,16 @@ export class MainThreadTask implements MainThreadTaskShape { } public dispose(): void { - Object.keys(this._activeHandles).forEach((handle) => { - this._taskService.unregisterTaskProvider(parseInt(handle, 10)); + this._providers.forEach((value) => { + value.disposable.dispose(); }); - this._activeHandles = Object.create(null); + this._providers.clear(); } public $registerTaskProvider(handle: number): Thenable { - this._taskService.registerTaskProvider(handle, { + let provider: ITaskProvider = { provideTasks: (validTypes: IStringDictionary) => { - return TPromise.wrap(this._proxy.$provideTasks(handle, validTypes)).then((value) => { + return Promise.resolve(this._proxy.$provideTasks(handle, validTypes)).then((value) => { let tasks: Task[] = []; for (let task of value.tasks) { let taskTransfer = task._source as any as ExtensionTaskSourceTransfer; @@ -417,15 +419,15 @@ export class MainThreadTask implements MainThreadTaskShape { return value; }); } - }); - this._activeHandles[handle] = true; - return TPromise.wrap(undefined); + }; + let disposable = this._taskService.registerTaskProvider(provider); + this._providers.set(handle, { disposable, provider }); + return Promise.resolve(undefined); } public $unregisterTaskProvider(handle: number): Thenable { - this._taskService.unregisterTaskProvider(handle); - delete this._activeHandles[handle]; - return TPromise.wrap(undefined); + this._providers.delete(handle); + return Promise.resolve(undefined); } public $fetchTasks(filter?: TaskFilterDTO): Thenable { @@ -442,7 +444,7 @@ export class MainThreadTask implements MainThreadTaskShape { } public $executeTask(value: TaskHandleDTO | TaskDTO): Thenable { - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { if (TaskHandleDTO.is(value)) { let workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder)); this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task) => { @@ -468,7 +470,7 @@ export class MainThreadTask implements MainThreadTaskShape { } public $terminateTask(id: string): Thenable { - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { this._taskService.getActiveTasks().then((tasks) => { for (let task of tasks) { if (id === task._id) { @@ -506,12 +508,18 @@ export class MainThreadTask implements MainThreadTaskShape { return URI.parse(`${info.scheme}://${info.authority}${path}`); }, context: this._extHostContext, - resolveVariables: (workspaceFolder: IWorkspaceFolder, variables: Set): TPromise> => { + resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet): Promise => { let vars: string[] = []; - variables.forEach(item => vars.push(item)); - return TPromise.wrap(this._proxy.$resolveVariables(workspaceFolder.uri, vars)).then(values => { - let result = new Map(); - Object.keys(values).forEach(key => result.set(key, values[key])); + toResolve.variables.forEach(item => vars.push(item)); + return Promise.resolve(this._proxy.$resolveVariables(workspaceFolder.uri, { process: toResolve.process, variables: vars })).then(values => { + let result = { + process: undefined as string, + variables: new Map() + }; + Object.keys(values.variables).forEach(key => result.variables.set(key, values[key])); + if (Types.isString(values.process)) { + result.process = values.process; + } return result; }); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 35192f668ad..0369631af27 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -5,7 +5,6 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; @@ -64,12 +63,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape ignoreConfigurationCwd: true, env }; - return TPromise.as(this.terminalService.createTerminal(shellLaunchConfig).id); + return Promise.resolve(this.terminalService.createTerminal(shellLaunchConfig).id); } public $createTerminalRenderer(name: string): Thenable { const instance = this.terminalService.createTerminalRenderer(name); - return TPromise.as(instance.id); + return Promise.resolve(instance.id); } public $show(terminalId: number, preserveFocus: boolean): void { diff --git a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts index ef702f42e53..8b36577c8dd 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts @@ -6,10 +6,11 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Disposable } from 'vs/base/common/lifecycle'; import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from '../node/extHost.protocol'; -import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeViewer, ViewsRegistry, ICustomViewDescriptor } from 'vs/workbench/common/views'; +import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeViewer, ViewsRegistry, ICustomViewDescriptor, IRevealOptions } from 'vs/workbench/common/views'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { distinct } from 'vs/base/common/arrays'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { isUndefinedOrNull, isNumber } from 'vs/base/common/types'; @extHostNamedCustomer(MainContext.MainThreadTreeViews) export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape { @@ -26,12 +27,13 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } - $registerTreeViewDataProvider(treeViewId: string): void { + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): void { const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService); this._dataProviders.set(treeViewId, dataProvider); const viewer = this.getTreeViewer(treeViewId); if (viewer) { viewer.dataProvider = dataProvider; + viewer.showCollapseAllAction = !!options.showCollapseAll; this.registerListeners(treeViewId, viewer); this._proxy.$setVisible(treeViewId, viewer.visible); } else { @@ -39,22 +41,58 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie } } - $reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: { select: boolean, focus: boolean }): Thenable { + $reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Thenable { return this.viewsService.openView(treeViewId, options.focus) .then(() => { const viewer = this.getTreeViewer(treeViewId); - return viewer ? viewer.reveal(item, parentChain, options) : null; + return this.reveal(viewer, this._dataProviders.get(treeViewId), item, parentChain, options); }); } - $refresh(treeViewId: string, itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem }): TPromise { + $refresh(treeViewId: string, itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem }): Thenable { const viewer = this.getTreeViewer(treeViewId); const dataProvider = this._dataProviders.get(treeViewId); if (viewer && dataProvider) { const itemsToRefresh = dataProvider.getItemsToRefresh(itemsToRefreshByHandle); return viewer.refresh(itemsToRefresh.length ? itemsToRefresh : void 0); } - return TPromise.as(null); + return null; + } + + private async reveal(treeViewer: ITreeViewer, dataProvider: TreeViewDataProvider, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): TPromise { + options = options ? options : { select: false, focus: false }; + const select = isUndefinedOrNull(options.select) ? false : options.select; + const focus = isUndefinedOrNull(options.focus) ? false : options.focus; + let expand = Math.min(isNumber(options.expand) ? options.expand : options.expand === true ? 1 : 0, 3); + + if (dataProvider.isEmpty()) { + // Refresh if empty + await treeViewer.refresh(); + } + for (const parent of parentChain) { + await treeViewer.expand(parent); + } + item = dataProvider.getItem(item.handle); + if (item) { + await treeViewer.reveal(item); + if (select) { + treeViewer.setSelection([item]); + } + if (focus) { + treeViewer.setFocus(item); + } + let itemsToExpand = [item]; + for (; itemsToExpand.length > 0 && expand > 0; expand--) { + await treeViewer.expand(itemsToExpand); + itemsToExpand = itemsToExpand.reduce((result, item) => { + item = dataProvider.getItem(item.handle); + if (item && item.children && item.children.length) { + result.push(...item.children); + } + return result; + }, []); + } + } } private registerListeners(treeViewId: string, treeViewer: ITreeViewer): void { @@ -110,7 +148,7 @@ class TreeViewDataProvider implements ITreeViewDataProvider { const itemsToRefresh: ITreeItem[] = []; if (itemsToRefreshByHandle) { for (const treeItemHandle of Object.keys(itemsToRefreshByHandle)) { - const currentTreeItem = this.itemsMap.get(treeItemHandle); + const currentTreeItem = this.getItem(treeItemHandle); if (currentTreeItem) { // Refresh only if the item exists const treeItem = itemsToRefreshByHandle[treeItemHandle]; // Update the current item with refreshed item @@ -132,8 +170,16 @@ class TreeViewDataProvider implements ITreeViewDataProvider { return itemsToRefresh; } + getItem(treeItemHandle: string): ITreeItem { + return this.itemsMap.get(treeItemHandle); + } + + isEmpty(): boolean { + return this.itemsMap.size === 0; + } + private postGetChildren(elements: ITreeItem[]): ITreeItem[] { - const result = []; + const result: ITreeItem[] = []; if (elements) { for (const element of elements) { this.itemsMap.set(element.handle, element); diff --git a/src/vs/workbench/api/electron-browser/mainThreadUrls.ts b/src/vs/workbench/api/electron-browser/mainThreadUrls.ts index f2ee377a15a..ab3585598ed 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadUrls.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadUrls.ts @@ -5,7 +5,6 @@ import { ExtHostContext, IExtHostContext, MainContext, MainThreadUrlsShape, ExtHostUrlsShape } from 'vs/workbench/api/node/extHost.protocol'; import { extHostNamedCustomer } from './extHostCustomers'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -19,12 +18,12 @@ class ExtensionUrlHandler implements IURLHandler { readonly extensionId: string ) { } - handleURL(uri: URI): TPromise { + handleURL(uri: URI): Promise { if (uri.authority !== this.extensionId) { - return TPromise.as(false); + return Promise.resolve(false); } - return TPromise.wrap(this.proxy.$handleExternalUri(this.handle, uri)).then(() => true); + return Promise.resolve(this.proxy.$handleExternalUri(this.handle, uri)).then(() => true); } } @@ -49,14 +48,14 @@ export class MainThreadUrls implements MainThreadUrlsShape { this.handlers.set(handle, { extensionId, disposable }); this.inactiveExtensionUrlHandler.registerExtensionHandler(extensionId, handler); - return TPromise.as(null); + return Promise.resolve(null); } $unregisterUriHandler(handle: number): Thenable { const tuple = this.handlers.get(handle); if (!tuple) { - return TPromise.as(null); + return Promise.resolve(null); } const { extensionId, disposable } = tuple; @@ -65,7 +64,7 @@ export class MainThreadUrls implements MainThreadUrlsShape { this.handlers.delete(handle); disposable.dispose(); - return TPromise.as(null); + return Promise.resolve(null); } dispose(): void { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 03c26a2eb02..74a4a653241 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -5,7 +5,6 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as map from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -19,6 +18,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import * as vscode from 'vscode'; import { extHostNamedCustomer } from './extHostCustomers'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver { @@ -45,7 +45,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv @IWebviewEditorService private readonly _webviewService: IWebviewEditorService, @IOpenerService private readonly _openerService: IOpenerService, @IExtensionService private readonly _extensionService: IExtensionService, - + @ITelemetryService private readonly _telemetryService: ITelemetryService ) { this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews); _editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._toDispose); @@ -68,6 +68,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv title: string, showOptions: { viewColumn: EditorViewColumn | null, preserveFocus: boolean }, options: WebviewInputOptions, + extensionId: string, extensionLocation: UriComponents ): void { const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null); @@ -84,6 +85,13 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._webviews.set(handle, webview); this._activeWebview = handle; + + /* __GDPR__ + "webviews:createWebviewPanel" : { + "extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId }); } public $disposeWebview(handle: WebviewPanelHandle): void { @@ -133,7 +141,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv editor.sendMessage(message); } - return TPromise.as(editors.length > 0); + return Promise.resolve(editors.length > 0); } public $registerSerializer(viewType: string): void { @@ -144,9 +152,9 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._revivers.delete(viewType); } - public reviveWebview(webview: WebviewEditorInput): TPromise { + public reviveWebview(webview: WebviewEditorInput): Promise { const viewType = webview.state.viewType; - return this._extensionService.activateByEvent(`onWebviewPanel:${viewType}`).then(() => { + return Promise.resolve(this._extensionService.activateByEvent(`onWebviewPanel:${viewType}`).then(() => { const handle = 'revival-' + MainThreadWebviews.revivalPool++; this._webviews.set(handle, webview); webview._events = this.createWebviewEventDelegate(handle); @@ -164,7 +172,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv .then(undefined, () => { webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType); }); - }); + })); } public canRevive(webview: WebviewEditorInput): boolean { @@ -175,14 +183,13 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv return this._revivers.has(webview.state.viewType) || !!webview.reviver; } - private _onWillShutdown(): TPromise { + private _onWillShutdown(): boolean { this._webviews.forEach((view) => { if (this.canRevive(view)) { view.state.state = view.webviewState; } }); - - return TPromise.as(false); // Don't veto shutdown + return false; // Don't veto shutdown } private createWebviewEventDelegate(handle: WebviewPanelHandle) { @@ -317,4 +324,4 @@ function reviveWebviewIcon( light: URI.revive(value.light), dark: URI.revive(value.dark) }; -} \ No newline at end of file +} diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index fbf413cfb3b..a57c1ae6512 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -6,19 +6,18 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; -import { IFolderQuery, IPatternInfo, IQueryOptions, ISearchConfiguration, ISearchProgressItem, ISearchQuery, ISearchService, QueryType } from 'vs/platform/search/common/search'; +import { IFolderQuery, IPatternInfo, ISearchConfiguration, ISearchProgressItem, ISearchService, QueryType, IFileQuery } from 'vs/platform/search/common/search'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; -import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { QueryBuilder, ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; @@ -41,6 +40,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { @IConfigurationService private readonly _configurationService: IConfigurationService, @IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService, @IStatusbarService private readonly _statusbarService: IStatusbarService, + @IWindowService private readonly _windowService: IWindowService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILabelService private readonly _labelService: ILabelService ) { @@ -141,13 +141,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return !folderConfig.search.followSymlinks; }); - const query: ISearchQuery = { + // TODO replace wth QueryBuilder + folderQueries.forEach(fq => { + fq.ignoreSymlinks = ignoreSymlinks; + }); + + const query: IFileQuery = { folderQueries, type: QueryType.File, maxResults, disregardExcludeSettings: excludePatternOrDisregardExcludes === false, useRipgrep, - ignoreSymlinks + _reason: 'startFileSearch' }; if (typeof includePattern === 'string') { query.includePattern = { [includePattern]: true }; @@ -159,36 +164,37 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { this._searchService.extendQuery(query); - return this._searchService.search(query, token).then(result => { + return this._searchService.fileSearch(query, token).then(result => { return result.results.map(m => m.resource); }, err => { if (!isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } return undefined; }); } - $startTextSearch(pattern: IPatternInfo, options: IQueryOptions, requestId: number, token: CancellationToken): Thenable { + $startTextSearch(pattern: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Thenable { const workspace = this._contextService.getWorkspace(); const folders = workspace.folders.map(folder => folder.uri); const queryBuilder = this._instantiationService.createInstance(QueryBuilder); const query = queryBuilder.text(pattern, folders, options); + query._reason = 'startTextSearch'; const onProgress = (p: ISearchProgressItem) => { - if (p.matches) { + if (p.results) { this._proxy.$handleTextSearchResult(p, requestId); } }; - const search = this._searchService.search(query, token, onProgress).then( + const search = this._searchService.textSearch(query, token, onProgress).then( result => { return { limitHit: result.limitHit }; }, err => { if (!isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } return undefined; @@ -201,17 +207,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { const queryBuilder = this._instantiationService.createInstance(QueryBuilder); const folders = this._contextService.getWorkspace().folders.map(folder => folder.uri); const query = queryBuilder.file(folders, { + _reason: 'checkExists', includePattern: includes.join(', '), exists: true }); - return this._searchService.search(query, token).then( + return this._searchService.fileSearch(query, token).then( result => { return result.limitHit; }, err => { if (!isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } return undefined; @@ -225,6 +232,10 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return result.results.every(each => each.success === true); }); } + + $resolveProxy(url: string): Thenable { + return this._windowService.resolveProxy(url); + } } CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (accessor: ServicesAccessor, workspace: URI, disableExtensions: string[]) { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 119dfefdd62..be4dc17380c 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -3,81 +3,70 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import * as errors from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; import { TernarySearchTree } from 'vs/base/common/map'; -import { score } from 'vs/editor/common/modes/languageSelector'; +import * as paths from 'vs/base/common/paths'; import * as platform from 'vs/base/common/platform'; -import * as errors from 'vs/base/common/errors'; -import product from 'vs/platform/node/product'; +import Severity from 'vs/base/common/severity'; +import { URI } from 'vs/base/common/uri'; +import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; +import { OverviewRulerLane } from 'vs/editor/common/model'; +import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; +import { score } from 'vs/editor/common/modes/languageSelector'; +import * as files from 'vs/platform/files/common/files'; import pkg from 'vs/platform/node/package'; -import { ExtHostFileSystemEventService } from 'vs/workbench/api/node/extHostFileSystemEventService'; -import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; -import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; +import product from 'vs/platform/node/product'; +import { ExtHostContext, IInitData, IMainContext, MainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands'; +import { ExtHostClipboard } from 'vs/workbench/api/node/extHostClipboard'; +import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; +import { ExtHostComments } from 'vs/workbench/api/node/extHostComments'; +import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService'; +import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations'; +import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; +import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs'; import { ExtHostDocumentContentProvider } from 'vs/workbench/api/node/extHostDocumentContentProviders'; import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/node/extHostDocumentSaveParticipant'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; -import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; -import { ExtHostTreeViews } from 'vs/workbench/api/node/extHostTreeViews'; -import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { ExtHostQuickOpen } from 'vs/workbench/api/node/extHostQuickOpen'; -import { ExtHostProgress } from 'vs/workbench/api/node/extHostProgress'; -import { ExtHostSCM } from 'vs/workbench/api/node/extHostSCM'; -import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; -import { ExtHostStatusBar } from 'vs/workbench/api/node/extHostStatusBar'; -import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; -import { ExtHostOutputService } from 'vs/workbench/api/node/extHostOutputService'; -import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService'; -import { ExtHostMessageService } from 'vs/workbench/api/node/extHostMessageService'; -import { ExtHostEditors } from 'vs/workbench/api/node/extHostTextEditors'; -import { ExtHostLanguages } from 'vs/workbench/api/node/extHostLanguages'; -import { ExtHostLanguageFeatures, ISchemeTransformer } from 'vs/workbench/api/node/extHostLanguageFeatures'; -import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands'; -import { ExtHostTask } from 'vs/workbench/api/node/extHostTask'; -import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService'; -import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow'; -import * as extHostTypes from 'vs/workbench/api/node/extHostTypes'; -import { URI } from 'vs/base/common/uri'; -import Severity from 'vs/base/common/severity'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import * as vscode from 'vscode'; -import * as paths from 'vs/base/common/paths'; -import * as files from 'vs/platform/files/common/files'; -import { MainContext, ExtHostContext, IInitData, IMainContext } from './extHost.protocol'; -import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; -import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; -import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; -import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs'; -import { ExtHostFileSystem } from 'vs/workbench/api/node/extHostFileSystem'; -import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations'; -import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; +import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; import { ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator'; -import { OverviewRulerLane } from 'vs/editor/common/model'; +import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; +import { ExtHostFileSystem } from 'vs/workbench/api/node/extHostFileSystem'; +import { ExtHostFileSystemEventService } from 'vs/workbench/api/node/extHostFileSystemEventService'; +import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; +import { ExtHostLanguageFeatures, ISchemeTransformer } from 'vs/workbench/api/node/extHostLanguageFeatures'; +import { ExtHostLanguages } from 'vs/workbench/api/node/extHostLanguages'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; -import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview'; -import { ExtHostComments } from './extHostComments'; -import { ExtHostSearch } from './extHostSearch'; -import { ExtHostUrls } from './extHostUrls'; -import { localize } from 'vs/nls'; -import { ExtHostClipboard } from 'vs/workbench/api/node/extHostClipboard'; +import { ExtHostMessageService } from 'vs/workbench/api/node/extHostMessageService'; +import { ExtHostOutputService } from 'vs/workbench/api/node/extHostOutputService'; +import { ExtHostProgress } from 'vs/workbench/api/node/extHostProgress'; +import { ExtHostQuickOpen } from 'vs/workbench/api/node/extHostQuickOpen'; +import { ExtHostSCM } from 'vs/workbench/api/node/extHostSCM'; +import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; +import { ExtHostStatusBar } from 'vs/workbench/api/node/extHostStatusBar'; import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; +import { ExtHostTask } from 'vs/workbench/api/node/extHostTask'; +import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService'; +import { ExtHostEditors } from 'vs/workbench/api/node/extHostTextEditors'; +import { ExtHostTreeViews } from 'vs/workbench/api/node/extHostTreeViews'; +import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; +import * as extHostTypes from 'vs/workbench/api/node/extHostTypes'; +import { ExtHostUrls } from 'vs/workbench/api/node/extHostUrls'; +import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview'; +import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow'; +import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; +import { IExtensionDescription, throwProposedApiError, checkProposedApiEnabled, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; +import * as vscode from 'vscode'; export interface IExtensionApiFactory { (extension: IExtensionDescription): typeof vscode; } -export function checkProposedApiEnabled(extension: IExtensionDescription): void { - if (!extension.enableProposedApi) { - throwProposedApiError(extension); - } -} - -function throwProposedApiError(extension: IExtensionDescription): never { - throw new Error(`[${extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.id}`); -} - function proposedApiFunction(extension: IExtensionDescription, fn: T): T { if (extension.enableProposedApi) { return fn; @@ -145,7 +134,8 @@ export function createApiFactory( const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments); // Register an output channel for exthost log - extHostOutputService.createOutputChannelFromLogFile(localize('extensionsLog', "Extension Host"), extHostLogService.logFile); + const name = localize('extensionsLog', "Extension Host"); + extHostOutputService.createOutputChannelFromLogFile(name, extHostLogService.logFile); // Register API-ish commands ExtHostApiCommands.register(extHostCommands); @@ -157,7 +147,7 @@ export function createApiFactory( // extension should specify then the `file`-scheme, e.g `{ scheme: 'fooLang', language: 'fooLang' }` // We only inform once, it is not a warning because we just want to raise awareness and because // we cannot say if the extension is doing it right or wrong... - let checkSelector = (function () { + const checkSelector = (function () { let done = (!extension.isUnderDevelopment); function informOnce(selector: vscode.DocumentSelector) { if (!done) { @@ -182,6 +172,24 @@ export function createApiFactory( }; })(); + // Warn when trying to use the vscode.previewHtml command as it does not work properly in all scenarios and + // has security concerns. + const checkCommand = (() => { + let done = !extension.isUnderDevelopment; + const informOnce = () => { + if (!done) { + done = true; + console.warn(`Extension '${extension.id}' uses the 'vscode.previewHtml' command which is deprecated and will be removed. Please update your extension to use the Webview API: https://go.microsoft.com/fwlink/?linkid=2039309`); + } + }; + return (commandId: string) => { + if (commandId === 'vscode.previewHtml') { + informOnce(); + } + return commandId; + }; + })(); + // namespace: commands const commands: typeof vscode.commands = { registerCommand(id: string, command: (...args: any[]) => T | Thenable, thisArgs?: any): vscode.Disposable { @@ -221,7 +229,7 @@ export function createApiFactory( }); }), executeCommand(id: string, ...args: any[]): Thenable { - return extHostCommands.executeCommand(id, ...args); + return extHostCommands.executeCommand(checkCommand(id), ...args); }, getCommands(filterInternal: boolean = false): Thenable { return extHostCommands.getCommands(filterInternal); @@ -284,64 +292,64 @@ export function createApiFactory( return score(typeConverters.LanguageSelector.from(selector), document.uri, document.languageId, true); }, registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { - return extHostLanguageFeatures.registerCodeActionProvider(checkSelector(selector), provider, extension, metadata); + return extHostLanguageFeatures.registerCodeActionProvider(extension, checkSelector(selector), provider, metadata); }, registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { - return extHostLanguageFeatures.registerCodeLensProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider); }, registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDefinitionProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider); }, registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable { - return extHostLanguageFeatures.registerImplementationProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerImplementationProvider(extension, checkSelector(selector), provider); }, registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { - return extHostLanguageFeatures.registerTypeDefinitionProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerTypeDefinitionProvider(extension, checkSelector(selector), provider); }, registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable { - return extHostLanguageFeatures.registerHoverProvider(checkSelector(selector), provider, extension.id); + return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.id); }, registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentHighlightProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); }, registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable { - return extHostLanguageFeatures.registerReferenceProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerReferenceProvider(extension, checkSelector(selector), provider); }, registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable { - return extHostLanguageFeatures.registerRenameProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerRenameProvider(extension, checkSelector(selector), provider); }, registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentSymbolProvider(checkSelector(selector), provider, extension); + return extHostLanguageFeatures.registerDocumentSymbolProvider(extension, checkSelector(selector), provider); }, registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable { - return extHostLanguageFeatures.registerWorkspaceSymbolProvider(provider); + return extHostLanguageFeatures.registerWorkspaceSymbolProvider(extension, provider); }, registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentFormattingEditProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerDocumentFormattingEditProvider(extension, checkSelector(selector), provider); }, registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentRangeFormattingEditProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerDocumentRangeFormattingEditProvider(extension, checkSelector(selector), provider); }, registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable { - return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters)); + return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(extension, checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters)); }, registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, firstItem?: string | vscode.SignatureHelpProviderMetadata, ...remaining: string[]): vscode.Disposable { if (typeof firstItem === 'object') { - return extHostLanguageFeatures.registerSignatureHelpProvider(checkSelector(selector), provider, firstItem); + return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, firstItem); } - return extHostLanguageFeatures.registerSignatureHelpProvider(checkSelector(selector), provider, typeof firstItem === 'undefined' ? [] : [firstItem, ...remaining]); + return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, typeof firstItem === 'undefined' ? [] : [firstItem, ...remaining]); }, registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable { - return extHostLanguageFeatures.registerCompletionItemProvider(checkSelector(selector), provider, triggerCharacters); + return extHostLanguageFeatures.registerCompletionItemProvider(extension, checkSelector(selector), provider, triggerCharacters); }, registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentLinkProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerDocumentLinkProvider(extension, checkSelector(selector), provider); }, registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { - return extHostLanguageFeatures.registerColorProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerColorProvider(extension, checkSelector(selector), provider); }, registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { - return extHostLanguageFeatures.registerFoldingRangeProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerFoldingRangeProvider(extension, checkSelector(selector), provider); }, setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => { return extHostLanguageFeatures.setLanguageConfiguration(language, configuration); @@ -363,11 +371,11 @@ export function createApiFactory( return extHostTerminalService.terminals; }, showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Thenable { - let documentPromise: TPromise; + let documentPromise: Promise; if (URI.isUri(documentOrUri)) { - documentPromise = TPromise.wrap(workspace.openTextDocument(documentOrUri)); + documentPromise = Promise.resolve(workspace.openTextDocument(documentOrUri)); } else { - documentPromise = TPromise.wrap(documentOrUri); + documentPromise = Promise.resolve(documentOrUri); } return documentPromise.then(document => { return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus); @@ -450,7 +458,7 @@ export function createApiFactory( return extHostOutputService.createOutputChannel(name); }, createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { - return extHostWebviews.createWebview(extension.extensionLocation, viewType, title, showOptions, options); + return extHostWebviews.createWebview(extension, viewType, title, showOptions, options); }, createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal { if (typeof nameOrOptions === 'object') { @@ -462,10 +470,10 @@ export function createApiFactory( return extHostTerminalService.createTerminalRenderer(name); }), registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { - return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider); + return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension); }, createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider }): vscode.TreeView { - return extHostTreeViews.createTreeView(viewId, options); + return extHostTreeViews.createTreeView(viewId, options, extension); }, registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer); @@ -555,9 +563,9 @@ export function createApiFactory( let options = uriOrFileNameOrOptions as { language?: string; content?: string; }; if (typeof uriOrFileNameOrOptions === 'string') { - uriPromise = TPromise.as(URI.file(uriOrFileNameOrOptions)); + uriPromise = Promise.resolve(URI.file(uriOrFileNameOrOptions)); } else if (uriOrFileNameOrOptions instanceof URI) { - uriPromise = TPromise.as(uriOrFileNameOrOptions); + uriPromise = Promise.resolve(uriOrFileNameOrOptions); } else if (!options || typeof options === 'object') { uriPromise = extHostDocuments.createDocumentData(options); } else { @@ -619,7 +627,7 @@ export function createApiFactory( return exthostCommentProviders.registerDocumentCommentProvider(provider); }), registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => { - return exthostCommentProviders.registerWorkspaceCommentProvider(provider); + return exthostCommentProviders.registerWorkspaceCommentProvider(extension.id, provider); }), onDidRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => { return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables); @@ -795,6 +803,7 @@ export function createApiFactory( ThemeColor: extHostTypes.ThemeColor, ThemeIcon: extHostTypes.ThemeIcon, TreeItem: extHostTypes.TreeItem, + TreeItem2: extHostTypes.TreeItem, TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState, Uri: URI, ViewColumn: extHostTypes.ViewColumn, @@ -844,7 +853,7 @@ class Extension implements vscode.Extension { } } -export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): TPromise { +export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): Promise { return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie)); } @@ -882,19 +891,3 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT return defaultApiImpl; }; } - -const nullExtensionDescription: IExtensionDescription = { - id: 'nullExtensionDescription', - name: 'Null Extension Description', - publisher: 'vscode', - activationEvents: undefined, - contributes: undefined, - enableProposedApi: false, - engines: undefined, - extensionDependencies: undefined, - extensionLocation: undefined, - isBuiltin: false, - isUnderDevelopment: false, - main: undefined, - version: undefined -}; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 94d303c6bd0..8d3b6988fb1 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; import { SerializedError } from 'vs/base/common/errors'; import { IDisposable } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; @@ -24,23 +25,23 @@ import { LabelRules } from 'vs/platform/label/common/label'; import { LogLevel } from 'vs/platform/log/common/log'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { IPickOptions, IQuickInputButton, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { IPatternInfo, IQueryOptions, IRawFileMatch2, IRawSearchQuery, ISearchCompleteStats } from 'vs/platform/search/common/search'; +import { IPatternInfo, IRawFileMatch2, IRawQuery, IRawTextQuery, ISearchCompleteStats } from 'vs/platform/search/common/search'; import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar'; import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { EndOfLine, IFileOperationOptions, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes'; import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; import { TaskDTO, TaskExecutionDTO, TaskFilterDTO, TaskHandleDTO, TaskProcessEndedDTO, TaskProcessStartedDTO, TaskSystemInfoDTO } from 'vs/workbench/api/shared/tasks'; -import { ITreeItem } from 'vs/workbench/common/views'; -import { IConfig, ITerminalSettings, IAdapterDescriptor } from 'vs/workbench/parts/debug/common/debug'; +import { ITreeItem, IRevealOptions } from 'vs/workbench/common/views'; +import { IAdapterDescriptor, IConfig, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug'; +import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks'; import { ITerminalDimensions } from 'vs/workbench/parts/terminal/common/terminal'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol, ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; -import { IProgressOptions, IProgressStep } from 'vs/workbench/services/progress/common/progress'; +import { IRPCProtocol, ProxyIdentifier, createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId } from 'vs/workbench/services/extensions/node/proxyIdentifier'; +import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; -import { CancellationToken } from 'vs/base/common/cancellation'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -67,6 +68,7 @@ export interface IInitData { telemetryInfo: ITelemetryInfo; logLevel: LogLevel; logsLocation: URI; + remoteAuthority?: string | null; } export interface IConfigurationInitData extends IConfigurationData { @@ -79,6 +81,7 @@ export interface IWorkspaceConfigurationChangeEventData { } export interface IExtHostContext extends IRPCProtocol { + remoteAuthority: string; } export interface IMainContext extends IRPCProtocol { @@ -101,7 +104,7 @@ export interface MainThreadCommandsShape extends IDisposable { export interface MainThreadCommentsShape extends IDisposable { $registerDocumentCommentProvider(handle: number): void; $unregisterDocumentCommentProvider(handle: number): void; - $registerWorkspaceCommentProvider(handle: number): void; + $registerWorkspaceCommentProvider(handle: number, extensionId: string): void; $unregisterWorkspaceCommentProvider(handle: number): void; $onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void; } @@ -209,9 +212,9 @@ export interface MainThreadTextEditorsShape extends IDisposable { } export interface MainThreadTreeViewsShape extends IDisposable { - $registerTreeViewDataProvider(treeViewId: string): void; + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): void; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Thenable; - $reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: { select: boolean, focus: boolean }): Thenable; + $reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Thenable; } export interface MainThreadErrorsShape extends IDisposable { @@ -415,9 +418,9 @@ export interface TransferInputBox extends BaseTransferQuickInput { } export interface MainThreadQuickOpenShape extends IDisposable { - $show(options: IPickOptions, token: CancellationToken): Thenable; - $setItems(items: TransferQuickPickItems[]): Thenable; - $setError(error: Error): Thenable; + $show(instance: number, options: IPickOptions, token: CancellationToken): Thenable; + $setItems(instance: number, items: TransferQuickPickItems[]): Thenable; + $setError(instance: number, error: Error): Thenable; $input(options: vscode.InputBoxOptions, validateInput: boolean, token: CancellationToken): Thenable; $createOrUpdate(params: TransferQuickInput): Thenable; $dispose(id: number): Thenable; @@ -445,7 +448,7 @@ export interface WebviewPanelShowOptions { } export interface MainThreadWebviewsShape extends IDisposable { - $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionLocation: UriComponents): void; + $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: string, extensionLocation: UriComponents): void; $disposeWebview(handle: WebviewPanelHandle): void; $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewPanelHandle, value: string): void; @@ -482,10 +485,11 @@ export interface ExtHostUrlsShape { export interface MainThreadWorkspaceShape extends IDisposable { $startFileSearch(includePattern: string, includeFolder: URI, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Thenable; - $startTextSearch(query: IPatternInfo, options: IQueryOptions, requestId: number, token: CancellationToken): Thenable; + $startTextSearch(query: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Thenable; $checkExists(includes: string[], token: CancellationToken): Thenable; $saveAll(includeUntitled?: boolean): Thenable; $updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable; + $resolveProxy(url: string): Thenable; } export interface IFileChangeDto { @@ -577,6 +581,7 @@ export interface MainThreadSCMShape extends IDisposable { $setInputBoxValue(sourceControlHandle: number, value: string): void; $setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void; + $setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void; $setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void; } @@ -588,11 +593,11 @@ export interface MainThreadDebugServiceShape extends IDisposable { $acceptDAError(handle: number, name: string, message: string, stack: string): void; $acceptDAExit(handle: number, code: number, signal: string): void; $registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasProvideDaMethod: boolean, hasProvideTrackerMethod: boolean, handle: number): Thenable; - $unregisterDebugConfigurationProvider(handle: number): Thenable; + $unregisterDebugConfigurationProvider(handle: number): void; $startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration): Thenable; $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Thenable; - $appendDebugConsole(value: string): Thenable; - $startBreakpointEvents(): Thenable; + $appendDebugConsole(value: string): void; + $startBreakpointEvents(): void; $registerBreakpoints(breakpoints: (ISourceMultiBreakpointDto | IFunctionBreakpointDto)[]): Thenable; $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Thenable; } @@ -707,9 +712,9 @@ export interface ExtHostFileSystemShape { } export interface ExtHostSearchShape { - $provideFileSearchResults(handle: number, session: number, query: IRawSearchQuery, token: CancellationToken): Thenable; + $provideFileSearchResults(handle: number, session: number, query: IRawQuery, token: CancellationToken): Thenable; + $provideTextSearchResults(handle: number, session: number, query: IRawTextQuery, token: CancellationToken): Thenable; $clearCache(cacheKey: string): Thenable; - $provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, query: IRawSearchQuery, token: CancellationToken): Thenable; } export interface ExtHostExtensionServiceShape { @@ -911,7 +916,7 @@ export interface ExtHostTaskShape { $onDidStartTaskProcess(value: TaskProcessStartedDTO): void; $onDidEndTaskProcess(value: TaskProcessEndedDTO): void; $OnDidEndTask(execution: TaskExecutionDTO): void; - $resolveVariables(workspaceFolder: UriComponents, variables: string[]): Thenable; + $resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string }, variables: string[] }): Thenable<{ process?: string; variables: { [key: string]: string } }>; } export interface IBreakpointDto { diff --git a/src/vs/workbench/api/node/extHostClipboard.ts b/src/vs/workbench/api/node/extHostClipboard.ts index 342845ba00d..90c739c54c7 100644 --- a/src/vs/workbench/api/node/extHostClipboard.ts +++ b/src/vs/workbench/api/node/extHostClipboard.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { IMainContext, MainContext, MainThreadClipboardShape } from 'vs/workbench/api/node/extHost.protocol'; import * as vscode from 'vscode'; diff --git a/src/vs/workbench/api/node/extHostComments.ts b/src/vs/workbench/api/node/extHostComments.ts index 9d06aa6a57d..1ab328793c7 100644 --- a/src/vs/workbench/api/node/extHostComments.ts +++ b/src/vs/workbench/api/node/extHostComments.ts @@ -5,7 +5,6 @@ import { asThenable } from 'vs/base/common/async'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as modes from 'vs/editor/common/modes'; import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverters'; @@ -32,11 +31,12 @@ export class ExtHostComments implements ExtHostCommentsShape { } registerWorkspaceCommentProvider( + extensionId: string, provider: vscode.WorkspaceCommentProvider ): vscode.Disposable { const handle = ExtHostComments.handlePool++; this._workspaceProviders.set(handle, provider); - this._proxy.$registerWorkspaceCommentProvider(handle); + this._proxy.$registerWorkspaceCommentProvider(handle, extensionId); this.registerListeners(handle, provider); return { @@ -68,7 +68,7 @@ export class ExtHostComments implements ExtHostCommentsShape { const ran = extHostTypeConverter.Range.to(range); if (!data || !data.document) { - return TPromise.as(null); + return Promise.resolve(null); } const provider = this._documentProviders.get(handle); @@ -82,7 +82,7 @@ export class ExtHostComments implements ExtHostCommentsShape { const ran = extHostTypeConverter.Range.to(range); if (!data || !data.document) { - return TPromise.as(null); + return Promise.resolve(null); } const provider = this._documentProviders.get(handle); @@ -120,7 +120,7 @@ export class ExtHostComments implements ExtHostCommentsShape { $provideDocumentComments(handle: number, uri: UriComponents): Thenable { const data = this._documents.getDocumentData(URI.revive(uri)); if (!data || !data.document) { - return TPromise.as(null); + return Promise.resolve(null); } const provider = this._documentProviders.get(handle); @@ -132,7 +132,7 @@ export class ExtHostComments implements ExtHostCommentsShape { $provideWorkspaceComments(handle: number): Thenable { const provider = this._workspaceProviders.get(handle); if (!provider) { - return TPromise.as(null); + return Promise.resolve(null); } return asThenable(() => { diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 6552de5fc9e..a7c7ab899cd 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -6,11 +6,9 @@ import * as paths from 'vs/base/common/paths'; import { Schemas } from 'vs/base/common/network'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter } from 'vs/base/common/event'; import { asThenable } from 'vs/base/common/async'; import * as nls from 'vs/nls'; -import { deepClone } from 'vs/base/common/objects'; import { MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, IMainContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto @@ -71,7 +69,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { private _aexCommands: Map; private _debugAdapters: Map; - private _debugAdaptersTrackers: Map; + private _debugAdaptersTrackers: Map; private _variableResolver: IConfigurationResolverService; @@ -287,7 +285,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { // RPC methods (ExtHostDebugServiceShape) - public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable { if (args.kind === 'integrated') { @@ -300,7 +298,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { }); } - return new TPromise(resolve => { + return new Promise(resolve => { if (this._integratedTerminalInstance) { this._integratedTerminalInstance.processId.then(pid => { resolve(hasChildprocesses(pid)); @@ -318,7 +316,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this._integratedTerminalInstance.show(); - return new TPromise((resolve, error) => { + return new Promise((resolve) => { setTimeout(_ => { const command = prepareCommand(args, config); this._integratedTerminalInstance.sendText(command, true); @@ -337,7 +335,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return void 0; } - public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): TPromise { + public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { if (!this._variableResolver) { this._variableResolver = new ExtHostVariableResolverService(this._workspaceService, this._editorsService, this._configurationService); } @@ -353,10 +351,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } }; } - return TPromise.wrap(this._variableResolver.resolveAny(ws, config)); + return Promise.resolve(this._variableResolver.resolveAny(ws, config)); } - public $startDASession(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): TPromise { + public $startDASession(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable { const mythis = this; return this.getAdapterDescriptor(this._providerByType.get(config.type), sessionDto, folderUri, config).then(adapter => { @@ -392,17 +390,14 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { da.onMessage(message => { - // since we modify Source.paths in the message in place, we need to make a copy of it (see #61129) - const msg = deepClone(message); - if (tracker) { - tracker.fromDebugAdapter(msg); + tracker.fromDebugAdapter(message); } // DA -> VS Code - convertToVSCPaths(msg, source => stringToUri(source)); + message = convertToVSCPaths(message, source => stringToUri(source)); - mythis._debugServiceProxy.$acceptDAMessage(handle, msg); + mythis._debugServiceProxy.$acceptDAMessage(handle, message); }); da.onError(err => { if (tracker) { @@ -429,9 +424,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { }); } - public $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): TPromise { + public $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): Promise { + // VS Code -> DA - convertToDAPaths(message, source => uriToString(source)); + message = convertToDAPaths(message, source => uriToString(source)); const tracker = this._debugAdaptersTrackers.get(handle); if (tracker) { @@ -445,7 +441,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return void 0; } - public $stopDASession(handle: number): TPromise { + public $stopDASession(handle: number): Thenable { const tracker = this._debugAdaptersTrackers.get(handle); this._debugAdaptersTrackers.delete(handle); @@ -526,10 +522,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { public $provideDebugConfigurations(handle: number, folderUri: UriComponents | undefined): Thenable { let provider = this._providerByHandle.get(handle); if (!provider) { - return TPromise.wrapError(new Error('no handler found')); + return Promise.reject(new Error('no handler found')); } if (!provider.provideDebugConfigurations) { - return TPromise.wrapError(new Error('handler has no method provideDebugConfigurations')); + return Promise.reject(new Error('handler has no method provideDebugConfigurations')); } return asThenable(() => provider.provideDebugConfigurations(this.getFolder(folderUri), CancellationToken.None)); } @@ -537,10 +533,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { public $resolveDebugConfiguration(handle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Thenable { let provider = this._providerByHandle.get(handle); if (!provider) { - return TPromise.wrapError(new Error('no handler found')); + return Promise.reject(new Error('no handler found')); } if (!provider.resolveDebugConfiguration) { - return TPromise.wrapError(new Error('handler has no method resolveDebugConfiguration')); + return Promise.reject(new Error('handler has no method resolveDebugConfiguration')); } return asThenable(() => provider.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, CancellationToken.None)); } @@ -548,10 +544,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { public $provideDebugAdapter(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable { let provider = this._providerByHandle.get(handle); if (!provider) { - return TPromise.wrapError(new Error('no handler found')); + return Promise.reject(new Error('no handler found')); } if (!provider.debugAdapterExecutable && !provider.provideDebugAdapter) { - return TPromise.wrapError(new Error('handler has no methods provideDebugAdapter or debugAdapterExecutable')); + return Promise.reject(new Error('handler has no methods provideDebugAdapter or debugAdapterExecutable')); } return this.getAdapterDescriptor(provider, this.getSession(sessionDto), folderUri, config); } @@ -602,7 +598,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return false; } - private getDebugAdapterTrackers(sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): TPromise { + private getDebugAdapterTrackers(sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Promise { const session = this.getSession(sessionDto); const folder = this.getFolder(folderUri); @@ -610,12 +606,24 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { const type = config.type; const promises = this._providers .filter(pair => pair.provider.provideDebugAdapterTracker && (pair.type === type || pair.type === '*')) - .map(pair => pair.provider.provideDebugAdapterTracker(session, folder, config, CancellationToken.None)); + .map(pair => asThenable(() => pair.provider.provideDebugAdapterTracker(session, folder, config, CancellationToken.None)).then(p => p).catch(err => null)); - return TPromise.join(promises).then(trackers => { - if (trackers.length > 0) { - return new MultiTracker(trackers); - } + return Promise.race([ + Promise.all(promises).then(trackers => { + trackers = trackers.filter(t => t); // filter null + if (trackers.length > 0) { + return new MultiTracker(trackers); + } + return undefined; + }), + new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + clearTimeout(timeout); + reject(new Error('timeout')); + }, 1000); + }) + ]).catch(err => { + // ignore errors return undefined; }); } @@ -624,7 +632,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { // a "debugServer" attribute in the launch config takes precedence if (typeof config.debugServer === 'number') { - return TPromise.wrap(new DebugAdapterServer(config.debugServer)); + return Promise.resolve(new DebugAdapterServer(config.debugServer)); } if (debugConfigProvider) { @@ -649,7 +657,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } // fallback: use executable information from package.json - return TPromise.wrap(ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type)); + return Promise.resolve(ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type)); } private startBreakpoints() { @@ -799,9 +807,9 @@ interface IDapTransport { stop(): void; } -class MultiTracker implements vscode.IDebugAdapterTracker { +class MultiTracker implements vscode.DebugAdapterTracker { - constructor(private trackers: vscode.IDebugAdapterTracker[]) { + constructor(private trackers: vscode.DebugAdapterTracker[]) { } startDebugAdapter(): void { @@ -869,8 +877,8 @@ class DirectDebugAdapter extends AbstractDebugAdapter { } } - startSession(): TPromise { - return TPromise.wrap(void 0); + startSession(): Promise { + return Promise.resolve(void 0); } // VSCode -> DA @@ -878,8 +886,8 @@ class DirectDebugAdapter extends AbstractDebugAdapter { this.transport.sendUp(message); } - stopSession(): TPromise { + stopSession(): Promise { this.transport.stop(); - return TPromise.wrap(void 0); + return Promise.resolve(void 0); } } diff --git a/src/vs/workbench/api/node/extHostDocumentData.ts b/src/vs/workbench/api/node/extHostDocumentData.ts index 95f41a49009..4c4f59a9c2a 100644 --- a/src/vs/workbench/api/node/extHostDocumentData.ts +++ b/src/vs/workbench/api/node/extHostDocumentData.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { ok } from 'vs/base/common/assert'; -import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings'; -import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; -import { URI } from 'vs/base/common/uri'; -import { Range, Position, EndOfLine } from 'vs/workbench/api/node/extHostTypes'; -import * as vscode from 'vscode'; -import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; -import { MainThreadDocumentsShape } from './extHost.protocol'; import { Schemas } from 'vs/base/common/network'; +import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; +import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; +import { ensureValidWordDefinition, getWordAtText } from 'vs/editor/common/model/wordHelper'; +import { MainThreadDocumentsShape } from 'vs/workbench/api/node/extHost.protocol'; +import { EndOfLine, Position, Range } from 'vs/workbench/api/node/extHostTypes'; +import * as vscode from 'vscode'; const _modeId2WordDefinition = new Map(); export function setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void { diff --git a/src/vs/workbench/api/node/extHostDocuments.ts b/src/vs/workbench/api/node/extHostDocuments.ts index eca8c510bbe..01dd9719bc4 100644 --- a/src/vs/workbench/api/node/extHostDocuments.ts +++ b/src/vs/workbench/api/node/extHostDocuments.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import * as TypeConverters from './extHostTypeConverters'; -import * as vscode from 'vscode'; -import { MainContext, MainThreadDocumentsShape, ExtHostDocumentsShape, IMainContext } from './extHost.protocol'; -import { ExtHostDocumentData, setWordDefinitionFor } from './extHostDocumentData'; -import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel'; +import { ExtHostDocumentsShape, IMainContext, MainContext, MainThreadDocumentsShape } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostDocumentData, setWordDefinitionFor } from 'vs/workbench/api/node/extHostDocumentData'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; +import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters'; +import * as vscode from 'vscode'; export class ExtHostDocuments implements ExtHostDocumentsShape { diff --git a/src/vs/workbench/api/node/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/node/extHostDocumentsAndEditors.ts index b017a92c280..bee6279e748 100644 --- a/src/vs/workbench/api/node/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/node/extHostDocumentsAndEditors.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; -import { dispose } from 'vs/base/common/lifecycle'; -import { MainContext, ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IMainContext } from './extHost.protocol'; -import { ExtHostDocumentData } from './extHostDocumentData'; -import { ExtHostTextEditor } from './extHostTextEditor'; import * as assert from 'assert'; -import * as typeConverters from './extHostTypeConverters'; +import { Emitter, Event } from 'vs/base/common/event'; +import { dispose } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { Disposable } from './extHostTypes'; +import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IMainContext, MainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData'; +import { ExtHostTextEditor } from 'vs/workbench/api/node/extHostTextEditor'; +import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; +import { Disposable } from 'vs/workbench/api/node/extHostTypes'; export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape { diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index 05f10aa5768..aa1108d2ef5 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -6,12 +6,11 @@ import * as nls from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; const hasOwnProperty = Object.hasOwnProperty; -const NO_OP_VOID_PROMISE = TPromise.wrap(void 0); +const NO_OP_VOID_PROMISE = Promise.resolve(void 0); export interface IExtensionMemento { get(key: string, defaultValue: T): T; @@ -32,8 +31,8 @@ export interface IExtensionContext { * Represents the source code (module) of an extension. */ export interface IExtensionModule { - activate(ctx: IExtensionContext): TPromise; - deactivate(): void; + activate?(ctx: IExtensionContext): Promise; + deactivate?(): void; } /** @@ -124,18 +123,18 @@ export class ExtensionActivationTimesBuilder { export class ActivatedExtension { public readonly activationFailed: boolean; - public readonly activationFailedError: Error; + public readonly activationFailedError: Error | null; public readonly activationTimes: ExtensionActivationTimes; public readonly module: IExtensionModule; - public readonly exports: IExtensionAPI; + public readonly exports: IExtensionAPI | undefined; public readonly subscriptions: IDisposable[]; constructor( activationFailed: boolean, - activationFailedError: Error, + activationFailedError: Error | null, activationTimes: ExtensionActivationTimes, module: IExtensionModule, - exports: IExtensionAPI, + exports: IExtensionAPI | undefined, subscriptions: IDisposable[] ) { this.activationFailed = activationFailed; @@ -162,7 +161,7 @@ export class FailedExtension extends ActivatedExtension { export interface IExtensionsActivatorHost { showMessage(severity: Severity, message: string): void; - actualActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise; + actualActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise; } export class ExtensionActivatedByEvent { @@ -184,7 +183,7 @@ export class ExtensionsActivator { private readonly _registry: ExtensionDescriptionRegistry; private readonly _host: IExtensionsActivatorHost; - private readonly _activatingExtensions: { [extensionId: string]: TPromise; }; + private readonly _activatingExtensions: { [extensionId: string]: Promise; }; private readonly _activatedExtensions: { [extensionId: string]: ActivatedExtension; }; /** * A map of already activated events to speed things up if the same activation event is triggered multiple times. @@ -210,7 +209,7 @@ export class ExtensionsActivator { return this._activatedExtensions[extensionId]; } - public activateByEvent(activationEvent: string, reason: ExtensionActivationReason): TPromise { + public activateByEvent(activationEvent: string, reason: ExtensionActivationReason): Promise { if (this._alreadyActivatedEvents[activationEvent]) { return NO_OP_VOID_PROMISE; } @@ -220,7 +219,7 @@ export class ExtensionsActivator { }); } - public activateById(extensionId: string, reason: ExtensionActivationReason): TPromise { + public activateById(extensionId: string, reason: ExtensionActivationReason): Promise { let desc = this._registry.getExtensionDescription(extensionId); if (!desc) { throw new Error('Extension `' + extensionId + '` is not known'); @@ -273,15 +272,15 @@ export class ExtensionsActivator { } } - private _activateExtensions(extensionDescriptions: IExtensionDescription[], reason: ExtensionActivationReason, recursionLevel: number): TPromise { + private _activateExtensions(extensionDescriptions: IExtensionDescription[], reason: ExtensionActivationReason, recursionLevel: number): Promise { // console.log(recursionLevel, '_activateExtensions: ', extensionDescriptions.map(p => p.id)); if (extensionDescriptions.length === 0) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } extensionDescriptions = extensionDescriptions.filter((p) => !hasOwnProperty.call(this._activatedExtensions, p.id)); if (extensionDescriptions.length === 0) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } if (recursionLevel > 10) { @@ -292,7 +291,7 @@ export class ExtensionsActivator { const error = new Error('More than 10 levels of dependencies (most likely a dependency loop)'); this._activatedExtensions[extensionDescriptions[i].id] = new FailedExtension(error); } - return TPromise.as(void 0); + return Promise.resolve(void 0); } let greenMap: { [id: string]: IExtensionDescription; } = Object.create(null), @@ -316,7 +315,7 @@ export class ExtensionsActivator { if (red.length === 0) { // Finally reached only leafs! - return TPromise.join(green.map((p) => this._activateExtension(p, reason))).then(_ => void 0); + return Promise.all(green.map((p) => this._activateExtension(p, reason))).then(_ => void 0); } return this._activateExtensions(green, reason, recursionLevel + 1).then(_ => { @@ -324,9 +323,9 @@ export class ExtensionsActivator { }); } - private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise { + private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { if (hasOwnProperty.call(this._activatedExtensions, extensionDescription.id)) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } if (hasOwnProperty.call(this._activatingExtensions, extensionDescription.id)) { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index d3296b59da3..7b74cca1e07 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -3,24 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { join } from 'path'; -import { mkdirp, dirExists, realpath, writeFile } from 'vs/base/node/pfs'; -import Severity from 'vs/base/common/severity'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; -import { createApiFactory, initializeExtensionApi } from 'vs/workbench/api/node/extHost.api.impl'; -import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IMainContext } from './extHost.protocol'; -import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes, ExtensionActivationReason, ExtensionActivatedByEvent, ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; -import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { TernarySearchTree } from 'vs/base/common/map'; import { Barrier } from 'vs/base/common/async'; -import { ILogService } from 'vs/platform/log/common/log'; -import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { TernarySearchTree } from 'vs/base/common/map'; +import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; +import { dirExists, mkdirp, realpath, writeFile } from 'vs/base/node/pfs'; +import { ILogService } from 'vs/platform/log/common/log'; +import { createApiFactory, initializeExtensionApi } from 'vs/workbench/api/node/extHost.api.impl'; +import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, IWorkspaceData, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionMemento, IExtensionModule } from 'vs/workbench/api/node/extHostExtensionActivator'; +import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; +import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; +import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; +import { connectProxyResolver } from 'vs/workbench/node/proxyResolver'; class ExtensionMemento implements IExtensionMemento { @@ -100,7 +100,7 @@ class ExtensionStoragePath { private async _getOrCreateWorkspaceStoragePath(): Promise { if (!this._workspace) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } const storageName = this._workspace.id; @@ -130,7 +130,6 @@ class ExtensionStoragePath { } } } - export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private readonly _barrier: Barrier; @@ -141,7 +140,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private readonly _proxy: MainThreadExtensionServiceShape; private readonly _extHostLogService: ExtHostLogService; private _activator: ExtensionsActivator; - private _extensionPathIndex: TPromise>; + private _extensionPathIndex: Promise>; /** * This class is constructed manually because it is a service, so it doesn't use any ctor injection */ @@ -149,12 +148,13 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { extHostContext: IMainContext, extHostWorkspace: ExtHostWorkspace, extHostConfiguration: ExtHostConfiguration, - extHostLogService: ExtHostLogService + extHostLogService: ExtHostLogService, + mainThreadTelemetry: MainThreadTelemetryShape ) { this._barrier = new Barrier(); this._registry = new ExtensionDescriptionRegistry(initData.extensions); this._extHostLogService = extHostLogService; - this._mainThreadTelemetry = extHostContext.getProxy(MainContext.MainThreadTelemetry); + this._mainThreadTelemetry = mainThreadTelemetry; this._storage = new ExtHostStorage(extHostContext); this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment); this._proxy = extHostContext.getProxy(MainContext.MainThreadExtensionService); @@ -164,6 +164,9 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, this._extHostLogService, this._storage); initializeExtensionApi(this, apiFactory).then(() => { + // Do this when extension service exists, but extensions are not being activated yet. + return connectProxyResolver(extHostWorkspace, extHostConfiguration, this, this._extHostLogService, this._mainThreadTelemetry); + }).then(() => { this._activator = new ExtensionsActivator(this._registry, { showMessage: (severity: Severity, message: string): void => { @@ -181,7 +184,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } }, - actualActivateExtension: (extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise => { + actualActivateExtension: (extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise => { return this._activateExtension(extensionDescription, reason); } }); @@ -190,7 +193,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { }); } - public onExtensionAPIReady(): TPromise { + public onExtensionAPIReady(): Thenable { return this._barrier.wait(); } @@ -201,7 +204,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return false; } - public activateByEvent(activationEvent: string, startup: boolean): TPromise { + public activateByEvent(activationEvent: string, startup: boolean): Thenable { const reason = new ExtensionActivatedByEvent(startup, activationEvent); if (this._barrier.isOpen()) { return this._activator.activateByEvent(activationEvent, reason); @@ -210,7 +213,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } } - public activateById(extensionId: string, reason: ExtensionActivationReason): TPromise { + public activateById(extensionId: string, reason: ExtensionActivationReason): Thenable { if (this._barrier.isOpen()) { return this._activator.activateById(extensionId, reason); } else { @@ -218,12 +221,12 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } } - public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): TPromise { + public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): Thenable { return this.activateById(extensionId, reason).then(() => { const extension = this._activator.getActivatedExtension(extensionId); if (extension.activationFailed) { // activation failed => bubble up the error as the promise result - return TPromise.wrapError(extension.activationFailedError); + return Promise.reject(extension.activationFailedError); } return void 0; }); @@ -246,7 +249,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } // create trie to enable fast 'filename -> extension id' look up - public getExtensionPathIndex(): TPromise> { + public getExtensionPathIndex(): Promise> { if (!this._extensionPathIndex) { const tree = TernarySearchTree.forPaths(); const extensions = this.getAllExtensionDescriptions().map(ext => { @@ -255,14 +258,14 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } return realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext)); }); - this._extensionPathIndex = TPromise.join(extensions).then(() => tree); + this._extensionPathIndex = Promise.all(extensions).then(() => tree); } return this._extensionPathIndex; } - public deactivate(extensionId: string): TPromise { - let result: TPromise = TPromise.as(void 0); + public deactivate(extensionId: string): Thenable { + let result = Promise.resolve(void 0); if (!this._barrier.isOpen()) { return result; @@ -280,9 +283,9 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { // call deactivate if available try { if (typeof extension.module.deactivate === 'function') { - result = TPromise.wrap(extension.module.deactivate()).then(null, (err) => { + result = Promise.resolve(extension.module.deactivate()).then(null, (err) => { // TODO: Do something with err if this is not the shutdown case - return TPromise.as(void 0); + return Promise.resolve(void 0); }); } } catch (err) { @@ -305,7 +308,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { // --- impl - private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise { + private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => { const activationTimes = activatedExtension.activationTimes; let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); @@ -317,7 +320,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { }); } - private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise { + private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { let event = getTelemetryActivationEvent(extensionDescription, reason); /* __GDPR__ "activatePlugin" : { @@ -329,36 +332,27 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { this._mainThreadTelemetry.$publicLog('activatePlugin', event); if (!extensionDescription.main) { // Treat the extension as being empty => NOT AN ERROR CASE - return TPromise.as(new EmptyExtension(ExtensionActivationTimes.NONE)); + return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE)); } this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`); const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); - return TPromise.join([ + return Promise.all([ loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder), this._loadExtensionContext(extensionDescription) ]).then(values => { return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, values[0], values[1], activationTimesBuilder); - }, (errors: any[]) => { - // Avoid failing with an array of errors, fail with a single error - if (errors[0]) { - return TPromise.wrapError(errors[0]); - } - if (errors[1]) { - return TPromise.wrapError(errors[1]); - } - return undefined; }); } - private _loadExtensionContext(extensionDescription: IExtensionDescription): TPromise { + private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage); let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage); this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`); - return TPromise.join([ + return Promise.all([ globalState.whenReady, workspaceState.whenReady, this._storagePath.whenReady @@ -393,20 +387,20 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { try { activationTimesBuilder.activateCallStart(); logService.trace(`ExtensionService#_callActivateOptional ${extensionId}`); - const activateResult: TPromise = extensionModule.activate.apply(global, [context]); + const activateResult: Thenable = extensionModule.activate.apply(global, [context]); activationTimesBuilder.activateCallStop(); activationTimesBuilder.activateResolveStart(); - return TPromise.as(activateResult).then((value) => { + return Promise.resolve(activateResult).then((value) => { activationTimesBuilder.activateResolveStop(); return value; }); } catch (err) { - return TPromise.wrapError(err); + return Promise.reject(err); } } else { // No activate found => the module is the extension's exports - return TPromise.as(extensionModule); + return Promise.resolve(extensionModule); } } @@ -417,18 +411,18 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } } -function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise { +function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { let r: T | null = null; activationTimesBuilder.codeLoadingStart(); logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`); try { r = require.__$__nodeRequire(modulePath); } catch (e) { - return TPromise.wrapError(e); + return Promise.reject(e); } finally { activationTimesBuilder.codeLoadingStop(); } - return TPromise.as(r); + return Promise.resolve(r); } function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any { diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index 982fccfd7b4..6bdc935edf4 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape, IFileChangeDto } from './extHost.protocol'; import * as vscode from 'vscode'; import * as files from 'vs/platform/files/common/files'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; import { Range, FileChangeType } from 'vs/workbench/api/node/extHostTypes'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; @@ -64,9 +64,10 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { private readonly _usedSchemes = new Set(); private readonly _watches = new Map(); + private _linkProviderRegistration: IDisposable; private _handlePool: number = 0; - constructor(mainContext: IMainContext, extHostLanguageFeatures: ExtHostLanguageFeatures) { + constructor(mainContext: IMainContext, private _extHostLanguageFeatures: ExtHostLanguageFeatures) { this._proxy = mainContext.getProxy(MainContext.MainThreadFileSystem); this._usedSchemes.add(Schemas.file); this._usedSchemes.add(Schemas.untitled); @@ -77,8 +78,16 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { this._usedSchemes.add(Schemas.https); this._usedSchemes.add(Schemas.mailto); this._usedSchemes.add(Schemas.data); + } - extHostLanguageFeatures.registerDocumentLinkProvider('*', this._linkProvider); + dispose(): void { + dispose(this._linkProviderRegistration); + } + + private _registerLinkProviderIfNotYetRegistered(): void { + if (!this._linkProviderRegistration) { + this._linkProviderRegistration = this._extHostLanguageFeatures.registerDocumentLinkProvider(undefined, '*', this._linkProvider); + } } registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean, isReadonly?: boolean } = {}) { @@ -87,6 +96,9 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { throw new Error(`a provider for the scheme '${scheme}' is already registered`); } + // + this._registerLinkProviderIfNotYetRegistered(); + const handle = this._handlePool++; this._linkProvider.add(scheme); this._usedSchemes.add(scheme); @@ -153,41 +165,59 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { return { type, ctime, mtime, size }; } + private _checkProviderExists(handle: number): void { + if (!this._fsProvider.has(handle)) { + const err = new Error(); + err.name = 'ENOPRO'; + err.message = `no provider`; + throw err; + } + } + $stat(handle: number, resource: UriComponents): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat); } $readdir(handle: number, resource: UriComponents): Promise<[string, files.FileType][]> { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).readDirectory(URI.revive(resource))); } $readFile(handle: number, resource: UriComponents): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).readFile(URI.revive(resource))).then(data => { return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength); }); } $writeFile(handle: number, resource: UriComponents, content: Buffer, opts: files.FileWriteOptions): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).writeFile(URI.revive(resource), content, opts)); } $delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).delete(URI.revive(resource), opts)); } $rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts)); } $copy(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).copy(URI.revive(oldUri), URI.revive(newUri), opts)); } $mkdir(handle: number, resource: UriComponents): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).createDirectory(URI.revive(resource))); } $watch(handle: number, session: number, resource: UriComponents, opts: files.IWatchOptions): void { + this._checkProviderExists(handle); let subscription = this._fsProvider.get(handle).watch(URI.revive(resource), opts); this._watches.set(session, subscription); } @@ -201,18 +231,22 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { } $open(handle: number, resource: UriComponents): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).open(URI.revive(resource))); } $close(handle: number, fd: number): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).close(fd)); } $read(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).read(fd, pos, data, offset, length)); } $write(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise { + this._checkProviderExists(handle); return Promise.resolve(this._fsProvider.get(handle).write(fd, pos, data, offset, length)); } diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 99673b0df8d..fd9a617a2e2 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -575,7 +575,7 @@ class SuggestAdapter { const pos = typeConvert.Position.to(position); return asThenable( - () => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.from(context)) + () => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.to(context)) ).then(value => { const _id = this._idPool++; @@ -842,6 +842,13 @@ type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapt | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter; +class AdapterData { + constructor( + readonly adapter: Adapter, + readonly extension: IExtensionDescription | undefined + ) { } +} + export interface ISchemeTransformer { transformOutgoing(scheme: string): string; } @@ -856,7 +863,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { private _commands: ExtHostCommands; private _heapService: ExtHostHeapService; private _diagnostics: ExtHostDiagnostics; - private _adapter = new Map(); + private _adapter = new Map(); private readonly _logService: ILogService; constructor( @@ -925,23 +932,35 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } private _withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => Thenable): Thenable { - let adapter = this._adapter.get(handle); - if (!(adapter instanceof ctor)) { - return Promise.reject(new Error('no adapter found')); + let data = this._adapter.get(handle); + if (data.adapter instanceof ctor) { + let t1: number; + if (data.extension) { + t1 = Date.now(); + this._logService.trace(`[${data.extension.id}] INVOKE provider '${(ctor as any).name}'`); + } + let p = callback(data.adapter); + if (data.extension) { + Promise.resolve(p).then( + () => this._logService.trace(`[${data.extension.id}] provider DONE after ${Date.now() - t1}ms`), + err => this._logService.trace(`[${data.extension.id}] provider FAILED`, err) + ); + } + return p; } - return callback(adapter); + return Promise.reject(new Error('no adapter found')); } - private _addNewAdapter(adapter: Adapter): number { + private _addNewAdapter(adapter: Adapter, extension: IExtensionDescription): number { const handle = this._nextHandle(); - this._adapter.set(handle, adapter); + this._adapter.set(handle, new AdapterData(adapter, extension)); return handle; } // --- outline - registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, extension?: IExtensionDescription): vscode.Disposable { - const handle = this._addNewAdapter(new OutlineAdapter(this._documents, provider)); + registerDocumentSymbolProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable { + const handle = this._addNewAdapter(new OutlineAdapter(this._documents, provider), extension); this._proxy.$registerOutlineSupport(handle, this._transformDocumentSelector(selector), extension ? extension.displayName || extension.name : undefined); return this._createDisposable(handle); } @@ -952,11 +971,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- code lens - registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { + registerCodeLensProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { const handle = this._nextHandle(); const eventHandle = typeof provider.onDidChangeCodeLenses === 'function' ? this._nextHandle() : undefined; - this._adapter.set(handle, new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider)); + this._adapter.set(handle, new AdapterData(new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider), extension)); this._proxy.$registerCodeLensSupport(handle, this._transformDocumentSelector(selector), eventHandle); let result = this._createDisposable(handle); @@ -978,8 +997,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- declaration - registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { - const handle = this._addNewAdapter(new DefinitionAdapter(this._documents, provider)); + registerDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { + const handle = this._addNewAdapter(new DefinitionAdapter(this._documents, provider), extension); this._proxy.$registerDeclaractionSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -988,8 +1007,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position, token)); } - registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable { - const handle = this._addNewAdapter(new ImplementationAdapter(this._documents, provider)); + registerImplementationProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable { + const handle = this._addNewAdapter(new ImplementationAdapter(this._documents, provider), extension); this._proxy.$registerImplementationSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -998,8 +1017,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position, token)); } - registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { - const handle = this._addNewAdapter(new TypeDefinitionAdapter(this._documents, provider)); + registerTypeDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { + const handle = this._addNewAdapter(new TypeDefinitionAdapter(this._documents, provider), extension); this._proxy.$registerTypeDefinitionSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1010,8 +1029,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- extra info - registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable { - const handle = this._addNewAdapter(new HoverAdapter(this._documents, provider)); + registerHoverProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable { + const handle = this._addNewAdapter(new HoverAdapter(this._documents, provider), extension); this._proxy.$registerHoverProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1022,8 +1041,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- occurrences - registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { - const handle = this._addNewAdapter(new DocumentHighlightAdapter(this._documents, provider)); + registerDocumentHighlightProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { + const handle = this._addNewAdapter(new DocumentHighlightAdapter(this._documents, provider), extension); this._proxy.$registerDocumentHighlightProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1034,8 +1053,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- references - registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable { - const handle = this._addNewAdapter(new ReferenceAdapter(this._documents, provider)); + registerReferenceProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable { + const handle = this._addNewAdapter(new ReferenceAdapter(this._documents, provider), extension); this._proxy.$registerReferenceSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1046,8 +1065,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- quick fix - registerCodeActionProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, extension?: IExtensionDescription, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { - const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension ? extension.id : '')); + registerCodeActionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { + const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension.id), extension); this._proxy.$registerQuickFixSupport(handle, this._transformDocumentSelector(selector), metadata && metadata.providedCodeActionKinds ? metadata.providedCodeActionKinds.map(kind => kind.value) : undefined); return this._createDisposable(handle); } @@ -1059,8 +1078,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- formatting - registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable { - const handle = this._addNewAdapter(new DocumentFormattingAdapter(this._documents, provider)); + registerDocumentFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable { + const handle = this._addNewAdapter(new DocumentFormattingAdapter(this._documents, provider), extension); this._proxy.$registerDocumentFormattingSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1069,8 +1088,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options, token)); } - registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable { - const handle = this._addNewAdapter(new RangeFormattingAdapter(this._documents, provider)); + registerDocumentRangeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable { + const handle = this._addNewAdapter(new RangeFormattingAdapter(this._documents, provider), extension); this._proxy.$registerRangeFormattingSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1079,8 +1098,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangeFormattingEdits(URI.revive(resource), range, options, token)); } - registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, triggerCharacters: string[]): vscode.Disposable { - const handle = this._addNewAdapter(new OnTypeFormattingAdapter(this._documents, provider)); + registerOnTypeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, triggerCharacters: string[]): vscode.Disposable { + const handle = this._addNewAdapter(new OnTypeFormattingAdapter(this._documents, provider), extension); this._proxy.$registerOnTypeFormattingSupport(handle, this._transformDocumentSelector(selector), triggerCharacters); return this._createDisposable(handle); } @@ -1091,8 +1110,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- navigate types - registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable { - const handle = this._addNewAdapter(new NavigateTypeAdapter(provider)); + registerWorkspaceSymbolProvider(extension: IExtensionDescription, provider: vscode.WorkspaceSymbolProvider): vscode.Disposable { + const handle = this._addNewAdapter(new NavigateTypeAdapter(provider), extension); this._proxy.$registerNavigateTypeSupport(handle); return this._createDisposable(handle); } @@ -1111,8 +1130,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- rename - registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable { - const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider)); + registerRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable { + const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider), extension); this._proxy.$registerRenameSupport(handle, this._transformDocumentSelector(selector), RenameAdapter.supportsResolving(provider)); return this._createDisposable(handle); } @@ -1127,8 +1146,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- suggestion - registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable { - const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider)); + registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable { + const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider), extension); this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider)); return this._createDisposable(handle); } @@ -1147,12 +1166,12 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- parameter hints - registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, metadataOrTriggerChars?: string[] | vscode.SignatureHelpProviderMetadata): vscode.Disposable { + registerSignatureHelpProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, metadataOrTriggerChars?: string[] | vscode.SignatureHelpProviderMetadata): vscode.Disposable { const metadata: ISerializedSignatureHelpProviderMetadata = Array.isArray(metadataOrTriggerChars) ? { triggerCharacters: metadataOrTriggerChars, retriggerCharacters: [] } : metadataOrTriggerChars; - const handle = this._addNewAdapter(new SignatureHelpAdapter(this._documents, provider)); + const handle = this._addNewAdapter(new SignatureHelpAdapter(this._documents, provider), extension); this._proxy.$registerSignatureHelpProvider(handle, this._transformDocumentSelector(selector), metadata); return this._createDisposable(handle); } @@ -1163,8 +1182,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- links - registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { - const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, this._heapService, provider)); + registerDocumentLinkProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { + const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, this._heapService, provider), extension); this._proxy.$registerDocumentLinkProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1177,8 +1196,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link, token)); } - registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { - const handle = this._addNewAdapter(new ColorProviderAdapter(this._documents, provider)); + registerColorProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { + const handle = this._addNewAdapter(new ColorProviderAdapter(this._documents, provider), extension); this._proxy.$registerDocumentColorProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1191,8 +1210,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token)); } - registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { - const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider)); + registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { + const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider), extension); this._proxy.$registerFoldingRangeProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } diff --git a/src/vs/workbench/api/node/extHostProgress.ts b/src/vs/workbench/api/node/extHostProgress.ts index c677576a617..ebe5cc7f407 100644 --- a/src/vs/workbench/api/node/extHostProgress.ts +++ b/src/vs/workbench/api/node/extHostProgress.ts @@ -7,11 +7,10 @@ import { ProgressOptions } from 'vscode'; import { MainThreadProgressShape, ExtHostProgressShape } from './extHost.protocol'; import { ProgressLocation } from './extHostTypeConverters'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { Progress } from 'vs/platform/progress/common/progress'; +import { Progress, IProgressStep } from 'vs/platform/progress/common/progress'; import { localize } from 'vs/nls'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { debounce } from 'vs/base/common/decorators'; -import { IProgressStep } from 'vs/workbench/services/progress/common/progress'; export class ExtHostProgress implements ExtHostProgressShape { diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index e7c92c50fd4..7b1b7e8dd35 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -7,7 +7,6 @@ import { asThenable } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode'; @@ -29,6 +28,8 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { private _sessions = new Map(); + private _instances = 0; + constructor(mainContext: IMainContext, workspace: ExtHostWorkspace, commands: ExtHostCommands) { this._proxy = mainContext.getProxy(MainContext.MainThreadQuickOpen); this._workspace = workspace; @@ -43,9 +44,11 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { // clear state from last invocation this._onDidSelectItem = undefined; - const itemsPromise = >TPromise.wrap(itemsOrItemsPromise); + const itemsPromise = >Promise.resolve(itemsOrItemsPromise); - const quickPickWidget = this._proxy.$show({ + const instance = ++this._instances; + + const quickPickWidget = this._proxy.$show(instance, { placeHolder: options && options.placeHolder, matchOnDescription: options && options.matchOnDescription, matchOnDetail: options && options.matchOnDetail, @@ -80,9 +83,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { description = item.description; detail = item.detail; picked = item.picked; - if (enableProposedApi) { - alwaysShow = item.alwaysShow; - } + alwaysShow = item.alwaysShow; } pickItems.push({ label, @@ -102,7 +103,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { } // show items - this._proxy.$setItems(pickItems); + this._proxy.$setItems(instance, pickItems); return quickPickWidget.then(handle => { if (typeof handle === 'number') { @@ -118,9 +119,9 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { return undefined; } - this._proxy.$setError(err); + this._proxy.$setError(instance, err); - return TPromise.wrapError(err); + return Promise.reject(err); }); } @@ -143,9 +144,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { return undefined; } - this._proxy.$setError(err); - - return TPromise.wrapError(err); + return Promise.reject(err); }); } @@ -480,7 +479,7 @@ class ExtHostQuickPick extends ExtHostQuickInput implem private _selectedItems: T[] = []; private _onDidChangeSelectionEmitter = new Emitter(); - constructor(proxy: MainThreadQuickOpenShape, extensionId: string, private _enableProposedApi: boolean, onDispose: () => void) { + constructor(proxy: MainThreadQuickOpenShape, extensionId: string, enableProposedApi: boolean, onDispose: () => void) { super(proxy, extensionId, onDispose); this._disposables.push( this._onDidChangeActiveEmitter, @@ -508,7 +507,7 @@ class ExtHostQuickPick extends ExtHostQuickInput implem handle: i, detail: item.detail, picked: item.picked, - alwaysShow: this._enableProposedApi ? item.alwaysShow : undefined + alwaysShow: item.alwaysShow })) }); } diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index a322633776e..169dbf9650a 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter, once } from 'vs/base/common/event'; import { debounce } from 'vs/base/common/decorators'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -199,6 +198,18 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { this._proxy.$setValidationProviderIsEnabled(this._sourceControlHandle, !!fn); } + private _visible: boolean = true; + + get visible(): boolean { + return this._visible; + } + + set visible(visible: boolean | undefined) { + visible = !!visible; + this._visible = visible; + this._proxy.$setInputBoxVisibility(this._sourceControlHandle, visible); + } + constructor(private _extension: IExtensionDescription, private _proxy: MainThreadSCMShape, private _sourceControlHandle: number) { // noop } @@ -604,23 +615,23 @@ export class ExtHostSCM implements ExtHostSCMShape { const sourceControl = this._sourceControls.get(sourceControlHandle); if (!sourceControl || !sourceControl.quickDiffProvider) { - return TPromise.as(null); + return Promise.resolve(null); } return asThenable(() => sourceControl.quickDiffProvider.provideOriginalResource(uri, token)); } - $onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise { + $onInputBoxValueChange(sourceControlHandle: number, value: string): Promise { this.logService.trace('ExtHostSCM#$onInputBoxValueChange', sourceControlHandle); const sourceControl = this._sourceControls.get(sourceControlHandle); if (!sourceControl) { - return TPromise.as(null); + return Promise.resolve(null); } sourceControl.inputBox.$onInputBoxValueChange(value); - return TPromise.as(null); + return Promise.resolve(null); } $executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): Thenable { @@ -629,13 +640,13 @@ export class ExtHostSCM implements ExtHostSCMShape { const sourceControl = this._sourceControls.get(sourceControlHandle); if (!sourceControl) { - return TPromise.as(null); + return Promise.resolve(null); } const group = sourceControl.getResourceGroup(groupHandle); if (!group) { - return TPromise.as(null); + return Promise.resolve(null); } return group.$executeResourceCommand(handle); @@ -647,19 +658,19 @@ export class ExtHostSCM implements ExtHostSCMShape { const sourceControl = this._sourceControls.get(sourceControlHandle); if (!sourceControl) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } if (!sourceControl.inputBox.validateInput) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } return asThenable(() => sourceControl.inputBox.validateInput(value, cursorPosition)).then(result => { if (!result) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } - return TPromise.as<[string, number]>([result.message, result.type]); + return Promise.resolve<[string, number]>([result.message, result.type]); }); } @@ -697,6 +708,6 @@ export class ExtHostSCM implements ExtHostSCMShape { }); this._selectedSourceControlHandles = set; - return TPromise.as(null); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts index 114f4b1d328..c59b1ab89e0 100644 --- a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts +++ b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts @@ -7,19 +7,17 @@ import * as path from 'path'; import * as arrays from 'vs/base/common/arrays'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { toErrorMessage } from 'vs/base/common/errorMessage'; import { canceled } from 'vs/base/common/errors'; import * as glob from 'vs/base/common/glob'; import * as resources from 'vs/base/common/resources'; import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; -import { ICachedSearchStats, IFileIndexProviderStats, IFileMatch, IFileSearchStats, IFolderQuery, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search'; +import { ICachedSearchStats, IFileIndexProviderStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, ISearchCompleteStats } from 'vs/platform/search/common/search'; +import { IDirectoryEntry, IDirectoryTree, IInternalFileMatch } from 'vs/workbench/services/search/node/fileSearchManager'; +import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search'; import * as vscode from 'vscode'; -import { resolvePatternsForProvider, QueryGlobTester } from 'vs/workbench/services/search/node/search'; -import { IInternalFileMatch, IDirectoryTree, IDirectoryEntry } from 'vs/workbench/services/search/node/fileSearchManager'; interface IInternalSearchComplete { limitHit: boolean; @@ -44,7 +42,7 @@ export class FileIndexSearchEngine { private globalExcludePattern: glob.ParsedExpression; - constructor(private config: ISearchQuery, private provider: vscode.FileIndexProvider) { + constructor(private config: IFileQuery, private provider: vscode.FileIndexProvider) { this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || null; @@ -66,15 +64,11 @@ export class FileIndexSearchEngine { this.activeCancellationTokens = new Set(); } - public search(_onResult: (match: IInternalFileMatch) => void): TPromise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }> { - if (this.config.folderQueries.length !== 1) { - throw new Error('Searches just one folder'); - } - + public search(_onResult: (match: IInternalFileMatch) => void): Promise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }> { // Searches a single folder const folderQuery = this.config.folderQueries[0]; - return new TPromise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }>((resolve, reject) => { + return new Promise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }>((resolve, reject) => { const onResult = (match: IInternalFileMatch) => { this.resultCount++; _onResult(match); @@ -99,21 +93,31 @@ export class FileIndexSearchEngine { }); } - return this.searchInFolder(folderQuery, _onResult) - .then(stats => { - resolve({ - isLimitHit: this.isLimitHit, - stats - }); - }, (err: Error) => { - reject(new Error(toErrorMessage(err))); + return Promise.all(this.config.folderQueries.map(fq => this.searchInFolder(folderQuery, onResult))).then(stats => { + resolve({ + isLimitHit: this.isLimitHit, + stats: { + directoriesWalked: this.dirsWalked, + filesWalked: this.filesWalked, + fileWalkTime: stats.map(s => s.fileWalkTime).reduce((s, c) => s + c, 0), + providerTime: stats.map(s => s.providerTime).reduce((s, c) => s + c, 0), + providerResultCount: stats.map(s => s.providerResultCount).reduce((s, c) => s + c, 0) + } }); + }, (errs: Error[]) => { + if (!Array.isArray(errs)) { + errs = [errs]; + } + + errs = errs.filter(e => !!e); + return Promise.reject(errs[0]); + }); }); } - private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): TPromise { + private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): Promise { let cancellation = new CancellationTokenSource(); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { const options = this.getSearchOptionsForFolder(fq); const tree = this.initDirectoryTree(); @@ -141,7 +145,7 @@ export class FileIndexSearchEngine { let providerSW: StopWatch; let providerTime: number; let fileWalkTime: number; - new TPromise(resolve => process.nextTick(resolve)) + new Promise(resolve => process.nextTick(resolve)) .then(() => { this.activeCancellationTokens.add(cancellation); providerSW = StopWatch.create(); @@ -185,9 +189,9 @@ export class FileIndexSearchEngine { folder: fq.folder, excludes, includes, - useIgnoreFiles: !this.config.disregardIgnoreFiles, - useGlobalIgnoreFiles: !this.config.disregardGlobalIgnoreFiles, - followSymlinks: !this.config.ignoreSymlinks + useIgnoreFiles: !fq.disregardIgnoreFiles, + useGlobalIgnoreFiles: !fq.disregardGlobalIgnoreFiles, + followSymlinks: !fq.ignoreSymlinks }; } @@ -299,7 +303,7 @@ export class FileIndexSearchManager { private readonly folderCacheKeys = new Map>(); - public fileSearch(config: ISearchQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { + public fileSearch(config: IFileQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise { if (config.sortByScore) { let sortedSearch = this.trySortedSearchFromCache(config, token); if (!sortedSearch) { @@ -336,7 +340,7 @@ export class FileIndexSearchManager { }); } - private getFolderCacheKey(config: ISearchQuery): string { + private getFolderCacheKey(config: IFileQuery): string { const uri = config.folderQueries[0].folder.toString(); const folderCacheKey = config.cacheKey && `${uri}_${config.cacheKey}`; if (!this.folderCacheKeys.get(config.cacheKey)) { @@ -354,7 +358,7 @@ export class FileIndexSearchManager { }; } - private doSortedSearch(engine: FileIndexSearchEngine, config: ISearchQuery, token: CancellationToken): TPromise { + private doSortedSearch(engine: FileIndexSearchEngine, config: IFileQuery, token: CancellationToken): Promise { let allResultsPromise = createCancelablePromise>(token => { return this.doSearch(engine, token); }); @@ -376,7 +380,7 @@ export class FileIndexSearchManager { allResultsPromise = this.preventCancellation(allResultsPromise); } - return TPromise.wrap( + return Promise.resolve( allResultsPromise.then(complete => { const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create(); @@ -408,7 +412,7 @@ export class FileIndexSearchManager { return this.caches[cacheKey] = new Cache(); } - private trySortedSearchFromCache(config: ISearchQuery, token: CancellationToken): TPromise { + private trySortedSearchFromCache(config: IFileQuery, token: CancellationToken): Promise { const folderCacheKey = this.getFolderCacheKey(config); const cache = folderCacheKey && this.caches[folderCacheKey]; if (!cache) { @@ -442,7 +446,7 @@ export class FileIndexSearchManager { return undefined; } - private sortResults(config: IRawSearchQuery, results: IInternalFileMatch[], scorerCache: ScorerCache, token: CancellationToken): TPromise { + private sortResults(config: IFileQuery, results: IInternalFileMatch[], scorerCache: ScorerCache, token: CancellationToken): Promise { // we use the same compare function that is used later when showing the results using fuzzy scoring // this is very important because we are also limiting the number of results by config.maxResults // and as such we want the top items to be included in this result set if the number of items @@ -464,7 +468,7 @@ export class FileIndexSearchManager { } } - private getResultsFromCache(cache: Cache, searchValue: string, token: CancellationToken): TPromise> { + private getResultsFromCache(cache: Cache, searchValue: string, token: CancellationToken): Promise> { const cacheLookupSW = StopWatch.create(); if (path.isAbsolute(searchValue)) { @@ -498,7 +502,7 @@ export class FileIndexSearchManager { const cacheLookupTime = cacheLookupSW.elapsed(); const cacheFilterSW = StopWatch.create(); - return new TPromise>((c, e) => { + return new Promise>((c, e) => { token.onCancellationRequested(() => e(canceled())); cacheRow.promise.then(complete => { @@ -534,7 +538,7 @@ export class FileIndexSearchManager { }); } - private doSearch(engine: FileIndexSearchEngine, token: CancellationToken): TPromise> { + private doSearch(engine: FileIndexSearchEngine, token: CancellationToken): Promise> { token.onCancellationRequested(() => engine.cancel()); const results: IInternalFileMatch[] = []; const onResult = match => results.push(match); @@ -548,9 +552,9 @@ export class FileIndexSearchManager { }); } - public clearCache(cacheKey: string): TPromise { + public clearCache(cacheKey: string): Promise { if (!this.folderCacheKeys.has(cacheKey)) { - return TPromise.wrap(undefined); + return Promise.resolve(undefined); } const expandedKeys = this.folderCacheKeys.get(cacheKey); @@ -558,7 +562,7 @@ export class FileIndexSearchManager { this.folderCacheKeys.delete(cacheKey); - return TPromise.as(undefined); + return Promise.resolve(undefined); } private preventCancellation(promise: CancelablePromise): CancelablePromise { diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index 200f05ffa49..cf20b24f285 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -6,17 +6,16 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; import { ILogService } from 'vs/platform/log/common/log'; -import { IFolderQuery, IPatternInfo, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search'; +import { IFileQuery, IFolderQuery, IRawFileQuery, IRawQuery, IRawTextQuery, ISearchCompleteStats, ITextQuery } from 'vs/platform/search/common/search'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { FileIndexSearchManager } from 'vs/workbench/api/node/extHostSearch.fileIndex'; import { FileSearchManager } from 'vs/workbench/services/search/node/fileSearchManager'; import { SearchService } from 'vs/workbench/services/search/node/rawSearchService'; import { RipgrepSearchProvider } from 'vs/workbench/services/search/node/ripgrepSearchProvider'; import { OutputChannel } from 'vs/workbench/services/search/node/ripgrepSearchUtils'; -import { IFolderSearch, IRawSearch, isSerializedFileMatch, isSerializedSearchComplete, isSerializedSearchSuccess } from 'vs/workbench/services/search/node/search'; +import { isSerializedFileMatch } from 'vs/workbench/services/search/node/search'; import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager'; import * as vscode from 'vscode'; import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from './extHost.protocol'; @@ -29,8 +28,11 @@ export class ExtHostSearch implements ExtHostSearchShape { private readonly _proxy: MainThreadSearchShape; private readonly _textSearchProvider = new Map(); + private readonly _textSearchUsedSchemes = new Set(); private readonly _fileSearchProvider = new Map(); + private readonly _fileSearchUsedSchemes = new Set(); private readonly _fileIndexProvider = new Map(); + private readonly _fileIndexUsedSchemes = new Set(); private _handlePool: number = 0; private _internalFileSearchHandle: number; @@ -55,20 +57,32 @@ export class ExtHostSearch implements ExtHostSearchShape { } registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable { + if (this._textSearchUsedSchemes.has(scheme)) { + throw new Error(`a provider for the scheme '${scheme}' is already registered`); + } + + this._textSearchUsedSchemes.add(scheme); const handle = this._handlePool++; this._textSearchProvider.set(handle, provider); this._proxy.$registerTextSearchProvider(handle, this._transformScheme(scheme)); return toDisposable(() => { + this._textSearchUsedSchemes.delete(scheme); this._textSearchProvider.delete(handle); this._proxy.$unregisterProvider(handle); }); } registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider): IDisposable { + if (this._fileSearchUsedSchemes.has(scheme)) { + throw new Error(`a provider for the scheme '${scheme}' is already registered`); + } + + this._fileSearchUsedSchemes.add(scheme); const handle = this._handlePool++; this._fileSearchProvider.set(handle, provider); this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme)); return toDisposable(() => { + this._fileSearchUsedSchemes.delete(scheme); this._fileSearchProvider.delete(handle); this._proxy.$unregisterProvider(handle); }); @@ -86,16 +100,22 @@ export class ExtHostSearch implements ExtHostSearchShape { } registerFileIndexProvider(scheme: string, provider: vscode.FileIndexProvider): IDisposable { + if (this._fileIndexUsedSchemes.has(scheme)) { + throw new Error(`a provider for the scheme '${scheme}' is already registered`); + } + + this._fileIndexUsedSchemes.add(scheme); const handle = this._handlePool++; this._fileIndexProvider.set(handle, provider); this._proxy.$registerFileIndexProvider(handle, this._transformScheme(scheme)); return toDisposable(() => { + this._fileIndexUsedSchemes.delete(scheme); this._fileSearchProvider.delete(handle); this._proxy.$unregisterProvider(handle); // TODO@roblou - unregisterFileIndexProvider }); } - $provideFileSearchResults(handle: number, session: number, rawQuery: IRawSearchQuery, token: CancellationToken): Thenable { + $provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: CancellationToken): Thenable { const query = reviveQuery(rawQuery); if (handle === this._internalFileSearchHandle) { return this.doInternalFileSearch(handle, session, query, token); @@ -114,60 +134,23 @@ export class ExtHostSearch implements ExtHostSearchShape { } } - private doInternalFileSearch(handle: number, session: number, rawQuery: ISearchQuery, token: CancellationToken): Thenable { - return new Promise((resolve, reject) => { - const query: IRawSearch = { - folderQueries: [], - ignoreSymlinks: rawQuery.ignoreSymlinks, - filePattern: rawQuery.filePattern, - excludePattern: rawQuery.excludePattern, - includePattern: rawQuery.includePattern, - contentPattern: rawQuery.contentPattern, - maxResults: rawQuery.maxResults, - exists: rawQuery.exists, - sortByScore: rawQuery.sortByScore, - cacheKey: rawQuery.cacheKey, - maxFilesize: rawQuery.maxFileSize, - useRipgrep: rawQuery.useRipgrep, - disregardIgnoreFiles: rawQuery.disregardIgnoreFiles, - previewOptions: rawQuery.previewOptions, - disregardGlobalIgnoreFiles: rawQuery.disregardGlobalIgnoreFiles - }; - query.folderQueries = rawQuery.folderQueries.map(fq => ({ - disregardGlobalIgnoreFiles: fq.disregardGlobalIgnoreFiles, - disregardIgnoreFiles: fq.disregardIgnoreFiles, - excludePattern: fq.excludePattern, - fileEncoding: fq.fileEncoding, - folder: fq.folder.fsPath, - includePattern: fq.includePattern - })); + private doInternalFileSearch(handle: number, session: number, rawQuery: IFileQuery, token: CancellationToken): Thenable { + const onResult = (ev) => { + if (isSerializedFileMatch(ev)) { + ev = [ev]; + } - const event = this._internalFileSearchProvider.fileSearch(query); - event(ev => { - if (isSerializedSearchComplete(ev)) { - if (isSerializedSearchSuccess(ev)) { - resolve(ev); - return; - } else { - reject(ev); - return; - } - } else { - if (isSerializedFileMatch(ev)) { - ev = [ev]; - } + if (Array.isArray(ev)) { + this._proxy.$handleFileMatch(handle, session, ev.map(m => URI.file(m.path))); + return; + } - if (Array.isArray(ev)) { - this._proxy.$handleFileMatch(handle, session, ev.map(m => URI.file(m.path))); - return; - } + if (ev.message) { + this._logService.debug('ExtHostSearch', ev.message); + } + }; - if (ev.message) { - this._logService.debug('ExtHostSearch', ev.message); - } - } - }); - }); + return this._internalFileSearchProvider.doFileSearch(rawQuery, onResult, token); } $clearCache(cacheKey: string): Thenable { @@ -180,14 +163,14 @@ export class ExtHostSearch implements ExtHostSearchShape { return this._fileIndexSearchManager.clearCache(cacheKey); } - $provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, rawQuery: IRawSearchQuery, token: CancellationToken): Thenable { + $provideTextSearchResults(handle: number, session: number, rawQuery: IRawTextQuery, token: CancellationToken): Thenable { const provider = this._textSearchProvider.get(handle); if (!provider.provideTextSearchResults) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } const query = reviveQuery(rawQuery); - const engine = new TextSearchManager(pattern, query, provider, this._extfs); + const engine = new TextSearchManager(query, provider, this._extfs); return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token); } } @@ -201,9 +184,9 @@ function registerEHProviders(extHostSearch: ExtHostSearch, logService: ILogServi } } -function reviveQuery(rawQuery: IRawSearchQuery): ISearchQuery { +function reviveQuery(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery { return { - ...rawQuery, + ...rawQuery, // TODO ...{ folderQueries: rawQuery.folderQueries && rawQuery.folderQueries.map(reviveFolderQuery), extraFileResources: rawQuery.extraFileResources && rawQuery.extraFileResources.map(components => URI.revive(components)) diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 2d7f310b78a..bbb2ca0138c 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -3,11 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; + import { URI, UriComponents } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import * as Objects from 'vs/base/common/objects'; import { asThenable } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; +import { win32 } from 'vs/base/node/processes'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import * as tasks from 'vs/workbench/parts/tasks/common/tasks'; @@ -231,14 +234,15 @@ namespace TaskPanelKind { namespace PresentationOptions { export function from(value: vscode.TaskPresentationOptions): tasks.PresentationOptions { if (value === void 0 || value === null) { - return { reveal: tasks.RevealKind.Always, echo: true, focus: false, panel: tasks.PanelKind.Shared, showReuseMessage: true }; + return { reveal: tasks.RevealKind.Always, echo: true, focus: false, panel: tasks.PanelKind.Shared, showReuseMessage: true, clear: false }; } return { reveal: TaskRevealKind.from(value.reveal), echo: value.echo === void 0 ? true : !!value.echo, focus: !!value.focus, panel: TaskPanelKind.from(value.panel), - showReuseMessage: value.showReuseMessage === void 0 ? true : !!value.showReuseMessage + showReuseMessage: value.showReuseMessage === void 0 ? true : !!value.showReuseMessage, + clear: value.clear === void 0 ? false : !!value.clear, }; } } @@ -604,7 +608,7 @@ namespace TaskDTO { if (typeof value.scope === 'number') { scope = value.scope; } else { - scope = value.scope.uri.toJSON(); + scope = value.scope.uri; } } if (!definition || !scope) { @@ -882,9 +886,12 @@ export class ExtHostTask implements ExtHostTaskShape { }); } - public $resolveVariables(uriComponents: UriComponents, variables: string[]): any { + public $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Thenable<{ process?: string, variables: { [key: string]: string; } }> { let uri: URI = URI.revive(uriComponents); - let result: { [key: string]: string; } = Object.create(null); + let result = { + process: undefined as string, + variables: Object.create(null) + }; let workspaceFolder = this._workspaceService.resolveWorkspaceFolder(uri); let resolver = new ExtHostVariableResolverService(this._workspaceService, this._editorService, this._configurationService); let ws: IWorkspaceFolder = { @@ -895,10 +902,24 @@ export class ExtHostTask implements ExtHostTaskShape { throw new Error('Not implemented'); } }; - for (let variable of variables) { - result[variable] = resolver.resolve(ws, variable); + for (let variable of toResolve.variables) { + result.variables[variable] = resolver.resolve(ws, variable); } - return result; + if (toResolve.process !== void 0) { + let paths: string[] | undefined = undefined; + if (toResolve.process.path !== void 0) { + paths = toResolve.process.path.split(path.delimiter); + for (let i = 0; i < paths.length; i++) { + paths[i] = resolver.resolve(ws, paths[i]); + } + } + result.process = win32.findExecutable( + resolver.resolve(ws, toResolve.process.name), + toResolve.process.cwd !== void 0 ? resolver.resolve(ws, toResolve.process.cwd) : undefined, + paths + ); + } + return Promise.resolve(result); } private nextHandle(): number { diff --git a/src/vs/workbench/api/node/extHostTextEditor.ts b/src/vs/workbench/api/node/extHostTextEditor.ts index 286e1521399..b9716f63542 100644 --- a/src/vs/workbench/api/node/extHostTextEditor.ts +++ b/src/vs/workbench/api/node/extHostTextEditor.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { ok } from 'vs/base/common/assert'; -import { readonly, illegalArgument } from 'vs/base/common/errors'; +import { illegalArgument, readonly } from 'vs/base/common/errors'; import { IdGenerator } from 'vs/base/common/idGenerator'; -import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData'; -import { Selection, Range, Position, EndOfLine, TextEditorRevealType, TextEditorLineNumbersStyle, SnippetString } from './extHostTypes'; -import { ISingleEditOperation } from 'vs/editor/common/model'; -import * as TypeConverters from './extHostTypeConverters'; -import { MainThreadTextEditorsShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from './extHost.protocol'; -import * as vscode from 'vscode'; import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; import { IRange } from 'vs/editor/common/core/range'; +import { ISingleEditOperation } from 'vs/editor/common/model'; +import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData'; +import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters'; +import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/node/extHostTypes'; +import * as vscode from 'vscode'; export class TextEditorDecorationType implements vscode.TextEditorDecorationType { @@ -25,7 +25,7 @@ export class TextEditorDecorationType implements vscode.TextEditorDecorationType constructor(proxy: MainThreadTextEditorsShape, options: vscode.DecorationRenderOptions) { this.key = TextEditorDecorationType._Keys.nextId(); this._proxy = proxy; - this._proxy.$registerTextEditorDecorationType(this.key, /* URI vs Uri */ options); + this._proxy.$registerTextEditorDecorationType(this.key, TypeConverters.DecorationRenderOptions.from(options)); } public dispose(): void { diff --git a/src/vs/workbench/api/node/extHostTextEditors.ts b/src/vs/workbench/api/node/extHostTextEditors.ts index fa5d98a187f..def77250f79 100644 --- a/src/vs/workbench/api/node/extHostTextEditors.ts +++ b/src/vs/workbench/api/node/extHostTextEditors.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; -import { TextEditorSelectionChangeKind } from './extHostTypes'; -import * as TypeConverters from './extHostTypeConverters'; -import { TextEditorDecorationType, ExtHostTextEditor } from './extHostTextEditor'; -import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors'; -import { MainContext, MainThreadTextEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IMainContext, IEditorPropertiesChangeData } from './extHost.protocol'; +import { Emitter, Event } from 'vs/base/common/event'; +import { ExtHostEditorsShape, IEditorPropertiesChangeData, IMainContext, ITextDocumentShowOptions, ITextEditorPositionData, MainContext, MainThreadTextEditorsShape } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; +import { ExtHostTextEditor, TextEditorDecorationType } from 'vs/workbench/api/node/extHostTextEditor'; +import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters'; +import { TextEditorSelectionChangeKind } from 'vs/workbench/api/node/extHostTypes'; import * as vscode from 'vscode'; export class ExtHostEditors implements ExtHostEditorsShape { diff --git a/src/vs/workbench/api/node/extHostTreeViews.ts b/src/vs/workbench/api/node/extHostTreeViews.ts index 9614729d309..3e6eea23578 100644 --- a/src/vs/workbench/api/node/extHostTreeViews.ts +++ b/src/vs/workbench/api/node/extHostTreeViews.ts @@ -8,19 +8,40 @@ import * as vscode from 'vscode'; import { basename } from 'vs/base/common/paths'; import { URI } from 'vs/base/common/uri'; import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Disposable } from 'vs/base/common/lifecycle'; import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol'; -import { ITreeItem, TreeViewItemHandleArg } from 'vs/workbench/common/views'; +import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands'; import { asThenable } from 'vs/base/common/async'; import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/node/extHostTypes'; -import { isUndefinedOrNull } from 'vs/base/common/types'; +import { isUndefinedOrNull, isString } from 'vs/base/common/types'; import { equals } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; +import { IExtensionDescription, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; type TreeItemHandle = string; +function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeItemLabel { + if (isString(label)) { + return { label }; + } + + if (label + && typeof label === 'object' + && typeof label.label === 'string') { + checkProposedApiEnabled(extension); + let highlights: [number, number][] = void 0; + if (Array.isArray(label.highlights)) { + highlights = (<[number, number][]>label.highlights).filter((highlight => highlight.length === 2 && typeof highlight[0] === 'number' && typeof highlight[1] === 'number')); + highlights = highlights.length ? highlights : void 0; + } + return { label: label.label, highlights }; + } + + return void 0; +} + + export class ExtHostTreeViews implements ExtHostTreeViewsShape { private treeViews: Map> = new Map>(); @@ -40,16 +61,20 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { }); } - registerTreeDataProvider(id: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { - const treeView = this.createTreeView(id, { treeDataProvider }); + registerTreeDataProvider(id: string, treeDataProvider: vscode.TreeDataProvider, extension: IExtensionDescription): vscode.Disposable { + const treeView = this.createTreeView(id, { treeDataProvider }, extension); return { dispose: () => treeView.dispose() }; } - createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider }): vscode.TreeView { + createTreeView(viewId: string, options: vscode.TreeViewOptions, extension: IExtensionDescription): vscode.TreeView { if (!options || !options.treeDataProvider) { throw new Error('Options with treeDataProvider is mandatory'); } - const treeView = this.createExtHostTreeViewer(viewId, options.treeDataProvider); + if (options.showCollapseAll) { + checkProposedApiEnabled(extension); + } + + const treeView = this.createExtHostTreeViewer(viewId, options, extension); return { get onDidCollapseElement() { return treeView.onDidCollapseElement; }, get onDidExpandElement() { return treeView.onDidExpandElement; }, @@ -57,7 +82,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { get onDidChangeSelection() { return treeView.onDidChangeSelection; }, get visible() { return treeView.visible; }, get onDidChangeVisibility() { return treeView.onDidChangeVisibility; }, - reveal: (element: T, options?: { select?: boolean, focus?: boolean }): Thenable => { + reveal: (element: T, options?: IRevealOptions): Thenable => { return treeView.reveal(element, options); }, dispose: () => { @@ -70,7 +95,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { $getChildren(treeViewId: string, treeItemHandle?: string): Thenable { const treeView = this.treeViews.get(treeViewId); if (!treeView) { - return TPromise.wrapError(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId))); + return Promise.reject(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId))); } return treeView.getChildren(treeItemHandle); } @@ -99,8 +124,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { treeView.setVisible(isVisible); } - private createExtHostTreeViewer(id: string, dataProvider: vscode.TreeDataProvider): ExtHostTreeView { - const treeView = new ExtHostTreeView(id, dataProvider, this._proxy, this.commands.converter, this.logService); + private createExtHostTreeViewer(id: string, options: vscode.TreeViewOptions, extension: IExtensionDescription): ExtHostTreeView { + const treeView = new ExtHostTreeView(id, options, this._proxy, this.commands.converter, this.logService, extension); this.treeViews.set(id, treeView); return treeView; } @@ -122,6 +147,8 @@ class ExtHostTreeView extends Disposable { private static LABEL_HANDLE_PREFIX = '0'; private static ID_HANDLE_PREFIX = '1'; + private readonly dataProvider: vscode.TreeDataProvider; + private roots: TreeNode[] | null = null; private elements: Map = new Map(); private nodes: Map = new Map(); @@ -144,17 +171,18 @@ class ExtHostTreeView extends Disposable { private _onDidChangeVisibility: Emitter = this._register(new Emitter()); readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; - private refreshPromise: TPromise = TPromise.as(null); + private refreshPromise: Promise = Promise.resolve(null); - constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService) { + constructor(private viewId: string, options: vscode.TreeViewOptions, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) { super(); - this.proxy.$registerTreeViewDataProvider(viewId); + this.dataProvider = options.treeDataProvider; + this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll }); if (this.dataProvider.onDidChangeTreeData) { let refreshingPromise, promiseCallback; this._register(debounceEvent(this.dataProvider.onDidChangeTreeData, (last, current) => { if (!refreshingPromise) { // New refresh has started - refreshingPromise = new TPromise((c, e) => promiseCallback = c); + refreshingPromise = new Promise(c => promiseCallback = c); this.refreshPromise = this.refreshPromise.then(() => refreshingPromise); } return last ? [...last, current] : [current]; @@ -170,11 +198,11 @@ class ExtHostTreeView extends Disposable { const parentElement = parentHandle ? this.getExtensionElement(parentHandle) : void 0; if (parentHandle && !parentElement) { console.error(`No tree item with id \'${parentHandle}\' found.`); - return TPromise.as([]); + return Promise.resolve([]); } const childrenNodes = this.getChildrenNodes(parentHandle); // Get it from cache - return (childrenNodes ? TPromise.as(childrenNodes) : this.fetchChildrenNodes(parentElement)) + return (childrenNodes ? Promise.resolve(childrenNodes) : this.fetchChildrenNodes(parentElement)) .then(nodes => nodes.map(n => n.item)); } @@ -182,18 +210,19 @@ class ExtHostTreeView extends Disposable { return this.elements.get(treeItemHandle); } - reveal(element: T, options?: { select?: boolean, focus?: boolean }): TPromise { + reveal(element: T, options?: IRevealOptions): Promise { options = options ? options : { select: true, focus: false }; const select = isUndefinedOrNull(options.select) ? true : options.select; const focus = isUndefinedOrNull(options.focus) ? false : options.focus; + const expand = isUndefinedOrNull(options.expand) ? false : options.expand; if (typeof this.dataProvider.getParent !== 'function') { - return TPromise.wrapError(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`)); + return Promise.reject(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`)); } return this.refreshPromise .then(() => this.resolveUnknownParentChain(element)) .then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1]) - .then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus })), error => this.logService.error(error)); + .then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus, expand })), error => this.logService.error(error)); } setExpanded(treeItemHandle: TreeItemHandle, expanded: boolean): void { @@ -225,7 +254,7 @@ class ExtHostTreeView extends Disposable { return this.resolveParent(element) .then((parent) => { if (!parent) { - return TPromise.as([]); + return Promise.resolve([]); } return this.resolveUnknownParentChain(parent) .then(result => this.resolveTreeNode(parent, result[result.length - 1]) @@ -239,12 +268,16 @@ class ExtHostTreeView extends Disposable { private resolveParent(element: T): Thenable { const node = this.nodes.get(element); if (node) { - return TPromise.as(node.parent ? this.elements.get(node.parent.item.handle) : null); + return Promise.resolve(node.parent ? this.elements.get(node.parent.item.handle) : null); } return asThenable(() => this.dataProvider.getParent(element)); } private resolveTreeNode(element: T, parent?: TreeNode): Thenable { + const node = this.nodes.get(element); + if (node) { + return Promise.resolve(node); + } return asThenable(() => this.dataProvider.getTreeItem(element)) .then(extTreeItem => this.createHandle(element, extTreeItem, parent, true)) .then(handle => this.getChildren(parent ? parent.item.handle : null) @@ -253,7 +286,7 @@ class ExtHostTreeView extends Disposable { if (cachedElement) { const node = this.nodes.get(cachedElement); if (node) { - return TPromise.as(node); + return Promise.resolve(node); } } throw new Error(`Cannot resolve tree item for element ${handle}`); @@ -280,7 +313,7 @@ class ExtHostTreeView extends Disposable { const parentNode = parentElement ? this.nodes.get(parentElement) : void 0; return asThenable(() => this.dataProvider.getChildren(parentElement)) - .then(elements => TPromise.join( + .then(elements => Promise.all( (elements || []) .filter(element => !!element) .map(element => asThenable(() => this.dataProvider.getTreeItem(element)) @@ -299,7 +332,7 @@ class ExtHostTreeView extends Disposable { return this.refreshHandles(handlesToRefresh); } } - return TPromise.as(null); + return Promise.resolve(null); } private getHandlesToRefresh(elements: T[]): TreeItemHandle[] { @@ -332,9 +365,9 @@ class ExtHostTreeView extends Disposable { return handlesToUpdate; } - private refreshHandles(itemHandles: TreeItemHandle[]): TPromise { + private refreshHandles(itemHandles: TreeItemHandle[]): Promise { const itemsToRefresh: { [treeItemHandle: string]: ITreeItem } = {}; - return TPromise.join(itemHandles.map(treeItemHandle => + return Promise.all(itemHandles.map(treeItemHandle => this.refreshNode(treeItemHandle) .then(node => { if (node) { @@ -384,7 +417,7 @@ class ExtHostTreeView extends Disposable { const item = { handle, parentHandle: parent ? parent.item.handle : void 0, - label: extensionTreeItem.label, + label: toTreeItemLabel(extensionTreeItem.label, this.extension), resourceUri: extensionTreeItem.resourceUri, tooltip: typeof extensionTreeItem.tooltip === 'string' ? extensionTreeItem.tooltip : void 0, command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command) : void 0, @@ -403,8 +436,9 @@ class ExtHostTreeView extends Disposable { return `${ExtHostTreeView.ID_HANDLE_PREFIX}/${id}`; } + const treeItemLabel = toTreeItemLabel(label, this.extension); const prefix: string = parent ? parent.item.handle : ExtHostTreeView.LABEL_HANDLE_PREFIX; - let elementId = label ? label : resourceUri ? basename(resourceUri.path) : ''; + let elementId = treeItemLabel ? treeItemLabel.label : resourceUri ? basename(resourceUri.path) : ''; elementId = elementId.indexOf('/') !== -1 ? elementId.replace('/', '//') : elementId; const existingHandle = this.nodes.has(element) ? this.nodes.get(element).item.handle : void 0; const childrenNodes = (this.getChildrenNodes(parent) || []); diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 0a690d9bdcd..de2d33911d8 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -8,11 +8,11 @@ import * as types from './extHostTypes'; import * as search from 'vs/workbench/parts/search/common/search'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; -import { IDecorationOptions } from 'vs/editor/common/editorCommon'; -import { EndOfLineSequence } from 'vs/editor/common/model'; +import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon'; +import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model'; import * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; -import { ProgressLocation as MainProgressLocation } from 'vs/workbench/services/progress/common/progress'; +import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; @@ -42,14 +42,14 @@ export interface SelectionLike extends RangeLike { export namespace Selection { export function to(selection: ISelection): types.Selection { - let { selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn } = selection; - let start = new types.Position(selectionStartLineNumber - 1, selectionStartColumn - 1); - let end = new types.Position(positionLineNumber - 1, positionColumn - 1); + const { selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn } = selection; + const start = new types.Position(selectionStartLineNumber - 1, selectionStartColumn - 1); + const end = new types.Position(positionLineNumber - 1, positionColumn - 1); return new types.Selection(start, end); } export function from(selection: SelectionLike): ISelection { - let { anchor, active } = selection; + const { anchor, active } = selection; return { selectionStartLineNumber: anchor.line + 1, selectionStartColumn: anchor.character + 1, @@ -64,7 +64,7 @@ export namespace Range { if (!range) { return undefined; } - let { start, end } = range; + const { start, end } = range; return { startLineNumber: start.line + 1, startColumn: start.character + 1, @@ -77,7 +77,7 @@ export namespace Range { if (!range) { return undefined; } - let { startLineNumber, startColumn, endLineNumber, endColumn } = range; + const { startLineNumber, startColumn, endLineNumber, endColumn } = range; return new types.Range(startLineNumber - 1, startColumn - 1, endLineNumber - 1, endColumn - 1); } } @@ -252,21 +252,142 @@ export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.Deco } } -export const TextEdit = { +function pathOrURIToURI(value: string | URI): URI { + if (typeof value === 'undefined') { + return value; + } + if (typeof value === 'string') { + return URI.file(value); + } else { + return value; + } +} - from(edit: vscode.TextEdit): modes.TextEdit { +export namespace ThemableDecorationAttachmentRenderOptions { + export function from(options: vscode.ThemableDecorationAttachmentRenderOptions): IContentDecorationRenderOptions { + if (typeof options === 'undefined') { + return options; + } + return { + contentText: options.contentText, + contentIconPath: pathOrURIToURI(options.contentIconPath), + border: options.border, + borderColor: options.borderColor, + fontStyle: options.fontStyle, + fontWeight: options.fontWeight, + textDecoration: options.textDecoration, + color: options.color, + backgroundColor: options.backgroundColor, + margin: options.margin, + width: options.width, + height: options.height, + }; + } +} + +export namespace ThemableDecorationRenderOptions { + export function from(options: vscode.ThemableDecorationRenderOptions): IThemeDecorationRenderOptions { + if (typeof options === 'undefined') { + return options; + } + return { + backgroundColor: options.backgroundColor, + outline: options.outline, + outlineColor: options.outlineColor, + outlineStyle: options.outlineStyle, + outlineWidth: options.outlineWidth, + border: options.border, + borderColor: options.borderColor, + borderRadius: options.borderRadius, + borderSpacing: options.borderSpacing, + borderStyle: options.borderStyle, + borderWidth: options.borderWidth, + fontStyle: options.fontStyle, + fontWeight: options.fontWeight, + textDecoration: options.textDecoration, + cursor: options.cursor, + color: options.color, + opacity: options.opacity, + letterSpacing: options.letterSpacing, + gutterIconPath: pathOrURIToURI(options.gutterIconPath), + gutterIconSize: options.gutterIconSize, + overviewRulerColor: options.overviewRulerColor, + before: ThemableDecorationAttachmentRenderOptions.from(options.before), + after: ThemableDecorationAttachmentRenderOptions.from(options.after), + }; + } +} + +export namespace DecorationRangeBehavior { + export function from(value: types.DecorationRangeBehavior): TrackedRangeStickiness { + if (typeof value === 'undefined') { + return value; + } + switch (value) { + case types.DecorationRangeBehavior.OpenOpen: + return TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges; + case types.DecorationRangeBehavior.ClosedClosed: + return TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; + case types.DecorationRangeBehavior.OpenClosed: + return TrackedRangeStickiness.GrowsOnlyWhenTypingBefore; + case types.DecorationRangeBehavior.ClosedOpen: + return TrackedRangeStickiness.GrowsOnlyWhenTypingAfter; + } + } +} + +export namespace DecorationRenderOptions { + export function from(options: vscode.DecorationRenderOptions): IDecorationRenderOptions { + return { + isWholeLine: options.isWholeLine, + rangeBehavior: DecorationRangeBehavior.from(options.rangeBehavior), + overviewRulerLane: options.overviewRulerLane, + light: ThemableDecorationRenderOptions.from(options.light), + dark: ThemableDecorationRenderOptions.from(options.dark), + + backgroundColor: options.backgroundColor, + outline: options.outline, + outlineColor: options.outlineColor, + outlineStyle: options.outlineStyle, + outlineWidth: options.outlineWidth, + border: options.border, + borderColor: options.borderColor, + borderRadius: options.borderRadius, + borderSpacing: options.borderSpacing, + borderStyle: options.borderStyle, + borderWidth: options.borderWidth, + fontStyle: options.fontStyle, + fontWeight: options.fontWeight, + textDecoration: options.textDecoration, + cursor: options.cursor, + color: options.color, + opacity: options.opacity, + letterSpacing: options.letterSpacing, + gutterIconPath: pathOrURIToURI(options.gutterIconPath), + gutterIconSize: options.gutterIconSize, + overviewRulerColor: options.overviewRulerColor, + before: ThemableDecorationAttachmentRenderOptions.from(options.before), + after: ThemableDecorationAttachmentRenderOptions.from(options.after), + }; + } +} + +export namespace TextEdit { + + export function from(edit: vscode.TextEdit): modes.TextEdit { return { text: edit.newText, eol: EndOfLine.from(edit.newEol), range: Range.from(edit.range) }; - }, - to(edit: modes.TextEdit): types.TextEdit { - let result = new types.TextEdit(Range.to(edit.range), edit.text); + } + + export function to(edit: modes.TextEdit): types.TextEdit { + const result = new types.TextEdit(Range.to(edit.range), edit.text); result.newEol = EndOfLine.to(edit.eol); return result; } -}; +} export namespace WorkspaceEdit { export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors): WorkspaceEditDto { @@ -277,7 +398,7 @@ export namespace WorkspaceEdit { const [uri, uriOrEdits] = entry; if (Array.isArray(uriOrEdits)) { // text edits - let doc = documents ? documents.getDocument(uri.toString()) : undefined; + const doc = documents ? documents.getDocument(uri.toString()) : undefined; result.edits.push({ resource: uri, modelVersionId: doc && doc.version, edits: uriOrEdits.map(TextEdit.from) }); } else { // resource edits @@ -339,11 +460,11 @@ export namespace SymbolKind { _fromMapping[types.SymbolKind.TypeParameter] = modes.SymbolKind.TypeParameter; export function from(kind: vscode.SymbolKind): modes.SymbolKind { - return _fromMapping[kind] || modes.SymbolKind.Property; + return typeof _fromMapping[kind] === 'number' ? _fromMapping[kind] : modes.SymbolKind.Property; } export function to(kind: modes.SymbolKind): vscode.SymbolKind { - for (let k in _fromMapping) { + for (const k in _fromMapping) { if (_fromMapping[k] === kind) { return Number(k); } @@ -373,7 +494,7 @@ export namespace WorkspaceSymbol { export namespace DocumentSymbol { export function from(info: vscode.DocumentSymbol): modes.DocumentSymbol { - let result: modes.DocumentSymbol = { + const result: modes.DocumentSymbol = { name: info.name, detail: info.detail, range: Range.from(info.range), @@ -386,7 +507,7 @@ export namespace DocumentSymbol { return result; } export function to(info: modes.DocumentSymbol): vscode.DocumentSymbol { - let result = new types.DocumentSymbol( + const result = new types.DocumentSymbol( info.name, info.detail, SymbolKind.to(info.kind), @@ -400,17 +521,18 @@ export namespace DocumentSymbol { } } -export const location = { - from(value: vscode.Location): modes.Location { +export namespace location { + export function from(value: vscode.Location): modes.Location { return { range: value.range && Range.from(value.range), uri: value.uri }; - }, - to(value: modes.Location): types.Location { + } + + export function to(value: modes.Location): types.Location { return new types.Location(value.uri, Range.to(value.range)); } -}; +} export namespace DefinitionLink { export function from(value: vscode.Location | vscode.DefinitionLink): modes.DefinitionLink { @@ -454,7 +576,7 @@ export namespace DocumentHighlight { } export namespace CompletionTriggerKind { - export function from(kind: modes.CompletionTriggerKind) { + export function to(kind: modes.CompletionTriggerKind) { switch (kind) { case modes.CompletionTriggerKind.TriggerCharacter: return types.CompletionTriggerKind.TriggerCharacter; @@ -468,17 +590,17 @@ export namespace CompletionTriggerKind { } export namespace CompletionContext { - export function from(context: modes.CompletionContext): types.CompletionContext { + export function to(context: modes.CompletionContext): types.CompletionContext { return { - triggerKind: CompletionTriggerKind.from(context.triggerKind), + triggerKind: CompletionTriggerKind.to(context.triggerKind), triggerCharacter: context.triggerCharacter }; } } -export const CompletionItemKind = { +export namespace CompletionItemKind { - from(kind: types.CompletionItemKind): modes.CompletionItemKind { + export function from(kind: types.CompletionItemKind): modes.CompletionItemKind { switch (kind) { case types.CompletionItemKind.Method: return modes.CompletionItemKind.Method; case types.CompletionItemKind.Function: return modes.CompletionItemKind.Function; @@ -507,9 +629,9 @@ export const CompletionItemKind = { case types.CompletionItemKind.TypeParameter: return modes.CompletionItemKind.TypeParameter; } return modes.CompletionItemKind.Property; - }, + } - to(kind: modes.CompletionItemKind): types.CompletionItemKind { + export function to(kind: modes.CompletionItemKind): types.CompletionItemKind { switch (kind) { case modes.CompletionItemKind.Method: return types.CompletionItemKind.Method; case modes.CompletionItemKind.Function: return types.CompletionItemKind.Function; @@ -539,7 +661,7 @@ export const CompletionItemKind = { } return types.CompletionItemKind.Property; } -}; +} export namespace CompletionItemInsertTextRule { @@ -656,7 +778,7 @@ export namespace DocumentLink { export namespace ColorPresentation { export function to(colorPresentation: modes.IColorPresentation): types.ColorPresentation { - let cp = new types.ColorPresentation(colorPresentation.label); + const cp = new types.ColorPresentation(colorPresentation.label); if (colorPresentation.textEdit) { cp.textEdit = TextEdit.to(colorPresentation.textEdit); } @@ -734,7 +856,7 @@ export namespace ProgressLocation { export namespace FoldingRange { export function from(r: vscode.FoldingRange): modes.FoldingRange { - let range: modes.FoldingRange = { start: r.start + 1, end: r.end + 1 }; + const range: modes.FoldingRange = { start: r.start + 1, end: r.end + 1 }; if (r.kind) { range.kind = FoldingRangeKind.from(r.kind); } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 555c76511c7..48b9f1afdba 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -48,9 +48,13 @@ export class Disposable { export class Position { static Min(...positions: Position[]): Position { - let result = positions.pop(); - for (let p of positions) { - if (p.isBefore(result)) { + if (positions.length === 0) { + throw new TypeError(); + } + let result = positions[0]; + for (let i = 1; i < positions.length; i++) { + let p = positions[i]; + if (p.isBefore(result!)) { result = p; } } @@ -58,9 +62,13 @@ export class Position { } static Max(...positions: Position[]): Position { - let result = positions.pop(); - for (let p of positions) { - if (p.isAfter(result)) { + if (positions.length === 0) { + throw new TypeError(); + } + let result = positions[0]; + for (let i = 1; i < positions.length; i++) { + let p = positions[i]; + if (p.isAfter(result!)) { result = p; } } @@ -579,9 +587,6 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { res.push(candidate.edit); } } - if (res.length === 0) { - return undefined; - } return res; } @@ -811,7 +816,7 @@ export class Diagnostic { }; } - static isEqual(a: Diagnostic, b: Diagnostic): boolean { + static isEqual(a: Diagnostic | undefined, b: Diagnostic | undefined): boolean { if (a === b) { return true; } @@ -832,7 +837,7 @@ export class Diagnostic { export class Hover { public contents: vscode.MarkdownString[] | vscode.MarkedString[]; - public range: Range; + public range: Range | undefined; constructor( contents: vscode.MarkdownString | vscode.MarkedString | vscode.MarkdownString[] | vscode.MarkedString[], @@ -916,7 +921,7 @@ export class SymbolInformation { name: string; location: Location; kind: SymbolKind; - containerName: string; + containerName: string | undefined; constructor(name: string, kind: SymbolKind, containerName: string, location: Location); constructor(name: string, kind: SymbolKind, range: Range, uri?: URI, containerName?: string); @@ -1035,7 +1040,7 @@ export class CodeLens { range: Range; - command: vscode.Command; + command: vscode.Command | undefined; constructor(range: Range, command?: vscode.Command) { this.range = range; @@ -1115,7 +1120,7 @@ export class SignatureHelp { export enum SignatureHelpTriggerReason { Invoke = 1, TriggerCharacter = 2, - Retrigger = 3, + ContentChange = 3, } export enum CompletionTriggerKind { @@ -1164,7 +1169,7 @@ export enum CompletionItemInsertTextRule { export class CompletionItem implements vscode.CompletionItem { label: string; - kind: CompletionItemKind; + kind: CompletionItemKind | undefined; detail: string; documentation: string | MarkdownString; sortText: string; @@ -1186,7 +1191,7 @@ export class CompletionItem implements vscode.CompletionItem { toJSON(): any { return { label: this.label, - kind: CompletionItemKind[this.kind], + kind: this.kind && CompletionItemKind[this.kind], detail: this.detail, documentation: this.documentation, sortText: this.sortText, @@ -1791,16 +1796,16 @@ export enum ProgressLocation { export class TreeItem { - label?: string; + label?: string | vscode.TreeItemLabel; resourceUri?: URI; iconPath?: string | URI | { light: string | URI; dark: string | URI }; command?: vscode.Command; contextValue?: string; tooltip?: string; - constructor(label: string, collapsibleState?: vscode.TreeItemCollapsibleState) + constructor(label: string | vscode.TreeItemLabel, collapsibleState?: vscode.TreeItemCollapsibleState) constructor(resourceUri: URI, collapsibleState?: vscode.TreeItemCollapsibleState) - constructor(arg1: string | URI, public collapsibleState: vscode.TreeItemCollapsibleState = TreeItemCollapsibleState.None) { + constructor(arg1: string | vscode.TreeItemLabel | URI, public collapsibleState: vscode.TreeItemCollapsibleState = TreeItemCollapsibleState.None) { if (arg1 instanceof URI) { this.resourceUri = arg1; } else { diff --git a/src/vs/workbench/api/node/extHostUrls.ts b/src/vs/workbench/api/node/extHostUrls.ts index f8b7b37f9f4..23bfb2e3d6c 100644 --- a/src/vs/workbench/api/node/extHostUrls.ts +++ b/src/vs/workbench/api/node/extHostUrls.ts @@ -6,7 +6,6 @@ import * as vscode from 'vscode'; import { MainContext, IMainContext, ExtHostUrlsShape, MainThreadUrlsShape } from './extHost.protocol'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { toDisposable } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -45,7 +44,7 @@ export class ExtHostUrls implements ExtHostUrlsShape { const handler = this.handlers.get(handle); if (!handler) { - return TPromise.as(null); + return Promise.resolve(null); } try { handler.handleUri(URI.revive(uri)); @@ -53,6 +52,6 @@ export class ExtHostUrls implements ExtHostUrlsShape { onUnexpectedError(err); } - return TPromise.as(null); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index 4b44c2e6600..93c50645b77 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -5,12 +5,12 @@ import { Emitter, Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; import * as vscode from 'vscode'; import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol'; import { Disposable } from './extHostTypes'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; type IconPath = URI | { light: URI, dark: URI }; @@ -239,7 +239,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { } public createWebview( - extensionLocation: URI, + extension: IExtensionDescription, viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, @@ -252,7 +252,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { }; const handle = ExtHostWebviews.newHandle(); - this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extensionLocation); + this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.id, extension.extensionLocation); const webview = new ExtHostWebview(handle, this._proxy, options); const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview); @@ -311,7 +311,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { panel.dispose(); this._webviewPanels.delete(handle); } - return TPromise.as(void 0); + return Promise.resolve(void 0); } $deserializeWebviewPanel( @@ -324,7 +324,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { ): Thenable { const serializer = this._serializers.get(viewType); if (!serializer) { - return TPromise.wrapError(new Error(`No serializer found for '${viewType}'`)); + return Promise.reject(new Error(`No serializer found for '${viewType}'`)); } const webview = new ExtHostWebview(webviewHandle, this._proxy, options); diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index 08a022a9fad..b982e5149c7 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { join, relative } from 'path'; -import { delta as arrayDelta } from 'vs/base/common/arrays'; +import { delta as arrayDelta, mapArrayOrNot } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { TernarySearchTree } from 'vs/base/common/map'; import { Counter } from 'vs/base/common/numbers'; @@ -16,13 +17,13 @@ import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ILogService } from 'vs/platform/log/common/log'; import { Severity } from 'vs/platform/notification/common/notification'; -import { IQueryOptions, IRawFileMatch2 } from 'vs/platform/search/common/search'; +import { IRawFileMatch2, resultIsMatch } from 'vs/platform/search/common/search'; import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Range, RelativePattern } from 'vs/workbench/api/node/extHostTypes'; +import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import * as vscode from 'vscode'; import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol'; -import { CancellationToken } from 'vs/base/common/cancellation'; function isFolderEqual(folderA: URI, folderB: URI): boolean { return isEqual(folderA, folderB, !isLinux); @@ -399,7 +400,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { } : options.previewOptions; - const queryOptions: IQueryOptions = { + const queryOptions: ITextQueryBuilderOptions = { ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined, disregardIgnoreFiles: typeof options.useIgnoreFiles === 'boolean' ? !options.useIgnoreFiles : undefined, disregardGlobalIgnoreFiles: typeof options.useGlobalIgnoreFiles === 'boolean' ? !options.useGlobalIgnoreFiles : undefined, @@ -407,6 +408,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { fileEncoding: options.encoding, maxResults: options.maxResults, previewOptions, + afterContext: options.afterContext, + beforeContext: options.beforeContext, includePattern: options.include && globPatternToString(options.include), excludePattern: options.exclude && globPatternToString(options.exclude) @@ -419,15 +422,28 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { return; } - p.matches.forEach(match => { - callback({ - uri: URI.revive(p.resource), - preview: { - text: match.preview.text, - match: new Range(match.preview.match.startLineNumber, match.preview.match.startColumn, match.preview.match.endLineNumber, match.preview.match.endColumn) - }, - range: new Range(match.range.startLineNumber, match.range.startColumn, match.range.endLineNumber, match.range.endColumn) - }); + const uri = URI.revive(p.resource); + p.results.forEach(result => { + if (resultIsMatch(result)) { + callback({ + uri, + preview: { + text: result.preview.text, + matches: mapArrayOrNot( + result.preview.matches, + m => new Range(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn)) + }, + ranges: mapArrayOrNot( + result.ranges, + r => new Range(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn)) + }); + } else { + callback({ + uri, + text: result.text, + lineNumber: result.lineNumber + }); + } }); }; @@ -453,4 +469,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { saveAll(includeUntitled?: boolean): Thenable { return this._proxy.$saveAll(includeUntitled); } + + resolveProxy(url: string): Thenable { + return this._proxy.$resolveProxy(url); + } } diff --git a/src/vs/workbench/api/shared/tasks.ts b/src/vs/workbench/api/shared/tasks.ts index c9fe2575b20..4710c201e2a 100644 --- a/src/vs/workbench/api/shared/tasks.ts +++ b/src/vs/workbench/api/shared/tasks.ts @@ -16,6 +16,7 @@ export interface TaskPresentationOptionsDTO { focus?: boolean; panel?: number; showReuseMessage?: boolean; + clear?: boolean; } export interface ExecutionOptionsDTO { diff --git a/src/vs/workbench/browser/actions.ts b/src/vs/workbench/browser/actions.ts index 3a936e8da9e..a7af610e5dc 100644 --- a/src/vs/workbench/browser/actions.ts +++ b/src/vs/workbench/browser/actions.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action, IAction } from 'vs/base/common/actions'; import { BaseActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -92,7 +91,7 @@ export class ContributableActionProvider implements IActionProvider { return false; } - getActions(tree: ITree, element: any): TPromise { + getActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; const context = this.toContext(tree, element); @@ -105,7 +104,7 @@ export class ContributableActionProvider implements IActionProvider { } } - return Promise.resolve(prepareActions(actions)); + return prepareActions(actions); } hasSecondaryActions(tree: ITree, element: any): boolean { @@ -122,7 +121,7 @@ export class ContributableActionProvider implements IActionProvider { return false; } - getSecondaryActions(tree: ITree, element: any): TPromise { + getSecondaryActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; const context = this.toContext(tree, element); @@ -135,7 +134,7 @@ export class ContributableActionProvider implements IActionProvider { } } - return Promise.resolve(prepareActions(actions)); + return prepareActions(actions); } getActionItem(tree: ITree, element: any, action: Action): BaseActionItem { diff --git a/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts b/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts index c5d450fae04..921dc26b558 100644 --- a/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts +++ b/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts @@ -28,8 +28,9 @@ export class ToggleSidebarVisibilityAction extends Action { run(): Thenable { const hideSidebar = this.partService.isVisible(Parts.SIDEBAR_PART); + this.partService.setSideBarHidden(hideSidebar); - return this.partService.setSideBarHidden(hideSidebar); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index 06d97406eb4..aa9936a1fd1 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -95,10 +95,8 @@ export abstract class Composite extends Component implements IComposite { * Note that DOM-dependent calculations should be performed from the setVisible() * call. Only then the composite will be part of the DOM. */ - create(parent: HTMLElement): Promise { + create(parent: HTMLElement): void { this.parent = parent; - - return Promise.resolve(null); } updateStyles(): void { @@ -120,14 +118,11 @@ export abstract class Composite extends Component implements IComposite { * is called more than once during workbench lifecycle depending on the user interaction. * The composite will be on-DOM if visible is set to true and off-DOM otherwise. * - * The returned promise is complete when the composite is visible. As such it is valid - * to do a long running operation from this call. Typically this operation should be - * fast though because setVisible might be called many times during a session. + * Typically this operation should be fast though because setVisible might be called many times during a session. + * If there is a long running opertaion it is fine to have it running in the background asyncly and return before. */ - setVisible(visible: boolean): Promise { + setVisible(visible: boolean): void { this.visible = visible; - - return Promise.resolve(null); } /** diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 34c0b5ae2ba..b45ed470aaa 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -25,7 +25,7 @@ import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart'; import { getZoomFactor } from 'vs/base/browser/browser'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import * as perf from 'vs/base/common/performance'; const TITLE_BAR_HEIGHT = isMacintosh ? 22 : 30; const STATUS_BAR_HEIGHT = 22; @@ -68,8 +68,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr private _panelHeight: number; private _panelWidth: number; - private saveStateScheduler: RunOnceScheduler; - constructor( private parent: HTMLElement, private workbenchContainer: HTMLElement, @@ -102,9 +100,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr this.sashXTwo = new Sash(this.workbenchContainer, this); this.sashY = new Sash(this.workbenchContainer, this, { orientation: Orientation.HORIZONTAL }); - // State scheduler - this.saveStateScheduler = this._register(new RunOnceScheduler(() => this.saveState(), 800)); - this.registerListeners(); } @@ -121,10 +116,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr private registerListeners(): void { this._register(this.themeService.onThemeChange(_ => this.layout())); this._register(this.parts.editor.onDidSizeConstraintsChange(() => this.onDidEditorSizeConstraintsChange())); - this._register(this.storageService.onWillSaveState(() => { - this.saveStateScheduler.dispose(); - this.saveState(); - })); this.registerSashListeners(); } @@ -270,7 +261,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr let sidebarPosition = this.partService.getSideBarPosition(); let isSidebarVisible = this.partService.isVisible(Parts.SIDEBAR_PART); let newSashWidth = (sidebarPosition === Position.LEFT) ? startSidebarWidth + e.currentX - startX : startSidebarWidth - e.currentX + startX; - let promise: Thenable = Promise.resolve(null); // Sidebar visible if (isSidebarVisible) { @@ -278,7 +268,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr // Automatically hide side bar when a certain threshold is met if (newSashWidth + HIDE_SIDEBAR_WIDTH_THRESHOLD < this.partLayoutInfo.sidebar.minWidth) { let dragCompensation = this.partLayoutInfo.sidebar.minWidth - HIDE_SIDEBAR_WIDTH_THRESHOLD; - promise = this.partService.setSideBarHidden(true); + this.partService.setSideBarHidden(true); startX = (sidebarPosition === Position.LEFT) ? Math.max(this.activitybarWidth, e.currentX - dragCompensation) : Math.min(e.currentX + dragCompensation, this.workbenchSize.width - this.activitybarWidth); this.sidebarWidth = startSidebarWidth; // when restoring sidebar, restore to the sidebar width we started from } @@ -296,12 +286,12 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr (sidebarPosition === Position.RIGHT && startX - e.currentX >= this.partLayoutInfo.sidebar.minWidth)) { startSidebarWidth = this.partLayoutInfo.sidebar.minWidth - (sidebarPosition === Position.LEFT ? e.currentX - startX : startX - e.currentX); this.sidebarWidth = this.partLayoutInfo.sidebar.minWidth; - promise = this.partService.setSideBarHidden(false); + this.partService.setSideBarHidden(false); } } if (doLayout) { - promise.then(() => this.layout({ source: Parts.SIDEBAR_PART })); + this.layout({ source: Parts.SIDEBAR_PART }); } })); @@ -309,7 +299,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr let doLayout = false; let isPanelVisible = this.partService.isVisible(Parts.PANEL_PART); let newSashHeight = startPanelHeight - (e.currentY - startY); - let promise: Thenable = Promise.resolve(null); // Panel visible if (isPanelVisible) { @@ -317,7 +306,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr // Automatically hide panel when a certain threshold is met if (newSashHeight + HIDE_PANEL_HEIGHT_THRESHOLD < this.partLayoutInfo.panel.minHeight) { let dragCompensation = this.partLayoutInfo.panel.minHeight - HIDE_PANEL_HEIGHT_THRESHOLD; - promise = this.partService.setPanelHidden(true); + this.partService.setPanelHidden(true); startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.titlebarHeight, e.currentY + dragCompensation); this.panelHeight = startPanelHeight; // when restoring panel, restore to the panel height we started from } @@ -334,12 +323,12 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr if (startY - e.currentY >= this.partLayoutInfo.panel.minHeight) { startPanelHeight = 0; this.panelHeight = this.partLayoutInfo.panel.minHeight; - promise = this.partService.setPanelHidden(false); + this.partService.setPanelHidden(false); } } if (doLayout) { - promise.then(() => this.layout({ source: Parts.PANEL_PART })); + this.layout({ source: Parts.PANEL_PART }); } })); @@ -347,7 +336,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr let doLayout = false; let isPanelVisible = this.partService.isVisible(Parts.PANEL_PART); let newSashWidth = startPanelWidth - (e.currentX - startXTwo); - let promise: Thenable = Promise.resolve(null); // Panel visible if (isPanelVisible) { @@ -355,7 +343,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr // Automatically hide panel when a certain threshold is met if (newSashWidth + HIDE_PANEL_WIDTH_THRESHOLD < this.partLayoutInfo.panel.minWidth) { let dragCompensation = this.partLayoutInfo.panel.minWidth - HIDE_PANEL_WIDTH_THRESHOLD; - promise = this.partService.setPanelHidden(true); + this.partService.setPanelHidden(true); startXTwo = Math.min(this.workbenchSize.width - this.activitybarWidth, e.currentX + dragCompensation); this.panelWidth = startPanelWidth; // when restoring panel, restore to the panel height we started from } @@ -372,31 +360,30 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr if (startXTwo - e.currentX >= this.partLayoutInfo.panel.minWidth) { startPanelWidth = 0; this.panelWidth = this.partLayoutInfo.panel.minWidth; - promise = this.partService.setPanelHidden(false); + this.partService.setPanelHidden(false); } } if (doLayout) { - promise.then(() => this.layout({ source: Parts.PANEL_PART })); + this.layout({ source: Parts.PANEL_PART }); } })); this._register(this.sashXOne.onDidEnd(() => { - this.saveStateScheduler.schedule(); + this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); })); this._register(this.sashY.onDidEnd(() => { - this.saveStateScheduler.schedule(); + this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); })); this._register(this.sashXTwo.onDidEnd(() => { - this.saveStateScheduler.schedule(); + this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); })); this._register(this.sashY.onDidReset(() => { this.panelHeight = this.sidebarHeight * DEFAULT_PANEL_SIZE_COEFFICIENT; - - this.saveStateScheduler.schedule(); + this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); this.layout(); })); @@ -405,22 +392,21 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr let activeViewlet = this.viewletService.getActiveViewlet(); let optimalWidth = activeViewlet && activeViewlet.getOptimalWidth(); this.sidebarWidth = Math.max(optimalWidth, DEFAULT_SIDEBAR_PART_WIDTH); + this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); - this.saveStateScheduler.schedule(); - - this.partService.setSideBarHidden(false).then(() => this.layout()); + this.partService.setSideBarHidden(false); + this.layout(); })); this._register(this.sashXTwo.onDidReset(() => { this.panelWidth = (this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth) * DEFAULT_PANEL_SIZE_COEFFICIENT; - - this.saveStateScheduler.schedule(); + this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); this.layout(); })); } - layout(options?: ILayoutOptions): void { // + layout(options?: ILayoutOptions): void { this.workbenchSize = getClientArea(this.parent); const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART); @@ -489,6 +475,8 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr } } + this.storageService.store(WorkbenchLayout.panelSizeBeforeMaximizedKey, this.panelSizeBeforeMaximized, StorageScope.GLOBAL); + const panelDimension = new Dimension(panelWidth, panelHeight); // Editor @@ -543,13 +531,16 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr if (!isSidebarHidden) { this.sidebarWidth = sidebarSize.width; + this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); } if (!isPanelHidden) { if (panelPosition === Position.BOTTOM) { this.panelHeight = panelDimension.height; + this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); } else { this.panelWidth = panelDimension.width; + this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); } } @@ -557,6 +548,20 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr position(this.workbenchContainer, 0, 0, 0, 0, 'relative'); size(this.workbenchContainer, this.workbenchSize.width, this.workbenchSize.height); + // Bug on Chrome: Sometimes Chrome wants to scroll the workbench container on layout changes. The fix is to reset scrolling in this case. + // uses set time to ensure this happens in th next frame (RAF will be at the end of this JS time slice and we don't want that) + setTimeout(() => { + perf.mark('willCheckAndFixWorkbenchLayout'); + const workbenchContainer = this.workbenchContainer; + if (workbenchContainer.scrollTop > 0) { + workbenchContainer.scrollTop = 0; + } + if (workbenchContainer.scrollLeft > 0) { + workbenchContainer.scrollLeft = 0; + } + perf.mark('didCheckAndFixWorkbenchLayout'); + }); + // Title Part const titleContainer = this.parts.titlebar.getContainer(); if (isTitlebarHidden) { @@ -655,9 +660,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr // Propagate to Context View this.contextViewService.layout(); - - // Schedule save state - this.saveStateScheduler.schedule(); } getVerticalSashTop(sash: Sash): number { @@ -759,13 +761,4 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr this.layout(); } } - - private saveState(): void { - this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); - - this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); - this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); - - this.storageService.store(WorkbenchLayout.panelSizeBeforeMaximizedKey, this.panelSizeBeforeMaximized, StorageScope.GLOBAL); - } } diff --git a/src/vs/workbench/browser/panel.ts b/src/vs/workbench/browser/panel.ts index b44595941cb..aa6b2dad501 100644 --- a/src/vs/workbench/browser/panel.ts +++ b/src/vs/workbench/browser/panel.ts @@ -77,10 +77,12 @@ export abstract class TogglePanelAction extends Action { run(): Thenable { if (this.isPanelFocused()) { - return this.partService.setPanelHidden(true); + this.partService.setPanelHidden(true); + } else { + this.panelService.openPanel(this.panelId, true); } - return this.panelService.openPanel(this.panelId, true); + return Promise.resolve(null); } private isPanelActive(): boolean { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 8df90240983..35301761967 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -62,7 +62,8 @@ export class ViewletActivityAction extends ActivityAction { // Hide sidebar if selected viewlet already visible if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.activity.id) { this.logAction('hide'); - return this.partService.setSideBarHidden(true); + this.partService.setSideBarHidden(true); + return Promise.resolve(null); } this.logAction('show'); @@ -96,7 +97,8 @@ export class ToggleViewletAction extends Action { // Hide sidebar if selected viewlet already visible if (sideBarVisible && activeViewlet && activeViewlet.getId() === this._viewlet.id) { - return this.partService.setSideBarHidden(true); + this.partService.setSideBarHidden(true); + return Promise.resolve(null); } return this.viewletService.openViewlet(this._viewlet.id, true); @@ -158,7 +160,7 @@ export class GlobalActivityActionItem extends ActivityActionItem { this.contextMenuService.showContextMenu({ getAnchor: () => location, - getActions: () => Promise.resolve(actions), + getActions: () => actions, onHide: () => dispose(actions) }); } @@ -194,7 +196,7 @@ export class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinne } } -class SwitchSidebarViewAction extends Action { +class SwitchSideBarViewAction extends Action { constructor( id: string, @@ -223,10 +225,10 @@ class SwitchSidebarViewAction extends Action { } } -export class PreviousSidebarViewAction extends SwitchSidebarViewAction { +export class PreviousSideBarViewAction extends SwitchSideBarViewAction { - static readonly ID = 'workbench.action.previousSidebarView'; - static LABEL = nls.localize('previousSidebarView', 'Previous Sidebar View'); + static readonly ID = 'workbench.action.previousSideBarView'; + static LABEL = nls.localize('previousSideBarView', 'Previous Side Bar View'); constructor( id: string, @@ -242,10 +244,10 @@ export class PreviousSidebarViewAction extends SwitchSidebarViewAction { } } -export class NextSidebarViewAction extends SwitchSidebarViewAction { +export class NextSideBarViewAction extends SwitchSideBarViewAction { - static readonly ID = 'workbench.action.nextSidebarView'; - static LABEL = nls.localize('nextSidebarView', 'Next Sidebar View'); + static readonly ID = 'workbench.action.nextSideBarView'; + static LABEL = nls.localize('nextSideBarView', 'Next Side Bar View'); constructor( id: string, @@ -262,8 +264,8 @@ export class NextSidebarViewAction extends SwitchSidebarViewAction { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(PreviousSidebarViewAction, PreviousSidebarViewAction.ID, PreviousSidebarViewAction.LABEL), 'View: Open Previous Sidebar View', nls.localize('view', "View")); -registry.registerWorkbenchAction(new SyncActionDescriptor(NextSidebarViewAction, NextSidebarViewAction.ID, NextSidebarViewAction.LABEL), 'View: Open Next Sidebar View', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(PreviousSideBarViewAction, PreviousSideBarViewAction.ID, PreviousSideBarViewAction.LABEL), 'View: Open Previous Side Bar View', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(NextSideBarViewAction, NextSideBarViewAction.ID, NextSideBarViewAction.LABEL), 'View: Open Next Side Bar View', nls.localize('view', "View")); registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 9b868aedfd6..3b9768daa13 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -265,7 +265,6 @@ export class ActivitybarPart extends Part { this.enableCompositeActions(viewlet); const activeViewlet = this.viewletService.getActiveViewlet(); if (activeViewlet && activeViewlet.getId() === viewlet.id) { - this.compositeBar.pin(viewlet.id); this.compositeBar.activateComposite(viewlet.id); } } diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 93f55214716..58ea1049732 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -35,7 +35,7 @@ export interface ICompositeBarOptions { getContextMenuActions: () => Action[]; openComposite: (compositeId: string) => TPromise; getDefaultCompositeId: () => string; - hidePart: () => TPromise; + hidePart: () => void; } export class CompositeBar extends Widget implements ICompositeBar { @@ -67,7 +67,7 @@ export class CompositeBar extends Widget implements ICompositeBar { } getComposites(): ICompositeBarItem[] { - return this.model.items; + return [...this.model.items]; } getPinnedComposites(): ICompositeBarItem[] { @@ -401,7 +401,7 @@ export class CompositeBar extends Widget implements ICompositeBar { const event = new StandardMouseEvent(e); this.contextMenuService.showContextMenu({ getAnchor: () => { return { x: event.posx, y: event.posy }; }, - getActions: () => Promise.resolve(this.getContextMenuActions()) + getActions: () => this.getContextMenuActions() }); } diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index 36dcbd54c80..a694d8971d3 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -250,10 +250,14 @@ export class ActivityActionItem extends BaseActionItem { if (badge instanceof NumberBadge) { if (badge.number) { let number = badge.number.toString(); - if (badge.number > 9999) { - number = nls.localize('largeNumberBadge', '10k+'); - } else if (badge.number > 999) { - number = number.charAt(0) + 'k'; + if (badge.number > 999) { + const noOfThousands = badge.number / 1000; + const floor = Math.floor(noOfThousands); + if (noOfThousands > floor) { + number = nls.localize('largeNumberBadge1', '{0}k+', floor); + } else { + number = nls.localize('largeNumberBadge2', '{0}k', noOfThousands); + } } this.badgeContent.textContent = number; dom.show(this.badge); @@ -369,7 +373,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem { this.contextMenuService.showContextMenu({ getAnchor: () => this.element, - getActions: () => Promise.resolve(this.actions), + getActions: () => this.actions, onHide: () => dispose(this.actions) }); } @@ -594,7 +598,7 @@ export class CompositeActionItem extends ActivityActionItem { this.contextMenuService.showContextMenu({ getAnchor: () => anchor, getActionsContext: () => this.activity.id, - getActions: () => Promise.resolve(actions) + getActions: () => actions }); } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index e41c2c1c501..91e691fc547 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -6,11 +6,9 @@ import 'vs/css!./media/compositepart'; import * as nls from 'vs/nls'; import { defaultGenerator } from 'vs/base/common/idGenerator'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { Emitter } from 'vs/base/common/event'; -import * as types from 'vs/base/common/types'; import * as errors from 'vs/base/common/errors'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -49,7 +47,7 @@ export interface ICompositeTitleLabel { export abstract class CompositePart extends Part { - protected _onDidCompositeOpen = this._register(new Emitter()); + protected _onDidCompositeOpen = this._register(new Emitter<{ composite: IComposite, focus: boolean }>()); protected _onDidCompositeClose = this._register(new Emitter()); protected toolBar: ToolBar; @@ -96,7 +94,7 @@ export abstract class CompositePart extends Part { this.lastActiveCompositeId = storageService.get(activeCompositeSettingsKey, StorageScope.WORKSPACE, this.defaultCompositeId); } - protected openComposite(id: string, focus?: boolean): TPromise { + protected openComposite(id: string, focus?: boolean): Composite { // Check if composite already visible and just focus in that case if (this.activeComposite && this.activeComposite.getId() === id) { if (focus) { @@ -104,66 +102,57 @@ export abstract class CompositePart extends Part { } // Fullfill promise with composite that is being opened - return Promise.resolve(this.activeComposite); + return this.activeComposite; } // Open return this.doOpenComposite(id, focus); } - private doOpenComposite(id: string, focus?: boolean): TPromise { + private doOpenComposite(id: string, focus?: boolean): Composite { // Use a generated token to avoid race conditions from long running promises const currentCompositeOpenToken = defaultGenerator.nextId(); this.currentCompositeOpenToken = currentCompositeOpenToken; // Hide current - let hidePromise: TPromise; if (this.activeComposite) { - hidePromise = this.hideActiveComposite(); - } else { - hidePromise = Promise.resolve(null); + this.hideActiveComposite(); } - return hidePromise.then(() => { + // Update Title + this.updateTitle(id); - // Update Title - this.updateTitle(id); + // Create composite + const composite = this.createComposite(id, true); - // Create composite - const composite = this.createComposite(id, true); + // Check if another composite opened meanwhile and return in that case + if ((this.currentCompositeOpenToken !== currentCompositeOpenToken) || (this.activeComposite && this.activeComposite.getId() !== composite.getId())) { + return undefined; + } - // Check if another composite opened meanwhile and return in that case - if ((this.currentCompositeOpenToken !== currentCompositeOpenToken) || (this.activeComposite && this.activeComposite.getId() !== composite.getId())) { - return Promise.resolve(null); - } - - // Check if composite already visible and just focus in that case - if (this.activeComposite && this.activeComposite.getId() === composite.getId()) { - if (focus) { - composite.focus(); - } - - // Fullfill promise with composite that is being opened - return Promise.resolve(composite); - } - - // Show Composite and Focus - return this.showComposite(composite).then(() => { - if (focus) { - composite.focus(); - } - - // Fullfill promise with composite that is being opened - return composite; - }); - }).then(composite => { - if (composite) { - this._onDidCompositeOpen.fire(composite); + // Check if composite already visible and just focus in that case + if (this.activeComposite && this.activeComposite.getId() === composite.getId()) { + if (focus) { + composite.focus(); } + this._onDidCompositeOpen.fire({ composite, focus }); return composite; - }); + } + + // Show Composite and Focus + this.showComposite(composite); + if (focus) { + composite.focus(); + } + + // Return with the composite that is being opened + if (composite) { + this._onDidCompositeOpen.fire({ composite, focus }); + } + + return composite; } protected createComposite(id: string, isActive?: boolean): Composite { @@ -196,7 +185,7 @@ export abstract class CompositePart extends Part { throw new Error(strings.format('Unable to find composite with id {0}', id)); } - protected showComposite(composite: Composite): TPromise { + protected showComposite(composite: Composite): void { // Remember Composite this.activeComposite = composite; @@ -212,8 +201,6 @@ export abstract class CompositePart extends Part { // Remember this.lastActiveCompositeId = this.activeComposite.getId(); - let createCompositePromise: TPromise; - // Composites created for the first time let compositeContainer = this.mapCompositeToCompositeContainer[composite.getId()]; if (!compositeContainer) { @@ -223,93 +210,83 @@ export abstract class CompositePart extends Part { addClasses(compositeContainer, this.compositeCSSClass); compositeContainer.id = composite.getId(); - createCompositePromise = composite.create(compositeContainer).then(() => { - composite.updateStyles(); - }); + composite.create(compositeContainer); + composite.updateStyles(); // Remember composite container this.mapCompositeToCompositeContainer[composite.getId()] = compositeContainer; } - // Composite already exists but is hidden - else { - createCompositePromise = Promise.resolve(null); - } - // Report progress for slow loading composites (but only if we did not create the composites before already) const progressService = this.mapProgressServiceToComposite[composite.getId()]; if (progressService && !compositeContainer) { - this.mapProgressServiceToComposite[composite.getId()].showWhile(createCompositePromise, this.partService.isCreated() ? 800 : 3200 /* less ugly initial startup */); + this.mapProgressServiceToComposite[composite.getId()].showWhile(Promise.resolve(), this.partService.isCreated() ? 800 : 3200 /* less ugly initial startup */); } // Fill Content and Actions - return createCompositePromise.then(() => { + // Make sure that the user meanwhile did not open another composite or closed the part containing the composite + if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) { + return void 0; + } - // Make sure that the user meanwhile did not open another composite or closed the part containing the composite - if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) { - return void 0; + // Take Composite on-DOM and show + this.getContentArea().appendChild(compositeContainer); + show(compositeContainer); + + // Setup action runner + this.toolBar.actionRunner = composite.getActionRunner(); + + // Update title with composite title if it differs from descriptor + const descriptor = this.registry.getComposite(composite.getId()); + if (descriptor && descriptor.name !== composite.getTitle()) { + this.updateTitle(composite.getId(), composite.getTitle()); + } + + // Handle Composite Actions + let actionsBinding = this.mapActionsBindingToComposite[composite.getId()]; + if (!actionsBinding) { + actionsBinding = this.collectCompositeActions(composite); + this.mapActionsBindingToComposite[composite.getId()] = actionsBinding; + } + actionsBinding(); + + if (this.telemetryActionsListener) { + this.telemetryActionsListener.dispose(); + this.telemetryActionsListener = null; + } + + // Action Run Handling + this.telemetryActionsListener = this.toolBar.actionRunner.onDidRun(e => { + + // Check for Error + if (e.error && !errors.isPromiseCanceledError(e.error)) { + this.notificationService.error(e.error); } - // Take Composite on-DOM and show - this.getContentArea().appendChild(compositeContainer); - show(compositeContainer); - - // Setup action runner - this.toolBar.actionRunner = composite.getActionRunner(); - - // Update title with composite title if it differs from descriptor - const descriptor = this.registry.getComposite(composite.getId()); - if (descriptor && descriptor.name !== composite.getTitle()) { - this.updateTitle(composite.getId(), composite.getTitle()); + // Log in telemetry + if (this.telemetryService) { + /* __GDPR__ + "workbenchActionExecuted" : { + "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: this.nameForTelemetry }); } + }); - // Handle Composite Actions - let actionsBinding = this.mapActionsBindingToComposite[composite.getId()]; - if (!actionsBinding) { - actionsBinding = this.collectCompositeActions(composite); - this.mapActionsBindingToComposite[composite.getId()] = actionsBinding; - } - actionsBinding(); + // Indicate to composite that it is now visible + composite.setVisible(true); - if (this.telemetryActionsListener) { - this.telemetryActionsListener.dispose(); - this.telemetryActionsListener = null; - } + // Make sure that the user meanwhile did not open another composite or closed the part containing the composite + if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) { + return; + } - // Action Run Handling - this.telemetryActionsListener = this.toolBar.actionRunner.onDidRun(e => { - - // Check for Error - if (e.error && !errors.isPromiseCanceledError(e.error)) { - this.notificationService.error(e.error); - } - - // Log in telemetry - if (this.telemetryService) { - /* __GDPR__ - "workbenchActionExecuted" : { - "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: this.nameForTelemetry }); - } - }); - - // Indicate to composite that it is now visible - return composite.setVisible(true).then(() => { - - // Make sure that the user meanwhile did not open another composite or closed the part containing the composite - if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) { - return; - } - - // Make sure the composite is layed out - if (this.contentAreaSize) { - composite.layout(this.contentAreaSize); - } - }); - }, error => this.onError(error)); + // Make sure the composite is layed out + if (this.contentAreaSize) { + composite.layout(this.contentAreaSize); + } } protected onTitleAreaUpdate(compositeId: string): void { @@ -371,9 +348,9 @@ export abstract class CompositePart extends Part { return this.lastActiveCompositeId; } - protected hideActiveComposite(): TPromise { + protected hideActiveComposite(): Composite { if (!this.activeComposite) { - return Promise.resolve(null); // Nothing to do + return undefined; // Nothing to do } const composite = this.activeComposite; @@ -382,21 +359,20 @@ export abstract class CompositePart extends Part { const compositeContainer = this.mapCompositeToCompositeContainer[composite.getId()]; // Indicate to Composite - return composite.setVisible(false).then(() => { + composite.setVisible(false); - // Take Container Off-DOM and hide - compositeContainer.remove(); - hide(compositeContainer); + // Take Container Off-DOM and hide + compositeContainer.remove(); + hide(compositeContainer); - // Clear any running Progress - this.progressBar.stop().hide(); + // Clear any running Progress + this.progressBar.stop().hide(); - // Empty Actions - this.toolBar.setActions([])(); - this._onDidCompositeClose.fire(composite); + // Empty Actions + this.toolBar.setActions([])(); + this._onDidCompositeClose.fire(composite); - return composite; - }); + return composite; } createTitleArea(parent: HTMLElement): HTMLElement { @@ -465,10 +441,6 @@ export abstract class CompositePart extends Part { return contentContainer; } - private onError(error: any): void { - this.notificationService.error(types.isString(error) ? new Error(error) : error); - } - getProgressIndicator(id: string): IProgressService { return this.mapProgressServiceToComposite[id]; } diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index 9a811cbe56b..91ea4062660 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -105,15 +105,11 @@ export abstract class BaseEditor extends Panel implements IEditor { this._options = options; } - create(parent: HTMLElement): void; // create is sync for editors - create(parent: HTMLElement): Promise; - create(parent: HTMLElement): Promise { - const res = super.create(parent); + create(parent: HTMLElement): void { + super.create(parent); // Create Editor this.createEditor(parent); - - return res; } /** @@ -121,15 +117,10 @@ export abstract class BaseEditor extends Panel implements IEditor { */ protected abstract createEditor(parent: HTMLElement): void; - setVisible(visible: boolean, group?: IEditorGroup): void; // setVisible is sync for editors - setVisible(visible: boolean, group?: IEditorGroup): Promise; - setVisible(visible: boolean, group?: IEditorGroup): Promise { - const promise = super.setVisible(visible); - + setVisible(visible: boolean, group?: IEditorGroup): void { + super.setVisible(visible); // Propagate to Editor this.setEditorVisible(visible, group); - - return promise; } /** @@ -308,4 +299,4 @@ export class EditorMemento implements IEditorMemento { }); }); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index 875b26ffe2f..e5303fad2a9 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -70,6 +70,7 @@ export abstract class BreadcrumbsConfig { static UseQuickPick = BreadcrumbsConfig._stub('breadcrumbs.useQuickPick'); static FilePath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.filePath'); static SymbolPath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.symbolPath'); + static SymbolSortOrder = BreadcrumbsConfig._stub<'position' | 'name' | 'type'>('breadcrumbs.symbolSortOrder'); static FilterOnType = BreadcrumbsConfig._stub('breadcrumbs.filterOnType'); static FileExcludes = BreadcrumbsConfig._stub('files.exclude'); @@ -142,6 +143,17 @@ Registry.as(Extensions.Configuration).registerConfigurat localize('symbolpath.last', "Only show the current symbol in the breadcrumbs view."), ] }, + 'breadcrumbs.symbolSortOrder': { + description: localize('symbolSortOrder', "Controls how symbols are sorted in the breadcrumbs outline view."), + type: 'string', + default: 'position', + enum: ['position', 'name', 'type'], + enumDescriptions: [ + localize('symbolSortOrder.position', "Show symbol outline in file position order."), + localize('symbolSortOrder.name', "Show symbol outline in alphabetical order."), + localize('symbolSortOrder.type', "Show symbol outline in symbol type order."), + ] + }, // 'breadcrumbs.filterOnType': { // description: localize('filterOnType', "Controls whether the breadcrumb picker filters or highlights when typing."), // type: 'boolean', diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index cfe0453ff28..ab096bf28c5 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -39,12 +39,12 @@ import { FileLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { BreadcrumbsPicker, createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; -import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { SideBySideEditorInput } from 'vs/workbench/common/editor'; import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; class Item extends BreadcrumbsItem { @@ -152,7 +152,7 @@ export class BreadcrumbsControl { constructor( container: HTMLElement, private readonly _options: IBreadcrumbsControlOptions, - private readonly _editorGroup: EditorGroupView, + private readonly _editorGroup: IEditorGroupView, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IContextViewService private readonly _contextViewService: IContextViewService, @IEditorService private readonly _editorService: IEditorService, diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 4000944371a..23ce7a08111 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -18,7 +18,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; import 'vs/css!./media/breadcrumbscontrol'; import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; -import { OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree'; +import { OutlineDataSource, OutlineItemComparator, OutlineRenderer, OutlineItemCompareType } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; @@ -52,6 +52,7 @@ export abstract class BreadcrumbsPicker { protected readonly _treeContainer: HTMLDivElement; protected readonly _tree: HighlightingWorkbenchTree; protected readonly _focus: dom.IFocusTracker; + protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>; private _layoutInfo: ILayoutInfo; private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>(); @@ -88,14 +89,16 @@ export abstract class BreadcrumbsPicker { this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`; this._domNode.appendChild(this._treeContainer); + this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService); + const filterConfig = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService); this._disposables.push(filterConfig); - const treeConifg = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined, highlighter: undefined }); + const treeConfig = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined, highlighter: undefined }); this._tree = this._instantiationService.createInstance( HighlightingWorkbenchTree, this._treeContainer, - treeConifg, + treeConfig, { useShadows: false, filterOnType: filterConfig.getValue(), showTwistie: false, twistiePixels: 12 }, { placeholder: localize('placeholder', "Find") } ); @@ -144,6 +147,7 @@ export abstract class BreadcrumbsPicker { this._onDidPickElement.dispose(); this._tree.dispose(); this._focus.dispose(); + this._symbolSortOrder.dispose(); } setInput(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void { @@ -464,7 +468,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration { config.dataSource = this._instantiationService.createInstance(OutlineDataSource); config.renderer = this._instantiationService.createInstance(OutlineRenderer); - config.sorter = new OutlineItemComparator(); + config.sorter = new OutlineItemComparator(this._getOutlineItemComparator()); config.highlighter = new OutlineHighlighter(); return config; } @@ -477,6 +481,18 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { return element; } } + + private _getOutlineItemComparator(): OutlineItemCompareType { + switch (this._symbolSortOrder.getValue()) { + case 'name': + return OutlineItemCompareType.ByName; + case 'type': + return OutlineItemCompareType.ByKind; + case 'position': + default: + return OutlineItemCompareType.ByPosition; + } + } } //#endregion diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index eaa16b8caa9..3ab4eb91b15 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -333,7 +333,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(JoinTwoGroupsAction, J registry.registerWorkbenchAction(new SyncActionDescriptor(JoinAllGroupsAction, JoinAllGroupsAction.ID, JoinAllGroupsAction.LABEL), 'View: Join Editors of All Groups', category); registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBetweenGroupsAction, NavigateBetweenGroupsAction.ID, NavigateBetweenGroupsAction.LABEL), 'View: Navigate Between Editor Groups', category); registry.registerWorkbenchAction(new SyncActionDescriptor(ResetGroupSizesAction, ResetGroupSizesAction.ID, ResetGroupSizesAction.LABEL), 'View: Reset Editor Group Sizes', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Sidebar', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Side Bar', category); registry.registerWorkbenchAction(new SyncActionDescriptor(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Maximize Editor Group', category); registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorLeftInGroupAction, MoveEditorLeftInGroupAction.ID, MoveEditorLeftInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', category); registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorRightInGroupAction, MoveEditorRightInGroupAction.ID, MoveEditorRightInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', category); diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index ed046115ec1..bcf9cf0dcdf 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -897,8 +897,7 @@ export class MaximizeGroupAction extends Action { run(): Thenable { if (this.editorService.activeEditor) { this.editorGroupService.arrangeGroups(GroupsArrangement.MINIMIZE_OTHERS); - - return this.partService.setSideBarHidden(true); + this.partService.setSideBarHidden(true); } return Promise.resolve(false); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 2fb789ef7ab..2e19e4fc266 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -308,7 +308,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Show it this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => TPromise.as(actions), + getActions: () => actions, onHide: () => this.focus() }); } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 32d0ffcde89..e94317b65ae 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -799,6 +799,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor centerLayout(active: boolean): void { this.centeredLayoutWidget.activate(active); + this._activeGroup.focus(); } isLayoutCentered(): boolean { @@ -876,7 +877,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Create new const groupViews: IEditorGroupView[] = []; const gridWidget = SerializableGrid.deserialize(serializedGrid, { - fromJSON: (serializedEditorGroup: ISerializedEditorGroup) => { + fromJSON: (serializedEditorGroup: ISerializedEditorGroup | null) => { let groupView: IEditorGroupView; if (reuseGroupViews.length > 0) { groupView = reuseGroupViews.shift(); diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 1545851c286..a2f95c7b9b4 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -15,7 +15,6 @@ import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { Action } from 'vs/base/common/actions'; import { language, LANGUAGE_DEFAULT, AccessibilitySupport } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; -import { IMode } from 'vs/editor/common/modes'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput } from 'vs/workbench/common/editor'; import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -32,7 +31,7 @@ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { SUPPORTED_ENCODINGS, IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -977,16 +976,16 @@ export class ChangeModeAction extends Action { } // Find mode - let mode: Promise; + let languageSelection: ILanguageSelection; if (pick === autoDetectMode) { - mode = this.modeService.getOrCreateModeByFilepathOrFirstLine(toResource(activeEditor, { supportSideBySide: true }).fsPath, textModel.getLineContent(1)); + languageSelection = this.modeService.createByFilepathOrFirstLine(toResource(activeEditor, { supportSideBySide: true }).fsPath, textModel.getLineContent(1)); } else { - mode = this.modeService.getOrCreateModeByLanguageName(pick.label); + languageSelection = this.modeService.createByLanguageName(pick.label); } // Change mode models.forEach(textModel => { - this.modelService.setMode(textModel, mode); + this.modelService.setMode(textModel, languageSelection); }); }); } @@ -1237,13 +1236,14 @@ export class ChangeEncodingAction extends Action { return { id: key, label: SUPPORTED_ENCODINGS[key].labelLong, description: key }; }); + const items = picks.slice() as IQuickPickItem[]; + // If we have a guessed encoding, show it first unless it matches the configured encoding if (guessedEncoding && configuredEncoding !== guessedEncoding && SUPPORTED_ENCODINGS[guessedEncoding]) { picks.unshift({ type: 'separator' }); picks.unshift({ id: guessedEncoding, label: SUPPORTED_ENCODINGS[guessedEncoding].labelLong, description: nls.localize('guessedEncoding', "Guessed from content") }); } - const items = picks.filter(p => p.type !== 'separator') as IQuickPickItem[]; return this.quickInputService.pick(picks, { placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"), activeItem: items[typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : -1] diff --git a/src/vs/workbench/browser/parts/editor/editorWidgets.ts b/src/vs/workbench/browser/parts/editor/editorWidgets.ts index ef3a854481b..adf02391dfb 100644 --- a/src/vs/workbench/browser/parts/editor/editorWidgets.ts +++ b/src/vs/workbench/browser/parts/editor/editorWidgets.ts @@ -64,8 +64,15 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget { this._domNode = $('.floating-click-widget'); this._register(attachStylerCallback(this.themeService, { buttonBackground, buttonForeground, editorBackground, editorForeground, contrastBorder }, colors => { - this._domNode.style.backgroundColor = colors.buttonBackground ? colors.buttonBackground.toString() : colors.editorBackground.toString(); - this._domNode.style.color = colors.buttonForeground ? colors.buttonForeground.toString() : colors.editorForeground.toString(); + const backgroundColor = colors.buttonBackground ? colors.buttonBackground : colors.editorBackground; + if (backgroundColor) { + this._domNode.style.backgroundColor = backgroundColor.toString(); + } + + const foregroundColor = colors.buttonForeground ? colors.buttonForeground : colors.editorForeground; + if (foregroundColor) { + this._domNode.style.color = foregroundColor.toString(); + } const borderColor = colors.contrastBorder ? colors.contrastBorder.toString() : null; this._domNode.style.borderWidth = borderColor ? '1px' : null; @@ -149,7 +156,12 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit private createOpenWorkspaceWidgetRenderer(): void { if (!this.openWorkspaceButton) { this.openWorkspaceButton = this.instantiationService.createInstance(FloatingClickWidget, this.editor, localize('openWorkspace', "Open Workspace"), null); - this._register(this.openWorkspaceButton.onClick(() => this.windowService.openWindow([this.editor.getModel().uri]))); + this._register(this.openWorkspaceButton.onClick(() => { + const model = this.editor.getModel(); + if (model) { + this.windowService.openWindow([model.uri]); + } + })); this.openWorkspaceButton.render(); } diff --git a/src/vs/workbench/browser/parts/editor/resourceViewer.ts b/src/vs/workbench/browser/parts/editor/resourceViewer.ts index 73805e8ea9e..57bd8008a12 100644 --- a/src/vs/workbench/browser/parts/editor/resourceViewer.ts +++ b/src/vs/workbench/browser/parts/editor/resourceViewer.ts @@ -284,7 +284,7 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem { DOM.addDisposableListener(this.statusBarItem, DOM.EventType.CLICK, () => { this.contextMenuService.showContextMenu({ getAnchor: () => container, - getActions: () => Promise.resolve(this.zoomActions) + getActions: () => this.zoomActions }); }); } @@ -414,7 +414,7 @@ class InlineImageView { DOM.removeClass(image, 'scale-to-fit'); image.style.minWidth = `${(image.naturalWidth * scale)}px`; - image.style.widows = `${(image.naturalWidth * scale)}px`; + image.style.width = `${(image.naturalWidth * scale)}px`; const newWidth = image.width; const scaleFactor = (newWidth - oldWidth) / oldWidth; @@ -438,7 +438,7 @@ class InlineImageView { updateScale(scale); } - disposables.push(DOM.addDisposableListener(container, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + disposables.push(DOM.addDisposableListener(window, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { if (!image) { return; } @@ -451,7 +451,7 @@ class InlineImageView { } })); - disposables.push(DOM.addDisposableListener(container, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { + disposables.push(DOM.addDisposableListener(window, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { if (!image) { return; } diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 210381d9a8a..0550722b1ca 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -288,7 +288,7 @@ export abstract class TitleControl extends Themable { // Show it this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(actions), + getActions: () => actions, getActionsContext: () => ({ groupId: this.group.id, editorIndex: this.group.getIndexOfEditor(editor) } as IEditorCommandsContext), getKeyBinding: (action) => this.getKeybinding(action), onHide: () => { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index ea830ef9c51..76dc13247ad 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -86,7 +86,7 @@ export class NotificationsList extends Themable { this._register((this.list.onContextMenu(e => { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => Promise.resolve([copyAction]), + getActions: () => [copyAction], getActionsContext: () => e.element, actionRunner }); diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 6132c599e14..bd3e64742eb 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -44,9 +44,9 @@ export class NotificationsToasts extends Themable { private static PURGE_TIMEOUT: { [severity: number]: number } = (() => { const intervals = Object.create(null); - intervals[Severity.Info] = 10000; - intervals[Severity.Warning] = 12000; - intervals[Severity.Error] = 15000; + intervals[Severity.Info] = 15000; + intervals[Severity.Warning] = 18000; + intervals[Severity.Error] = 20000; return intervals; })(); @@ -209,15 +209,29 @@ export class NotificationsToasts extends Themable { disposables.push(addDisposableListener(notificationToastContainer, EventType.MOUSE_OVER, () => isMouseOverToast = true)); disposables.push(addDisposableListener(notificationToastContainer, EventType.MOUSE_OUT, () => isMouseOverToast = false)); - // Install Timers - let timeoutHandle: any; + // Install Timers to Purge Notification + let purgeTimeoutHandle: any; const hideAfterTimeout = () => { - timeoutHandle = setTimeout(() => { - if ( - item.sticky || // never hide sticky notifications - notificationList.hasFocus() || // never hide notifications with focus - isMouseOverToast || // never hide notifications under mouse - !this.windowHasFocus // never hide when window has no focus + purgeTimeoutHandle = setTimeout(() => { + + // If the notification is sticky or prompting and the window does not have + // focus, we wait for the window to gain focus again before triggering + // the timeout again. This prevents an issue where focussing the window + // could immediately hide the notification because the timeout was triggered + // again. + if ((item.sticky || item.hasPrompt()) && !this.windowHasFocus) { + disposables.push(this.windowService.onDidChangeFocus(focus => { + if (focus) { + hideAfterTimeout(); + } + })); + } + + // Otherwise... + else if ( + item.sticky || // never hide sticky notifications + notificationList.hasFocus() || // never hide notifications with focus + isMouseOverToast // never hide notifications under mouse ) { hideAfterTimeout(); } else { @@ -228,7 +242,7 @@ export class NotificationsToasts extends Themable { hideAfterTimeout(); - disposables.push(toDisposable(() => clearTimeout(timeoutHandle))); + disposables.push(toDisposable(() => clearTimeout(purgeTimeoutHandle))); } private removeToast(item: INotificationViewItem): void { diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 7433c1b1560..f9fa7fd846b 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -30,7 +30,8 @@ export class ClosePanelAction extends Action { } run(): Thenable { - return this.partService.setPanelHidden(true); + this.partService.setPanelHidden(true); + return Promise.resolve(null); } } @@ -48,7 +49,8 @@ export class TogglePanelAction extends Action { } run(): Thenable { - return this.partService.setPanelHidden(this.partService.isVisible(Parts.PANEL_PART)); + this.partService.setPanelHidden(this.partService.isVisible(Parts.PANEL_PART)); + return Promise.resolve(null); } } @@ -70,7 +72,8 @@ class FocusPanelAction extends Action { // Show panel if (!this.partService.isVisible(Parts.PANEL_PART)) { - return this.partService.setPanelHidden(false); + this.partService.setPanelHidden(false); + return Promise.resolve(null); } // Focus into active panel @@ -79,7 +82,7 @@ class FocusPanelAction extends Action { panel.focus(); } - return Promise.resolve(true); + return Promise.resolve(null); } } @@ -116,7 +119,8 @@ export class TogglePanelPositionAction extends Action { run(): Thenable { const position = this.partService.getPanelPosition(); - return this.partService.setPanelPosition(position === Position.BOTTOM ? Position.RIGHT : Position.BOTTOM); + this.partService.setPanelPosition(position === Position.BOTTOM ? Position.RIGHT : Position.BOTTOM); + return Promise.resolve(null); } dispose(): void { @@ -153,9 +157,12 @@ export class ToggleMaximizedPanelAction extends Action { } run(): Thenable { - const promise: Thenable = !this.partService.isVisible(Parts.PANEL_PART) ? this.partService.setPanelHidden(false) : Promise.resolve(null); + if (!this.partService.isVisible(Parts.PANEL_PART)) { + this.partService.setPanelHidden(false); + } - return promise.then(() => this.partService.toggleMaximizedPanel()); + this.partService.toggleMaximizedPanel(); + return Promise.resolve(null); } dispose(): void { @@ -175,7 +182,9 @@ export class PanelActivityAction extends ActivityAction { } run(event: any): Thenable { - return this.panelService.openPanel(this.activity.id, true).then(() => this.activate()); + this.panelService.openPanel(this.activity.id, true); + this.activate(); + return Promise.resolve(null); } } @@ -202,7 +211,8 @@ export class SwitchPanelViewAction extends Action { break; } } - return this.panelService.openPanel(targetPanelId, true); + this.panelService.openPanel(targetPanelId, true); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 6b2332052da..fa2ace5f865 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/panelpart'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction } from 'vs/base/common/actions'; -import { Event } from 'vs/base/common/event'; +import { Event, mapEvent } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { IPanel } from 'vs/workbench/common/panel'; @@ -27,13 +26,13 @@ import { CompositeBar } from 'vs/workbench/browser/parts/compositeBar'; import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Dimension } from 'vs/base/browser/dom'; +import { Dimension, trackFocus } from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -const ActivePanleContextId = 'activePanel'; -export const ActivePanelContext = new RawContextKey(ActivePanleContextId, ''); +export const ActivePanelContext = new RawContextKey('activePanel', ''); +export const PanelFocusContext = new RawContextKey('panelFocus', false); export class PanelPart extends CompositePart implements IPanelService { @@ -45,6 +44,7 @@ export class PanelPart extends CompositePart implements IPanelService { _serviceBrand: any; private activePanelContextKey: IContextKey; + private panelFocusContextKey: IContextKey; private blockOpeningPanel: boolean; private compositeBar: CompositeBar; private compositeActions: { [compositeId: string]: { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null); @@ -85,7 +85,7 @@ export class PanelPart extends CompositePart implements IPanelService { icon: false, storageId: PanelPart.PINNED_PANELS, orientation: ActionsOrientation.HORIZONTAL, - openComposite: (compositeId: string) => this.openPanel(compositeId, true), + openComposite: (compositeId: string) => Promise.resolve(this.openPanel(compositeId, true)), getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction, getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction, getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(PanelActivityAction, this.getPanel(compositeId)), @@ -114,18 +114,32 @@ export class PanelPart extends CompositePart implements IPanelService { } this.activePanelContextKey = ActivePanelContext.bindTo(contextKeyService); + this.panelFocusContextKey = PanelFocusContext.bindTo(contextKeyService); this.registerListeners(); } + create(parent: HTMLElement): void { + super.create(parent); + + const focusTracker = trackFocus(parent); + + focusTracker.onDidFocus(() => { + this.panelFocusContextKey.set(true); + }); + focusTracker.onDidBlur(() => { + this.panelFocusContextKey.set(false); + }); + } + private registerListeners(): void { - this._register(this.onDidPanelOpen(this._onDidPanelOpen, this)); + this._register(this.onDidPanelOpen(({ panel }) => this._onDidPanelOpen(panel))); this._register(this.onDidPanelClose(this._onDidPanelClose, this)); this._register(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor))); // Activate panel action on opening of a panel - this._register(this.onDidPanelOpen(panel => { + this._register(this.onDidPanelOpen(({ panel }) => { this.compositeBar.activateComposite(panel.getId()); this.layoutCompositeBar(); // Need to relayout composite bar since different panels have different action bar width })); @@ -146,8 +160,8 @@ export class PanelPart extends CompositePart implements IPanelService { } } - get onDidPanelOpen(): Event { - return this._onDidCompositeOpen.event; + get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean }> { + return mapEvent(this._onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); } get onDidPanelClose(): Event { @@ -165,23 +179,22 @@ export class PanelPart extends CompositePart implements IPanelService { title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder); } - openPanel(id: string, focus?: boolean): TPromise { + openPanel(id: string, focus?: boolean): Panel { if (this.blockOpeningPanel) { - return Promise.resolve(null); // Workaround against a potential race condition + return null; // Workaround against a potential race condition } // First check if panel is hidden and show if so - let promise = TPromise.wrap(null); if (!this.partService.isVisible(Parts.PANEL_PART)) { try { this.blockOpeningPanel = true; - promise = this.partService.setPanelHidden(false); + this.partService.setPanelHidden(false); } finally { this.blockOpeningPanel = false; } } - return promise.then(() => this.openComposite(id, focus)); + return this.openComposite(id, focus); } showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable { @@ -232,8 +245,8 @@ export class PanelPart extends CompositePart implements IPanelService { return this.getLastActiveCompositetId(); } - hideActivePanel(): TPromise { - return this.hideActiveComposite().then(composite => void 0); + hideActivePanel(): void { + this.hideActiveComposite(); } protected createTitleLabel(parent: HTMLElement): ICompositeTitleLabel { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index b08a87ffb8f..99b4fe1d5e7 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -13,7 +13,6 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CancellationToken } from 'vs/base/common/cancellation'; import { QuickInputList } from './quickInputList'; import { QuickInputBox } from './quickInputBox'; @@ -1041,8 +1040,8 @@ export class QuickInputService extends Component implements IQuickInputService { this.updateStyles(); } - pick>(picks: TPromise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): TPromise { - return new TPromise((doResolve, reject) => { + pick>(picks: Promise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): Promise { + return new Promise((doResolve, reject) => { let resolve = (result: any) => { resolve = doResolve; if (options.onKeyMods) { @@ -1117,7 +1116,7 @@ export class QuickInputService extends Component implements IQuickInputService { input.quickNavigate = options.quickNavigate; input.contextKey = options.contextKey; input.busy = true; - TPromise.join([picks, options.activeItem]) + Promise.all([picks, options.activeItem]) .then(([items, _activeItem]) => { activeItem = _activeItem; input.busy = false; @@ -1130,29 +1129,29 @@ export class QuickInputService extends Component implements IQuickInputService { } }); input.show(); - TPromise.wrap(picks).then(null, err => { + Promise.resolve(picks).then(null, err => { reject(err); input.hide(); }); }); } - input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): TPromise { - return new TPromise((resolve, reject) => { + input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { + return new Promise((resolve, reject) => { if (token.isCancellationRequested) { resolve(undefined); return; } const input = this.createInputBox(); - const validateInput = options.validateInput || (() => TPromise.as(undefined)); + const validateInput = options.validateInput || (() => >Promise.resolve(undefined)); const onDidValueChange = debounceEvent(input.onDidChangeValue, (last, cur) => cur, 100); let validationValue = options.value || ''; - let validation = TPromise.wrap(validateInput(validationValue)); + let validation = Promise.resolve(validateInput(validationValue)); const disposables = [ input, onDidValueChange(value => { if (value !== validationValue) { - validation = TPromise.wrap(validateInput(value)); + validation = Promise.resolve(validateInput(value)); validationValue = value; } validation.then(result => { @@ -1164,7 +1163,7 @@ export class QuickInputService extends Component implements IQuickInputService { input.onDidAccept(() => { const value = input.value; if (value !== validationValue) { - validation = TPromise.wrap(validateInput(value)); + validation = Promise.resolve(validateInput(value)); validationValue = value; } validation.then(result => { @@ -1335,17 +1334,17 @@ export class QuickInputService extends Component implements IQuickInputService { accept() { this.onDidAcceptEmitter.fire(); - return TPromise.as(undefined); + return Promise.resolve(undefined); } back() { this.onDidTriggerButtonEmitter.fire(this.backButton); - return TPromise.as(undefined); + return Promise.resolve(undefined); } cancel() { this.hide(); - return TPromise.as(undefined); + return Promise.resolve(undefined); } layout(dimension: dom.Dimension): void { @@ -1411,8 +1410,8 @@ export class BackAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Promise { this.quickInputService.back(); - return TPromise.as(null); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index 21eef9b5085..399829c93db 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -116,7 +116,7 @@ class ListElementRenderer implements IListRenderer element.label, openController: { shouldOpen: () => false }, // Workaround #58124 + setRowLineHeight: false, multipleSelectionSupport: false }) as WorkbenchList; this.list.getHTMLElement().id = id; diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 678a224a115..fa22428e5b0 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/quickopen'; -import { TPromise, ValueCallback } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import * as browser from 'vs/base/browser/browser'; import * as strings from 'vs/base/common/strings'; @@ -54,6 +53,8 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; const HELP_PREFIX = '?'; +type ValueCallback = (value: T | Thenable) => void; + export class QuickOpenController extends Component implements IQuickOpenService { private static readonly MAX_SHORT_RESPONSE_TIME = 500; @@ -73,7 +74,7 @@ export class QuickOpenController extends Component implements IQuickOpenService private lastSubmittedInputValue: string; private quickOpenWidget: QuickOpenWidget; private dimension: Dimension; - private mapResolvedHandlersToPrefix: { [prefix: string]: TPromise; } = Object.create(null); + private mapResolvedHandlersToPrefix: { [prefix: string]: Promise; } = Object.create(null); private mapContextKeyToContext: { [id: string]: IContextKey; } = Object.create(null); private handlerOnOpenCalled: { [prefix: string]: boolean; } = Object.create(null); private promisesToCompleteOnHide: ValueCallback[] = []; @@ -153,12 +154,12 @@ export class QuickOpenController extends Component implements IQuickOpenService } } - show(prefix?: string, options?: IShowOptions): TPromise { + show(prefix?: string, options?: IShowOptions): Promise { let quickNavigateConfiguration = options ? options.quickNavigateConfiguration : void 0; let inputSelection = options ? options.inputSelection : void 0; let autoFocus = options ? options.autoFocus : void 0; - const promiseCompletedOnHide = new TPromise(c => { + const promiseCompletedOnHide = new Promise(c => { this.promisesToCompleteOnHide.push(c); }); @@ -381,7 +382,7 @@ export class QuickOpenController extends Component implements IQuickOpenService return; } - let resultPromise: TPromise; + let resultPromise: Promise; let resultPromiseDone = false; if (handlerDescriptor) { @@ -426,7 +427,7 @@ export class QuickOpenController extends Component implements IQuickOpenService }); } - private handleDefaultHandler(handler: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): TPromise { + private handleDefaultHandler(handler: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise { // Fill in history results if matching and we are configured to search in history let matchingHistoryEntries: QuickOpenEntry[]; @@ -514,7 +515,7 @@ export class QuickOpenController extends Component implements IQuickOpenService } } - private handleSpecificHandler(handlerDescriptor: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): TPromise { + private handleSpecificHandler(handlerDescriptor: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise { return this.resolveHandler(handlerDescriptor).then((resolvedHandler: QuickOpenHandler) => { // Remove handler prefix from search value @@ -585,7 +586,7 @@ export class QuickOpenController extends Component implements IQuickOpenService return mapEntryToPath; } - private resolveHandler(handler: QuickOpenHandlerDescriptor): TPromise { + private resolveHandler(handler: QuickOpenHandlerDescriptor): Promise { let result = this._resolveHandler(handler); const id = handler.getId(); @@ -603,11 +604,11 @@ export class QuickOpenController extends Component implements IQuickOpenService return result.then(null, (error) => { delete this.mapResolvedHandlersToPrefix[id]; - return TPromise.wrapError(new Error(`Unable to instantiate quick open handler ${handler.getId()}: ${JSON.stringify(error)}`)); + return Promise.reject(new Error(`Unable to instantiate quick open handler ${handler.getId()}: ${JSON.stringify(error)}`)); }); } - private _resolveHandler(handler: QuickOpenHandlerDescriptor): TPromise { + private _resolveHandler(handler: QuickOpenHandlerDescriptor): Promise { const id = handler.getId(); // Return Cached @@ -835,7 +836,7 @@ export class RemoveFromEditorHistoryAction extends Action { super(id, label); } - run(): TPromise { + run(): Thenable { interface IHistoryPickEntry extends IQuickPickItem { input: IEditorInput | IResourceInput; } diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index b8e794ab73f..89e8e8156bd 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -21,18 +21,23 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; +import { Event, mapEvent } from 'vs/base/common/event'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER } from 'vs/workbench/common/theme'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Dimension, EventType, addDisposableListener } from 'vs/base/browser/dom'; +import { Dimension, EventType, addDisposableListener, trackFocus } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; + +const SideBarFocusContextId = 'sideBarFocus'; +export const SidebarFocusContext = new RawContextKey(SideBarFocusContextId, false); export class SidebarPart extends CompositePart { static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid'; + private sideBarFocusContextKey: IContextKey; private blockOpeningViewlet: boolean; constructor( @@ -44,7 +49,8 @@ export class SidebarPart extends CompositePart { @IPartService partService: IPartService, @IKeybindingService keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService + @IThemeService themeService: IThemeService, + @IContextKeyService contextKeyService: IContextKeyService, ) { super( notificationService, @@ -64,16 +70,31 @@ export class SidebarPart extends CompositePart { id, { hasTitle: true, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0 } ); + + this.sideBarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService); } get onDidViewletOpen(): Event { - return this._onDidCompositeOpen.event as Event; + return mapEvent(this._onDidCompositeOpen.event, compositeEvent => compositeEvent.composite); } get onDidViewletClose(): Event { return this._onDidCompositeClose.event as Event; } + create(parent: HTMLElement): void { + super.create(parent); + + const focusTracker = trackFocus(parent); + + focusTracker.onDidFocus(() => { + this.sideBarFocusContextKey.set(true); + }); + focusTracker.onDidBlur(() => { + this.sideBarFocusContextKey.set(false); + }); + } + createTitleArea(parent: HTMLElement): HTMLElement { const titleArea = super.createTitleArea(parent); @@ -103,23 +124,22 @@ export class SidebarPart extends CompositePart { container.style.borderLeftColor = !isPositionLeft ? borderColor : null; } - openViewlet(id: string, focus?: boolean): TPromise { + openViewlet(id: string, focus?: boolean): Viewlet { if (this.blockOpeningViewlet) { - return Promise.resolve(null); // Workaround against a potential race condition + return null; // Workaround against a potential race condition } // First check if sidebar is hidden and show if so - let promise = TPromise.wrap(null); if (!this.partService.isVisible(Parts.SIDEBAR_PART)) { try { this.blockOpeningViewlet = true; - promise = this.partService.setSideBarHidden(false); + this.partService.setSideBarHidden(false); } finally { this.blockOpeningViewlet = false; } } - return promise.then(() => this.openComposite(id, focus)) as TPromise; + return this.openComposite(id, focus) as Viewlet; } getActiveViewlet(): IViewlet { @@ -130,8 +150,8 @@ export class SidebarPart extends CompositePart { return this.getLastActiveCompositetId(); } - hideActiveViewlet(): TPromise { - return this.hideActiveComposite().then(composite => void 0); + hideActiveViewlet(): void { + this.hideActiveComposite(); } layout(dimension: Dimension): Dimension[] { @@ -150,7 +170,7 @@ export class SidebarPart extends CompositePart { const anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(contextMenuActions), + getActions: () => contextMenuActions, getActionItem: action => this.actionItemProvider(action as Action), actionRunner: activeViewlet.getActionRunner() }); @@ -177,7 +197,7 @@ class FocusSideBarAction extends Action { // Show side bar if (!this.partService.isVisible(Parts.SIDEBAR_PART)) { - return this.partService.setSideBarHidden(false); + return Promise.resolve(this.partService.setSideBarHidden(false)); } // Focus into active viewlet diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index eb4c5513346..48e617f2c1c 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -288,7 +288,7 @@ class StatusBarEntryItem implements IStatusbarItem { this.contextMenuService.showContextMenu({ getAnchor: () => el, getActionsContext: () => this.entry.extensionId, - getActions: () => Promise.resolve([manageExtensionAction]) + getActions: () => [manageExtensionAction] }); })); } diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index aae7522e581..f399f28882a 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -101,6 +101,7 @@ -webkit-app-region: no-drag; height: 100%; width: 138px; + margin-left: auto; } .monaco-workbench.fullscreen > .part.titlebar > .window-controls-container { @@ -192,7 +193,7 @@ } .monaco-workbench .menubar .menubar-menu-items-holder.monaco-menu-container { - font-family: "Segoe WPC", "Segoe UI", ".SFNSDisplay-Light", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; outline: 0; border: none; } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 492d1ef3b3f..b7ca7da0ba4 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -15,7 +15,7 @@ import { ActionRunner, IActionRunner, IAction, Action } from 'vs/base/common/act import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import * as DOM from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isLinux } from 'vs/base/common/platform'; import { Menu, IMenuOptions, SubmenuAction, MENU_MNEMONIC_REGEX, cleanMnemonic, MENU_ESCAPED_MNEMONIC_REGEX } from 'vs/base/browser/ui/menu/menu'; import { KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -32,6 +32,9 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; import { Gesture, EventType, GestureEvent } from 'vs/base/browser/touch'; import { attachMenuStyler } from 'vs/platform/theme/common/styler'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; const $ = DOM.$; @@ -124,7 +127,10 @@ export class MenubarControl extends Disposable { @IKeybindingService private keybindingService: IKeybindingService, @IConfigurationService private configurationService: IConfigurationService, @ILabelService private labelService: ILabelService, - @IUpdateService private updateService: IUpdateService + @IUpdateService private updateService: IUpdateService, + @IStorageService private storageService: IStorageService, + @INotificationService private notificationService: INotificationService, + @IPreferencesService private preferencesService: IPreferencesService ) { super(); @@ -167,6 +173,8 @@ export class MenubarControl extends Disposable { this.recentlyOpened = recentlyOpened; }); + this.detectAndRecommendCustomTitlebar(); + this.registerListeners(); } @@ -348,6 +356,7 @@ export class MenubarControl extends Disposable { if (event.affectsConfiguration('window.menuBarVisibility')) { this.setUnfocusedState(); + this.detectAndRecommendCustomTitlebar(); } } @@ -366,13 +375,17 @@ export class MenubarControl extends Disposable { } private hideMenubar(): void { - this.container.style.display = 'none'; - this._onVisibilityChange.fire(false); + if (this.container.style.display !== 'none') { + this.container.style.display = 'none'; + this._onVisibilityChange.fire(false); + } } private showMenubar(): void { - this.container.style.display = 'flex'; - this._onVisibilityChange.fire(true); + if (this.container.style.display !== 'flex') { + this.container.style.display = 'flex'; + this._onVisibilityChange.fire(true); + } } private onModifierKeyToggled(modifierKeyStatus: IModifierKeyStatus): void { @@ -432,6 +445,41 @@ export class MenubarControl extends Disposable { }); } + private detectAndRecommendCustomTitlebar(): void { + if (!isLinux) { + return; + } + + if (!this.storageService.getBoolean('menubar/electronFixRecommended', StorageScope.GLOBAL, false)) { + if (this.currentMenubarVisibility === 'hidden' || this.currentTitlebarStyleSetting === 'custom') { + // Issue will not arise for user, abort notification + return; + } + + const message = nls.localize('menubar.electronFixRecommendation', "If you experience hard to read text in the menu bar, we recommend trying out the custom title bar."); + this.notificationService.prompt(Severity.Info, message, [ + { + label: nls.localize('goToSetting', "Open Settings"), + run: () => { + return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' }); + } + }, + { + label: nls.localize('moreInfo', "More Info"), + run: () => { + window.open('https://go.microsoft.com/fwlink/?linkid=2038566'); + } + }, + { + label: nls.localize('neverShowAgain', "Don't Show Again"), + run: () => { + this.storageService.store('menubar/electronFixRecommended', true, StorageScope.GLOBAL); + } + } + ]); + } + } + private registerListeners(): void { // Update when config changes this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); @@ -694,7 +742,7 @@ export class MenubarControl extends Disposable { this.insertActionsBefore(action, target); if (action instanceof SubmenuItemAction) { const submenu = this.menuService.createMenu(action.item.submenu, this.contextKeyService); - const submenuActions = []; + const submenuActions: SubmenuAction[] = []; updateActions(submenu, submenuActions); target.push(new SubmenuAction(action.label, submenuActions)); } else { @@ -838,8 +886,8 @@ export class MenubarControl extends Disposable { } })); - this._register(DOM.addDisposableListener(window, DOM.EventType.KEY_DOWN, (e) => { - if (!this.currentEnableMenuBarMnemonics || !e.altKey || e.ctrlKey) { + this._register(DOM.addDisposableListener(window, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + if (!this.currentEnableMenuBarMnemonics || !e.altKey || e.ctrlKey || e.defaultPrevented) { return; } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 6682bffba75..7fbef229748 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -26,7 +26,7 @@ import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { Color } from 'vs/base/common/color'; import { trim } from 'vs/base/common/strings'; -import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener, getComputedStyle } from 'vs/base/browser/dom'; +import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener } from 'vs/base/browser/dom'; import { MenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { template, getBaseLabel } from 'vs/base/common/labels'; @@ -57,14 +57,6 @@ export class TitlebarPart extends Part implements ITitleService { private pendingTitle: string; private representedFileName: string; - private initialSizing: { - titleFontSize?: number; - titlebarHeight?: number; - controlsWidth?: number; - appIconSize?: number; - appIconWidth?: number; - } = Object.create(null); - private isInactive: boolean; private properties: ITitleProperties; @@ -395,6 +387,8 @@ export class TitlebarPart extends Part implements ITitleService { }, 0 /* need a timeout because we are in capture phase */); }, true /* use capture to know the currently active element properly */)); + this.updateStyles(); + return this.titleContainer; } @@ -462,7 +456,7 @@ export class TitlebarPart extends Part implements ITitleService { if (actions.length) { this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(actions), + getActions: () => actions, onHide: () => actions.forEach(a => a.dispose()) }); } @@ -499,93 +493,45 @@ export class TitlebarPart extends Part implements ITitleService { private adjustTitleMarginToCenter(): void { setTimeout(() => { - // Center the title in the window - const currentAppIconWidth = this.appIcon ? parseInt(getComputedStyle(this.appIcon).width, 10) : 0; - let currentMenubarWidth = parseInt(getComputedStyle(this.menubar).width, 10); - currentMenubarWidth = isNaN(currentMenubarWidth) ? 0 : currentMenubarWidth; - const currentTotalWidth = parseInt(getComputedStyle(document.body).width, 10); - const currentTitleWidth = parseInt(getComputedStyle(this.title).width, 10); - const currentWindowControlsWidth = this.windowControls ? parseInt(getComputedStyle(this.windowControls).width, 10) : 0; - - let leftMargin = (currentTotalWidth / 2) - (currentTitleWidth / 2) - (currentMenubarWidth + currentAppIconWidth); - let rightMargin = currentTotalWidth - (currentAppIconWidth + currentMenubarWidth + leftMargin + currentTitleWidth + currentWindowControlsWidth); - - // Center if we can, leaving some space on both sides - if (leftMargin >= 20 && rightMargin >= 20) { - this.title.style.marginLeft = `${leftMargin}px`; + // Cannot center + if (!isMacintosh && + (this.appIcon.clientWidth + this.menubar.clientWidth + 10 > (this.titleContainer.clientWidth - this.title.clientWidth) / 2 || + this.titleContainer.clientWidth - this.windowControls.clientWidth - 10 < (this.titleContainer.clientWidth + this.title.clientWidth) / 2)) { + this.title.style.position = null; + this.title.style.left = null; + this.title.style.transform = null; } else { - this.title.style.marginLeft = null; + this.title.style.position = 'absolute'; + this.title.style.left = '50%'; + this.title.style.transform = 'translate(-50%, 0)'; } - }, 0); // delay so that we can get accurate information about the widths - } - - private updateLayout(dimension: Dimension) { - // Store initital title sizing if we need to prevent zooming - if (typeof this.initialSizing.titleFontSize !== 'number') { - this.initialSizing.titleFontSize = parseInt(getComputedStyle(this.title).fontSize, 10); - } - - if (typeof this.initialSizing.titlebarHeight !== 'number') { - this.initialSizing.titlebarHeight = parseInt(getComputedStyle(this.title).height, 10); - } - - // Only prevent zooming behavior on macOS or when the menubar is not visible - if (isMacintosh || this.configurationService.getValue('window.menuBarVisibility') === 'hidden') { - // To prevent zooming we need to adjust the font size with the zoom factor - const newHeight = this.initialSizing.titlebarHeight / getZoomFactor(); - this.title.style.fontSize = `${this.initialSizing.titleFontSize / getZoomFactor()}px`; - this.title.style.lineHeight = `${newHeight}px`; - - // Windows/Linux specific layout - if (isWindows || isLinux) { - if (typeof this.initialSizing.controlsWidth !== 'number') { - this.initialSizing.controlsWidth = parseInt(getComputedStyle(this.windowControls).width, 10); - } - - const appIconComputedStyles = getComputedStyle(this.appIcon); - if (typeof this.initialSizing.appIconWidth !== 'number') { - this.initialSizing.appIconWidth = parseInt(appIconComputedStyles.width, 10); - } - - if (typeof this.initialSizing.appIconSize !== 'number') { - this.initialSizing.appIconSize = parseInt(appIconComputedStyles.backgroundSize, 10); - } - - const currentAppIconHeight = parseInt(appIconComputedStyles.height, 10); - const newControlsWidth = this.initialSizing.controlsWidth / getZoomFactor(); - const newAppIconWidth = this.initialSizing.appIconWidth / getZoomFactor(); - const newAppIconSize = this.initialSizing.appIconSize / getZoomFactor(); - - // Adjust app icon mimic menubar - this.appIcon.style.width = `${newAppIconWidth}px`; - this.appIcon.style.backgroundSize = `${newAppIconSize}px`; - this.appIcon.style.paddingTop = `${(newHeight - currentAppIconHeight) / 2.0}px`; - this.appIcon.style.paddingBottom = `${(newHeight - currentAppIconHeight) / 2.0}px`; - - // Adjust windows controls - this.windowControls.style.width = `${newControlsWidth}px`; - } - } else { - // We need to undo zoom prevention - this.title.style.fontSize = null; - this.title.style.lineHeight = null; - - this.appIcon.style.width = null; - this.appIcon.style.backgroundSize = null; - this.appIcon.style.paddingTop = null; - this.appIcon.style.paddingBottom = null; - - this.windowControls.style.width = null; - } - - if (this.menubarPart) { - const menubarDimension = new Dimension(undefined, dimension.height); - this.menubarPart.layout(menubarDimension); - } + }, 0); // delay so that we can get accurate information about widths } layout(dimension: Dimension): Dimension[] { - this.updateLayout(dimension); + if (this.configurationService.getValue('window.titleBarStyle') === 'custom') { + // Only prevent zooming behavior on macOS or when the menubar is not visible + if (isMacintosh || this.configurationService.getValue('window.menuBarVisibility') === 'hidden') { + this.title.style.zoom = `${1.0 / getZoomFactor()}`; + if (isWindows || isLinux) { + this.appIcon.style.zoom = `${1.0 / getZoomFactor()}`; + this.windowControls.style.zoom = `${1.0 / getZoomFactor()}`; + } + } else { + this.title.style.zoom = null; + if (isWindows || isLinux) { + this.appIcon.style.zoom = null; + this.windowControls.style.zoom = null; + } + } + + this.adjustTitleMarginToCenter(); + + if (this.menubarPart) { + const menubarDimension = new Dimension(undefined, dimension.height); + this.menubarPart.layout(menubarDimension); + } + } return super.layout(dimension); } diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index b2c0e44e3ee..bbf7d828c51 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -8,37 +8,38 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IAction, IActionItem, ActionRunner } from 'vs/base/common/actions'; +import { IAction, IActionItem, ActionRunner, Action } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { ContextAwareMenuItemActionItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IViewsService, ITreeViewer, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ICustomViewDescriptor, ViewsRegistry, ViewContainer } from 'vs/workbench/common/views'; +import { IViewsService, ITreeViewer, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ICustomViewDescriptor, ViewsRegistry, ViewContainer, ITreeItemLabel } from 'vs/workbench/common/views'; import { IViewletViewOptions, FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IProgressService2 } from 'vs/workbench/services/progress/common/progress'; +import { IProgressService2 } from 'vs/platform/progress/common/progress'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import * as DOM from 'vs/base/browser/dom'; -import { isUndefinedOrNull } from 'vs/base/common/types'; import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree'; import { ResourceLabel } from 'vs/workbench/browser/labels'; import { ActionBar, IActionItemProvider, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { URI } from 'vs/base/common/uri'; import { basename } from 'vs/base/common/paths'; -import { LIGHT, FileThemeIcon, FolderThemeIcon } from 'vs/platform/theme/common/themeService'; +import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { FileKind } from 'vs/platform/files/common/files'; import { WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { localize } from 'vs/nls'; +import { timeout } from 'vs/base/common/async'; +import { CollapseAllAction } from 'vs/base/parts/tree/browser/treeDefaults'; +import { editorFindMatchHighlight, editorFindMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; export class CustomTreeViewPanel extends ViewletPanel { - private menus: TitleMenus; private treeViewer: ITreeViewer; constructor( @@ -46,20 +47,20 @@ export class CustomTreeViewPanel extends ViewletPanel { @INotificationService private notificationService: INotificationService, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, - @IInstantiationService private instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @IViewsService viewsService: IViewsService, ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService); - this.treeViewer = (ViewsRegistry.getView(options.id)).treeViewer; + const { treeViewer } = (ViewsRegistry.getView(options.id)); + this.treeViewer = treeViewer; + this.treeViewer.onDidChangeActions(() => this.updateActions(), this, this.disposables); this.disposables.push(toDisposable(() => this.treeViewer.setVisibility(false))); - this.menus = this.instantiationService.createInstance(TitleMenus, this.id); - this.menus.onDidChangeTitle(() => this.updateActions(), this, this.disposables); this.updateTreeVisibility(); } - setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => this.updateTreeVisibility()); + setVisible(visible: boolean): void { + super.setVisible(visible); + this.updateTreeVisibility(); } focus(): void { @@ -81,11 +82,11 @@ export class CustomTreeViewPanel extends ViewletPanel { } getActions(): IAction[] { - return [...this.menus.getTitleActions()]; + return [...this.treeViewer.getPrimaryActions()]; } getSecondaryActions(): IAction[] { - return this.menus.getTitleSecondaryActions(); + return [...this.treeViewer.getSecondaryActions()]; } getActionItem(action: IAction): IActionItem { @@ -163,7 +164,7 @@ class TitleMenus implements IDisposable { } class Root implements ITreeItem { - label = 'root'; + label = { label: 'root' }; handle = '0'; parentHandle = null; collapsibleState = TreeItemCollapsibleState.Expanded; @@ -178,6 +179,7 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { private activated: boolean = false; private _hasIconForParentNode = false; private _hasIconForLeafNode = false; + private _showCollapseAllAction = false; private domNode: HTMLElement; private treeContainer: HTMLElement; @@ -185,6 +187,7 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { private tree: FileIconThemableWorkbenchTree; private root: ITreeItem; private elementsToRefresh: ITreeItem[] = []; + private menus: TitleMenus; private _dataProvider: ITreeViewDataProvider; @@ -200,6 +203,9 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { private _onDidChangeVisibility: Emitter = this._register(new Emitter()); readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; + private _onDidChangeActions: Emitter = this._register(new Emitter()); + readonly onDidChangeActions: Event = this._onDidChangeActions.event; + constructor( private id: string, private container: ViewContainer, @@ -207,10 +213,13 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { @IWorkbenchThemeService private themeService: IWorkbenchThemeService, @IInstantiationService private instantiationService: IInstantiationService, @ICommandService private commandService: ICommandService, - @IConfigurationService private configurationService: IConfigurationService + @IConfigurationService private configurationService: IConfigurationService, + @IProgressService2 private progressService: IProgressService2 ) { super(); this.root = new Root(); + this.menus = this._register(this.instantiationService.createInstance(TitleMenus, this.id)); + this._register(this.menus.onDidChangeTitle(() => this._onDidChangeActions.fire())); this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); this._register(this.themeService.onThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); this._register(this.configurationService.onDidChangeConfiguration(e => { @@ -240,7 +249,7 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { }); } }; - DOM.removeClass(this.domNode, 'message'); + this.hideMessage(); this.refresh(); } else { this._dataProvider = null; @@ -260,6 +269,30 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { return this.isVisible; } + get showCollapseAllAction(): boolean { + return this._showCollapseAllAction; + } + + set showCollapseAllAction(showCollapseAllAction: boolean) { + if (this._showCollapseAllAction !== !!showCollapseAllAction) { + this._showCollapseAllAction = !!showCollapseAllAction; + this._onDidChangeActions.fire(); + } + } + + getPrimaryActions(): IAction[] { + if (this.showCollapseAllAction) { + const collapseAllAction = new Action('vs.tree.collapse', localize('collapse', "Collapse"), 'monaco-tree-action collapse-all', true, () => this.tree ? new CollapseAllAction(this.tree, true).run() : Promise.resolve()); + return [...this.menus.getTitleActions(), collapseAllAction]; + } else { + return this.menus.getTitleActions(); + } + } + + getSecondaryActions(): IAction[] { + return this.menus.getTitleSecondaryActions(); + } + setVisibility(isVisible: boolean): void { isVisible = !!isVisible; if (this.isVisible === isVisible) { @@ -341,6 +374,10 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { this.message.innerText = message; } + private hideMessage(): void { + DOM.removeClass(this.domNode, 'message'); + } + layout(size: number) { this.domNode.style.height = size + 'px'; if (this.tree) { @@ -351,7 +388,7 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { getOptimalWidth(): number { if (this.tree) { const parentNode = this.tree.getHTMLElement(); - const childNodes = [].slice.call(parentNode.querySelectorAll('.outline-item-label > a')); + const childNodes = ([] as Element[]).slice.call(parentNode.querySelectorAll('.outline-item-label > a')); return DOM.getLargestChildWidth(parentNode, childNodes); } return 0; @@ -372,37 +409,34 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { return Promise.resolve(null); } - reveal(item: ITreeItem, parentChain: ITreeItem[], options?: { select?: boolean, focus?: boolean }): TPromise { - if (this.dataProvider && this.tree && this.isVisible) { - options = options ? options : { select: false, focus: false }; - const select = isUndefinedOrNull(options.select) ? false : options.select; - const focus = isUndefinedOrNull(options.focus) ? false : options.focus; + expand(itemOrItems: ITreeItem | ITreeItem[]): TPromise { + itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems]; + return this.tree.expandAll(itemOrItems); + } - const root: Root = this.tree.getInput(); - const promise: Thenable = root.children ? Promise.resolve(null) : this.refresh(); // Refresh if root is not populated - return promise.then(() => { - var result = Promise.resolve(null); - parentChain.forEach((e) => { - result = result.then(() => this.tree.expand(e)); - }); - return result.then(() => this.tree.reveal(item)) - .then(() => { - if (select) { - this.tree.setSelection([item], { source: 'api' }); - } - if (focus) { - this.focus(); - this.tree.setFocus(item); - } - }); - }); - } - return Promise.resolve(null); + setSelection(items: ITreeItem[]): void { + this.tree.setSelection(items, { source: 'api' }); + } + + setFocus(item: ITreeItem): void { + this.focus(); + this.tree.setFocus(item); + } + + reveal(item: ITreeItem): TPromise { + return this.tree.reveal(item); } private activate() { + this.hideMessage(); if (!this.activated) { - this.extensionService.activateByEvent(`onView:${this.id}`); + this.progressService.withProgress({ location: this.container.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) + .then(() => timeout(2000)) + .then(() => { + if (!this.dataProvider) { + this.showMessage(noDataProviderMessage); + } + }); this.activated = true; } } @@ -452,7 +486,7 @@ class TreeDataSource implements IDataSource { getChildren(tree: ITree, node: ITreeItem): TPromise { if (this.treeView.dataProvider) { - return this.progressService.withProgress({ location: this.container }, () => this.treeView.dataProvider.getChildren(node)); + return this.progressService.withProgress({ location: this.container.id }, () => this.treeView.dataProvider.getChildren(node)); } return Promise.resolve([]); } @@ -473,6 +507,19 @@ interface ITreeExplorerTemplateData { aligner: Aligner; } +// todo@joh,sandy make this proper and contributable from extensions +registerThemingParticipant((theme, collector) => { + + const findMatchHighlightColor = theme.getColor(editorFindMatchHighlight); + if (findMatchHighlightColor) { + collector.addRule(`.file-icon-themable-tree .monaco-tree-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${findMatchHighlightColor}; }`); + } + const findMatchHighlightColorBorder = theme.getColor(editorFindMatchHighlightBorder); + if (findMatchHighlightColorBorder) { + collector.addRule(`.file-icon-themable-tree .monaco-tree-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${findMatchHighlightColorBorder}; box-sizing: border-box; }`); + } +}); + class TreeRenderer implements IRenderer { private static readonly ITEM_HEIGHT = 22; @@ -500,7 +547,7 @@ class TreeRenderer implements IRenderer { DOM.addClass(container, 'custom-view-tree-node-item'); const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); - const resourceLabel = this.instantiationService.createInstance(ResourceLabel, container, {}); + const resourceLabel = this.instantiationService.createInstance(ResourceLabel, container, { supportHighlights: true, donotSupportOcticons: true }); DOM.addClass(resourceLabel.element, 'custom-view-tree-node-item-resourceLabel'); const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); const actionBar = new ActionBar(actionsContainer, { @@ -513,7 +560,9 @@ class TreeRenderer implements IRenderer { renderElement(tree: ITree, node: ITreeItem, templateId: string, templateData: ITreeExplorerTemplateData): void { const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; - const label = node.label ? node.label : resource ? basename(resource.path) : ''; + const treeItemLabel: ITreeItemLabel = node.label ? node.label : resource ? { label: basename(resource.path) } : void 0; + const label = treeItemLabel ? treeItemLabel.label : void 0; + const matches = treeItemLabel && treeItemLabel.highlights ? treeItemLabel.highlights.map(([start, end]) => ({ start, end })) : void 0; const icon = this.themeService.getTheme().type === LIGHT ? node.icon : node.iconDark; const iconUrl = icon ? URI.revive(icon) : null; const title = node.tooltip ? node.tooltip : resource ? void 0 : label; @@ -524,9 +573,9 @@ class TreeRenderer implements IRenderer { if (resource || node.themeIcon) { const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); - templateData.resourceLabel.setLabel({ name: label, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'] }); + templateData.resourceLabel.setLabel({ name: label, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); } else { - templateData.resourceLabel.setLabel({ name: label }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'] }); + templateData.resourceLabel.setLabel({ name: label }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); } templateData.icon.style.backgroundImage = iconUrl ? `url('${iconUrl.toString(true)}')` : ''; @@ -641,9 +690,7 @@ class TreeController extends WorkbenchTreeController { this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => { - return Promise.resolve(actions); - }, + getActions: () => actions, getActionItem: (action) => { const keybinding = this._keybindingService.lookupKeybinding(action.id); diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index 2e805f980fa..dca476911da 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -5,7 +5,6 @@ import 'vs/css!./media/panelviewlet'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter, filterEvent } from 'vs/base/common/event'; import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler'; @@ -77,12 +76,10 @@ export abstract class ViewletPanel extends Panel implements IView { this.actionRunner = options.actionRunner; } - setVisible(visible: boolean): TPromise { + setVisible(visible: boolean): void { if (this._isVisible !== visible) { this._isVisible = visible; } - - return TPromise.wrap(null); } isVisible(): boolean { @@ -211,12 +208,11 @@ export class PanelViewlet extends Viewlet { super(id, configurationService, partService, telemetryService, themeService, storageService); } - create(parent: HTMLElement): Promise { - return super.create(parent).then(() => { - this.panelview = this._register(new PanelView(parent, this.options)); - this._register(this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel))); - this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); - }); + create(parent: HTMLElement): void { + super.create(parent); + this.panelview = this._register(new PanelView(parent, this.options)); + this._register(this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel))); + this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); } private showContextMenu(event: StandardMouseEvent): void { @@ -233,7 +229,7 @@ export class PanelViewlet extends Viewlet { let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(this.getContextMenuActions()) + getActions: () => this.getContextMenuActions() }); } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index f5289fb7784..e8942821e51 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -15,7 +15,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IContextKeyService, IContextKeyChangeEvent, IReadableSet, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Event, chain, filterEvent, Emitter } from 'vs/base/common/event'; import { sortedDiff, firstIndex, move } from 'vs/base/common/arrays'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { MenuId, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -467,7 +467,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel { private static loadViewsStates(viewletStateStorageId: string, hiddenViewsStorageId: string, storageService: IStorageService, contextService: IWorkspaceContextService): Map { const viewStates = new Map(); - const storedViewsStates = JSON.parse(storageService.get(viewletStateStorageId, contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? StorageScope.WORKSPACE : StorageScope.GLOBAL, '{}')); + const storedViewsStates = JSON.parse(storageService.get(viewletStateStorageId, StorageScope.WORKSPACE, '{}')); const viewsVisibilityStates = PersistentContributableViewsModel.loadViewsVisibilityState(hiddenViewsStorageId, storageService, contextService); for (const { id, isHidden } of values(viewsVisibilityStates)) { const viewState = storedViewsStates[id]; @@ -546,7 +546,7 @@ export class ViewsService extends Disposable implements IViewsService { if (viewDescriptor) { const viewletDescriptor = this.viewletService.getViewlet(viewDescriptor.container.id); if (viewletDescriptor) { - return this.viewletService.openViewlet(viewletDescriptor.id) + return this.viewletService.openViewlet(viewletDescriptor.id, focus) .then((viewlet: IViewsViewlet) => { if (viewlet && viewlet.openView) { return viewlet.openView(id, focus); diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 234d6789aa4..57c945aeb64 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import * as DOM from 'vs/base/browser/dom'; import { dispose, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IAction } from 'vs/base/common/actions'; @@ -42,12 +41,11 @@ export abstract class TreeViewsViewletPanel extends ViewletPanel { } } - setVisible(visible: boolean): TPromise { + setVisible(visible: boolean): void { if (this.isVisible() !== visible) { - return super.setVisible(visible) - .then(() => this.updateTreeVisibility(this.tree, visible && this.isExpanded())); + super.setVisible(visible); + this.updateTreeVisibility(this.tree, visible && this.isExpanded()); } - return TPromise.wrap(null); } focus(): void { @@ -139,35 +137,34 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView this.viewletState = this.getMemento(StorageScope.WORKSPACE); this.visibleViewsStorageId = `${id}.numberOfVisibleViews`; - this.visibleViewsCountFromCache = this.storageService.getInteger(this.visibleViewsStorageId, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY ? StorageScope.GLOBAL : StorageScope.WORKSPACE, 0); + this.visibleViewsCountFromCache = this.storageService.getInteger(this.visibleViewsStorageId, StorageScope.WORKSPACE, 1); this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables))); } - create(parent: HTMLElement): Promise { - return super.create(parent).then(() => { - this._register(this.onDidSashChange(() => this.saveViewSizes())); - this.viewsModel.onDidAdd(added => this.onDidAddViews(added)); - this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed)); - const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => { - const size = this.viewsModel.getSize(viewDescriptor.id); - const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id); - return ({ viewDescriptor, index, size, collapsed }); - }); - if (addedViews.length) { - this.onDidAddViews(addedViews); - } - - // Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609 - this.extensionService.whenInstalledExtensionsRegistered().then(() => { - this.areExtensionsReady = true; - if (this.panels.length) { - this.updateTitleArea(); - this.updateViewHeaders(); - } - }); - - this.focus(); + create(parent: HTMLElement): void { + super.create(parent); + this._register(this.onDidSashChange(() => this.saveViewSizes())); + this.viewsModel.onDidAdd(added => this.onDidAddViews(added)); + this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed)); + const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => { + const size = this.viewsModel.getSize(viewDescriptor.id); + const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id); + return ({ viewDescriptor, index, size, collapsed }); }); + if (addedViews.length) { + this.onDidAddViews(addedViews); + } + + // Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609 + this.extensionService.whenInstalledExtensionsRegistered().then(() => { + this.areExtensionsReady = true; + if (this.panels.length) { + this.updateTitleArea(); + this.updateViewHeaders(); + } + }); + + this.focus(); } getContextMenuActions(): IAction[] { @@ -190,14 +187,13 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView return result; } - setVisible(visible: boolean): Promise { - return super.setVisible(visible) - .then(() => Promise.all(this.panels.filter(view => view.isVisible() !== visible) - .map((view) => view.setVisible(visible)))) - .then(() => void 0); + setVisible(visible: boolean): void { + super.setVisible(visible); + this.panels.filter(view => view.isVisible() !== visible) + .map((view) => view.setVisible(visible)); } - openView(id: string, focus?: boolean): TPromise { + openView(id: string, focus?: boolean): IView { if (focus) { this.focus(); } @@ -210,7 +206,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView if (focus) { view.focus(); } - return Promise.resolve(view); + return view; } movePanel(from: ViewletPanel, to: ViewletPanel): void { @@ -322,7 +318,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(actions) + getActions: () => actions }); } diff --git a/src/vs/workbench/browser/quickopen.ts b/src/vs/workbench/browser/quickopen.ts index da267822879..72f74e980e6 100644 --- a/src/vs/workbench/browser/quickopen.ts +++ b/src/vs/workbench/browser/quickopen.ts @@ -326,7 +326,7 @@ export class QuickOpenAction extends Action { this.enabled = !!this.quickOpenService; } - run(context?: any): TPromise { + run(context?: any): Promise { // Show with prefix this.quickOpenService.show(this.prefix); diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index fafbc3cd78d..94284a6052a 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -136,7 +136,7 @@ export class ShowViewletAction extends Action { this.enabled = !!this.viewletService && !!this.editorGroupService; } - run(): Promise { + run(): Thenable { // Pass focus to viewlet if not open or focused if (this.otherViewletShowing() || !this.sidebarHasFocus()) { diff --git a/src/vs/workbench/buildfile.js b/src/vs/workbench/buildfile.js index 1dfb66c06b4..b9f60119de5 100644 --- a/src/vs/workbench/buildfile.js +++ b/src/vs/workbench/buildfile.js @@ -22,7 +22,7 @@ exports.collectModules = function () { createModuleDescription('vs/workbench/parts/debug/node/telemetryApp', []), createModuleDescription('vs/workbench/services/search/node/searchApp', []), - createModuleDescription('vs/workbench/services/search/node/worker/searchWorkerApp', []), + createModuleDescription('vs/workbench/services/search/node/legacy/worker/searchWorkerApp', []), createModuleDescription('vs/workbench/services/files/node/watcher/unix/watcherApp', []), createModuleDescription('vs/workbench/services/files/node/watcher/nsfw/watcherApp', []), diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 6ae29b06ebf..db7432b7ff6 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -5,11 +5,10 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModel, ITextBufferFactory } from 'vs/editor/common/model'; -import { IMode } from 'vs/editor/common/modes'; import { EditorModel } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ITextSnapshot } from 'vs/platform/files/common/files'; @@ -72,22 +71,22 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd */ protected createTextEditorModel(value: ITextBufferFactory, resource?: URI, modeId?: string): TPromise { const firstLineText = this.getFirstLineText(value); - const mode = this.getOrCreateMode(this.modeService, modeId, firstLineText); + const languageSelection = this.getOrCreateMode(this.modeService, modeId, firstLineText); - return TPromise.as(this.doCreateTextEditorModel(value, mode, resource)); + return TPromise.as(this.doCreateTextEditorModel(value, languageSelection, resource)); } - private doCreateTextEditorModel(value: ITextBufferFactory, mode: Promise, resource: URI): EditorModel { + private doCreateTextEditorModel(value: ITextBufferFactory, languageSelection: ILanguageSelection, resource: URI): EditorModel { let model = resource && this.modelService.getModel(resource); if (!model) { - model = this.modelService.createModel(value, mode, resource); + model = this.modelService.createModel(value, languageSelection, resource); this.createdEditorModel = true; // Make sure we clean up when this model gets disposed this.registerModelDisposeListener(model); } else { this.modelService.updateModel(model, value); - this.modelService.setMode(model, mode); + this.modelService.setMode(model, languageSelection); } this.textEditorModelHandle = model.uri; @@ -113,8 +112,8 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd * * @param firstLineText optional first line of the text buffer to set the mode on. This can be used to guess a mode from content. */ - protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): Promise { - return modeService.getOrCreateMode(modeId); + protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): ILanguageSelection { + return modeService.create(modeId); } /** diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 936c4732993..91c66b68e74 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -9,9 +9,8 @@ import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel' import { URI } from 'vs/base/common/uri'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { CONTENT_CHANGE_EVENT_BUFFER_DELAY } from 'vs/platform/files/common/files'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { IMode } from 'vs/editor/common/modes'; import { Event, Emitter } from 'vs/base/common/event'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -58,9 +57,9 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin this.registerListeners(); } - protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): Promise { + protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): ILanguageSelection { if (!modeId || modeId === PLAINTEXT_MODE_ID) { - return modeService.getOrCreateModeByFilepathOrFirstLine(this.resource.fsPath, firstLineText); // lookup mode via resource path if the provided modeId is unspecific + return modeService.createByFilepathOrFirstLine(this.resource.fsPath, firstLineText); // lookup mode via resource path if the provided modeId is unspecific } return super.getOrCreateMode(modeService, modeId, firstLineText); diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index 64f1ff2d39d..04e7e425275 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -125,7 +125,7 @@ export class NotificationsModel extends Disposable implements INotificationsMode } } - private findNotification(item: INotificationViewItem): INotificationViewItem { + private findNotification(item: INotificationViewItem): INotificationViewItem | undefined { for (let i = 0; i < this._notifications.length; i++) { const notification = this._notifications[i]; if (notification.equals(item)) { @@ -136,7 +136,7 @@ export class NotificationsModel extends Disposable implements INotificationsMode return void 0; } - private createViewItem(notification: INotification): INotificationViewItem { + private createViewItem(notification: INotification): INotificationViewItem | null { const item = NotificationViewItem.create(notification); if (!item) { return null; @@ -179,7 +179,7 @@ export interface INotificationViewItem { readonly severity: Severity; readonly sticky: boolean; readonly message: INotificationMessage; - readonly source: string; + readonly source: string | undefined; readonly actions: INotificationActions; readonly progress: INotificationViewItemProgress; @@ -195,6 +195,7 @@ export interface INotificationViewItem { toggle(): void; hasProgress(): boolean; + hasPrompt(): boolean; updateSeverity(severity: Severity): void; updateMessage(message: NotificationMessage): void; @@ -340,7 +341,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie private readonly _onDidLabelChange: Emitter = this._register(new Emitter()); get onDidLabelChange(): Event { return this._onDidLabelChange.event; } - static create(notification: INotification): INotificationViewItem { + static create(notification: INotification): INotificationViewItem | null { if (!notification || !notification.message || isPromiseCanceledError(notification.message)) { return null; // we need a message to show } @@ -357,7 +358,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie return null; // we need a message to show } - let actions: INotificationActions; + let actions: INotificationActions | undefined; if (notification.actions) { actions = notification.actions; } else if (isErrorWithActions(notification.message)) { @@ -367,9 +368,8 @@ export class NotificationViewItem extends Disposable implements INotificationVie return new NotificationViewItem(severity, notification.sticky, message, notification.source, actions); } - private static parseNotificationMessage(input: NotificationMessage): INotificationMessage { - let message: string; - + private static parseNotificationMessage(input: NotificationMessage): INotificationMessage | undefined { + let message: string | undefined; if (input instanceof Error) { message = toErrorMessage(input, false); } else if (typeof input === 'string') { @@ -377,7 +377,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie } if (!message) { - return null; // we need a message to show + return void 0; // we need a message to show } const raw = message; @@ -401,17 +401,13 @@ export class NotificationViewItem extends Disposable implements INotificationVie return { raw, value: message, links, original: input }; } - private constructor(private _severity: Severity, private _sticky: boolean, private _message: INotificationMessage, private _source: string, actions?: INotificationActions) { + private constructor(private _severity: Severity, private _sticky: boolean | undefined, private _message: INotificationMessage, private _source: string | undefined, actions?: INotificationActions) { super(); this.setActions(actions); } - private setActions(actions: INotificationActions): void { - if (!actions) { - actions = { primary: [], secondary: [] }; - } - + private setActions(actions: INotificationActions = { primary: [], secondary: [] }): void { if (!Array.isArray(actions.primary)) { actions.primary = []; } @@ -425,7 +421,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie } get canCollapse(): boolean { - return this._actions.primary.length === 0; + return !this.hasPrompt(); } get expanded(): boolean { @@ -441,11 +437,11 @@ export class NotificationViewItem extends Disposable implements INotificationVie return true; // explicitly sticky } - const hasPrimaryActions = Array.isArray(this._actions.primary) && this._actions.primary.length > 0; + const hasPrompt = this.hasPrompt(); if ( - (hasPrimaryActions && this._severity === Severity.Error) || // notification errors with actions are sticky - (!hasPrimaryActions && this._expanded) || // notifications that got expanded are sticky - (this._progress && !this._progress.state.done) // notifications with running progress are sticky + (hasPrompt && this._severity === Severity.Error) || // notification errors with actions are sticky + (!hasPrompt && this._expanded) || // notifications that got expanded are sticky + (this._progress && !this._progress.state.done) // notifications with running progress are sticky ) { return true; } @@ -453,6 +449,14 @@ export class NotificationViewItem extends Disposable implements INotificationVie return false; // not sticky } + hasPrompt(): boolean { + if (!this._actions.primary) { + return false; + } + + return this._actions.primary.length > 0; + } + hasProgress(): boolean { return !!this._progress; } @@ -470,7 +474,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie return this._message; } - get source(): string { + get source(): string | undefined { return this._source; } @@ -543,13 +547,13 @@ export class NotificationViewItem extends Disposable implements INotificationVie return false; } - const primaryActions = this._actions.primary; - const otherPrimaryActions = other.actions.primary; - if (primaryActions.length !== otherPrimaryActions.length) { + if (this._message.value !== other.message.value) { return false; } - if (this._message.value !== other.message.value) { + const primaryActions = this._actions.primary || []; + const otherPrimaryActions = other.actions.primary || []; + if (primaryActions.length !== otherPrimaryActions.length) { return false; } @@ -571,7 +575,7 @@ export class ChoiceAction extends Action { private _keepOpen: boolean; constructor(id: string, choice: IPromptChoice) { - super(id, choice.label, null, true, () => { + super(id, choice.label, void 0, true, () => { // Pass to runner choice.run(); @@ -582,7 +586,7 @@ export class ChoiceAction extends Action { return Promise.resolve(void 0); }); - this._keepOpen = choice.keepOpen; + this._keepOpen = !!choice.keepOpen; } get keepOpen(): boolean { diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 6ee19a19cec..a5ffddb8e59 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -122,7 +122,7 @@ export const TAB_ACTIVE_FOREGROUND = registerColor('tab.activeForeground', { export const TAB_INACTIVE_FOREGROUND = registerColor('tab.inactiveForeground', { dark: transparent(TAB_ACTIVE_FOREGROUND, 0.5), - light: transparent(TAB_ACTIVE_FOREGROUND, 0.5), + light: transparent(TAB_ACTIVE_FOREGROUND, 0.7), hc: Color.white }, nls.localize('tabInactiveForeground', "Inactive tab foreground color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 7d168a3455f..2148acc60e4 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -17,6 +17,7 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { values } from 'vs/base/common/map'; import { Registry } from 'vs/platform/registry/common/platform'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IAction } from 'vs/base/common/actions'; export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test'; @@ -218,7 +219,7 @@ export interface IView { export interface IViewsViewlet extends IViewlet { - openView(id: string, focus?: boolean): TPromise; + openView(id: string, focus?: boolean): IView; } @@ -238,6 +239,10 @@ export interface ITreeViewer extends IDisposable { dataProvider: ITreeViewDataProvider; + showCollapseAllAction: boolean; + + readonly visible: boolean; + readonly onDidExpandItem: Event; readonly onDidCollapseItem: Event; @@ -246,7 +251,7 @@ export interface ITreeViewer extends IDisposable { readonly onDidChangeVisibility: Event; - readonly visible: boolean; + readonly onDidChangeActions: Event; refresh(treeItems?: ITreeItem[]): TPromise; @@ -260,7 +265,27 @@ export interface ITreeViewer extends IDisposable { getOptimalWidth(): number; - reveal(item: ITreeItem, parentChain: ITreeItem[], options: { select?: boolean }): TPromise; + reveal(item: ITreeItem): TPromise; + + expand(itemOrItems: ITreeItem | ITreeItem[]): TPromise; + + setSelection(items: ITreeItem[]): void; + + setFocus(item: ITreeItem): void; + + getPrimaryActions(): IAction[]; + + getSecondaryActions(): IAction[]; +} + +export interface IRevealOptions { + + select?: boolean; + + focus?: boolean; + + expand?: boolean | number; + } export interface ICustomViewDescriptor extends IViewDescriptor { @@ -280,6 +305,14 @@ export enum TreeItemCollapsibleState { Expanded = 2 } +export interface ITreeItemLabel { + + label: string; + + highlights?: [number, number][]; + +} + export interface ITreeItem { handle: string; @@ -288,7 +321,7 @@ export interface ITreeItem { collapsibleState: TreeItemCollapsibleState; - label?: string; + label?: ITreeItemLabel; icon?: UriComponents; @@ -311,4 +344,4 @@ export interface ITreeViewDataProvider { getChildren(element?: ITreeItem): TPromise; -} \ No newline at end of file +} diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index e43c1246438..41c99e534f8 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -735,27 +735,27 @@ export abstract class BaseNavigationAction extends Action { } if (isSidebarFocus) { - return this.navigateOnSidebarFocus(isSidebarPositionLeft, isPanelPositionDown); + return Promise.resolve(this.navigateOnSidebarFocus(isSidebarPositionLeft, isPanelPositionDown)); } return Promise.resolve(false); } - protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { return Promise.resolve(true); } - protected navigateOnPanelFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + protected navigateOnPanelFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { return Promise.resolve(true); } - protected navigateOnSidebarFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { - return Promise.resolve(true); + protected navigateOnSidebarFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean | IViewlet { + return true; } - protected navigateToPanel(): Thenable { + protected navigateToPanel(): IPanel | boolean { if (!this.partService.isVisible(Parts.PANEL_PART)) { - return Promise.resolve(false); + return false; } const activePanelId = this.panelService.getActivePanel().getId(); @@ -763,7 +763,7 @@ export abstract class BaseNavigationAction extends Action { return this.panelService.openPanel(activePanelId, true); } - protected navigateToSidebar(): Promise { + protected navigateToSidebar(): Thenable { if (!this.partService.isVisible(Parts.SIDEBAR_PART)) { return Promise.resolve(false); } @@ -773,23 +773,23 @@ export abstract class BaseNavigationAction extends Action { return this.viewletService.openViewlet(activeViewletId, true); } - protected navigateAcrossEditorGroup(direction: GroupDirection): Promise { + protected navigateAcrossEditorGroup(direction: GroupDirection): boolean { return this.doNavigateToEditorGroup({ direction }); } - protected navigateToEditorGroup(location: GroupLocation): Promise { + protected navigateToEditorGroup(location: GroupLocation): boolean { return this.doNavigateToEditorGroup({ location }); } - private doNavigateToEditorGroup(scope: IFindGroupScope): Promise { + private doNavigateToEditorGroup(scope: IFindGroupScope): boolean { const targetGroup = this.editorGroupService.findGroup(scope, this.editorGroupService.activeGroup); if (targetGroup) { targetGroup.focus(); - return Promise.resolve(true); + return true; } - return Promise.resolve(false); + return false; } } @@ -809,39 +809,37 @@ export class NavigateLeftAction extends BaseNavigationAction { super(id, label, editorGroupService, panelService, partService, viewletService); } - protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { - return this.navigateAcrossEditorGroup(GroupDirection.LEFT) - .then(didNavigate => { - if (didNavigate) { - return Promise.resolve(true); - } + protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { + const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.LEFT); + if (didNavigate) { + return Promise.resolve(true); + } - if (isSidebarPositionLeft) { - return this.navigateToSidebar(); - } + if (isSidebarPositionLeft) { + return this.navigateToSidebar(); + } - return Promise.resolve(false); - }); + return Promise.resolve(false); } - protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { + protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Thenable { if (isPanelPositionDown && isSidebarPositionLeft) { return this.navigateToSidebar(); } if (!isPanelPositionDown) { - return this.navigateToEditorGroup(GroupLocation.LAST); + return Promise.resolve(this.navigateToEditorGroup(GroupLocation.LAST)); } return Promise.resolve(false); } - protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean { if (!isSidebarPositionLeft) { return this.navigateToEditorGroup(GroupLocation.LAST); } - return Promise.resolve(false); + return false; } } @@ -861,26 +859,16 @@ export class NavigateRightAction extends BaseNavigationAction { super(id, label, editorGroupService, panelService, partService, viewletService); } - protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { - return this.navigateAcrossEditorGroup(GroupDirection.RIGHT) - .then(didNavigate => { - if (didNavigate) { - return Promise.resolve(true); - } + protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Thenable { + const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.RIGHT); + if (didNavigate) { + return Promise.resolve(true); + } - if (!isPanelPositionDown) { - return this.navigateToPanel(); - } + if (!isPanelPositionDown) { + return Promise.resolve(this.navigateToPanel()); + } - if (!isSidebarPositionLeft) { - return this.navigateToSidebar(); - } - - return Promise.resolve(false); - }); - } - - protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { if (!isSidebarPositionLeft) { return this.navigateToSidebar(); } @@ -888,12 +876,20 @@ export class NavigateRightAction extends BaseNavigationAction { return Promise.resolve(false); } - protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { + if (!isSidebarPositionLeft) { + return this.navigateToSidebar(); + } + + return Promise.resolve(false); + } + + protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean { if (isSidebarPositionLeft) { return this.navigateToEditorGroup(GroupLocation.FIRST); } - return Promise.resolve(false); + return false; } } @@ -914,12 +910,12 @@ export class NavigateUpAction extends BaseNavigationAction { } protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { - return this.navigateAcrossEditorGroup(GroupDirection.UP); + return Promise.resolve(this.navigateAcrossEditorGroup(GroupDirection.UP)); } protected navigateOnPanelFocus(_isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { if (isPanelPositionDown) { - return this.navigateToEditorGroup(GroupLocation.LAST); + return Promise.resolve(this.navigateToEditorGroup(GroupLocation.LAST)); } return Promise.resolve(false); @@ -943,18 +939,16 @@ export class NavigateDownAction extends BaseNavigationAction { } protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { - return this.navigateAcrossEditorGroup(GroupDirection.DOWN) - .then(didNavigate => { - if (didNavigate) { - return Promise.resolve(true); - } + const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.DOWN); + if (didNavigate) { + return Promise.resolve(true); + } - if (isPanelPositionDown) { - return this.navigateToPanel(); - } + if (isPanelPositionDown) { + return Promise.resolve(this.navigateToPanel()); + } - return Promise.resolve(false); - }); + return Promise.resolve(false); } } @@ -1142,7 +1136,7 @@ export class ToggleWindowTabsBar extends Action { export class OpenTwitterUrlAction extends Action { static readonly ID = 'workbench.action.openTwitterUrl'; - static LABEL = nls.localize('openTwitterUrl', "Join us on Twitter", product.applicationName); + static LABEL = nls.localize('openTwitterUrl', "Join Us on Twitter", product.applicationName); constructor( id: string, diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index 5b8015378f6..880dbe5f55b 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -61,7 +61,9 @@ export function registerCommands(): void { else if (focused instanceof ObjectTree) { const list = focused; - list.focusNext(count); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.focusNext(count, false, fakeKeyboardEvent); + const listFocus = list.getFocus(); if (listFocus.length) { list.reveal(listFocus[0]); @@ -110,10 +112,12 @@ export function registerCommands(): void { const focus = list.getFocus() ? list.getFocus()[0] : void 0; const selection = list.getSelection(); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + if (selection && selection.indexOf(focus) >= 0) { - list.setSelection(selection.filter(s => s !== previousFocus)); + list.setSelection(selection.filter(s => s !== previousFocus), fakeKeyboardEvent); } else { - list.setSelection(selection.concat(focus)); + list.setSelection(selection.concat(focus), fakeKeyboardEvent); } } @@ -140,19 +144,7 @@ export function registerCommands(): void { const focused = accessor.get(IListService).lastFocusedList; // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - // Focus down first - const previousFocus = list.getFocus() ? list.getFocus()[0] : void 0; - focusDown(accessor, arg2); - - // Then adjust selection - expandMultiSelection(focused, previousFocus); - } - - // ObjectTree - else if (focused instanceof ObjectTree) { + if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree) { const list = focused; // Focus down first @@ -199,7 +191,9 @@ export function registerCommands(): void { else if (focused instanceof ObjectTree) { const list = focused; - list.focusPrevious(count); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.focusPrevious(count, false, fakeKeyboardEvent); + const listFocus = list.getFocus(); if (listFocus.length) { list.reveal(listFocus[0]); @@ -289,7 +283,8 @@ export function registerCommands(): void { const parent = tree.getParentElement(focus); if (parent) { - tree.setFocus(parent); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setFocus([parent], fakeKeyboardEvent); tree.reveal(parent); } } @@ -335,7 +330,8 @@ export function registerCommands(): void { const child = tree.getFirstElementChild(focus); if (child) { - tree.setFocus(child); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setFocus([child], fakeKeyboardEvent); tree.reveal(child); } } @@ -380,7 +376,8 @@ export function registerCommands(): void { else if (focused instanceof ObjectTree) { const list = focused; - list.focusPreviousPage(); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.focusPreviousPage(fakeKeyboardEvent); list.reveal(list.getFocus()[0]); } @@ -417,7 +414,8 @@ export function registerCommands(): void { else if (focused instanceof ObjectTree) { const list = focused; - list.focusNextPage(); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.focusNextPage(fakeKeyboardEvent); list.reveal(list.getFocus()[0]); } @@ -470,7 +468,8 @@ export function registerCommands(): void { return; } - list.setFocus([first]); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.setFocus([first], fakeKeyboardEvent); list.reveal(first); } @@ -522,7 +521,8 @@ export function registerCommands(): void { return; } - list.setFocus([last]); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.setFocus([last], fakeKeyboardEvent); list.reveal(last); } @@ -557,7 +557,8 @@ export function registerCommands(): void { // ObjectTree else if (focused instanceof ObjectTree) { const list = focused; - list.setSelection(list.getFocus()); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.setSelection(list.getFocus(), fakeKeyboardEvent); list.open(list.getFocus()); } @@ -642,11 +643,12 @@ export function registerCommands(): void { // ObjectTree else if (focused instanceof ObjectTree) { const list = focused; + const fakeKeyboardEvent = new KeyboardEvent('keydown'); if (list.getSelection().length > 0) { - list.setSelection([]); + list.setSelection([], fakeKeyboardEvent); } else if (list.getFocus().length > 0) { - list.setFocus([]); + list.setFocus([], fakeKeyboardEvent); } } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 85834585bcb..01e65c752ee 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -66,7 +66,7 @@ if (OpenTipsAndTricksUrlAction.AVAILABLE) { workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); } -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join us on Twitter', helpCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory); @@ -392,7 +392,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { group: '3_feedback', command: { id: 'workbench.action.openTwitterUrl', - title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join us on Twitter") + title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter") }, order: 1 }); @@ -521,12 +521,12 @@ configurationRegistry.registerConfiguration({ }, 'workbench.editor.enablePreview': { 'type': 'boolean', - 'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are kept (e.g. via double click or editing) and show up with an italic font style."), + 'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing) and show up with an italic font style."), 'default': true }, 'workbench.editor.enablePreviewFromQuickOpen': { 'type': 'boolean', - 'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether opened editors from Quick Open show as preview. Preview editors are reused until they are kept (e.g. via double click or editing)."), + 'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether opened editors from Quick Open show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing)."), 'default': true }, 'workbench.editor.closeOnFileDelete': { @@ -600,7 +600,7 @@ configurationRegistry.registerConfiguration({ 'workbench.settings.openDefaultKeybindings': { 'type': 'boolean', 'description': nls.localize('openDefaultKeybindings', "Controls whether opening keybinding settings also opens an editor showing all default keybindings."), - 'default': false + 'default': true }, 'workbench.sideBar.location': { 'type': 'string', @@ -677,6 +677,12 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."), 'default': true, 'tags': ['usesOnlineServices'] + }, + //TODO@Ben remove ('enableLegacyStorage') after a while + 'workbench.enableLegacyStorage': { + 'type': 'boolean', + 'description': nls.localize('workbench.enableLegacyStorage', "Switches back to the previous storage implementation. Only change this setting if advised to do so."), + 'default': false } } }); @@ -818,6 +824,19 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('window.nativeTabs', "Enables macOS Sierra window tabs. Note that changes require a full restart to apply and that native tabs will disable a custom title bar style if configured."), 'included': isMacintosh && parseFloat(os.release()) >= 16 // Minimum: macOS Sierra (10.12.x = darwin 16.x) }, + 'window.nativeFullScreen': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('window.nativeFullScreen', "Controls if native full-screen should be used on macOS. Disable this option to prevent macOS from creating a new space when going full-screen."), + 'included': isMacintosh + }, + 'window.smoothScrollingWorkaround': { + 'type': 'boolean', + 'default': false, + 'scope': ConfigurationScope.APPLICATION, + 'markdownDescription': nls.localize('window.smoothScrollingWorkaround', "Enable this workaround if scrolling is no longer smooth after restoring a minimized VS Code window. This is a workaround for an issue (https://github.com/Microsoft/vscode/issues/13612) where scrolling starts to lag on devices with precision trackpads like the Surface devices from Microsoft. Enabling this workaround can result in a little bit of layout flickering after restoring the window from minimized state but is otherwise harmless. Note: in order for this workaround to function, make sure to also set `#window.titleBarStyle#` to `native`."), + 'included': isWindows + }, 'window.clickThroughInactive': { 'type': 'boolean', 'default': true, diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 5e43bb79c31..f3422142fce 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -8,15 +8,15 @@ import * as perf from 'vs/base/common/performance'; import { WorkbenchShell } from 'vs/workbench/electron-browser/shell'; import * as browser from 'vs/base/browser/browser'; import { domContentLoaded } from 'vs/base/browser/dom'; -import * as errors from 'vs/base/common/errors'; +import { onUnexpectedError } from 'vs/base/common/errors'; import * as comparer from 'vs/base/common/comparers'; import * as platform from 'vs/base/common/platform'; import { URI as uri } from 'vs/base/common/uri'; import { IWorkspaceContextService, Workspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; +import { WorkspaceService, ISingleFolderWorkspaceInitializationPayload, IMultiFolderWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, IWorkspaceInitializationPayload, isSingleFolderWorkspaceInitializationPayload } from 'vs/workbench/services/configuration/node/configurationService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { stat } from 'vs/base/node/pfs'; +import { stat, exists, writeFile, readdir } from 'vs/base/node/pfs'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import * as gracefulFs from 'graceful-fs'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; @@ -31,11 +31,11 @@ import { IUpdateService } from 'vs/platform/update/common/update'; import { URLHandlerChannel, URLServiceChannelClient } from 'vs/platform/url/node/urlIpc'; import { IURLService } from 'vs/platform/url/common/url'; import { WorkspacesChannelClient } from 'vs/platform/workspaces/node/workspacesIpc'; -import { IWorkspacesService, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesService, ISingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import * as fs from 'fs'; import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log'; -import { StorageService, DelegatingStorageService } from 'vs/platform/storage/electron-browser/storageService'; +import { StorageService, DelegatingStorageService } from 'vs/platform/storage/node/storageService'; import { IssueChannelClient } from 'vs/platform/issue/node/issueIpc'; import { IIssueService } from 'vs/platform/issue/common/issue'; import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; @@ -43,7 +43,13 @@ import { RelayURLService } from 'vs/platform/url/common/urlService'; import { MenubarChannelClient } from 'vs/platform/menubar/node/menubarIpc'; import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { Schemas } from 'vs/base/common/network'; -import { sanitizeFilePath } from 'vs/base/node/extfs'; +import { sanitizeFilePath, mkdirp } from 'vs/base/node/extfs'; +import { basename, join } from 'path'; +import { createHash } from 'crypto'; +import { parseStorage, StorageObject } from 'vs/platform/storage/common/storageLegacyMigration'; +import { StorageScope } from 'vs/platform/storage/common/storage'; +import { endsWith } from 'vs/base/common/strings'; +import { IdleValue } from 'vs/base/common/async'; gracefulFs.gracefulify(fs); // enable gracefulFs @@ -65,7 +71,13 @@ export function startup(configuration: IWindowConfiguration): Promise { KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(); // Setup Intl for comparers - comparer.setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })); + comparer.setFileNameComparer(new IdleValue(() => { + const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); + return { + collator: collator, + collatorIsNumeric: collator.resolvedOptions().numeric + }; + })); // Open workbench return openWorkbench(configuration); @@ -97,83 +109,333 @@ function openWorkbench(configuration: IWindowConfiguration): Promise { const logService = createLogService(mainProcessClient, configuration, environmentService); logService.trace('openWorkbench configuration', JSON.stringify(configuration)); - return Promise.all([ - createAndInitializeWorkspaceService(configuration, environmentService), - createStorageService(environmentService, logService) - ]).then(services => { - const workspaceService = services[0]; - const storageLegacyService = createStorageLegacyService(workspaceService, environmentService); - const storageService = new DelegatingStorageService(services[1], storageLegacyService, logService); + // Resolve a workspace payload that we can get the workspace ID from + return createWorkspaceInitializationPayload(configuration, environmentService).then(payload => { - return domContentLoaded().then(() => { - perf.mark('willStartWorkbench'); + return Promise.all([ - // Create Shell - const shell = new WorkbenchShell(document.body, { - contextService: workspaceService, - configurationService: workspaceService, - environmentService, - logService, - storageLegacyService, - storageService - }, mainServices, mainProcessClient, configuration); + // Create and initialize workspace/configuration service + createWorkspaceService(payload, environmentService, logService), - // Gracefully Shutdown Storage - shell.onShutdown(event => { - event.join(storageService.close()); - }); + // Create and initialize storage service + createStorageService(payload, environmentService, logService) + ]).then(services => { + const workspaceService = services[0]; + const storageService = new DelegatingStorageService(services[1], createStorageLegacyService(workspaceService, environmentService), logService, workspaceService); - // Open Shell - shell.open(); + return domContentLoaded().then(() => { + perf.mark('willStartWorkbench'); - // Inform user about loading issues from the loader - (self).require.config({ - onError: err => { - if (err.errorCode === 'load') { - shell.onUnexpectedError(new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err)))); + // Create Shell + const shell = new WorkbenchShell(document.body, { + contextService: workspaceService, + configurationService: workspaceService, + environmentService, + logService, + storageService + }, mainServices, mainProcessClient, configuration); + + // Store meta file in workspace storage after workbench is running + shell.onRunning(() => { + ensureWorkspaceStorageFolderMeta(payload, workspaceService, environmentService); + }); + + // Gracefully Shutdown Storage + shell.onShutdown(event => { + event.join(storageService.close()); + }); + + // Open Shell + shell.open(); + + // Inform user about loading issues from the loader + (self).require.config({ + onError: err => { + if (err.errorCode === 'load') { + shell.onUnexpectedError(new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err)))); + } } - } + }); }); }); }); } -function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService): Promise { - return validateFolderUri(configuration.folderUri, configuration.verbose).then(validatedFolderUri => { - const workspaceService = new WorkspaceService(environmentService); +function createWorkspaceInitializationPayload(configuration: IWindowConfiguration, environmentService: EnvironmentService): Promise { - return workspaceService.initialize(configuration.workspace || validatedFolderUri || configuration).then(() => workspaceService, error => workspaceService); - }); -} - -function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose: boolean): Promise { - - // Return early if we do not have a single folder uri or if it is a non file uri - if (!folderUri || folderUri.scheme !== Schemas.file) { - return Promise.resolve(folderUri); + // Multi-root workspace + if (configuration.workspace) { + return Promise.resolve(configuration.workspace as IMultiFolderWorkspaceInitializationPayload); } - // Ensure absolute existing folder path - const sanitizedFolderPath = sanitizeFilePath(folderUri.fsPath, process.env['VSCODE_CWD'] || process.cwd()); - return stat(sanitizedFolderPath).then(stat => uri.file(sanitizedFolderPath), error => { - if (verbose) { - errors.onUnexpectedError(error); + // Single-folder workspace + let workspaceInitializationPayload: Promise = Promise.resolve(void 0); + if (configuration.folderUri) { + workspaceInitializationPayload = resolveSingleFolderWorkspaceInitializationPayload(configuration.folderUri); + } + + return workspaceInitializationPayload.then(payload => { + + // Fallback to empty workspace if we have no payload yet. + if (!payload) { + let id: string; + if (configuration.backupPath) { + id = basename(configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID + } else if (environmentService.isExtensionDevelopment) { + id = 'ext-dev'; // extension development window never stores backups and is a singleton + } else { + return Promise.reject(new Error('Unexpected window configuration without backupPath')); + } + + payload = { id } as IEmptyWorkspaceInitializationPayload; } - // Treat any error case as empty workbench case (no folder path) - return null; + return payload; }); } -function createStorageService(environmentService: IEnvironmentService, logService: ILogService): Promise { - const storageService = new StorageService(':memory:', logService, environmentService); +function resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFolderWorkspaceIdentifier): Promise { - return storageService.init().then(() => storageService); + // Return early the folder is not local + if (folderUri.scheme !== Schemas.file) { + return Promise.resolve({ id: createHash('md5').update(folderUri.toString()).digest('hex'), folder: folderUri }); + } + + function computeLocalDiskFolderId(folder: uri, stat: fs.Stats): string { + let ctime: number; + if (platform.isLinux) { + ctime = stat.ino; // Linux: birthtime is ctime, so we cannot use it! We use the ino instead! + } else if (platform.isMacintosh) { + ctime = stat.birthtime.getTime(); // macOS: birthtime is fine to use as is + } else if (platform.isWindows) { + if (typeof stat.birthtimeMs === 'number') { + ctime = Math.floor(stat.birthtimeMs); // Windows: fix precision issue in node.js 8.x to get 7.x results (see https://github.com/nodejs/node/issues/19897) + } else { + ctime = stat.birthtime.getTime(); + } + } + + // we use the ctime as extra salt to the ID so that we catch the case of a folder getting + // deleted and recreated. in that case we do not want to carry over previous state + return createHash('md5').update(folder.fsPath).update(ctime ? String(ctime) : '').digest('hex'); + } + + // For local: ensure path is absolute and exists + const sanitizedFolderPath = sanitizeFilePath(folderUri.fsPath, process.env['VSCODE_CWD'] || process.cwd()); + return stat(sanitizedFolderPath).then(stat => { + const sanitizedFolderUri = uri.file(sanitizedFolderPath); + return { + id: computeLocalDiskFolderId(sanitizedFolderUri, stat), + folder: sanitizedFolderUri + } as ISingleFolderWorkspaceInitializationPayload; + }, error => onUnexpectedError(error)); +} + +function getWorkspaceStoragePath(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService): string { + return join(environmentService.workspaceStorageHome, payload.id); // workspace home + workspace id; +} + +function prepareWorkspaceStorageFolder(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService): Thenable { + const workspaceStoragePath = getWorkspaceStoragePath(payload, environmentService); + + return exists(workspaceStoragePath).then(exists => { + if (exists) { + return workspaceStoragePath; + } + + return mkdirp(workspaceStoragePath).then(() => workspaceStoragePath); + }); +} + +function ensureWorkspaceStorageFolderMeta(payload: IWorkspaceInitializationPayload, workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): void { + const state = workspaceService.getWorkbenchState(); + if (state === WorkbenchState.EMPTY) { + return; // no storage meta for empty workspaces + } + + const workspaceStorageMetaPath = join(getWorkspaceStoragePath(payload, environmentService), 'workspace.json'); + + exists(workspaceStorageMetaPath).then(exists => { + if (exists) { + return void 0; // already existing + } + + const workspace = workspaceService.getWorkspace(); + + return writeFile(workspaceStorageMetaPath, JSON.stringify({ + configuration: workspace.configuration ? uri.revive(workspace.configuration).toString() : void 0, + folder: state === WorkbenchState.FOLDER ? uri.revive(workspace.folders[0].uri).toString() : void 0 + }, undefined, 2)); + }).then(null, error => onUnexpectedError(error)); +} + +function createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService): Promise { + const workspaceService = new WorkspaceService(environmentService); + + return workspaceService.initialize(payload).then(() => workspaceService, error => { + onUnexpectedError(error); + logService.error(error); + + return workspaceService; + }); +} + +function createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService): Thenable { + + // Prepare the workspace storage folder + return prepareWorkspaceStorageFolder(payload, environmentService).then(workspaceStorageFolder => { + + // Return early if we are using in-memory storage + const useInMemoryStorage = !!environmentService.extensionTestsPath; /* never keep any state when running extension tests */ + if (useInMemoryStorage) { + const storageService = new StorageService(StorageService.IN_MEMORY_PATH, true, logService, environmentService); + + return storageService.init().then(() => storageService); + } + + // Otherwise do a migration of previous workspace data if the DB does not exist yet + // TODO@Ben remove me after one milestone + const workspaceStorageDBPath = join(workspaceStorageFolder, 'storage.db'); + perf.mark('willCheckWorkspaceStorageExists'); + return exists(workspaceStorageDBPath).then(exists => { + perf.mark('didCheckWorkspaceStorageExists'); + + const storageService = new StorageService(workspaceStorageDBPath, true, logService, environmentService); + + return storageService.init().then(() => { + if (exists) { + return storageService; // return early if DB was already there + } + + perf.mark('willMigrateWorkspaceStorageKeys'); + return readdir(environmentService.extensionsPath).then(extensions => { + + // Otherwise, we migrate data from window.localStorage over + try { + const parsedStorage = parseStorage(window.localStorage); + + let workspaceItems: StorageObject; + if (isWorkspaceIdentifier(payload)) { + workspaceItems = parsedStorage.multiRoot.get(`root:${payload.id}`); + } else if (isSingleFolderWorkspaceInitializationPayload(payload)) { + workspaceItems = parsedStorage.folder.get(payload.folder.toString()); + } else { + if (payload.id === 'ext-dev') { + workspaceItems = parsedStorage.noWorkspace; + } else { + workspaceItems = parsedStorage.empty.get(`empty:${payload.id}`); + } + } + + const supportedKeys = new Map(); + [ + 'workbench.search.history', + 'history.entries', + 'ignoreNetVersionError', + 'ignoreEnospcError', + 'extensionUrlHandler.urlToHandle', + 'terminal.integrated.isWorkspaceShellAllowed', + 'workbench.tasks.ignoreTask010Shown', + 'workbench.tasks.recentlyUsedTasks', + 'workspaces.dontPromptToOpen', + 'output.activechannel', + 'outline/state', + 'extensionsAssistant/workspaceRecommendationsIgnore', + 'extensionsAssistant/dynamicWorkspaceRecommendations', + 'debug.repl.history', + 'editor.matchCase', + 'editor.wholeWord', + 'editor.isRegex', + 'lifecyle.lastShutdownReason', + 'debug.selectedroot', + 'debug.selectedconfigname', + 'debug.breakpoint', + 'debug.breakpointactivated', + 'debug.functionbreakpoint', + 'debug.exceptionbreakpoint', + 'debug.watchexpressions', + 'workbench.sidebar.activeviewletid', + 'workbench.panelpart.activepanelid', + 'workbench.zenmode.active', + 'workbench.centerededitorlayout.active', + 'workbench.sidebar.restore', + 'workbench.sidebar.hidden', + 'workbench.panel.hidden', + 'workbench.panel.location', + 'extensionsIdentifiers/disabled', + 'extensionsIdentifiers/enabled', + 'scm.views', + 'suggest/memories/first', + 'suggest/memories/recentlyUsed', + 'suggest/memories/recentlyUsedByPrefix', + 'workbench.view.explorer.numberOfVisibleViews', + 'workbench.view.extensions.numberOfVisibleViews', + 'workbench.view.debug.numberOfVisibleViews', + 'workbench.explorer.views.state', + 'workbench.view.extensions.state', + 'workbench.view.debug.state', + 'memento/workbench.editor.walkThroughPart', + 'memento/workbench.editor.settings2', + 'memento/workbench.editor.htmlPreviewPart', + 'memento/workbench.editor.defaultPreferences', + 'memento/workbench.editors.files.textFileEditor', + 'memento/workbench.editors.logViewer', + 'memento/workbench.editors.textResourceEditor', + 'memento/workbench.panel.output' + ].forEach(key => supportedKeys.set(key.toLowerCase(), key)); + + // Support extension storage as well (always the ID of the extension) + extensions.forEach(extension => { + let extensionId: string; + if (extension.indexOf('-') >= 0) { + extensionId = extension.substring(0, extension.lastIndexOf('-')); // convert "author.extension-0.2.5" => "author.extension" + } else { + extensionId = extension; + } + + if (extensionId) { + supportedKeys.set(extensionId.toLowerCase(), extensionId); + } + }); + + if (workspaceItems) { + Object.keys(workspaceItems).forEach(key => { + const value = workspaceItems[key]; + + // first check for a well known supported key and store with realcase value + const supportedKey = supportedKeys.get(key); + if (supportedKey) { + storageService.store(supportedKey, value, StorageScope.WORKSPACE); + } + + // fix lowercased ".numberOfVisibleViews" + else if (endsWith(key, '.numberOfVisibleViews'.toLowerCase())) { + const normalizedKey = key.substring(0, key.length - '.numberOfVisibleViews'.length) + '.numberOfVisibleViews'; + storageService.store(normalizedKey, value, StorageScope.WORKSPACE); + } + + // support dynamic keys + else if (key.indexOf('memento/') === 0 || key.indexOf('viewservice.') === 0 || endsWith(key, '.state')) { + storageService.store(key, value, StorageScope.WORKSPACE); + } + }); + } + } catch (error) { + onUnexpectedError(error); + logService.error(error); + } + + perf.mark('didMigrateWorkspaceStorageKeys'); + + return storageService; + }); + }); + }); + }); } function createStorageLegacyService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageLegacyService { let workspaceId: string; - let secondaryWorkspaceId: number; switch (workspaceService.getWorkbenchState()) { @@ -187,7 +449,6 @@ function createStorageLegacyService(workspaceService: IWorkspaceContextService, case WorkbenchState.FOLDER: const workspace: Workspace = workspaceService.getWorkspace(); workspaceId = workspace.folders[0].uri.toString(); - secondaryWorkspaceId = workspace.ctime; break; // finally, if we do not have a workspace open, we need to find another identifier for the window to store @@ -210,7 +471,7 @@ function createStorageLegacyService(workspaceService: IWorkspaceContextService, storage = window.localStorage; } - return new StorageLegacyService(storage, storage, workspaceId, secondaryWorkspaceId); + return new StorageLegacyService(storage, storage, workspaceId); } function createLogService(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration, environmentService: IEnvironmentService): ILogService { @@ -229,7 +490,7 @@ function createMainProcessServices(mainProcessClient: ElectronIPCClient, configu serviceCollection.set(IWindowsService, new WindowsChannelClient(windowsChannel)); const updateChannel = mainProcessClient.getChannel('update'); - serviceCollection.set(IUpdateService, new SyncDescriptor(UpdateChannelClient, updateChannel)); + serviceCollection.set(IUpdateService, new SyncDescriptor(UpdateChannelClient, [updateChannel])); const urlChannel = mainProcessClient.getChannel('url'); const mainUrlService = new URLServiceChannelClient(urlChannel); @@ -240,13 +501,13 @@ function createMainProcessServices(mainProcessClient: ElectronIPCClient, configu mainProcessClient.registerChannel('urlHandler', urlHandlerChannel); const issueChannel = mainProcessClient.getChannel('issue'); - serviceCollection.set(IIssueService, new SyncDescriptor(IssueChannelClient, issueChannel)); + serviceCollection.set(IIssueService, new SyncDescriptor(IssueChannelClient, [issueChannel])); const menubarChannel = mainProcessClient.getChannel('menubar'); - serviceCollection.set(IMenubarService, new SyncDescriptor(MenubarChannelClient, menubarChannel)); + serviceCollection.set(IMenubarService, new SyncDescriptor(MenubarChannelClient, [menubarChannel])); const workspacesChannel = mainProcessClient.getChannel('workspaces'); serviceCollection.set(IWorkspacesService, new WorkspacesChannelClient(workspacesChannel)); return serviceCollection; -} \ No newline at end of file +} diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 56d2972a7a0..d615bf8a01a 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -39,7 +39,6 @@ import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService'; -import { IStorageLegacyService } from 'vs/platform/storage/common/storageLegacyService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { InstantiationService } from 'vs/platform/instantiation/node/instantiationService'; @@ -76,7 +75,7 @@ import { HashService } from 'vs/workbench/services/hash/node/hashService'; import { IHashService } from 'vs/workbench/services/hash/common/hashService'; import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { DelegatingStorageService } from 'vs/platform/storage/electron-browser/storageService'; +import { DelegatingStorageService } from 'vs/platform/storage/node/storageService'; import { Event, Emitter } from 'vs/base/common/event'; import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; import { ILocalizationsChannel, LocalizationsChannelClient } from 'vs/platform/localizations/node/localizationsIpc'; @@ -88,7 +87,7 @@ import { NotificationService } from 'vs/workbench/services/notification/common/n import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { DialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService'; import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; -import { EventType, addDisposableListener, addClass } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, addClass, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { SearchHistoryService } from 'vs/workbench/services/search/node/searchHistoryService'; @@ -109,7 +108,6 @@ export interface ICoreServices { configurationService: IConfigurationService; environmentService: IEnvironmentService; logService: ILogService; - storageLegacyService: IStorageLegacyService; storageService: DelegatingStorageService; } @@ -122,7 +120,9 @@ export class WorkbenchShell extends Disposable { private readonly _onShutdown = this._register(new Emitter()); get onShutdown(): Event { return this._onShutdown.event; } - private storageLegacyService: IStorageLegacyService; + private readonly _onRunning = this._register(new Emitter()); + get onRunning(): Event { return this._onRunning.event; } + private storageService: DelegatingStorageService; private environmentService: IEnvironmentService; private logService: ILogService; @@ -153,7 +153,6 @@ export class WorkbenchShell extends Disposable { this.configurationService = coreServices.configurationService; this.environmentService = coreServices.environmentService; this.logService = coreServices.logService; - this.storageLegacyService = coreServices.storageLegacyService; this.storageService = coreServices.storageService; this.mainProcessServices = mainProcessServices; @@ -212,8 +211,10 @@ export class WorkbenchShell extends Disposable { // Startup Workbench workbench.startup().then(startupInfos => { - // Set lifecycle phase to `Runnning` so that other contributions can now do something + // Set lifecycle phase to `Runnning` so that other contributions can + // now do something we also emit this as event to interested parties outside this.lifecycleService.phase = LifecyclePhase.Running; + this._onRunning.fire(); // Startup Telemetry this.logStartupTelemetry(startupInfos); @@ -284,13 +285,17 @@ export class WorkbenchShell extends Disposable { } private logStorageTelemetry(): void { - const globalStorageInitDuration = perf.getDuration('willInitGlobalStorage', 'didInitGlobalStorage'); - const workspaceStorageInitDuration = perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage'); - const workbenchLoadDuration = perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'); - const localStorageAccessDuration = perf.getDuration('willAccessLocalStorage', 'didAccessLocalStorage'); - const localStorageReadDuration = perf.getDuration('willReadLocalStorage', 'didReadLocalStorage'); + const initialStartup = !!this.configuration.isInitialStartup; - let workspaceIntegrity: string; + const appReadyDuration = initialStartup ? perf.getDuration('main:started', 'main:appReady') : 0; + const workbenchReadyDuration = perf.getDuration(initialStartup ? 'main:started' : 'main:loadWindow', 'didStartWorkbench'); + const workspaceStorageRequireDuration = perf.getDuration('willRequireSQLite', 'didRequireSQLite'); + const workspaceStorageSchemaDuration = perf.getDuration('willSetupSQLiteSchema', 'didSetupSQLiteSchema'); + const workspaceStorageInitDuration = perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage'); + const workspaceStorageFileExistsDuration = perf.getDuration('willCheckWorkspaceStorageExists', 'didCheckWorkspaceStorageExists'); + const workspaceStorageMigrationDuration = perf.getDuration('willMigrateWorkspaceStorageKeys', 'didMigrateWorkspaceStorageKeys'); + const workbenchLoadDuration = perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'); + const localStorageDuration = perf.getDuration('willReadLocalStorage', 'didReadLocalStorage'); // Handle errors (avoid duplicates to reduce spam) const loggedStorageErrors = new Set(); @@ -301,67 +306,75 @@ export class WorkbenchShell extends Disposable { loggedStorageErrors.add(errorStr); /* __GDPR__ - "sqliteStorageError" : { - "globalReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "sqliteStorageError3" : { + "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceExistsTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageAccessTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceMigrationTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "localStorageTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceIntegrity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "storageError": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "storageError": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('sqliteStorageError', { - 'globalReadTime': globalStorageInitDuration, + this.telemetryService.publicLog('sqliteStorageError3', { + 'appReadyTime': appReadyDuration, + 'workbenchReadyTime': workbenchReadyDuration, + 'workspaceExistsTime': workspaceStorageFileExistsDuration, + 'workspaceMigrationTime': workspaceStorageMigrationDuration, + 'workspaceRequireTime': workspaceStorageRequireDuration, + 'workspaceSchemaTime': workspaceStorageSchemaDuration, 'workspaceReadTime': workspaceStorageInitDuration, - 'localStorageAccessTime': localStorageAccessDuration, - 'localStorageReadTime': localStorageReadDuration, + 'localStorageTime': localStorageDuration, 'workbenchRequireTime': workbenchLoadDuration, - 'globalKeys': this.storageService.storage.getSize(StorageScope.GLOBAL), 'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE), 'startupKind': this.lifecycleService.startupKind, - 'workspaceIntegrity': workspaceIntegrity, 'storageError': errorStr }); } })); - perf.mark('willCheckWorkspaceStorageIntegrity'); - this.storageService.storage.checkIntegrity(StorageScope.WORKSPACE, false).then(integrity => { - perf.mark('didCheckWorkspaceStorageIntegrity'); - workspaceIntegrity = integrity; + if (this.storageService.storage.hasErrors) { + return; // do not log performance numbers when errors occured + } - /* __GDPR__ - "sqliteStorageTimers" : { - "globalReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageAccessTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceIntegrity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceIntegrityCheckTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('sqliteStorageTimers', { - 'globalReadTime': globalStorageInitDuration, - 'workspaceReadTime': workspaceStorageInitDuration, - 'localStorageAccessTime': localStorageAccessDuration, - 'localStorageReadTime': localStorageReadDuration, - 'workspaceIntegrity': workspaceIntegrity, - 'workspaceIntegrityCheckTime': perf.getDuration('willCheckWorkspaceStorageIntegrity', 'didCheckWorkspaceStorageIntegrity'), - 'workbenchRequireTime': workbenchLoadDuration, - 'globalKeys': this.storageService.storage.getSize(StorageScope.GLOBAL), - 'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE), - 'startupKind': this.lifecycleService.startupKind - }); - }, error => errors.onUnexpectedError(error)); + if (this.environmentService.verbose) { + return; // do not log when running in verbose mode + } + + /* __GDPR__ + "sqliteStorageTimers3" : { + "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceExistsTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceMigrationTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "localStorageTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('sqliteStorageTimers3', { + 'appReadyTime': appReadyDuration, + 'workbenchReadyTime': workbenchReadyDuration, + 'workspaceExistsTime': workspaceStorageFileExistsDuration, + 'workspaceMigrationTime': workspaceStorageMigrationDuration, + 'workspaceRequireTime': workspaceStorageRequireDuration, + 'workspaceSchemaTime': workspaceStorageSchemaDuration, + 'workspaceReadTime': workspaceStorageInitDuration, + 'localStorageTime': localStorageDuration, + 'workbenchRequireTime': workbenchLoadDuration, + 'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE), + 'startupKind': this.lifecycleService.startupKind + }); } private initServiceCollection(container: HTMLElement): [IInstantiationService, ServiceCollection] { @@ -371,7 +384,6 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IEnvironmentService, this.environmentService); serviceCollection.set(ILabelService, new SyncDescriptor(LabelService)); serviceCollection.set(ILogService, this._register(this.logService)); - serviceCollection.set(IStorageLegacyService, this.storageLegacyService); serviceCollection.set(IStorageService, this.storageService); this.mainProcessServices.forEach((serviceIdentifier, serviceInstance) => { @@ -386,7 +398,7 @@ export class WorkbenchShell extends Disposable { this.broadcastService = instantiationService.createInstance(BroadcastService, this.configuration.windowId); serviceCollection.set(IBroadcastService, this.broadcastService); - serviceCollection.set(IWindowService, new SyncDescriptor(WindowService, this.configuration.windowId, this.configuration)); + serviceCollection.set(IWindowService, new SyncDescriptor(WindowService, [this.configuration.windowId, this.configuration])); const sharedProcess = (serviceCollection.get(IWindowsService)).whenSharedProcessReady() .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${this.configuration.windowId}`)); @@ -439,7 +451,7 @@ export class WorkbenchShell extends Disposable { const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions'))); const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel, DefaultURITransformer); - serviceCollection.set(IExtensionManagementServerService, new SyncDescriptor(ExtensionManagementServerService, extensionManagementChannelClient)); + serviceCollection.set(IExtensionManagementServerService, new SyncDescriptor(ExtensionManagementServerService, [extensionManagementChannelClient])); serviceCollection.set(IExtensionManagementService, extensionManagementChannelClient); const extensionEnablementService = this._register(instantiationService.createInstance(ExtensionEnablementService)); @@ -481,7 +493,7 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IIntegrityService, new SyncDescriptor(IntegrityServiceImpl)); const localizationsChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('localizations'))); - serviceCollection.set(ILocalizationsService, new SyncDescriptor(LocalizationsChannelClient, localizationsChannel)); + serviceCollection.set(ILocalizationsService, new SyncDescriptor(LocalizationsChannelClient, [localizationsChannel])); return [instantiationService, serviceCollection]; } @@ -517,13 +529,25 @@ export class WorkbenchShell extends Disposable { } private registerListeners(): void { + this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true))); + } - // Resize - this._register(addDisposableListener(window, EventType.RESIZE, e => { - if (e.target === window) { - this.layout(); + private onWindowResize(e: any, retry: boolean): void { + if (e.target === window) { + if (window.document && window.document.body && window.document.body.clientWidth === 0) { + // TODO@Ben this is an electron issue on macOS when simple fullscreen is enabled + // where for some reason the window clientWidth is reported as 0 when switching + // between simple fullscreen and normal screen. In that case we schedule the layout + // call at the next animation frame once, in the hope that the dimensions are + // proper then. + if (retry) { + scheduleAtNextAnimationFrame(() => this.onWindowResize(e, false)); + } + return; } - })); + + this.layout(); + } } onUnexpectedError(error: any): void { diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index c4873f273ac..0df78cd5cee 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -237,7 +237,7 @@ export class ElectronWindow extends Themable { this.contextMenuService.showContextMenu({ getAnchor: () => e, - getActions: () => TPromise.as(TextInputActions), + getActions: () => TextInputActions, onHide: () => target.focus() // fixes https://github.com/Microsoft/vscode/issues/52948 }); } diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 7b5064c4aa2..7f4b0eda2e8 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -68,7 +68,7 @@ import { TextFileService } from 'vs/workbench/services/textfile/electron-browser import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ISCMService } from 'vs/workbench/services/scm/common/scm'; import { SCMService } from 'vs/workbench/services/scm/common/scmService'; -import { IProgressService2 } from 'vs/workbench/services/progress/common/progress'; +import { IProgressService2 } from 'vs/platform/progress/common/progress'; import { ProgressService2 } from 'vs/workbench/services/progress/browser/progressService2'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -78,7 +78,7 @@ import { LifecycleService } from 'vs/platform/lifecycle/electron-browser/lifecyc import { IWindowService, IWindowConfiguration as IWindowSettings, IWindowConfiguration, IPath, MenuBarVisibility } from 'vs/platform/windows/common/windows'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IMenuService, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { MenuService } from 'vs/workbench/services/actions/common/menuService'; +import { MenuService } from 'vs/platform/actions/common/menuService'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; @@ -114,7 +114,7 @@ import { WorkbenchThemeService } from 'vs/workbench/services/themes/electron-bro import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { FileDialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService'; -import { LogStorageAction } from 'vs/platform/storage/electron-browser/storageService'; +import { LogStorageAction } from 'vs/platform/storage/node/storageService'; interface WorkbenchParams { configuration: IWindowConfiguration; @@ -358,7 +358,7 @@ export class Workbench extends Disposable implements IPartService { // Use themable context menus when custom titlebar is enabled to match custom menubar if (!isMacintosh && this.getCustomTitleBarStyle() === 'custom') { - serviceCollection.set(IContextMenuService, new SyncDescriptor(HTMLContextMenuService, null)); + serviceCollection.set(IContextMenuService, new SyncDescriptor(HTMLContextMenuService, [null])); } else { serviceCollection.set(IContextMenuService, new SyncDescriptor(NativeContextMenuService)); } @@ -444,7 +444,7 @@ export class Workbench extends Disposable implements IPartService { serviceCollection.set(IKeybindingEditingService, this.instantiationService.createInstance(KeybindingsEditingService)); // Configuration Resolver - serviceCollection.set(IConfigurationResolverService, new SyncDescriptor(ConfigurationResolverService, process.env)); + serviceCollection.set(IConfigurationResolverService, new SyncDescriptor(ConfigurationResolverService, [process.env])); // Quick open service (quick open controller) this.quickOpen = this.instantiationService.createInstance(QuickOpenController); @@ -533,11 +533,8 @@ export class Workbench extends Disposable implements IPartService { if (visible !== this.menubarToggled) { this.menubarToggled = visible; - if (this.menubarVisibility === 'toggle' || (browser.isFullscreen() && this.menubarVisibility === 'default')) { - if (browser.isFullscreen() && this.menubarVisibility === 'default') { - this._onTitleBarVisibilityChange.fire(); - } - + if (browser.isFullscreen() && (this.menubarVisibility === 'toggle' || this.menubarVisibility === 'default')) { + this._onTitleBarVisibilityChange.fire(); this.layout(); } } @@ -757,7 +754,8 @@ export class Workbench extends Disposable implements IPartService { perf.mark('willRestorePanel'); const isPanelToRestoreEnabled = !!this.panelPart.getPanels().filter(p => p.id === panelId).length; const panelIdToRestore = isPanelToRestoreEnabled ? panelId : panelRegistry.getDefaultPanelId(); - restorePromises.push(this.panelPart.openPanel(panelIdToRestore, false).then(() => perf.mark('didRestorePanel'))); + this.panelPart.openPanel(panelIdToRestore, false); + perf.mark('didRestorePanel'); } // Restore Zen Mode if active @@ -943,7 +941,7 @@ export class Workbench extends Disposable implements IPartService { const windowConfig = this.configurationService.getValue(); if (windowConfig && windowConfig.window) { - const useNativeTabs = windowConfig.window.nativeTabs; + const useNativeTabs = isMacintosh && windowConfig.window.nativeTabs; if (useNativeTabs) { return null; // native tabs on sierra do not work with custom title style } @@ -1371,7 +1369,7 @@ export class Workbench extends Disposable implements IPartService { } } - setSideBarHidden(hidden: boolean, skipLayout?: boolean): TPromise { + setSideBarHidden(hidden: boolean, skipLayout?: boolean): void { this.sideBarHidden = hidden; this.sideBarVisibleContext.set(!hidden); @@ -1383,47 +1381,45 @@ export class Workbench extends Disposable implements IPartService { } // If sidebar becomes hidden, also hide the current active Viewlet if any - let promise = TPromise.wrap(null); if (hidden && this.sidebarPart.getActiveViewlet()) { - promise = this.sidebarPart.hideActiveViewlet().then(() => { - const activePanel = this.panelPart.getActivePanel(); + this.sidebarPart.hideActiveViewlet(); + const activePanel = this.panelPart.getActivePanel(); - // Pass Focus to Editor or Panel if Sidebar is now hidden - if (this.hasFocus(Parts.PANEL_PART) && activePanel) { - activePanel.focus(); - } else { - this.editorGroupService.activeGroup.focus(); - } - }); + // Pass Focus to Editor or Panel if Sidebar is now hidden + if (this.hasFocus(Parts.PANEL_PART) && activePanel) { + activePanel.focus(); + } else { + this.editorGroupService.activeGroup.focus(); + } } // If sidebar becomes visible, show last active Viewlet or default viewlet else if (!hidden && !this.sidebarPart.getActiveViewlet()) { const viewletToOpen = this.sidebarPart.getLastActiveViewletId(); if (viewletToOpen) { - promise = this.viewletService.openViewlet(viewletToOpen, true) - .then(viewlet => viewlet || this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true)); + const viewlet = this.viewletService.openViewlet(viewletToOpen, true); + if (!viewlet) { + this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true); + } } } - return promise.then(() => { - // Remember in settings - const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; - if (hidden !== defaultHidden) { - this.storageService.store(Workbench.sidebarHiddenStorageKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE); - } else { - this.storageService.remove(Workbench.sidebarHiddenStorageKey, StorageScope.WORKSPACE); - } + // Remember in settings + const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; + if (hidden !== defaultHidden) { + this.storageService.store(Workbench.sidebarHiddenStorageKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(Workbench.sidebarHiddenStorageKey, StorageScope.WORKSPACE); + } - // Layout - if (!skipLayout) { - this.workbenchLayout.layout(); - } - }); + // Layout + if (!skipLayout) { + this.workbenchLayout.layout(); + } } - setPanelHidden(hidden: boolean, skipLayout?: boolean): TPromise { + setPanelHidden(hidden: boolean, skipLayout?: boolean): void { this.panelHidden = hidden; // Adjust CSS @@ -1434,35 +1430,31 @@ export class Workbench extends Disposable implements IPartService { } // If panel part becomes hidden, also hide the current active panel if any - let promise = TPromise.wrap(null); if (hidden && this.panelPart.getActivePanel()) { - promise = this.panelPart.hideActivePanel().then(() => { - this.editorGroupService.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden - }); + this.panelPart.hideActivePanel(); + this.editorGroupService.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden } // If panel part becomes visible, show last active panel or default panel else if (!hidden && !this.panelPart.getActivePanel()) { const panelToOpen = this.panelPart.getLastActivePanelId(); if (panelToOpen) { - promise = this.panelPart.openPanel(panelToOpen, true); + this.panelPart.openPanel(panelToOpen, true); } } - return promise.then(() => { - // Remember in settings - if (!hidden) { - this.storageService.store(Workbench.panelHiddenStorageKey, 'false', StorageScope.WORKSPACE); - } else { - this.storageService.remove(Workbench.panelHiddenStorageKey, StorageScope.WORKSPACE); - } + // Remember in settings + if (!hidden) { + this.storageService.store(Workbench.panelHiddenStorageKey, 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(Workbench.panelHiddenStorageKey, StorageScope.WORKSPACE); + } - // Layout - if (!skipLayout) { - this.workbenchLayout.layout(); - } - }); + // Layout + if (!skipLayout) { + this.workbenchLayout.layout(); + } } toggleMaximizedPanel(): void { @@ -1518,23 +1510,25 @@ export class Workbench extends Disposable implements IPartService { return this.panelPosition; } - setPanelPosition(position: Position): TPromise { - return (this.panelHidden ? this.setPanelHidden(false, true /* Skip Layout */) : TPromise.as(undefined)).then(() => { - const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; - const oldPositionValue = (this.panelPosition === Position.BOTTOM) ? 'bottom' : 'right'; - this.panelPosition = position; - this.storageService.store(Workbench.panelPositionStorageKey, PositionToString(this.panelPosition).toLowerCase(), StorageScope.WORKSPACE); + setPanelPosition(position: Position): void { + if (this.panelHidden) { + this.setPanelHidden(false, true /* Skip Layout */); + } - // Adjust CSS - DOM.removeClass(this.panelPart.getContainer(), oldPositionValue); - DOM.addClass(this.panelPart.getContainer(), newPositionValue); + const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; + const oldPositionValue = (this.panelPosition === Position.BOTTOM) ? 'bottom' : 'right'; + this.panelPosition = position; + this.storageService.store(Workbench.panelPositionStorageKey, PositionToString(this.panelPosition).toLowerCase(), StorageScope.WORKSPACE); - // Update Styles - this.panelPart.updateStyles(); + // Adjust CSS + DOM.removeClass(this.panelPart.getContainer(), oldPositionValue); + DOM.addClass(this.panelPart.getContainer(), newPositionValue); - // Layout - this.workbenchLayout.layout(); - }); + // Update Styles + this.panelPart.updateStyles(); + + // Layout + this.workbenchLayout.layout(); } //#endregion diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index a580fc9bdeb..c0ca715b39b 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -4,24 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as pfs from 'vs/base/node/pfs'; -import { TPromise } from 'vs/base/common/winjs.base'; import { join } from 'path'; -import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; +import { timeout } from 'vs/base/common/async'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import * as errors from 'vs/base/common/errors'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Counter } from 'vs/base/common/numbers'; +import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; +import { IURITransformer } from 'vs/base/common/uriIpc'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as pfs from 'vs/base/node/pfs'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; +import { IEnvironment, IInitData, IWorkspaceData, MainContext, MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { ExtensionActivatedByEvent } from 'vs/workbench/api/node/extHostExtensionActivator'; +import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; +import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { IInitData, IEnvironment, IWorkspaceData, MainContext, MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol'; -import * as errors from 'vs/base/common/errors'; -import { ExtensionActivatedByEvent } from 'vs/workbench/api/node/extHostExtensionActivator'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; -import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; -import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; -import { timeout } from 'vs/base/common/async'; -import { Counter } from 'vs/base/common/numbers'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; // we don't (yet) throw when extensions parse // uris that have no scheme @@ -68,7 +69,8 @@ export class ExtensionHostMain { private _mainThreadWorkspace: MainThreadWorkspaceShape; constructor(protocol: IMessagePassingProtocol, initData: IInitData) { - const rpcProtocol = new RPCProtocol(protocol); + const uriTransformer: IURITransformer = null; + const rpcProtocol = new RPCProtocol(protocol, null, uriTransformer); // ensure URIs are transformed and revived initData = this.transform(initData, rpcProtocol); @@ -89,7 +91,8 @@ export class ExtensionHostMain { this._extHostLogService.trace('initData', initData); this._extHostConfiguration = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration); - this._extensionService = new ExtHostExtensionService(initData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._extHostLogService); + const mainThreadTelemetry = rpcProtocol.getProxy(MainContext.MainThreadTelemetry); + this._extensionService = new ExtHostExtensionService(initData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._extHostLogService, mainThreadTelemetry); // error forwarding and stack trace scanning Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) @@ -292,7 +295,7 @@ export class ExtensionHostMain { // Execute the runner if it follows our spec if (testRunner && typeof testRunner.run === 'function') { - return new TPromise((c, e) => { + return new Promise((c, e) => { testRunner.run(this._environment.extensionTestsPath, (error, failures) => { if (error) { e(error.toString()); @@ -315,11 +318,11 @@ export class ExtensionHostMain { } private transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData { - initData.extensions.forEach((ext) => (ext).extensionLocation = URI.revive(ext.extensionLocation)); - initData.environment.appRoot = URI.revive(initData.environment.appRoot); - initData.environment.appSettingsHome = URI.revive(initData.environment.appSettingsHome); - initData.environment.extensionDevelopmentLocationURI = URI.revive(initData.environment.extensionDevelopmentLocationURI); - initData.logsLocation = URI.revive(initData.logsLocation); + initData.extensions.forEach((ext) => (ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation))); + initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot)); + initData.environment.appSettingsHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appSettingsHome)); + initData.environment.extensionDevelopmentLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionDevelopmentLocationURI)); + initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation)); initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace); return initData; } diff --git a/src/vs/workbench/node/extensionHostProcess.ts b/src/vs/workbench/node/extensionHostProcess.ts index 7fc7fb314dd..b2de25204b0 100644 --- a/src/vs/workbench/node/extensionHostProcess.ts +++ b/src/vs/workbench/node/extensionHostProcess.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nativeWatchdog from 'native-watchdog'; +import { createConnection } from 'net'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { ExtensionHostMain, exit } from 'vs/workbench/node/extensionHostMain'; -import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; +import { Event, filterEvent } from 'vs/base/common/event'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; -import { createConnection } from 'net'; -import { Event, filterEvent } from 'vs/base/common/event'; -import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; -import * as nativeWatchdog from 'native-watchdog'; import product from 'vs/platform/node/product'; +import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; +import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; +import { ExtensionHostMain, exit } from 'vs/workbench/node/extensionHostMain'; // With Electron 2.x and node.js 8.x the "natives" module // can cause a native crash (see https://github.com/nodejs/node/issues/19891 and diff --git a/src/vs/workbench/node/proxyResolver.ts b/src/vs/workbench/node/proxyResolver.ts new file mode 100644 index 00000000000..5da94293e6a --- /dev/null +++ b/src/vs/workbench/node/proxyResolver.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as http from 'http'; +import * as https from 'https'; +import * as nodeurl from 'url'; + +import { assign } from 'vs/base/common/objects'; +import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; +import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { ProxyAgent } from 'vscode-proxy-agent'; +import { MainThreadTelemetryShape } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; +import { URI } from 'vs/base/common/uri'; + +export function connectProxyResolver( + extHostWorkspace: ExtHostWorkspace, + extHostConfiguration: ExtHostConfiguration, + extensionService: ExtHostExtensionService, + extHostLogService: ExtHostLogService, + mainThreadTelemetry: MainThreadTelemetryShape +) { + const agent = createProxyAgent(extHostWorkspace, extHostLogService, mainThreadTelemetry); + const lookup = createPatchedModules(extHostConfiguration, agent); + return configureModuleLoading(extensionService, lookup); +} + +function createProxyAgent( + extHostWorkspace: ExtHostWorkspace, + extHostLogService: ExtHostLogService, + mainThreadTelemetry: MainThreadTelemetryShape +) { + let timeout: NodeJS.Timer | undefined; + let count = 0; + let duration = 0; + let errorCount = 0; + function logEvent() { + timeout = undefined; + /* __GDPR__ + "resolveProxy" : { + "count": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "errorCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + } + */ + mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount }); + count = duration = errorCount = 0; + } + + function resolveProxy(url: string, callback: (proxy?: string) => void) { + if (!timeout) { + timeout = setTimeout(logEvent, 10 * 60 * 1000); + } + + const start = Date.now(); + extHostWorkspace.resolveProxy(url) + .then(proxy => { + callback(proxy); + }).then(() => { + count++; + duration = Date.now() - start + duration; + }, err => { + errorCount++; + extHostLogService.error('resolveProxy', toErrorMessage(err)); + callback(); + }); + } + + return new ProxyAgent({ resolveProxy }); +} + +function createPatchedModules(extHostConfiguration: ExtHostConfiguration, agent: http.Agent) { + const setting = { + config: extHostConfiguration.getConfiguration('http') + .get('systemProxy') || 'off' + }; + extHostConfiguration.onDidChangeConfiguration(e => { + setting.config = extHostConfiguration.getConfiguration('http') + .get('systemProxy') || 'off'; + }); + + return { + http: { + off: assign({}, http, patches(http, agent, { config: 'off' }, true)), + on: assign({}, http, patches(http, agent, { config: 'on' }, true)), + force: assign({}, http, patches(http, agent, { config: 'force' }, true)), + onRequest: assign({}, http, patches(http, agent, setting, true)), + default: assign(http, patches(http, agent, setting, false)) // run last + }, + https: { + off: assign({}, https, patches(https, agent, { config: 'off' }, true)), + on: assign({}, https, patches(https, agent, { config: 'on' }, true)), + force: assign({}, https, patches(https, agent, { config: 'force' }, true)), + onRequest: assign({}, https, patches(https, agent, setting, true)), + default: assign(https, patches(https, agent, setting, false)) // run last + } + }; +} + +function patches(originals: typeof http | typeof https, agent: http.Agent, setting: { config: string; }, onRequest: boolean) { + + return { + get: patch(originals.get), + request: patch(originals.request) + }; + + function patch(original: typeof http.get) { + function patched(url: string | URL, options?: http.RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest { + if (typeof url !== 'string' && !(url && (url).searchParams)) { + callback = options; + options = url; + url = null; + } + if (typeof options === 'function') { + callback = options; + options = null; + } + options = options || {}; + + const config = onRequest && (options)._vscodeSystemProxy || setting.config; + if (config === 'off') { + return original.apply(null, arguments); + } + + if (!options.socketPath && (config === 'force' || config === 'on' && !options.agent)) { + if (url) { + const parsed = typeof url === 'string' ? nodeurl.parse(url) : url; + options = { + protocol: parsed.protocol, + hostname: parsed.hostname, + port: parsed.port, + path: parsed.pathname, + ...options + }; + } + options.agent = agent; + return original(options, callback); + } + + return original.apply(null, arguments); + } + return patched; + } +} + +function configureModuleLoading(extensionService: ExtHostExtensionService, lookup: ReturnType): Promise { + return extensionService.getExtensionPathIndex() + .then(extensionPaths => { + const node_module = require.__$__nodeRequire('module'); + const original = node_module._load; + node_module._load = function load(request: string, parent: any, isMain: any) { + if (request !== 'http' && request !== 'https') { + return original.apply(this, arguments); + } + + const modules = lookup[request]; + const ext = extensionPaths.findSubstr(URI.file(parent.filename).fsPath); + if (ext && ext.enableProposedApi) { + return modules[(ext).systemProxy] || modules.onRequest; + } + return modules.default; + }; + }); +} \ No newline at end of file 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 60bff5108e0..b96f1b75842 100644 --- a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts +++ b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts @@ -20,7 +20,7 @@ import Severity from 'vs/base/common/severity'; import { ILogService } from 'vs/platform/log/common/log'; import { getPathFromAmdModule } from 'vs/base/common/amd'; -function ignore(code: string, value: T | null = null): (err: any) => Promise { +function ignore(code: string, value: T): (err: any) => Promise { return err => err.code === code ? Promise.resolve(value) : Promise.reject(err); } @@ -70,7 +70,7 @@ class InstallAction extends Action { return Promise.resolve(null); } else { return pfs.unlink(this.target) - .then(null, ignore('ENOENT')) + .then(null, ignore('ENOENT', null)) .then(() => pfs.symlink(getSource(), this.target)) .then(null, err => { if (err.code === 'EACCES' || err.code === 'ENOENT') { @@ -147,7 +147,7 @@ class UninstallAction extends Action { const uninstall = () => { return pfs.unlink(this.target) - .then(null, ignore('ENOENT')); + .then(null, ignore('ENOENT', null)); }; return uninstall().then(null, err => { diff --git a/src/vs/workbench/parts/codeEditor/browser/menuPreventer.ts b/src/vs/workbench/parts/codeEditor/browser/menuPreventer.ts index 76e482fac77..7d5845d3519 100644 --- a/src/vs/workbench/parts/codeEditor/browser/menuPreventer.ts +++ b/src/vs/workbench/parts/codeEditor/browser/menuPreventer.ts @@ -5,9 +5,9 @@ import { KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; /** * Prevents the top-level menu from showing up when doing Alt + Click in the editor diff --git a/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts b/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts index d66877173a1..fc398f32204 100644 --- a/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts +++ b/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import './browser/menuPreventer'; import './electron-browser/accessibility'; import './electron-browser/inspectKeybindings'; import './electron-browser/largeFileOptimizations'; -import './browser/menuPreventer'; import './electron-browser/selectionClipboard'; import './electron-browser/textMate/inspectTMScopes'; import './electron-browser/toggleMinimap'; diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts b/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts index 713aeceeab1..265a7bc4745 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts @@ -5,30 +5,30 @@ import 'vs/css!./accessibility'; import * as nls from 'vs/nls'; +import * as dom from 'vs/base/browser/dom'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; +import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { Widget } from 'vs/base/browser/ui/widget'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; -import * as dom from 'vs/base/browser/dom'; -import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; -import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { Widget } from 'vs/base/browser/ui/widget'; -import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { URI } from 'vs/base/common/uri'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { registerEditorAction, registerEditorContribution, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorWidgetBackground, widgetShadow, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import * as editorOptions from 'vs/editor/common/config/editorOptions'; -import * as platform from 'vs/base/common/platform'; -import { alert } from 'vs/base/browser/ui/aria/aria'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { URI } from 'vs/base/common/uri'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { contrastBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey('accessibilityHelpWidgetVisible', false); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.ts b/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.ts index a928c52cffe..cb316c2b61b 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/inspectKeybindings.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IUntitledResourceInput } from 'vs/workbench/common/editor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; class InspectKeyMap extends EditorAction { diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts index ab65572a10c..0b266d0eeca 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts @@ -4,18 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import { ParseError, parse } from 'vs/base/common/json'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; import * as types from 'vs/base/common/types'; -import { parse, ParseError } from 'vs/base/common/json'; -import { CharacterPair, LanguageConfiguration, IAutoClosingPair, IAutoClosingPairConditional, IndentationRule, CommentRule, FoldingRules } from 'vs/editor/common/modes/languageConfiguration'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { URI } from 'vs/base/common/uri'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { CharacterPair, CommentRule, FoldingRules, IAutoClosingPair, IAutoClosingPairConditional, IndentationRule, LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IFileService } from 'vs/platform/files/common/files'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; -import { URI } from 'vs/base/common/uri'; -import { IFileService } from 'vs/platform/files/common/files'; interface IRegExp { pattern: string; @@ -67,12 +68,19 @@ export class LanguageConfigurationFileHandler { constructor( @ITextMateService textMateService: ITextMateService, @IModeService private readonly _modeService: IModeService, - @IFileService private readonly _fileService: IFileService + @IFileService private readonly _fileService: IFileService, + @IExtensionService private readonly _extensionService: IExtensionService ) { this._done = []; // Listen for hints that a language configuration is needed/usefull and then load it once - this._modeService.onDidCreateMode((mode) => this._loadConfigurationsForMode(mode.getLanguageIdentifier())); + this._modeService.onDidCreateMode((mode) => { + const languageIdentifier = mode.getLanguageIdentifier(); + // Modes can be instantiated before the extension points have finished registering + this._extensionService.whenInstalledExtensionsRegistered().then(() => { + this._loadConfigurationsForMode(languageIdentifier); + }); + }); textMateService.onDidEncounterLanguage((languageId) => { this._loadConfigurationsForMode(this._modeService.getLanguageIdentifier(languageId)); }); @@ -510,7 +518,7 @@ const schema: IJSONSchema = { }, decreaseIndentPattern: { type: ['string', 'object'], - description: nls.localize('schema.indentationRules.decreaseIndentPattern', 'If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches).'), + description: nls.localize('schema.indentationRules.decreaseIndentPattern', 'If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches).'), properties: { pattern: { type: 'string', diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/largeFileOptimizations.ts b/src/vs/workbench/parts/codeEditor/electron-browser/largeFileOptimizations.ts index 740ee8c15eb..c1a803fbfe3 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/largeFileOptimizations.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/largeFileOptimizations.ts @@ -6,12 +6,12 @@ import * as nls from 'vs/nls'; import * as path from 'path'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; /** * Shows a message when opening a large file which has been memory optimized (and features disabled). diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts b/src/vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts index 55a77ea03f0..b283333e0c9 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { clipboard } from 'electron'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { EndOfLinePreference } from 'vs/editor/common/model'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import { Range } from 'vs/editor/common/core/range'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { Range } from 'vs/editor/common/core/range'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { EndOfLinePreference } from 'vs/editor/common/model'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; export class SelectionClipboard extends Disposable implements IEditorContribution { private static SELECTION_LENGTH_LIMIT = 65536; diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts b/src/vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts index 4e3a0f1a14c..2aefe2da134 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; +import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; import { MenuPreventer } from 'vs/workbench/parts/codeEditor/browser/menuPreventer'; import { SelectionClipboard } from 'vs/workbench/parts/codeEditor/electron-browser/selectionClipboard'; -import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; -import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; -import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { TabCompletionController } from 'vs/workbench/parts/snippets/electron-browser/tabCompletion'; export function getSimpleCodeEditorWidgetOptions(): ICodeEditorWidgetOptions { diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts b/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts index baf87d67858..1a381b2e3d9 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts @@ -3,17 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, addClass, append, Dimension, removeClass } from 'vs/base/browser/dom'; +import 'vs/css!./media/suggestEnabledInput'; +import { $, Dimension, addClass, append, removeClass } from 'vs/base/browser/dom'; import { Widget } from 'vs/base/browser/ui/widget'; import { Color } from 'vs/base/common/color'; -import { chain, Emitter, Event } from 'vs/base/common/event'; +import { Emitter, Event, chain } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { mixin } from 'vs/base/common/objects'; import { isMacintosh } from 'vs/base/common/platform'; import { URI as uri } from 'vs/base/common/uri'; -import 'vs/css!./media/suggestEnabledInput'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; @@ -24,13 +26,11 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2 import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ColorIdentifier, inputBackground, inputBorder, inputForeground, inputPlaceholderForeground, selectionBackground, editorSelectionBackground } from 'vs/platform/theme/common/colorRegistry'; -import { attachStyler, IStyleOverrides, IThemable } from 'vs/platform/theme/common/styler'; +import { ColorIdentifier, editorSelectionBackground, inputBackground, inputBorder, inputForeground, inputPlaceholderForeground, selectionBackground } from 'vs/platform/theme/common/colorRegistry'; +import { IStyleOverrides, IThemable, attachStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { MenuPreventer } from 'vs/workbench/parts/codeEditor/browser/menuPreventer'; import { getSimpleEditorOptions } from 'vs/workbench/parts/codeEditor/browser/simpleEditorOptions'; -import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { mixin } from 'vs/base/common/objects'; import { SelectionClipboard } from 'vs/workbench/parts/codeEditor/electron-browser/selectionClipboard'; interface SuggestResultsProvider { @@ -179,10 +179,10 @@ export class SuggestEnabledInput extends Widget implements IThemable { return { suggestions: suggestionProvider.provideResults(query).map(result => { - return { + return { label: result, insertText: result, - overwriteBefore: alreadyTypedCount, + range: Range.fromPositions(position.delta(0, -alreadyTypedCount), position), sortText: validatedSuggestProvider.sortKey(result), kind: modes.CompletionItemKind.Keyword }; diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts b/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts index 945c584e6c1..3d1cb79443f 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts @@ -6,26 +6,25 @@ import 'vs/css!./inspectTMScopes'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; +import { CharCode } from 'vs/base/common/charCode'; +import { Color } from 'vs/base/common/color'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import { escape } from 'vs/base/common/strings'; -import { KeyCode } from 'vs/base/common/keyCodes'; +import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { registerEditorAction, registerEditorContribution, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditor, ContentWidgetPositionPreference, IContentWidget, IContentWidgetPosition, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IGrammar, StackElement, IToken } from 'vscode-textmate'; -import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; +import { FontStyle, LanguageIdentifier, StandardTokenType, TokenMetadata, TokenizationRegistry } from 'vs/editor/common/modes'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { TokenizationRegistry, LanguageIdentifier, FontStyle, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes'; -import { CharCode } from 'vs/base/common/charCode'; -import { findMatchingThemeRule } from 'vs/workbench/services/textMate/electron-browser/TMHelper'; -import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { Color } from 'vs/base/common/color'; -import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; -import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; +import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { findMatchingThemeRule } from 'vs/workbench/services/textMate/electron-browser/TMHelper'; +import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IGrammar, IToken, StackElement } from 'vscode-textmate'; class InspectTMScopesController extends Disposable implements IEditorContribution { @@ -180,7 +179,7 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget { private readonly _notificationService: INotificationService; private readonly _model: ITextModel; private readonly _domNode: HTMLElement; - private readonly _grammar: TPromise; + private readonly _grammar: Promise; constructor( editor: IActiveCodeEditor, diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts index 0b7cf367959..96576810093 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; export class ToggleMinimapAction extends Action { public static readonly ID = 'editor.action.toggleMinimap'; diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts index fdcd677e019..24cb7fe3aec 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as platform from 'vs/base/common/platform'; -import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import * as platform from 'vs/base/common/platform'; +import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; export class ToggleMultiCursorModifierAction extends Action { diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts index e0e854880ff..ce2765e5efe 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; export class ToggleRenderControlCharacterAction extends Action { diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts index b6d0bc806eb..db5bbbf573f 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; export class ToggleRenderWhitespaceAction extends Action { diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts index c3e25a6d1b2..f79615775aa 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts @@ -5,19 +5,19 @@ import * as nls from 'vs/nls'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { ITextModel } from 'vs/editor/common/model'; -import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { InternalEditorOptions, EDITOR_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { EDITOR_DEFAULTS, InternalEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ITextModel } from 'vs/editor/common/model'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { INotificationService } from 'vs/platform/notification/common/notification'; const transientWordWrapState = 'transientWordWrapState'; const isWordWrapMinifiedKey = 'isWordWrapMinified'; diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/workbenchReferenceSearch.ts b/src/vs/workbench/parts/codeEditor/electron-browser/workbenchReferenceSearch.ts index 593bc399f90..6e3236ffa27 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/workbenchReferenceSearch.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/workbenchReferenceSearch.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IStorageService } from 'vs/platform/storage/common/storage'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IStorageService } from 'vs/platform/storage/common/storage'; export class WorkbenchReferencesController extends ReferencesController { diff --git a/src/vs/workbench/parts/comments/common/commentModel.ts b/src/vs/workbench/parts/comments/common/commentModel.ts index 4c96d404f98..64d0efd2cfa 100644 --- a/src/vs/workbench/parts/comments/common/commentModel.ts +++ b/src/vs/workbench/parts/comments/common/commentModel.ts @@ -127,7 +127,7 @@ export class CommentsModel { } private groupByResource(commentThreads: CommentThread[]): ResourceWithCommentThreads[] { - const resourceCommentThreads = [] as ResourceWithCommentThreads[]; + const resourceCommentThreads: ResourceWithCommentThreads[] = []; const commentThreadsByResource = new Map(); for (const group of groupBy(commentThreads, CommentsModel._compareURIs)) { commentThreadsByResource.set(group[0].resource, new ResourceWithCommentThreads(URI.parse(group[0].resource), group)); diff --git a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts index ed0f88781a2..cda2ce3f13e 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts @@ -75,6 +75,7 @@ export class CommentNode extends Disposable { if (comment.userIconPath) { const img = dom.append(avatar, dom.$('img.avatar')); img.src = comment.userIconPath.toString(); + img.onerror = _ => img.remove(); } const commentDetailsContainer = dom.append(this._domNode, dom.$('.review-comment-contents')); @@ -123,7 +124,7 @@ export class CommentNode extends Disposable { const container = dom.append(this._commentEditContainer, dom.$('.edit-textarea')); this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, container, SimpleCommentEditor.getEditorOptions()); const resource = URI.parse(`comment:commentinput-${this.comment.commentId}-${Date.now()}.md`); - this._commentEditorModel = this.modelService.createModel('', this.modeService.getOrCreateModeByFilepathOrFirstLine(resource.path), resource, true); + this._commentEditorModel = this.modelService.createModel('', this.modeService.createByFilepathOrFirstLine(resource.path), resource, true); this._commentEditor.setModel(this._commentEditorModel); this._commentEditor.setValue(this.comment.body.value); @@ -185,7 +186,6 @@ export class CommentNode extends Disposable { private createDeleteAction(): Action { return new Action('comment.delete', nls.localize('label.delete', "Delete"), 'octicon octicon-x', true, () => { return this.dialogService.confirm({ - title: nls.localize('deleteCommentTitle', "Delete Comment"), message: nls.localize('confirmDelete', "Delete comment?"), type: 'question', primaryButton: nls.localize('label.delete', "Delete") diff --git a/src/vs/workbench/parts/comments/electron-browser/commentService.ts b/src/vs/workbench/parts/comments/electron-browser/commentService.ts index 3eece77e45c..8623e77007c 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentService.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentService.ts @@ -37,8 +37,8 @@ export interface ICommentService { registerDataProvider(owner: number, commentProvider: DocumentCommentProvider): void; unregisterDataProvider(owner: number): void; updateComments(event: CommentThreadChangedEvent): void; - createNewCommentThread(owner: number, resource: URI, range: Range, text: string): Promise; - replyToCommentThread(owner: number, resource: URI, range: Range, thread: CommentThread, text: string): Promise; + createNewCommentThread(owner: number, resource: URI, range: Range, text: string): Promise; + replyToCommentThread(owner: number, resource: URI, range: Range, thread: CommentThread, text: string): Promise; editComment(owner: number, resource: URI, comment: Comment, text: string): Promise; deleteComment(owner: number, resource: URI, comment: Comment): Promise; getComments(resource: URI): Promise; @@ -94,21 +94,21 @@ export class CommentService extends Disposable implements ICommentService { this._onDidUpdateCommentThreads.fire(event); } - createNewCommentThread(owner: number, resource: URI, range: Range, text: string): Promise { + async createNewCommentThread(owner: number, resource: URI, range: Range, text: string): Promise { const commentProvider = this._commentProviders.get(owner); if (commentProvider) { - return commentProvider.createNewCommentThread(resource, range, text, CancellationToken.None); + return await commentProvider.createNewCommentThread(resource, range, text, CancellationToken.None); } return null; } - replyToCommentThread(owner: number, resource: URI, range: Range, thread: CommentThread, text: string): Promise { + async replyToCommentThread(owner: number, resource: URI, range: Range, thread: CommentThread, text: string): Promise { const commentProvider = this._commentProviders.get(owner); if (commentProvider) { - return commentProvider.replyToCommentThread(resource, range, thread, text, CancellationToken.None); + return await commentProvider.replyToCommentThread(resource, range, thread, text, CancellationToken.None); } return null; @@ -121,7 +121,7 @@ export class CommentService extends Disposable implements ICommentService { return commentProvider.editComment(resource, comment, text, CancellationToken.None); } - return null; + return Promise.resolve(void 0); } deleteComment(owner: number, resource: URI, comment: Comment): Promise { @@ -135,7 +135,7 @@ export class CommentService extends Disposable implements ICommentService { } getComments(resource: URI): Promise { - const result = []; + const result: Promise[] = []; for (const handle of keys(this._commentProviders)) { const provider = this._commentProviders.get(handle); if ((provider).provideDocumentComments) { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index ad58f7bb531..9697e93878d 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -64,6 +64,7 @@ export class ReviewZoneWidget extends ZoneWidget { private _commentThread: modes.CommentThread; private _commentGlyph: CommentGlyphWidget; private _owner: number; + private _pendingComment: string; private _localToDispose: IDisposable[]; private _globalToDispose: IDisposable[]; private _markdownRenderer: MarkdownRenderer; @@ -89,12 +90,14 @@ export class ReviewZoneWidget extends ZoneWidget { editor: ICodeEditor, owner: number, commentThread: modes.CommentThread, + pendingComment: string, options: IOptions = {} ) { super(editor, options); this._resizeObserver = null; this._owner = owner; this._commentThread = commentThread; + this._pendingComment = pendingComment; this._isCollapsed = commentThread.collapsibleState !== modes.CommentThreadCollapsibleState.Expanded; this._globalToDispose = []; this._localToDispose = []; @@ -120,6 +123,16 @@ export class ReviewZoneWidget extends ZoneWidget { return this._onDidCreateThread.event; } + public getPosition(): IPosition | undefined { + let position: IPosition = this.position; + if (position) { + return position; + } + + position = this._commentGlyph.getPosition().position; + return position; + } + protected revealLine(lineNumber: number) { // we don't do anything here as we always do the reveal ourselves. } @@ -144,6 +157,18 @@ export class ReviewZoneWidget extends ZoneWidget { this.editor.revealRangeInCenter(this._commentThread.range); } + public getPendingComment(): string { + if (this._commentEditor) { + let model = this._commentEditor.getModel(); + + if (model.getValueLength() > 0) { // checking length is cheap + return model.getValue(); + } + } + + return null; + } + protected _fillContainer(container: HTMLElement): void { this.setCssClass('review-widget'); this._headElement = dom.$('.head'); @@ -264,7 +289,7 @@ export class ReviewZoneWidget extends ZoneWidget { this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, this._commentForm, SimpleCommentEditor.getEditorOptions()); const modeId = hasExistingComments ? this._commentThread.threadId : ++INMEM_MODEL_ID; const resource = URI.parse(`${COMMENT_SCHEME}:commentinput-${modeId}.md`); - const model = this.modelService.createModel('', this.modeService.getOrCreateModeByFilepathOrFirstLine(resource.path), resource, true); + const model = this.modelService.createModel(this._pendingComment || '', this.modeService.createByFilepathOrFirstLine(resource.path), resource, true); this._localToDispose.push(model); this._commentEditor.setModel(model); this._localToDispose.push(this._commentEditor); @@ -309,7 +334,7 @@ export class ReviewZoneWidget extends ZoneWidget { attachButtonStyler(button, this.themeService); button.label = 'Add comment'; - button.enabled = false; + button.enabled = model.getValueLength() > 0; this._localToDispose.push(this._commentEditor.onDidChangeModelContent(_ => { if (this._commentEditor.getValue()) { button.enabled = true; @@ -339,6 +364,11 @@ export class ReviewZoneWidget extends ZoneWidget { // If there are no existing comments, place focus on the text area. This must be done after show, which also moves focus. if (this._commentThread.reply && !this._commentThread.comments.length) { this._commentEditor.focus(); + } else if (this._commentEditor.getModel().getValueLength() > 0) { + if (!dom.hasClass(this._commentForm, 'expand')) { + dom.addClass(this._commentForm, 'expand'); + } + this._commentEditor.focus(); } } @@ -408,6 +438,7 @@ export class ReviewZoneWidget extends ZoneWidget { if (newCommentThread) { this._commentEditor.setValue(''); + this._pendingComment = ''; if (dom.hasClass(this._commentForm, 'expand')) { dom.removeClass(this._commentForm, 'expand'); } @@ -442,17 +473,20 @@ export class ReviewZoneWidget extends ZoneWidget { this._headingLabel.setAttribute('aria-label', label); } + private expandReplyArea() { + if (!dom.hasClass(this._commentForm, 'expand')) { + dom.addClass(this._commentForm, 'expand'); + this._commentEditor.focus(); + } + } + private createReplyButton() { this._reviewThreadReplyButton = dom.append(this._commentForm, dom.$('button.review-thread-reply-button')); this._reviewThreadReplyButton.title = nls.localize('reply', "Reply..."); this._reviewThreadReplyButton.textContent = nls.localize('reply', "Reply..."); // bind click/escape actions for reviewThreadReplyButton and textArea - this._reviewThreadReplyButton.onclick = () => { - if (!dom.hasClass(this._commentForm, 'expand')) { - dom.addClass(this._commentForm, 'expand'); - this._commentEditor.focus(); - } - }; + this._localToDispose.push(dom.addDisposableListener(this._reviewThreadReplyButton, 'click', _ => this.expandReplyArea())); + this._localToDispose.push(dom.addDisposableListener(this._reviewThreadReplyButton, 'focus', _ => this.expandReplyArea())); this._commentEditor.onDidBlurEditorWidget(() => { if (this._commentEditor.getModel().getValueLength() === 0 && dom.hasClass(this._commentForm, 'expand')) { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts index ae16db5eee0..c3a5715b91d 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts @@ -13,7 +13,7 @@ import { ICodeEditor, IEditorMouseEvent, IViewZone, MouseTargetType } from 'vs/e import { registerEditorContribution, EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IEditorContribution, IModelChangedEvent } from 'vs/editor/common/editorCommon'; import { IRange } from 'vs/editor/common/core/range'; import * as modes from 'vs/editor/common/modes'; import { peekViewResultsBackground, peekViewResultsSelectionBackground, peekViewTitleBackground } from 'vs/editor/contrib/referenceSearch/referencesWidget'; @@ -34,6 +34,7 @@ import { Color, RGBA } from 'vs/base/common/color'; import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; export const ctxReviewPanelVisible = new RawContextKey('reviewPanelVisible', false); @@ -63,7 +64,7 @@ export const overviewRulerCommentingRangeForeground = registerColor('editorGutte class CommentingRangeDecoration { private _decorationId: string; - get id(): string { + public get id(): string { return this._decorationId; } @@ -84,18 +85,18 @@ class CommentingRangeDecoration { } } - getCommentAction(): { replyCommand: modes.Command, ownerId: number } { + public getCommentAction(): { replyCommand: modes.Command, ownerId: number } { return { replyCommand: this._reply, ownerId: this._ownerId }; } - getOriginalRange() { + public getOriginalRange() { return this._range; } - getActiveRange() { + public getActiveRange() { return this._editor.getModel().getDecorationRange(this._decorationId); } } @@ -122,13 +123,13 @@ class CommentingRangeDecorator { this.commentsOptions = CommentingRangeDecorator.createDecoration('comment-thread', overviewRulerCommentingRangeForeground, options); } - update(editor: ICodeEditor, commentInfos: modes.CommentInfo[]) { + public update(editor: ICodeEditor, commentInfos: modes.CommentInfo[]) { let model = editor.getModel(); if (!model) { return; } - let commentingRangeDecorations = []; + let commentingRangeDecorations: CommentingRangeDecoration[] = []; for (let i = 0; i < commentInfos.length; i++) { let info = commentInfos[i]; info.commentingRanges.forEach(range => { @@ -142,7 +143,7 @@ class CommentingRangeDecorator { this.commentingRangeDecorations = commentingRangeDecorations; } - getMatchedCommentAction(line: number) { + public getMatchedCommentAction(line: number) { for (let i = 0; i < this.commentingRangeDecorations.length; i++) { let range = this.commentingRangeDecorations[i].getActiveRange(); @@ -154,7 +155,7 @@ class CommentingRangeDecorator { return null; } - dispose(): void { + public dispose(): void { this.disposables = dispose(this.disposables); this.commentingRangeDecorations = []; } @@ -168,11 +169,13 @@ export class ReviewController implements IEditorContribution { private _commentWidgets: ReviewZoneWidget[]; private _reviewPanelVisible: IContextKey; private _commentInfos: modes.CommentInfo[]; - // private _hasSetComments: boolean; private _commentingRangeDecorator: CommentingRangeDecorator; private mouseDownInfo: { lineNumber: number } | null = null; private _commentingRangeSpaceReserved = false; + private _computePromise: CancelablePromise | null; + private _pendingCommentCache: { [key: number]: { [key: string]: string } }; + private _pendingNewCommentCache: { [key: string]: { lineNumber: number, replyCommand: modes.Command, ownerId: number, pendingComment: string } }; constructor( editor: ICodeEditor, @@ -192,21 +195,25 @@ export class ReviewController implements IEditorContribution { this.localToDispose = []; this._commentInfos = []; this._commentWidgets = []; + this._pendingCommentCache = {}; + this._pendingNewCommentCache = {}; this._newCommentWidget = null; - // this._hasSetComments = false; + this._computePromise = null; this._reviewPanelVisible = ctxReviewPanelVisible.bindTo(contextKeyService); this._commentingRangeDecorator = new CommentingRangeDecorator(); - this.globalToDispose.push(this.commentService.onDidDeleteDataProvider(e => { + this.globalToDispose.push(this.commentService.onDidDeleteDataProvider(ownerId => { // Remove new comment widget and glyph, refresh comments - if (this._newCommentWidget) { + if (this._newCommentWidget && this._newCommentWidget.owner === ownerId) { this._newCommentWidget.dispose(); this._newCommentWidget = null; } - this.getComments(); + delete this._pendingCommentCache[ownerId]; + this.beginCompute(); })); + this.globalToDispose.push(this.commentService.onDidSetDataProvider(_ => this.beginCompute())); this.globalToDispose.push(this.commentService.onDidSetResourceCommentInfos(e => { const editorURI = this.editor && this.editor.getModel() && this.editor.getModel().uri; @@ -215,30 +222,40 @@ export class ReviewController implements IEditorContribution { } })); - this.globalToDispose.push(this.commentService.onDidSetDataProvider(_ => this.getComments())); - - this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged())); + this.globalToDispose.push(this.editor.onDidChangeModel(e => this.onModelChanged(e))); this.codeEditorService.registerDecorationType(COMMENTEDITOR_DECORATION_KEY, {}); + this.beginCompute(); } - private getComments(): void { - const editorURI = this.editor && this.editor.getModel() && this.editor.getModel().uri; + private beginCompute(): Promise { + this._computePromise = createCancelablePromise(token => { + const editorURI = this.editor && this.editor.getModel() && this.editor.getModel().uri; - if (editorURI) { - this.commentService.getComments(editorURI).then(commentInfos => { - this.setComments(commentInfos.filter(commentInfo => commentInfo !== null)); - }, error => console.log(error)); - } + if (editorURI) { + return this.commentService.getComments(editorURI); + } + + return Promise.resolve([]); + }); + + return this._computePromise.then(commentInfos => { + this.setComments(commentInfos.filter(commentInfo => commentInfo !== null)); + this._computePromise = null; + }, error => console.log(error)); } public static get(editor: ICodeEditor): ReviewController { return editor.getContribution(ID); } - public revealCommentThread(threadId: string, commentId?: string): void { + public revealCommentThread(threadId: string, commentId: string, fetchOnceIfNotExist: boolean): void { const commentThreadWidget = this._commentWidgets.filter(widget => widget.commentThread.threadId === threadId); if (commentThreadWidget.length === 1) { commentThreadWidget[0].reveal(commentId); + } else if (fetchOnceIfNotExist) { + this.beginCompute().then(_ => { + this.revealCommentThread(threadId, commentId, false); + }); } } @@ -292,11 +309,11 @@ export class ReviewController implements IEditorContribution { } } - getId(): string { + public getId(): string { return ID; } - dispose(): void { + public dispose(): void { this.globalToDispose = dispose(this.globalToDispose); this.localToDispose = dispose(this.localToDispose); @@ -309,18 +326,44 @@ export class ReviewController implements IEditorContribution { this.editor = null; } - public onModelChanged(): void { + public onModelChanged(e: IModelChangedEvent): void { this.localToDispose = dispose(this.localToDispose); if (this._newCommentWidget) { - // todo@peng store view state. + let pendingNewComment = this._newCommentWidget.getPendingComment(); + + if (e.oldModelUrl) { + if (pendingNewComment) { + // we can't fetch zone widget's position as the model is already gone + const position = this._newCommentWidget.getPosition(); + if (position) { + this._pendingNewCommentCache[e.oldModelUrl.toString()] = { + lineNumber: position.lineNumber, + ownerId: this._newCommentWidget.owner, + replyCommand: this._newCommentWidget.commentThread.reply, + pendingComment: pendingNewComment + }; + } + } else { + // clear cache if it is empty + delete this._pendingNewCommentCache[e.oldModelUrl.toString()]; + } + } + this._newCommentWidget.dispose(); this._newCommentWidget = null; + } else { + if (e.oldModelUrl) { + // remove pending new comment cache as there is no newCommentWidget anymore + delete this._pendingNewCommentCache[e.oldModelUrl.toString()]; + } } - this._commentWidgets.forEach(zone => { - zone.dispose(); - }); - this._commentWidgets = []; + this.removeCommentWidgetsAndStoreCache(); + + if (e.newModelUrl && this._pendingNewCommentCache[e.newModelUrl.toString()]) { + let newCommentCache = this._pendingNewCommentCache[e.newModelUrl.toString()]; + this.addComment(newCommentCache.lineNumber, newCommentCache.replyCommand, newCommentCache.ownerId, newCommentCache.pendingComment); + } this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); @@ -357,28 +400,24 @@ export class ReviewController implements IEditorContribution { } }); added.forEach(thread => { - let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, e.owner, thread, {}); + let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, e.owner, thread, null, {}); zoneWidget.display(thread.range.startLineNumber, this._commentingRangeDecorator.commentsOptions); this._commentWidgets.push(zoneWidget); this._commentInfos.filter(info => info.owner === e.owner)[0].threads.push(thread); }); })); + + this.beginCompute(); } - private addComment(lineNumber: number) { + private addComment(lineNumber: number, replyCommand: modes.Command, ownerId: number, pendingComment: string) { if (this._newCommentWidget !== null) { this.notificationService.warn(`Please submit the comment at line ${this._newCommentWidget.position.lineNumber} before creating a new one.`); return; } - let newCommentInfo = this._commentingRangeDecorator.getMatchedCommentAction(lineNumber); - if (!newCommentInfo) { - return; - } - // add new comment this._reviewPanelVisible.set(true); - const { replyCommand, ownerId } = newCommentInfo; this._newCommentWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, ownerId, { threadId: null, resource: null, @@ -391,7 +430,7 @@ export class ReviewController implements IEditorContribution { }, reply: replyCommand, collapsibleState: CommentThreadCollapsibleState.Expanded, - }, {}); + }, pendingComment, {}); this.localToDispose.push(this._newCommentWidget.onDidClose(e => { this._newCommentWidget = null; @@ -459,18 +498,28 @@ export class ReviewController implements IEditorContribution { if (e.target.element.className.indexOf('comment-diff-added') >= 0) { const lineNumber = e.target.position.lineNumber; - this.addComment(lineNumber); + let newCommentInfo = this._commentingRangeDecorator.getMatchedCommentAction(lineNumber); + if (!newCommentInfo) { + return; + } + const { replyCommand, ownerId } = newCommentInfo; + this.addComment(lineNumber, replyCommand, ownerId, null); } } - setComments(commentInfos: modes.CommentInfo[]): void { + + private setComments(commentInfos: modes.CommentInfo[]): void { + if (!this.editor) { + return; + } + this._commentInfos = commentInfos; let lineDecorationsWidth: number = this.editor.getConfiguration().layoutInfo.decorationsWidth; if (this._commentInfos.some(info => Boolean(info.commentingRanges && info.commentingRanges.length))) { if (!this._commentingRangeSpaceReserved) { this._commentingRangeSpaceReserved = true; - let extraEditorClassName = []; + let extraEditorClassName: string[] = []; if (this.editor.getRawConfiguration().extraEditorClassName) { extraEditorClassName = this.editor.getRawConfiguration().extraEditorClassName.split(' '); } @@ -495,24 +544,28 @@ export class ReviewController implements IEditorContribution { } } - // this._hasSetComments = true; - // create viewzones - this._commentWidgets.forEach(zone => { - zone.dispose(); - }); - - this._commentWidgets = []; + this.removeCommentWidgetsAndStoreCache(); this._commentInfos.forEach(info => { + let providerCacheStore = this._pendingCommentCache[info.owner]; info.threads.forEach(thread => { - let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, info.owner, thread, {}); + let pendingComment: string = null; + if (providerCacheStore) { + pendingComment = providerCacheStore[thread.threadId]; + } + + if (pendingComment) { + thread.collapsibleState = modes.CommentThreadCollapsibleState.Expanded; + } + + let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, info.owner, thread, pendingComment, {}); zoneWidget.display(thread.range.startLineNumber, this._commentingRangeDecorator.commentsOptions); this._commentWidgets.push(zoneWidget); }); }); - const commentingRanges = []; + const commentingRanges: IRange[] = []; this._commentInfos.forEach(info => { commentingRanges.push(...info.commentingRanges); }); @@ -534,6 +587,31 @@ export class ReviewController implements IEditorContribution { this.editor.focus(); this.editor.revealRangeInCenter(this.editor.getSelection()); } + + private removeCommentWidgetsAndStoreCache() { + if (this._commentWidgets) { + this._commentWidgets.forEach(zone => { + let pendingComment = zone.getPendingComment(); + let providerCacheStore = this._pendingCommentCache[zone.owner]; + + if (pendingComment) { + if (!providerCacheStore) { + this._pendingCommentCache[zone.owner] = {}; + } + + this._pendingCommentCache[zone.owner][zone.commentThread.threadId] = pendingComment; + } else { + if (providerCacheStore) { + delete providerCacheStore[zone.commentThread.threadId]; + } + } + + zone.dispose(); + }); + } + + this._commentWidgets = []; + } } export class NextCommentThreadAction extends EditorAction { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts index 1b445b8126e..de280198c18 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts @@ -49,7 +49,7 @@ export class CommentsPanel extends Panel { super(COMMENTS_PANEL_ID, telemetryService, themeService, storageService); } - public create(parent: HTMLElement): Promise { + public create(parent: HTMLElement): void { super.create(parent); dom.addClass(parent, 'comments-panel'); @@ -70,7 +70,7 @@ export class CommentsPanel extends Panel { this.applyStyles(styleElement); }); - return this.render(); + this.render(); } private applyStyles(styleElement: HTMLStyleElement) { @@ -167,29 +167,18 @@ export class CommentsPanel extends Panel { const control = this.editorService.activeTextEditorWidget; if (threadToReveal && isCodeEditor(control)) { const controller = ReviewController.get(control); - controller.revealCommentThread(threadToReveal, commentToReveal); + controller.revealCommentThread(threadToReveal, commentToReveal, false); } return true; } - let setCommentsForFile = new Promise((resolve, reject) => { - this.commentService.onDidSetResourceCommentInfos(e => { - if (e.resource.toString() === element.resource.toString()) { - resolve(); - } - }); - }); - const threadToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].threadId : element.threadId; const commentToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].comment : element.comment; if (commentToReveal.command) { - Promise.all([ - this.commandService.executeCommand(commentToReveal.command.id, ...commentToReveal.command.arguments), - setCommentsForFile - ]).then(_ => { + this.commandService.executeCommand(commentToReveal.command.id, ...commentToReveal.command.arguments).then(_ => { let activeWidget = this.editorService.activeTextEditorWidget; if (isDiffEditor(activeWidget)) { const originalEditorWidget = activeWidget.getOriginalEditor(); @@ -203,7 +192,7 @@ export class CommentsPanel extends Panel { } if (controller) { - controller.revealCommentThread(threadToReveal, commentToReveal.commentId); + controller.revealCommentThread(threadToReveal, commentToReveal.commentId, true); } } else { let activeEditor = this.editorService.activeEditor; @@ -212,7 +201,7 @@ export class CommentsPanel extends Panel { const control = this.editorService.activeTextEditorWidget; if (threadToReveal && isCodeEditor(control)) { const controller = ReviewController.get(control); - controller.revealCommentThread(threadToReveal, commentToReveal.commentId); + controller.revealCommentThread(threadToReveal, commentToReveal.commentId, true); } } } @@ -220,38 +209,33 @@ export class CommentsPanel extends Panel { return true; }); } else { - Promise.all([this.editorService.openEditor({ + this.editorService.openEditor({ resource: element.resource, options: { pinned: pinned, preserveFocus: preserveFocus, selection: range } - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP), setCommentsForFile]).then(vals => { - let editor = vals[0]; + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => { const control = editor.getControl(); if (threadToReveal && isCodeEditor(control)) { const controller = ReviewController.get(control); - controller.revealCommentThread(threadToReveal, commentToReveal.commentId); + controller.revealCommentThread(threadToReveal, commentToReveal.commentId, true); } - setCommentsForFile = null; }); } - return true; } - public setVisible(visible: boolean): Promise { + public setVisible(visible: boolean): void { const wasVisible = this.isVisible(); - return super.setVisible(visible) - .then(() => { - if (this.isVisible()) { - if (!wasVisible) { - this.refresh(); - } - } - }); + super.setVisible(visible); + if (this.isVisible()) { + if (!wasVisible) { + this.refresh(); + } + } } private refresh(): void { diff --git a/src/vs/workbench/parts/debug/browser/baseDebugView.ts b/src/vs/workbench/parts/debug/browser/baseDebugView.ts index 25f5179e000..62cec581ea2 100644 --- a/src/vs/workbench/parts/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/parts/debug/browser/baseDebugView.ts @@ -218,10 +218,11 @@ export class BaseDebugController extends WorkbenchTreeController { const anchor = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => this.actionProvider.getSecondaryActions(tree, element).then(actions => { + getActions: () => { + const actions = this.actionProvider.getSecondaryActions(tree, element); fillInContextMenuActions(this.contributedContextMenu, { arg: this.getContext(element) }, actions, this.contextMenuService); return actions; - }), + }, onHide: (wasCancelled?: boolean) => { if (wasCancelled) { tree.domFocus(); diff --git a/src/vs/workbench/parts/debug/browser/breakpointsView.ts b/src/vs/workbench/parts/debug/browser/breakpointsView.ts index 3f470d812b4..c74d9c262f7 100644 --- a/src/vs/workbench/parts/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/parts/debug/browser/breakpointsView.ts @@ -16,7 +16,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Constants } from 'vs/editor/common/core/uint'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IListVirtualDelegate, IListContextMenuEvent, IListRenderer } from 'vs/base/browser/ui/list/list'; import { IEditor } from 'vs/workbench/common/editor'; @@ -172,7 +171,7 @@ export class BreakpointsView extends ViewletPanel { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => Promise.resolve(actions), + getActions: () => actions, getActionsContext: () => element }); } @@ -192,12 +191,11 @@ export class BreakpointsView extends ViewletPanel { } } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { - if (visible && this.needsRefresh) { - this.onBreakpointsChange(); - } - }); + public setVisible(visible: boolean): void { + super.setVisible(visible); + if (visible && this.needsRefresh) { + this.onBreakpointsChange(); + } } private onBreakpointsChange(): void { @@ -554,7 +552,7 @@ class FunctionBreakpointInputRenderer implements IListRenderer { +export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Thenable { if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) { return Promise.resolve(null); } @@ -619,7 +617,7 @@ export function getBreakpointMessageAndClassName(debugService: IDebugService, br } if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition) { - const messages = []; + const messages: string[] = []; if (breakpoint.logMessage) { if (session && !session.capabilities.supportsLogPoints) { return { diff --git a/src/vs/workbench/parts/debug/browser/debugActionItems.ts b/src/vs/workbench/parts/debug/browser/debugActionItems.ts index 52fc337a4ae..fe8623eb04c 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionItems.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionItems.ts @@ -169,8 +169,9 @@ export class StartDebugActionItem implements IActionItem { if (this.options.length === 0) { this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: () => false }); + } else { + this.options.push({ label: StartDebugActionItem.SEPARATOR, handler: undefined }); } - this.options.push({ label: StartDebugActionItem.SEPARATOR, handler: undefined }); const disabledIdx = this.options.length - 1; launches.filter(l => !l.hidden).forEach(l => { diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index ab260b09869..2aa2fbc22dc 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import * as lifecycle from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -44,7 +43,7 @@ export abstract class AbstractDebugAction extends Action { this.updateEnablement(); } - public run(e?: any): TPromise { + public run(e?: any): Thenable { throw new Error('implement me'); } @@ -100,7 +99,7 @@ export class ConfigureAction extends AbstractDebugAction { this.class = this.debugService.getConfigurationManager().selectedConfiguration.name ? 'debug-action configure' : 'debug-action configure notification'; } - public run(event?: any): TPromise { + public run(event?: any): Thenable { if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.notificationService.info(nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration.")); return Promise.resolve(null); @@ -134,7 +133,7 @@ export class StartAction extends AbstractDebugAction { this.toDispose.push(this.contextService.onDidChangeWorkbenchState(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Thenable { const configurationManager = this.debugService.getConfigurationManager(); let launch = configurationManager.selectedConfiguration.launch; if (!launch || launch.getConfigurationNames().length === 0) { @@ -198,7 +197,7 @@ export class SelectAndStartAction extends AbstractDebugAction { super(id, label, undefined, debugService, keybindingService); } - public run(): TPromise { + public run(): Thenable { return this.quickOpenService.show('debug '); } } @@ -228,7 +227,7 @@ export class RestartAction extends AbstractDebugAction { this.updateLabel(session && session.configuration.request === 'attach' ? RestartAction.RECONNECT_LABEL : RestartAction.LABEL); } - public run(session: IDebugSession): TPromise { + public run(session: IDebugSession): Thenable { if (!session || !session.getId) { session = this.debugService.getViewModel().focusedSession; } @@ -258,7 +257,7 @@ export class StepOverAction extends AbstractDebugAction { super(id, label, 'debug-action step-over', debugService, keybindingService, 20); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -279,7 +278,7 @@ export class StepIntoAction extends AbstractDebugAction { super(id, label, 'debug-action step-into', debugService, keybindingService, 30); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -300,7 +299,7 @@ export class StepOutAction extends AbstractDebugAction { super(id, label, 'debug-action step-out', debugService, keybindingService, 40); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -321,7 +320,7 @@ export class StopAction extends AbstractDebugAction { super(id, label, 'debug-action stop', debugService, keybindingService, 80); } - public run(session: IDebugSession): TPromise { + public run(session: IDebugSession): Promise { if (!session || !session.getId) { session = this.debugService.getViewModel().focusedSession; } @@ -342,7 +341,7 @@ export class DisconnectAction extends AbstractDebugAction { super(id, label, 'debug-action disconnect', debugService, keybindingService, 80); } - public run(): TPromise { + public run(): Promise { const session = this.debugService.getViewModel().focusedSession; return this.debugService.stopSession(session); } @@ -360,7 +359,7 @@ export class ContinueAction extends AbstractDebugAction { super(id, label, 'debug-action continue', debugService, keybindingService, 10); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -381,9 +380,14 @@ export class PauseAction extends AbstractDebugAction { super(id, label, 'debug-action pause', debugService, keybindingService, 10); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; + if (!thread) { + const session = this.debugService.getViewModel().focusedSession; + const threads = session && session.getAllThreads(); + thread = threads && threads.length ? threads[0] : undefined; + } } return thread ? thread.pause() : Promise.resolve(null); @@ -402,7 +406,7 @@ export class TerminateThreadAction extends AbstractDebugAction { super(id, label, undefined, debugService, keybindingService); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -423,7 +427,7 @@ export class RestartFrameAction extends AbstractDebugAction { super(id, label, undefined, debugService, keybindingService); } - public run(frame: IStackFrame): TPromise { + public run(frame: IStackFrame): Promise { if (!frame) { frame = this.debugService.getViewModel().focusedStackFrame; } @@ -440,7 +444,7 @@ export class RemoveBreakpointAction extends AbstractDebugAction { super(id, label, 'debug-action remove', debugService, keybindingService); } - public run(breakpoint: IBreakpoint): TPromise { + public run(breakpoint: IBreakpoint): Promise { return breakpoint instanceof Breakpoint ? this.debugService.removeBreakpoints(breakpoint.getId()) : this.debugService.removeFunctionBreakpoints(breakpoint.getId()); } @@ -455,7 +459,7 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { return Promise.all([this.debugService.removeBreakpoints(), this.debugService.removeFunctionBreakpoints()]); } @@ -474,7 +478,7 @@ export class EnableAllBreakpointsAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { return this.debugService.enableOrDisableBreakpoints(true); } @@ -493,7 +497,7 @@ export class DisableAllBreakpointsAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { return this.debugService.enableOrDisableBreakpoints(false); } @@ -518,7 +522,7 @@ export class ToggleBreakpointsActivatedAction extends AbstractDebugAction { })); } - public run(): TPromise { + public run(): Promise { return this.debugService.setBreakpointsActivated(!this.debugService.getModel().areBreakpointsActivated()); } @@ -536,7 +540,7 @@ export class ReapplyBreakpointsAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { return this.debugService.setBreakpointsActivated(true); } @@ -556,7 +560,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { this.debugService.addFunctionBreakpoint(); return Promise.resolve(null); } @@ -575,7 +579,7 @@ export class SetValueAction extends AbstractDebugAction { super(id, label, null, debugService, keybindingService); } - public run(): TPromise { + public run(): Promise { if (this.variable instanceof Variable) { this.debugService.getViewModel().setSelectedExpression(this.variable); } @@ -599,7 +603,7 @@ export class AddWatchExpressionAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { this.debugService.addWatchExpression(); return Promise.resolve(undefined); } @@ -617,7 +621,7 @@ export class EditWatchExpressionAction extends AbstractDebugAction { super(id, label, undefined, debugService, keybindingService); } - public run(expression: Expression): TPromise { + public run(expression: Expression): Promise { this.debugService.getViewModel().setSelectedExpression(expression); return Promise.resolve(null); } @@ -632,7 +636,7 @@ export class AddToWatchExpressionsAction extends AbstractDebugAction { this.updateEnablement(); } - public run(): TPromise { + public run(): Promise { this.debugService.addWatchExpression(this.variable.evaluateName); return Promise.resolve(undefined); } @@ -650,7 +654,7 @@ export class RemoveWatchExpressionAction extends AbstractDebugAction { super(id, label, undefined, debugService, keybindingService); } - public run(expression: Expression): TPromise { + public run(expression: Expression): Promise { this.debugService.removeWatchExpressions(expression.getId()); return Promise.resolve(null); } @@ -665,7 +669,7 @@ export class RemoveAllWatchExpressionsAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { this.debugService.removeWatchExpressions(); return Promise.resolve(null); } @@ -690,7 +694,7 @@ export class ToggleReplAction extends TogglePanelAction { } private registerListeners(): void { - this.toDispose.push(this.panelService.onDidPanelOpen(panel => { + this.toDispose.push(this.panelService.onDidPanelOpen(({ panel }) => { if (panel.getId() === REPL_ID) { this.class = 'debug-action toggle-repl'; this.tooltip = ToggleReplAction.LABEL; @@ -716,8 +720,9 @@ export class FocusReplAction extends Action { super(id, label); } - public run(): TPromise { - return this.panelService.openPanel(REPL_ID, true); + public run(): Promise { + this.panelService.openPanel(REPL_ID, true); + return Promise.resolve(null); } } @@ -733,7 +738,7 @@ export class FocusSessionAction extends AbstractDebugAction { super(id, label, null, debugService, keybindingService, 100); } - public run(sessionName: string): TPromise { + public run(sessionName: string): Thenable { const session = this.debugService.getModel().getSessions().filter(p => p.getLabel() === sessionName).pop(); this.debugService.focusStackFrame(undefined, undefined, session, true); const stackFrame = this.debugService.getViewModel().focusedStackFrame; @@ -754,7 +759,7 @@ export class StepBackAction extends AbstractDebugAction { super(id, label, 'debug-action step-back', debugService, keybindingService, 50); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -777,7 +782,7 @@ export class ReverseContinueAction extends AbstractDebugAction { super(id, label, 'debug-action reverse-continue', debugService, keybindingService, 60); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -797,7 +802,7 @@ export class ReplCollapseAllAction extends CollapseAction { super(viewer, true, undefined); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return super.run(event).then(() => { this.toFocus.focus(); }); diff --git a/src/vs/workbench/parts/debug/browser/debugCommands.ts b/src/vs/workbench/parts/debug/browser/debugCommands.ts index 58ece5291f1..88d709b6eaa 100644 --- a/src/vs/workbench/parts/debug/browser/debugCommands.ts +++ b/src/vs/workbench/parts/debug/browser/debugCommands.ts @@ -22,6 +22,7 @@ import { openBreakpointSource } from 'vs/workbench/parts/debug/browser/breakpoin import { INotificationService } from 'vs/platform/notification/common/notification'; import { InputFocusedContext } from 'vs/platform/workbench/common/contextkeys'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { PanelFocusContext } from 'vs/workbench/browser/parts/panel/panelPart'; export const ADD_CONFIGURATION_ID = 'debug.addConfiguration'; export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint'; @@ -180,7 +181,7 @@ export function registerCommands(): void { const manager = accessor.get(IDebugService).getConfigurationManager(); if (accessor.get(IWorkspaceContextService).getWorkbenchState() === WorkbenchState.EMPTY) { accessor.get(INotificationService).info(nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration.")); - return Promise.resolve(null); + return undefined; } const launch = manager.getLaunches().filter(l => l.uri.toString() === launchUri).pop() || manager.selectedConfiguration.launch; @@ -208,14 +209,14 @@ export function registerCommands(): void { .filter(bp => (bp.column === position.column || !bp.column && position.column <= 1)).pop(); if (bp) { - return Promise.resolve(null); + return undefined; } if (debugService.getConfigurationManager().canSetBreakpointsIn(widget.getModel())) { - return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber, column: position.column > 1 ? position.column : undefined }]); + return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber, column: position.column > 1 ? position.column : undefined }], 'debugCommands.inlineBreakpointCommand'); } } - return Promise.resolve(null); + return undefined; }; KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingWeight.WorkbenchContrib, @@ -237,7 +238,7 @@ export function registerCommands(): void { id: TOGGLE_INLINE_BREAKPOINT_ID, title: nls.localize('addInlineBreakpoint', "Add Inline Breakpoint") }, - when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, EditorContextKeys.writable, EditorContextKeys.editorTextFocus), + when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), EditorContextKeys.editorTextFocus), group: 'debug', order: 1 }); @@ -258,7 +259,7 @@ export function registerCommands(): void { } } - return Promise.resolve(undefined); + return undefined; } }); } diff --git a/src/vs/workbench/parts/debug/browser/debugContentProvider.ts b/src/vs/workbench/parts/debug/browser/debugContentProvider.ts index bd68f7a90a6..ffc2139677b 100644 --- a/src/vs/workbench/parts/debug/browser/debugContentProvider.ts +++ b/src/vs/workbench/parts/debug/browser/debugContentProvider.ts @@ -5,7 +5,6 @@ import { URI as uri } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { guessMimeTypes, MIME_TEXT } from 'vs/base/common/mime'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -39,7 +38,7 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC textModelResolverService.registerTextModelContentProvider(DEBUG_SCHEME, this); } - public provideTextContent(resource: uri): TPromise { + public provideTextContent(resource: uri): Promise { let session: IDebugSession; @@ -56,20 +55,24 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC if (!session) { return Promise.reject(new Error(localize('unable', "Unable to resolve the resource without a debug session"))); } - const createErrModel = (message: string) => { + const createErrModel = (errMsg?: string) => { this.debugService.sourceIsNotAvailable(resource); - const modePromise = this.modeService.getOrCreateMode(MIME_TEXT); - return this.modelService.createModel(message, modePromise, resource); + const languageSelection = this.modeService.create(MIME_TEXT); + const message = errMsg + ? localize('canNotResolveSourceWithError', "Could not load source '{0}': {1}.", resource.path, errMsg) + : localize('canNotResolveSource', "Could not load source '{0}'.", resource.path); + return this.modelService.createModel(message, languageSelection, resource); }; return session.loadSource(resource).then(response => { - if (!response) { - return createErrModel(localize('canNotResolveSource', "Could not resolve resource {0}, no response from debug extension.", resource.toString())); + + if (response && response.body) { + const mime = response.body.mimeType || guessMimeTypes(resource.path)[0]; + const languageSelection = this.modeService.create(mime); + return this.modelService.createModel(response.body.content, languageSelection, resource); } - const mime = response.body.mimeType || guessMimeTypes(resource.path)[0]; - const modePromise = this.modeService.getOrCreateMode(mime); - return this.modelService.createModel(response.body.content, modePromise, resource); + return createErrModel(); }, (err: DebugProtocol.ErrorResponse) => createErrModel(err.message)); } diff --git a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts index ca7e8ac1021..f136aaa92e2 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -17,6 +16,8 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { openBreakpointSource } from 'vs/workbench/parts/debug/browser/breakpointsView'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { PanelFocusContext } from 'vs/workbench/browser/parts/panel/panelPart'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint'; class ToggleBreakpointAction extends EditorAction { @@ -34,7 +35,7 @@ class ToggleBreakpointAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); const position = editor.getPosition(); @@ -45,7 +46,7 @@ class ToggleBreakpointAction extends EditorAction { return Promise.all(bps.map(bp => debugService.removeBreakpoints(bp.getId()))); } if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { - return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber }]); + return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber }], 'debugEditorActions.toggleBreakpointAction'); } return Promise.resolve(null); @@ -98,12 +99,15 @@ class LogPointAction extends EditorAction { class RunToCursorAction extends EditorAction { + public static ID = 'editor.debug.action.runToCursor'; + public static LABEL = nls.localize('runToCursor', "Run to Cursor"); + constructor() { super({ - id: 'editor.debug.action.runToCursor', - label: nls.localize('runToCursor', "Run to Cursor"), + id: RunToCursorAction.ID, + label: RunToCursorAction.LABEL, alias: 'Debug: Run to Cursor', - precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, EditorContextKeys.writable, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), + precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), menuOpts: { group: 'debug', order: 2 @@ -111,14 +115,16 @@ class RunToCursorAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); - if (debugService.state !== State.Stopped) { + const focusedSession = debugService.getViewModel().focusedSession; + if (debugService.state !== State.Stopped || !focusedSession) { return Promise.resolve(null); } let breakpointToRemove: IBreakpoint; - const oneTimeListener = debugService.onDidChangeState(state => { + const oneTimeListener = focusedSession.onDidChangeState(() => { + const state = focusedSession.state; if (state === State.Stopped || state === State.Inactive) { if (breakpointToRemove) { debugService.removeBreakpoints(breakpointToRemove.getId()); @@ -130,7 +136,7 @@ class RunToCursorAction extends EditorAction { const position = editor.getPosition(); const uri = editor.getModel().uri; const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length); - return (bpExists ? Promise.resolve(null) : >debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column: position.column }])).then((breakpoints) => { + return (bpExists ? Promise.resolve(null) : >debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column: position.column }], 'debugEditorActions.runToCursorAction')).then((breakpoints) => { if (breakpoints && breakpoints.length) { breakpointToRemove = breakpoints[0]; } @@ -154,7 +160,7 @@ class SelectionToReplAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); const panelService = accessor.get(IPanelService); @@ -182,7 +188,7 @@ class SelectionToWatchExpressionsAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Thenable { const debugService = accessor.get(IDebugService); const viewletService = accessor.get(IViewletService); @@ -207,7 +213,7 @@ class ShowDebugHoverAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const position = editor.getPosition(); const word = editor.getModel().getWordAtPosition(position); if (!word) { @@ -224,7 +230,7 @@ class GoToBreakpointAction extends EditorAction { super(opts); } - public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Thenable { const debugService = accessor.get(IDebugService); const editorService = accessor.get(IEditorService); const currentUri = editor.getModel().uri; @@ -290,3 +296,12 @@ registerEditorAction(SelectionToWatchExpressionsAction); registerEditorAction(ShowDebugHoverAction); registerEditorAction(GoToNextBreakpointAction); registerEditorAction(GoToPreviousBreakpointAction); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: RunToCursorAction.ID, + title: RunToCursorAction.LABEL, + category: 'Debug' + }, + group: 'debug', + when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')), +}); diff --git a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts index 4efc4239469..0f6aa619a07 100644 --- a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts +++ b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as Filters from 'vs/base/common/filters'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as Quickopen from 'vs/workbench/browser/quickopen'; import * as QuickOpen from 'vs/base/parts/quickopen/common/quickOpen'; import * as Model from 'vs/base/parts/quickopen/browser/quickOpenModel'; @@ -93,7 +92,7 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler { return nls.localize('debugAriaLabel', "Type a name of a launch configuration to run."); } - public getResults(input: string, token: CancellationToken): TPromise { + public getResults(input: string, token: CancellationToken): Promise { const configurations: Model.QuickOpenEntry[] = []; const configManager = this.debugService.getConfigurationManager(); diff --git a/src/vs/workbench/parts/debug/browser/debugToolbar.ts b/src/vs/workbench/parts/debug/browser/debugToolbar.ts index cbdd02c1352..8e2fe051416 100644 --- a/src/vs/workbench/parts/debug/browser/debugToolbar.ts +++ b/src/vs/workbench/parts/debug/browser/debugToolbar.ts @@ -93,7 +93,7 @@ export class DebugToolbar extends Themable implements IWorkbenchContribution { this.updateScheduler = this._register(new RunOnceScheduler(() => { const state = this.debugService.state; const toolBarLocation = this.configurationService.getValue('debug').toolBarLocation; - if (state === State.Inactive || state === State.Initializing || toolBarLocation === 'docked' || toolBarLocation === 'hidden') { + if (state === State.Inactive || toolBarLocation === 'docked' || toolBarLocation === 'hidden') { return this.hide(); } diff --git a/src/vs/workbench/parts/debug/browser/debugViewlet.ts b/src/vs/workbench/parts/debug/browser/debugViewlet.ts index e026eaa791a..dea2d392366 100644 --- a/src/vs/workbench/parts/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/parts/debug/browser/debugViewlet.ts @@ -64,10 +64,9 @@ export class DebugViewlet extends ViewContainerViewlet { })); } - create(parent: HTMLElement): Promise { - return super.create(parent).then(() => { - DOM.addClass(parent, 'debug-viewlet'); - }); + create(parent: HTMLElement): void { + super.create(parent); + DOM.addClass(parent, 'debug-viewlet'); } public focus(): void { diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index e0d09dddc54..70e49207279 100644 --- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import { TreeViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as dom from 'vs/base/browser/dom'; import { normalize, isAbsolute, sep } from 'vs/base/common/paths'; import { IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; @@ -27,15 +26,11 @@ import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { ltrim } from 'vs/base/common/strings'; import { RunOnceScheduler } from 'vs/base/common/async'; +import { ResourceLabel, IResourceLabel, IResourceLabelOptions } from 'vs/workbench/browser/labels'; +import { FileKind } from 'vs/platform/files/common/files'; const SMART = true; -const $ = dom.$; - -const SESSION_TEMPLATE_ID = 'session'; -const SOURCE_TEMPLATE_ID = 'source'; -const ROOT_FOLDER_TEMPLATE_ID = 'node'; - class BaseTreeItem { private _showedMoreThanOne: boolean; @@ -56,6 +51,14 @@ class BaseTreeItem { setSource(session: IDebugSession, source: Source): void { this._source = source; + if (source.raw && source.raw.sources) { + for (const src of source.raw.sources) { + const s = new BaseTreeItem(this, src.name); + this._children[src.path] = s; + const ss = session.getSource(src); + s.setSource(session, ss); + } + } } createIfNeeded(key: string, factory: (parent: BaseTreeItem, label: string) => T): T { @@ -85,7 +88,7 @@ class BaseTreeItem { } getTemplateId(): string { - return SOURCE_TEMPLATE_ID; + return 'id'; } // a dynamic ID based on the parent chain; required for reparenting (see #55448) @@ -125,7 +128,7 @@ class BaseTreeItem { } // skips intermediate single-child nodes - getChildren(): TPromise { + getChildren(): Promise { const child = this.oneChild(); if (child) { return child.getChildren(); @@ -146,6 +149,9 @@ class BaseTreeItem { // skips intermediate single-child nodes getHoverLabel(): string { + if (this._source && this._parent && this._parent._source) { + return this._source.raw.path || this._source.raw.name; + } let label = this.getLabel(false); const parent = this.getParent(); if (parent) { @@ -174,7 +180,7 @@ class BaseTreeItem { } private oneChild(): BaseTreeItem { - if (SMART && !this._showedMoreThanOne) { + if (SMART && !this._source && !this._showedMoreThanOne && !(this instanceof RootFolderTreeItem) && !(this instanceof SessionTreeItem)) { const keys = Object.keys(this._children); if (keys.length === 1) { return this._children[keys[0]]; @@ -193,10 +199,6 @@ class RootFolderTreeItem extends BaseTreeItem { constructor(parent: BaseTreeItem, public folder: IWorkspaceFolder) { super(parent, folder.name); } - - getTemplateId(): string { - return ROOT_FOLDER_TEMPLATE_ID; - } } class RootTreeItem extends BaseTreeItem { @@ -240,15 +242,11 @@ class SessionTreeItem extends BaseTreeItem { return undefined; } - getTemplateId(): string { - return SESSION_TEMPLATE_ID; - } - hasChildren(): boolean { return true; } - getChildren(): TPromise { + getChildren(): Promise { if (!this._initialized) { this._initialized = true; @@ -372,6 +370,7 @@ export class LoadedScriptsView extends TreeViewsViewletPanel { protected renderBody(container: HTMLElement): void { dom.addClass(container, 'debug-loaded-scripts'); + dom.addClass(container, 'show-file-icons'); this.treeContainer = renderViewTree(container); @@ -478,11 +477,11 @@ class LoadedScriptsDataSource implements IDataSource { return element.hasChildren(); } - getChildren(tree: ITree, element: any): TPromise { + getChildren(tree: ITree, element: any): Promise { return element.getChildren(); } - getParent(tree: ITree, element: any): TPromise { + getParent(tree: ITree, element: any): Promise { return Promise.resolve(element.getParent()); } @@ -491,20 +490,17 @@ class LoadedScriptsDataSource implements IDataSource { } } -interface ISessionTemplateData { - session: HTMLElement; -} - -interface ISourceTemplateData { - source: HTMLElement; -} - -interface INodeTemplateData { - node: HTMLElement; +interface ITemplateData { + label: ResourceLabel; } class LoadedScriptsRenderer implements IRenderer { + constructor( + @IInstantiationService private instantiationService: IInstantiationService + ) { + } + getHeight(tree: ITree, element: any): number { return 22; } @@ -514,52 +510,46 @@ class LoadedScriptsRenderer implements IRenderer { } renderTemplate(tree: ITree, templateId: string, container: HTMLElement) { - - if (templateId === SESSION_TEMPLATE_ID) { - let data: ISessionTemplateData = Object.create(null); - data.session = dom.append(container, $('.session')); - return data; - } - - if (templateId === SOURCE_TEMPLATE_ID) { - let data: ISourceTemplateData = Object.create(null); - data.source = dom.append(container, $('.source')); - return data; - } - - let data: INodeTemplateData = Object.create(null); - data.node = dom.append(container, $('.node')); + let data: ITemplateData = Object.create(null); + data.label = this.instantiationService.createInstance(ResourceLabel, container, void 0); return data; } - renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { - if (templateId === SESSION_TEMPLATE_ID) { - this.renderSession(element, templateData); - } else if (templateId === SOURCE_TEMPLATE_ID) { - this.renderSource(element, templateData); - } else if (templateId === ROOT_FOLDER_TEMPLATE_ID) { - this.renderNode(element, templateData); + renderElement(tree: ITree, element: any, templateId: string, data: ITemplateData): void { + + const label: IResourceLabel = { + name: element.getLabel() + }; + const options: IResourceLabelOptions = { + title: element.getHoverLabel() + }; + + if (element instanceof RootFolderTreeItem) { + + options.fileKind = FileKind.ROOT_FOLDER; + + } else if (element instanceof SessionTreeItem) { + + options.title = nls.localize('loadedScriptsSession', "Debug Session"); + options.hideIcon = true; + + } else if (element instanceof BaseTreeItem) { + + const src = element.getSource(); + if (src && src.uri) { + label.resource = src.uri; + options.fileKind = FileKind.FILE; + } else { + options.fileKind = FileKind.FOLDER; + } } + + data.label.setLabel(label, options); } disposeTemplate(tree: ITree, templateId: string, templateData: any): void { // noop } - - private renderSession(session: SessionTreeItem, data: ISessionTemplateData): void { - data.session.title = nls.localize('loadedScriptsSession', "Session"); - data.session.textContent = session.getLabel(); - } - - private renderSource(source: BaseTreeItem, data: ISourceTemplateData): void { - data.source.title = source.getHoverLabel(); - data.source.textContent = source.getLabel(); - } - - private renderNode(node: BaseTreeItem, data: INodeTemplateData): void { - data.node.title = node.getHoverLabel(); - data.node.textContent = node.getLabel(); - } } class LoadedSciptsAccessibilityProvider implements IAccessibilityProvider { diff --git a/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css b/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css index 165dd70d690..f08164c7538 100644 --- a/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css +++ b/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ .monaco-editor .zone-widget .zone-widget-container.breakpoint-widget { - height: 30px !important; display: flex; border-color: #007ACC; } diff --git a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css index 1f760b93e71..3fb41aa0f3e 100644 --- a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css @@ -257,6 +257,11 @@ text-align: center; } +.debug-viewlet .debug-call-stack .show-more { + font-style: italic; + opacity: 0.35; +} + .monaco-workbench .debug-viewlet .monaco-tree-row .expression { white-space: pre; } diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 937ae541a8c..dff4da426ff 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import { URI as uri } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import severity from 'vs/base/common/severity'; import { Event } from 'vs/base/common/event'; import { IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; @@ -100,7 +99,7 @@ export interface IReplElementSource { export interface IExpressionContainer extends ITreeElement { readonly hasChildren: boolean; - getChildren(): TPromise>; + getChildren(): Promise>; } export interface IExpression extends IReplElement, IExpressionContainer { @@ -111,9 +110,9 @@ export interface IExpression extends IReplElement, IExpressionContainer { } export interface IDebugger { - createDebugAdapter(session: IDebugSession, root: IWorkspaceFolder, config: IConfig, outputService: IOutputService): TPromise; - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): TPromise; - getCustomTelemetryService(): TPromise; + createDebugAdapter(session: IDebugSession, root: IWorkspaceFolder, config: IConfig, outputService: IOutputService): Promise; + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise; + getCustomTelemetryService(): Thenable; } export enum State { @@ -156,7 +155,7 @@ export interface IDebugSession extends ITreeElement { getReplElements(): ReadonlyArray; removeReplExpressions(): void; - addReplExpression(stackFrame: IStackFrame, name: string): TPromise; + addReplExpression(stackFrame: IStackFrame, name: string): Promise; appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void; logToRepl(sev: severity, args: any[], frame?: { uri: uri, line: number, column: number }); @@ -178,37 +177,37 @@ export interface IDebugSession extends ITreeElement { // DAP request - initialize(dbgr: IDebugger): TPromise; - launchOrAttach(config: IConfig): TPromise; - restart(): TPromise; - terminate(restart?: boolean /* false */): TPromise; - disconnect(restart?: boolean /* false */): TPromise; + initialize(dbgr: IDebugger): Thenable; + launchOrAttach(config: IConfig): Promise; + restart(): Promise; + terminate(restart?: boolean /* false */): Promise; + disconnect(restart?: boolean /* false */): Promise; - sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): TPromise; - sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): TPromise; - sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): TPromise; + sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise; + sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise; + sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise; - stackTrace(threadId: number, startFrame: number, levels: number): TPromise; - exceptionInfo(threadId: number): TPromise; - scopes(frameId: number): TPromise; - variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): TPromise; - evaluate(expression: string, frameId?: number, context?: string): TPromise; - customRequest(request: string, args: any): TPromise; + stackTrace(threadId: number, startFrame: number, levels: number): Promise; + exceptionInfo(threadId: number): Promise; + scopes(frameId: number): Promise; + variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): Promise; + evaluate(expression: string, frameId?: number, context?: string): Promise; + customRequest(request: string, args: any): Promise; - restartFrame(frameId: number, threadId: number): TPromise; - next(threadId: number): TPromise; - stepIn(threadId: number): TPromise; - stepOut(threadId: number): TPromise; - stepBack(threadId: number): TPromise; - continue(threadId: number): TPromise; - reverseContinue(threadId: number): TPromise; - pause(threadId: number): TPromise; - terminateThreads(threadIds: number[]): TPromise; + restartFrame(frameId: number, threadId: number): Promise; + next(threadId: number): Promise; + stepIn(threadId: number): Promise; + stepOut(threadId: number): Promise; + stepBack(threadId: number): Promise; + continue(threadId: number): Promise; + reverseContinue(threadId: number): Promise; + pause(threadId: number): Promise; + terminateThreads(threadIds: number[]): Promise; - completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise; - setVariable(variablesReference: number, name: string, value: string): TPromise; - loadSource(resource: uri): TPromise; - getLoadedSources(): TPromise; + completions(frameId: number, text: string, position: Position, overwriteBefore: number): Promise; + setVariable(variablesReference: number, name: string, value: string): Promise; + loadSource(resource: uri): Promise; + getLoadedSources(): Promise; } export interface IThread extends ITreeElement { @@ -236,7 +235,7 @@ export interface IThread extends ITreeElement { /** * Information about the exception if an 'exception' stopped event raised and DA supports the 'exceptionInfo' request, otherwise null. */ - readonly exceptionInfo: TPromise; + readonly exceptionInfo: Promise; /** * Gets the callstack if it has already been received from the debug @@ -255,14 +254,14 @@ export interface IThread extends ITreeElement { */ readonly stopped: boolean; - next(): TPromise; - stepIn(): TPromise; - stepOut(): TPromise; - stepBack(): TPromise; - continue(): TPromise; - pause(): TPromise; - terminate(): TPromise; - reverseContinue(): TPromise; + next(): Promise; + stepIn(): Promise; + stepOut(): Promise; + stepBack(): Promise; + continue(): Promise; + pause(): Promise; + terminate(): Promise; + reverseContinue(): Promise; } export interface IScope extends IExpressionContainer { @@ -278,12 +277,12 @@ export interface IStackFrame extends ITreeElement { readonly frameId: number; readonly range: IRange; readonly source: Source; - getScopes(): TPromise>; - getMostSpecificScopes(range: IRange): TPromise>; + getScopes(): Promise>; + getMostSpecificScopes(range: IRange): Promise>; getSpecificSourceName(): string; - restart(): TPromise; + restart(): Promise; toString(): string; - openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean): TPromise; + openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean): Thenable; } export interface IEnablement extends ITreeElement { @@ -452,16 +451,16 @@ export interface IDebugAdapter extends IDisposable { readonly onExit: Event; onRequest(callback: (request: DebugProtocol.Request) => void); onEvent(callback: (event: DebugProtocol.Event) => void); - startSession(): TPromise; + startSession(): Promise; sendMessage(message: DebugProtocol.ProtocolMessage): void; sendResponse(response: DebugProtocol.Response): void; sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timemout?: number): void; - stopSession(): TPromise; + stopSession(): Promise; } export interface IDebugAdapterProvider extends ITerminalLauncher { createDebugAdapter(session: IDebugSession, folder: IWorkspaceFolder, config: IConfig): IDebugAdapter; - substituteVariables(folder: IWorkspaceFolder, config: IConfig): TPromise; + substituteVariables(folder: IWorkspaceFolder, config: IConfig): Promise; } export interface IDebugAdapterExecutable { @@ -519,15 +518,14 @@ export interface IDebuggerContribution extends IPlatformSpecificAdapterContribut export interface IDebugConfigurationProvider { readonly type: string; - handle: number; - resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig): TPromise; - provideDebugConfigurations?(folderUri: uri | undefined): TPromise; - provideDebugAdapter?(session: IDebugSession, folderUri: uri | undefined, config: IConfig): TPromise; + resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig): Promise; + provideDebugConfigurations?(folderUri: uri | undefined): Promise; + provideDebugAdapter?(session: IDebugSession, folderUri: uri | undefined, config: IConfig): Promise; hasTracker: boolean; } export interface ITerminalLauncher { - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise; + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise; } export interface ITerminalSettings { @@ -571,18 +569,19 @@ export interface IConfigurationManager { onDidSelectConfiguration: Event; needsToRunInExtHost(debugType: string): boolean; + hasDebugConfigurationProvider(debugType: string): boolean; - registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void; - unregisterDebugConfigurationProvider(handle: number): void; + registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable; + unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void; - resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any): TPromise; - provideDebugAdapter(session: IDebugSession, folderUri: uri | undefined, config: IConfig): TPromise; + resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any): Thenable; + provideDebugAdapter(session: IDebugSession, folderUri: uri | undefined, config: IConfig): Promise; registerDebugAdapterProvider(debugTypes: string[], debugAdapterLauncher: IDebugAdapterProvider): IDisposable; createDebugAdapter(session: IDebugSession, folder: IWorkspaceFolder, config: IConfig): IDebugAdapter; - substituteVariables(debugType: string, folder: IWorkspaceFolder, config: IConfig): TPromise; - runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise; + substituteVariables(debugType: string, folder: IWorkspaceFolder, config: IConfig): Promise; + runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise; } export interface ILaunch { @@ -628,7 +627,7 @@ export interface ILaunch { /** * Opens the launch.json file. Creates if it does not exist. */ - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }>; + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Thenable<{ editor: IEditor, created: boolean }>; } // Debug service interfaces @@ -676,7 +675,7 @@ export interface IDebugService { /** * Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes. */ - addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): TPromise; + addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[], context: string): Promise; /** * Updates the breakpoints. @@ -687,19 +686,19 @@ export interface IDebugService { * Enables or disables all breakpoints. If breakpoint is passed only enables or disables the passed breakpoint. * Notifies debug adapter of breakpoint changes. */ - enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): TPromise; + enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): Promise; /** * Sets the global activated property for all breakpoints. * Notifies debug adapter of breakpoint changes. */ - setBreakpointsActivated(activated: boolean): TPromise; + setBreakpointsActivated(activated: boolean): Promise; /** * Removes all breakpoints. If id is passed only removes the breakpoint associated with that id. * Notifies debug adapter of breakpoint changes. */ - removeBreakpoints(id?: string): TPromise; + removeBreakpoints(id?: string): Promise; /** * Adds a new function breakpoint for the given name. @@ -710,19 +709,19 @@ export interface IDebugService { * Renames an already existing function breakpoint. * Notifies debug adapter of breakpoint changes. */ - renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise; + renameFunctionBreakpoint(id: string, newFunctionName: string): Promise; /** * Removes all function breakpoints. If id is passed only removes the function breakpoint with the passed id. * Notifies debug adapter of breakpoint changes. */ - removeFunctionBreakpoints(id?: string): TPromise; + removeFunctionBreakpoints(id?: string): Promise; /** * Sends all breakpoints to the passed session. * If session is not passed, sends all breakpoints to each session. */ - sendAllBreakpoints(session?: IDebugSession): TPromise; + sendAllBreakpoints(session?: IDebugSession): Promise; /** * Adds a new watch expression and evaluates it against the debug adapter. @@ -752,17 +751,17 @@ export interface IDebugService { * Returns true if the start debugging was successfull. For compound launches, all configurations have to start successfuly for it to return success. * On errors the startDebugging will throw an error, however some error and cancelations are handled and in that case will simply return false. */ - startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): TPromise; + startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): Thenable; /** * Restarts a session or creates a new one if there is no active session. */ - restartSession(session: IDebugSession, restartData?: any): TPromise; + restartSession(session: IDebugSession, restartData?: any): Thenable; /** * Stops the session. If the session does not exist then stops all sessions. */ - stopSession(session: IDebugSession): TPromise; + stopSession(session: IDebugSession): Promise; /** * Makes unavailable all sources with the passed uri. Source will appear as grayed out in callstack view. @@ -788,8 +787,8 @@ export const enum BreakpointWidgetContext { } export interface IDebugEditorContribution extends IEditorContribution { - showHover(range: Range, focus: boolean): TPromise; + showHover(range: Range, focus: boolean): Promise; showBreakpointWidget(lineNumber: number, column: number, context?: BreakpointWidgetContext): void; closeBreakpointWidget(): void; - addLaunchConfiguration(): TPromise; + addLaunchConfiguration(): Promise; } diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 3d2fe5c1b7e..57bb5ad391c 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { URI as uri } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as lifecycle from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; @@ -81,7 +80,7 @@ export class RawObjectReplElement extends AbstractReplElement implements IExpres return (Array.isArray(this.valueObj) && this.valueObj.length > 0) || (isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0); } - public getChildren(): TPromise { + public getChildren(): Promise { let result: IExpression[] = []; if (Array.isArray(this.valueObj)) { result = (this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN) @@ -107,7 +106,7 @@ export class ExpressionContainer implements IExpressionContainer { public valueChanged: boolean; private _value: string; - protected children: TPromise; + protected children: Promise; constructor( protected session: IDebugSession, @@ -127,7 +126,7 @@ export class ExpressionContainer implements IExpressionContainer { this.children = undefined; // invalidate children cache } - public getChildren(): TPromise { + public getChildren(): Promise { if (!this.children) { this.children = this.doGetChildren(); } @@ -135,7 +134,7 @@ export class ExpressionContainer implements IExpressionContainer { return this.children; } - private doGetChildren(): TPromise { + private doGetChildren(): Promise { if (!this.hasChildren) { return Promise.resolve([]); } @@ -145,7 +144,7 @@ export class ExpressionContainer implements IExpressionContainer { } // Check if object has named variables, fetch them independent from indexed variables #9670 - const childrenThenable: Thenable = !!this.namedVariables ? this.fetchVariables(undefined, undefined, 'named') : Promise.resolve([]); + const childrenThenable = !!this.namedVariables ? this.fetchVariables(undefined, undefined, 'named') : Promise.resolve([]); return childrenThenable.then(childrenArray => { // Use a dynamic chunk size based on the number of elements #9774 let chunkSize = ExpressionContainer.BASE_CHUNK_SIZE; @@ -183,7 +182,7 @@ export class ExpressionContainer implements IExpressionContainer { return this.reference > 0; } - private fetchVariables(start: number, count: number, filter: 'indexed' | 'named'): TPromise { + private fetchVariables(start: number, count: number, filter: 'indexed' | 'named'): Promise { return this.session.variables(this.reference, filter, start, count).then(response => { return response && response.body && response.body.variables ? distinct(response.body.variables.filter(v => !!v && isString(v.name)), v => v.name).map( @@ -225,9 +224,9 @@ export class Expression extends ExpressionContainer implements IExpression { } } - public evaluate(session: IDebugSession, stackFrame: IStackFrame, context: string): TPromise { + public evaluate(session: IDebugSession, stackFrame: IStackFrame, context: string): Promise { if (!session || (!stackFrame && context !== 'repl')) { - this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate") : Expression.DEFAULT_VALUE; + this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate expressions") : Expression.DEFAULT_VALUE; this.available = false; this.reference = 0; @@ -279,7 +278,7 @@ export class Variable extends ExpressionContainer implements IExpression { this.value = value; } - public setVariable(value: string): TPromise { + public setVariable(value: string): Promise { return this.session.setVariable((this.parent).reference, this.name, value).then(response => { if (response && response.body) { this.value = response.body.value; @@ -316,7 +315,7 @@ export class Scope extends ExpressionContainer implements IScope { export class StackFrame implements IStackFrame { - private scopes: TPromise; + private scopes: Promise; constructor( public thread: IThread, @@ -334,7 +333,7 @@ export class StackFrame implements IStackFrame { return `stackframe:${this.thread.getId()}:${this.frameId}:${this.index}`; } - public getScopes(): TPromise { + public getScopes(): Promise { if (!this.scopes) { this.scopes = this.thread.session.scopes(this.frameId).then(response => { return response && response.body && response.body.scopes ? @@ -347,7 +346,11 @@ export class StackFrame implements IStackFrame { } public getSpecificSourceName(): string { - const otherSources = this.thread.getCallStack().map(sf => sf.source).filter(s => s !== this.source); + // To reduce flashing of the path name and the way we fetch stack frames + // We need to compute the source name based on the other frames in the stale call stack + let callStack = (this.thread).getStaleCallStack(); + callStack = callStack.length > 0 ? callStack : this.thread.getCallStack(); + const otherSources = callStack.map(sf => sf.source).filter(s => s !== this.source); let suffixLength = 0; otherSources.forEach(s => { if (s.name === this.source.name) { @@ -362,7 +365,7 @@ export class StackFrame implements IStackFrame { return (from > 0 ? '...' : '') + this.source.uri.path.substr(from); } - public getMostSpecificScopes(range: IRange): TPromise { + public getMostSpecificScopes(range: IRange): Promise { return this.getScopes().then(scopes => { scopes = scopes.filter(s => !s.expensive); const haveRangeInfo = scopes.some(s => !!s.range); @@ -376,7 +379,7 @@ export class StackFrame implements IStackFrame { }); } - public restart(): TPromise { + public restart(): Promise { return this.thread.session.restartFrame(this.frameId, this.thread.threadId); } @@ -384,7 +387,7 @@ export class StackFrame implements IStackFrame { return `${this.name} (${this.source.inMemory ? this.source.name : this.source.uri.fsPath}:${this.range.startLineNumber})`; } - public openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + public openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable { return !this.source.available ? Promise.resolve(null) : this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide, pinned); } @@ -418,7 +421,7 @@ export class Thread implements IThread { return this.callStack; } - public getStaleCallStack(): IStackFrame[] { + public getStaleCallStack(): ReadonlyArray { return this.staleCallStack; } @@ -429,7 +432,7 @@ export class Thread implements IThread { * Only fetches the first stack frame for performance reasons. Calling this method consecutive times * gets the remainder of the call stack. */ - public fetchCallStack(levels = 20): TPromise { + public fetchCallStack(levels = 20): Promise { if (!this.stopped) { return Promise.resolve(null); } @@ -444,7 +447,7 @@ export class Thread implements IThread { }); } - private getCallStackImpl(startFrame: number, levels: number): TPromise { + private getCallStackImpl(startFrame: number, levels: number): Promise { return this.session.stackTrace(this.threadId, startFrame, levels).then(response => { if (!response || !response.body) { return []; @@ -476,7 +479,7 @@ export class Thread implements IThread { /** * Returns exception info promise if the exception was thrown, otherwise null */ - public get exceptionInfo(): TPromise { + public get exceptionInfo(): Promise { if (this.stoppedDetails && this.stoppedDetails.reason === 'exception') { if (this.session.capabilities.supportsExceptionInfoRequest) { return this.session.exceptionInfo(this.threadId); @@ -489,35 +492,35 @@ export class Thread implements IThread { return Promise.resolve(null); } - public next(): TPromise { + public next(): Promise { return this.session.next(this.threadId); } - public stepIn(): TPromise { + public stepIn(): Promise { return this.session.stepIn(this.threadId); } - public stepOut(): TPromise { + public stepOut(): Promise { return this.session.stepOut(this.threadId); } - public stepBack(): TPromise { + public stepBack(): Promise { return this.session.stepBack(this.threadId); } - public continue(): TPromise { + public continue(): Promise { return this.session.continue(this.threadId); } - public pause(): TPromise { + public pause(): Promise { return this.session.pause(this.threadId); } - public terminate(): TPromise { + public terminate(): Promise { return this.session.terminateThreads([this.threadId]); } - public reverseContinue(): TPromise { + public reverseContinue(): Promise { return this.session.reverseContinue(this.threadId); } } @@ -758,8 +761,18 @@ export class DebugModel implements IDebugModel { } public addSession(session: IDebugSession): void { - // Make sure to remove all inactive sessions once a new session is started - this.sessions = this.sessions.filter(s => s.state !== State.Inactive); + this.sessions = this.sessions.filter(s => { + if (s.getId() === session.getId()) { + // Make sure to de-dupe if a session is re-intialized. In case of EH debugging we are adding a session again after an attach. + return false; + } + if (s.state === State.Inactive && s.getLabel() === session.getLabel()) { + // Make sure to remove all inactive sessions that are using the same configuration as the new session + return false; + } + + return true; + }); this.sessions.push(session); this._onDidChangeCallStack.fire(); } @@ -795,7 +808,7 @@ export class DebugModel implements IDebugModel { } } - public fetchCallStack(thread: Thread): TPromise { + public fetchCallStack(thread: Thread): Promise { if (thread.session.capabilities.supportsDelayedStackTraceLoading) { // For improved performance load the first stack frame and then load the rest async. return thread.fetchCallStack(1).then(() => { diff --git a/src/vs/workbench/parts/debug/common/debugSource.ts b/src/vs/workbench/parts/debug/common/debugSource.ts index 992a1526095..ed6a09b1e27 100644 --- a/src/vs/workbench/parts/debug/common/debugSource.ts +++ b/src/vs/workbench/parts/debug/common/debugSource.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { URI as uri } from 'vs/base/common/uri'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; @@ -49,16 +48,16 @@ export class Source { if (this.raw.sourceReference > 0) { this.uri = uri.parse(`${DEBUG_SCHEME}:${encodeURIComponent(path)}?session=${encodeURIComponent(sessionId)}&ref=${this.raw.sourceReference}`); } else { - if (isUri(path)) { + if (isUri(path)) { // path looks like a uri this.uri = uri.parse(path); } else { - // assume path + // assume a filesystem path if (paths.isAbsolute_posix(path) || paths.isAbsolute_win32(path)) { this.uri = uri.file(path); } else { - // path is relative - // should not happen because relative paths always have a sourceReference > 0 - console.error('cannot handle relative paths without sourceReference'); + // path is relative: since VS Code cannot deal with this by itself + // create a debug url that will result in a DAP 'source' request when the url is resolved. + this.uri = uri.parse(`${DEBUG_SCHEME}:${encodeURIComponent(path)}?session=${encodeURIComponent(sessionId)}`); } } } @@ -84,7 +83,7 @@ export class Source { return this.uri.scheme === DEBUG_SCHEME; } - public openInEditor(editorService: IEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + public openInEditor(editorService: IEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable { return !this.available ? Promise.resolve(null) : editorService.openEditor({ resource: this.uri, description: this.origin, diff --git a/src/vs/workbench/parts/debug/common/debugUtils.ts b/src/vs/workbench/parts/debug/common/debugUtils.ts index 24091e11889..a12f19a08eb 100644 --- a/src/vs/workbench/parts/debug/common/debugUtils.ts +++ b/src/vs/workbench/parts/debug/common/debugUtils.ts @@ -7,6 +7,7 @@ import { equalsIgnoreCase } from 'vs/base/common/strings'; import { IConfig } from 'vs/workbench/parts/debug/common/debug'; import { URI as uri } from 'vs/base/common/uri'; import { isAbsolute_posix, isAbsolute_win32 } from 'vs/base/common/paths'; +import { deepClone } from 'vs/base/common/objects'; const _formatPIIRegexp = /{([^}]+)}/g; @@ -106,20 +107,30 @@ export function uriToString(source: DebugProtocol.Source): void { // path hooks helpers -export function convertToDAPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (source: DebugProtocol.Source) => void): void { +export function convertToDAPaths(message: DebugProtocol.ProtocolMessage, fixSourcePaths: (source: DebugProtocol.Source) => void): DebugProtocol.ProtocolMessage { + + // since we modify Source.paths in the message in place, we need to make a copy of it (see #61129) + const msg = deepClone(message); + convertPaths(msg, (toDA: boolean, source: DebugProtocol.Source | undefined) => { if (toDA && source) { fixSourcePaths(source); } }); + return msg; } -export function convertToVSCPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (source: DebugProtocol.Source) => void): void { +export function convertToVSCPaths(message: DebugProtocol.ProtocolMessage, fixSourcePaths: (source: DebugProtocol.Source) => void): DebugProtocol.ProtocolMessage { + + // since we modify Source.paths in the message in place, we need to make a copy of it (see #61129) + const msg = deepClone(message); + convertPaths(msg, (toDA: boolean, source: DebugProtocol.Source | undefined) => { if (!toDA && source) { fixSourcePaths(source); } }); + return msg; } function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (toDA: boolean, source: DebugProtocol.Source | undefined) => void): void { diff --git a/src/vs/workbench/parts/debug/common/replModel.ts b/src/vs/workbench/parts/debug/common/replModel.ts index 983204bfa98..464d03c7f13 100644 --- a/src/vs/workbench/parts/debug/common/replModel.ts +++ b/src/vs/workbench/parts/debug/common/replModel.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import severity from 'vs/base/common/severity'; import { IReplElement, IStackFrame, IExpression, IReplElementSource, IDebugSession } from 'vs/workbench/parts/debug/common/debug'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbench/parts/debug/common/debugModel'; import { isUndefinedOrNull, isObject } from 'vs/base/common/types'; import { basenameOrAuthority } from 'vs/base/common/resources'; @@ -23,7 +22,7 @@ export class ReplModel { return this.replElements; } - public addReplExpression(stackFrame: IStackFrame, name: string): TPromise { + public addReplExpression(stackFrame: IStackFrame, name: string): Promise { const expression = new Expression(name); this.addReplElements([expression]); return expression.evaluate(this.session, stackFrame, 'repl'); diff --git a/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts b/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts index 5cbb9052047..4f32cee0f78 100644 --- a/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts +++ b/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts @@ -9,7 +9,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import * as lifecycle from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; -import { Position } from 'vs/editor/common/core/position'; +import { Position, IPosition } from 'vs/editor/common/core/position'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -34,7 +34,7 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { getSimpleCodeEditorWidgetOptions } from 'vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions'; import { getSimpleEditorOptions } from 'vs/workbench/parts/codeEditor/browser/simpleEditorOptions'; -import { Range } from 'vs/editor/common/core/range'; +import { IRange, Range } from 'vs/editor/common/core/range'; const $ = dom.$; const IPrivateBreakpointWidgetService = createDecorator('privateBreakopintWidgetService'); @@ -127,6 +127,16 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi } } + public show(rangeOrPos: IRange | IPosition, heightInLines: number) { + const lineNum = this.input.getModel().getLineCount(); + super.show(rangeOrPos, lineNum + 1); + } + + public fitHeightToContent() { + const lineNum = this.input.getModel().getLineCount(); + this._relayout(lineNum + 1); + } + protected _fillContainer(container: HTMLElement): void { this.setCssClass('breakpoint-widget'); const selectBox = new SelectBox([nls.localize('expression', "Expression"), nls.localize('hitCount', "Hit Count"), nls.localize('logMessage', "Log Message")], this.context, this.contextViewService, null, { ariaLabel: nls.localize('breakpointType', 'Breakpoint Type') }); @@ -144,6 +154,9 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi this.createBreakpointInput(dom.append(container, $('.inputContainer'))); this.input.getModel().setValue(this.getInputValue(this.breakpoint)); + this.toDispose.push(this.input.getModel().onDidChangeContent(() => { + this.fitHeightToContent(); + })); this.input.setPosition({ lineNumber: 1, column: this.input.getModel().getLineMaxColumn(1) }); // Due to an electron bug we have to do the timeout, otherwise we do not get focus setTimeout(() => this.input.focus(), 100); @@ -183,7 +196,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi condition, hitCondition, logMessage - }]); + }], `breakpointWidget`); } } @@ -191,7 +204,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi } protected _doLayout(heightInPixel: number, widthInPixel: number): void { - this.input.layout({ height: 18, width: widthInPixel - 113 }); + this.input.layout({ height: heightInPixel, width: widthInPixel - 113 }); } private createBreakpointInput(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index 16663836fa9..8ff6c7a88ff 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as dom from 'vs/base/browser/dom'; -import { TPromise } from 'vs/base/common/winjs.base'; import { TreeViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDebugService, State, IStackFrame, IDebugSession, IThread, CONTEXT_CALLSTACK_ITEM_TYPE } from 'vs/workbench/parts/debug/common/debug'; import { Thread, StackFrame, ThreadAndSessionIds, DebugModel } from 'vs/workbench/parts/debug/common/debugModel'; @@ -39,6 +38,7 @@ export class CallStackView extends TreeViewsViewletPanel { private ignoreSelectionChangedEvent: boolean; private treeContainer: HTMLElement; private callStackItemType: IContextKey; + private dataSource: CallStackDataSource; constructor( private options: IViewletViewOptions, @@ -77,6 +77,7 @@ export class CallStackView extends TreeViewsViewletPanel { } this.needsRefresh = false; + this.dataSource.deemphasizedStackFramesToShow = []; (this.tree.getInput() === newTreeInput ? this.tree.refresh() : this.tree.setInput(newTreeInput)) .then(() => this.updateTreeSelection()); }, 50); @@ -91,14 +92,14 @@ export class CallStackView extends TreeViewsViewletPanel { this.pauseMessageLabel = dom.append(this.pauseMessage, $('span.label')); } - public renderBody(container: HTMLElement): void { + renderBody(container: HTMLElement): void { dom.addClass(container, 'debug-call-stack'); this.treeContainer = renderViewTree(container); const actionProvider = new CallStackActionProvider(this.debugService, this.keybindingService, this.instantiationService); const controller = this.instantiationService.createInstance(CallStackController, actionProvider, MenuId.DebugCallStackContext, {}); - + this.dataSource = new CallStackDataSource(); this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { - dataSource: new CallStackDataSource(), + dataSource: this.dataSource, renderer: this.instantiationService.createInstance(CallStackRenderer), accessibilityProvider: this.instantiationService.createInstance(CallstackAccessibilityProvider), controller @@ -133,6 +134,10 @@ export class CallStackView extends TreeViewsViewletPanel { .then(() => this.tree.refresh()); } } + if (element instanceof Array) { + this.dataSource.deemphasizedStackFramesToShow.push(...element); + this.tree.refresh(); + } })); this.disposables.push(this.tree.onDidChangeFocus(() => { const focus = this.tree.getFocus(); @@ -179,7 +184,7 @@ export class CallStackView extends TreeViewsViewletPanel { super.layoutBody(size); } - private updateTreeSelection(): TPromise { + private updateTreeSelection(): Thenable { if (!this.tree.getInput()) { // Tree not initialized yet return Promise.resolve(null); @@ -217,12 +222,11 @@ export class CallStackView extends TreeViewsViewletPanel { }); } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { - if (visible && this.needsRefresh) { - this.onCallStackChangeScheduler.schedule(); - } - }); + setVisible(visible: boolean): void { + super.setVisible(visible); + if (visible && this.needsRefresh) { + this.onCallStackChangeScheduler.schedule(); + } } } @@ -248,19 +252,19 @@ class CallStackActionProvider implements IActionProvider { // noop } - public hasActions(tree: ITree, element: any): boolean { + hasActions(tree: ITree, element: any): boolean { return false; } - public getActions(tree: ITree, element: any): TPromise { - return Promise.resolve([]); + getActions(tree: ITree, element: any): IAction[] { + return []; } - public hasSecondaryActions(tree: ITree, element: any): boolean { + hasSecondaryActions(tree: ITree, element: any): boolean { return element !== tree.getInput(); } - public getSecondaryActions(tree: ITree, element: any): TPromise { + getSecondaryActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; if (element instanceof DebugSession) { actions.push(this.instantiationService.createInstance(RestartAction, RestartAction.ID, RestartAction.LABEL)); @@ -285,31 +289,62 @@ class CallStackActionProvider implements IActionProvider { actions.push(new CopyStackTraceAction(CopyStackTraceAction.ID, CopyStackTraceAction.LABEL)); } - return Promise.resolve(actions); + return actions; } - public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { + getActionItem(tree: ITree, element: any, action: IAction): IActionItem { return null; } } class CallStackDataSource implements IDataSource { - public getId(tree: ITree, element: any): string { + deemphasizedStackFramesToShow: IStackFrame[]; + + getId(tree: ITree, element: any): string { if (typeof element === 'string') { return element; } + if (element instanceof Array) { + return `showMore ${element[0].getId()}`; + } return element.getId(); } - public hasChildren(tree: ITree, element: any): boolean { + hasChildren(tree: ITree, element: any): boolean { return element instanceof DebugModel || element instanceof DebugSession || (element instanceof Thread && (element).stopped); } - public getChildren(tree: ITree, element: any): TPromise { + getChildren(tree: ITree, element: any): Promise { if (element instanceof Thread) { - return this.getThreadChildren(element); + return this.getThreadChildren(element).then(children => { + // Check if some stack frames should be hidden under a parent element since they are deemphasized + const result = []; + children.forEach((child, index) => { + if (child instanceof StackFrame && child.source && child.source.presentationHint === 'deemphasize') { + // Check if the user clicked to show the deemphasized source + if (this.deemphasizedStackFramesToShow.indexOf(child) === -1) { + if (result.length && result[result.length - 1] instanceof Array) { + // Collect all the stackframes that will be "collapsed" + result[result.length - 1].push(child); + return; + } + + const nextChild = index < children.length - 1 ? children[index + 1] : undefined; + if (nextChild instanceof StackFrame && nextChild.source && nextChild.source.presentationHint === 'deemphasize') { + // Start collecting stackframes that will be "collapsed" + result.push([child]); + return; + } + } + } + + result.push(child); + }); + + return result; + }); } if (element instanceof DebugModel) { return Promise.resolve(element.getSessions()); @@ -319,15 +354,15 @@ class CallStackDataSource implements IDataSource { return Promise.resolve(session.getAllThreads()); } - private getThreadChildren(thread: Thread): TPromise { + private getThreadChildren(thread: Thread): Promise<(IStackFrame | string | ThreadAndSessionIds)[]> { let callStack: any[] = thread.getCallStack(); - let callStackPromise: TPromise = Promise.resolve(null); + let callStackPromise: Promise = Promise.resolve(null); if (!callStack || !callStack.length) { callStackPromise = thread.fetchCallStack().then(() => callStack = thread.getCallStack()); } return callStackPromise.then(() => { - if (callStack.length === 1 && thread.session.capabilities.supportsDelayedStackTraceLoading) { + if (callStack.length === 1 && thread.session.capabilities.supportsDelayedStackTraceLoading && thread.stoppedDetails && thread.stoppedDetails.totalFrames > 1) { // To reduce flashing of the call stack view simply append the stale call stack // once we have the correct data the tree will refresh and we will no longer display it. callStack = callStack.concat(thread.getStaleCallStack().slice(1)); @@ -344,7 +379,7 @@ class CallStackDataSource implements IDataSource { }); } - public getParent(tree: ITree, element: any): TPromise { + getParent(tree: ITree, element: any): Promise { return Promise.resolve(null); } } @@ -367,7 +402,7 @@ interface IErrorTemplateData { label: HTMLElement; } -interface ILoadMoreTemplateData { +interface ILabelTemplateData { label: HTMLElement; } @@ -385,6 +420,7 @@ class CallStackRenderer implements IRenderer { private static readonly STACK_FRAME_TEMPLATE_ID = 'stackFrame'; private static readonly ERROR_TEMPLATE_ID = 'error'; private static readonly LOAD_MORE_TEMPLATE_ID = 'loadMore'; + private static readonly SHOW_MORE_TEMPLATE_ID = 'showMore'; private static readonly SESSION_TEMPLATE_ID = 'session'; constructor( @@ -393,11 +429,11 @@ class CallStackRenderer implements IRenderer { // noop } - public getHeight(tree: ITree, element: any): number { + getHeight(tree: ITree, element: any): number { return 22; } - public getTemplateId(tree: ITree, element: any): string { + getTemplateId(tree: ITree, element: any): string { if (element instanceof DebugSession) { return CallStackRenderer.SESSION_TEMPLATE_ID; } @@ -410,11 +446,17 @@ class CallStackRenderer implements IRenderer { if (typeof element === 'string') { return CallStackRenderer.ERROR_TEMPLATE_ID; } + if (element instanceof ThreadAndSessionIds) { + return CallStackRenderer.LOAD_MORE_TEMPLATE_ID; + } + if (element instanceof Array) { + return CallStackRenderer.SHOW_MORE_TEMPLATE_ID; + } - return CallStackRenderer.LOAD_MORE_TEMPLATE_ID; + return undefined; } - public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { if (templateId === CallStackRenderer.SESSION_TEMPLATE_ID) { let data: ISessionTemplateData = Object.create(null); data.session = dom.append(container, $('.session')); @@ -426,13 +468,19 @@ class CallStackRenderer implements IRenderer { } if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) { - let data: ILoadMoreTemplateData = Object.create(null); + let data: ILabelTemplateData = Object.create(null); data.label = dom.append(container, $('.load-more')); return data; } + if (templateId === CallStackRenderer.SHOW_MORE_TEMPLATE_ID) { + let data: ILabelTemplateData = Object.create(null); + data.label = dom.append(container, $('.show-more')); + + return data; + } if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) { - let data: ILoadMoreTemplateData = Object.create(null); + let data: ILabelTemplateData = Object.create(null); data.label = dom.append(container, $('.error')); return data; @@ -458,7 +506,7 @@ class CallStackRenderer implements IRenderer { return data; } - public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { if (templateId === CallStackRenderer.SESSION_TEMPLATE_ID) { this.renderSession(element, templateData); } else if (templateId === CallStackRenderer.THREAD_TEMPLATE_ID) { @@ -469,6 +517,8 @@ class CallStackRenderer implements IRenderer { this.renderError(element, templateData); } else if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) { this.renderLoadMore(templateData); + } else if (templateId === CallStackRenderer.SHOW_MORE_TEMPLATE_ID) { + this.renderShowMore(templateData, element); } } @@ -498,12 +548,20 @@ class CallStackRenderer implements IRenderer { data.label.title = element; } - private renderLoadMore(data: ILoadMoreTemplateData): void { + private renderLoadMore(data: ILabelTemplateData): void { data.label.textContent = nls.localize('loadMoreStackFrames', "Load More Stack Frames"); } + private renderShowMore(data: ILabelTemplateData, element: IStackFrame[]): void { + if (element.every(sf => sf.source && sf.source.origin && sf.source.origin === element[0].source.origin)) { + data.label.textContent = nls.localize('showMoreAndOrigin', "Show {0} More: {1}", element.length, element[0].source.origin); + } else { + data.label.textContent = nls.localize('showMoreStackFrames', "Show {0} More Stack Frames", element.length); + } + } + private renderStackFrame(stackFrame: IStackFrame, data: IStackFrameTemplateData): void { - dom.toggleClass(data.stackFrame, 'disabled', !stackFrame.source.available || stackFrame.source.presentationHint === 'deemphasize'); + dom.toggleClass(data.stackFrame, 'disabled', !stackFrame.source || !stackFrame.source.available || stackFrame.source.presentationHint === 'deemphasize'); dom.toggleClass(data.stackFrame, 'label', stackFrame.presentationHint === 'label'); dom.toggleClass(data.stackFrame, 'subtle', stackFrame.presentationHint === 'subtle'); @@ -525,7 +583,7 @@ class CallStackRenderer implements IRenderer { } } - public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + disposeTemplate(tree: ITree, templateId: string, templateData: any): void { // noop } } @@ -536,7 +594,7 @@ class CallstackAccessibilityProvider implements IAccessibilityProvider { // noop } - public getAriaLabel(tree: ITree, element: any): string { + getAriaLabel(tree: ITree, element: any): string { if (element instanceof Thread) { return nls.localize('threadAriaLabel', "Thread {0}, callstack, debug", (element).name); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index 42d478e77d2..248226309fc 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; import { URI as uri } from 'vs/base/common/uri'; @@ -80,32 +79,37 @@ export class ConfigurationManager implements IConfigurationManager { } } - public registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void { - if (!debugConfigurationProvider) { - return; - } + public registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable { - debugConfigurationProvider.handle = handle; - this.providers = this.providers.filter(p => p.handle !== handle); this.providers.push(debugConfigurationProvider); - const dbg = this.getDebugger(debugConfigurationProvider.type); - // Check if the provider contributes provideDebugConfigurations method - if (dbg && debugConfigurationProvider.provideDebugConfigurations) { - dbg.hasConfigurationProvider = true; + + return { + dispose: () => { + this.unregisterDebugConfigurationProvider(debugConfigurationProvider); + } + }; + } + + public unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void { + const ix = this.providers.indexOf(debugConfigurationProvider); + if (ix >= 0) { + this.providers.splice(ix, 1); } } + public hasDebugConfigurationProvider(debugType: string): boolean { + // check if there are providers for the given type that contribute a provideDebugConfigurations method + const providers = this.providers.filter(p => p.provideDebugConfigurations && (p.type === debugType)); + return providers.length > 0; + } + public needsToRunInExtHost(debugType: string): boolean { // if the given debugType matches any registered provider that has a provideTracker method, we need to run the DA in the EH const providers = this.providers.filter(p => p.hasTracker && (p.type === debugType || p.type === '*')); return providers.length > 0; } - public unregisterDebugConfigurationProvider(handle: number): void { - this.providers = this.providers.filter(p => p.handle !== handle); - } - - public resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig): TPromise { + public resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig): Thenable { return this.activateDebuggers(`onDebugResolve:${type}`).then(() => { // pipe the config through the promises sequentially. append at the end the '*' types const providers = this.providers.filter(p => p.type === type && p.resolveDebugConfiguration) @@ -123,13 +127,13 @@ export class ConfigurationManager implements IConfigurationManager { }); } - public provideDebugConfigurations(folderUri: uri | undefined, type: string): TPromise { + public provideDebugConfigurations(folderUri: uri | undefined, type: string): Thenable { return this.activateDebuggers('onDebugInitialConfigurations') .then(() => Promise.all(this.providers.filter(p => p.type === type && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations(folderUri))) .then(results => results.reduce((first, second) => first.concat(second), []))); } - public provideDebugAdapter(session: IDebugSession, folderUri: uri | undefined, config: IConfig): TPromise { + public provideDebugAdapter(session: IDebugSession, folderUri: uri | undefined, config: IConfig): Promise { const providers = this.providers.filter(p => p.type === config.type && p.provideDebugAdapter); if (providers.length === 1) { return providers[0].provideDebugAdapter(session, folderUri, config); @@ -160,7 +164,7 @@ export class ConfigurationManager implements IConfigurationManager { return undefined; } - public substituteVariables(debugType: string, folder: IWorkspaceFolder, config: IConfig): TPromise { + public substituteVariables(debugType: string, folder: IWorkspaceFolder, config: IConfig): Promise { let dap = this.getDebugAdapterProvider(debugType); if (dap) { return dap.substituteVariables(folder, config); @@ -168,7 +172,7 @@ export class ConfigurationManager implements IConfigurationManager { return Promise.resolve(config); } - public runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + public runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { let tl: ITerminalLauncher = this.getDebugAdapterProvider(debugType); if (!tl) { @@ -338,14 +342,14 @@ export class ConfigurationManager implements IConfigurationManager { return this.debuggers.filter(dbg => strings.equalsIgnoreCase(dbg.type, type)).pop(); } - public guessDebugger(type?: string): TPromise { + public guessDebugger(type?: string): Thenable { if (type) { const adapter = this.getDebugger(type); return Promise.resolve(adapter); } const activeTextEditorWidget = this.editorService.activeTextEditorWidget; - let candidates: TPromise; + let candidates: Thenable; if (isCodeEditor(activeTextEditorWidget)) { const model = activeTextEditorWidget.getModel(); const language = model ? model.getLanguageIdentifier().language : undefined; @@ -359,7 +363,7 @@ export class ConfigurationManager implements IConfigurationManager { } if (!candidates) { - candidates = this.activateDebuggers('onDebugInitialConfigurations').then(() => this.debuggers.filter(a => a.hasInitialConfiguration() || a.hasConfigurationProvider)); + candidates = this.activateDebuggers('onDebugInitialConfigurations').then(() => this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider())); } return candidates.then(debuggers => { @@ -378,7 +382,7 @@ export class ConfigurationManager implements IConfigurationManager { }); } - private activateDebuggers(activationEvent: string): TPromise { + private activateDebuggers(activationEvent: string): Thenable { return this.extensionService.activateByEvent(activationEvent).then(() => this.extensionService.activateByEvent('onDebug')); } @@ -459,7 +463,7 @@ class Launch implements ILaunch { return config.configurations.filter(config => config && config.name === name).shift(); } - public openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { + public openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Thenable<{ editor: IEditor, created: boolean }> { const resource = this.uri; let created = false; @@ -537,7 +541,7 @@ class WorkspaceLaunch extends Launch implements ILaunch { return this.configurationService.inspect('launch').workspace; } - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Thenable<{ editor: IEditor, created: boolean }> { return this.editorService.openEditor({ resource: this.contextService.getWorkspace().configuration, options: { preserveFocus } @@ -574,7 +578,7 @@ class UserLaunch extends Launch implements ILaunch { return this.configurationService.inspect('launch').user; } - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Thenable<{ editor: IEditor, created: boolean }> { return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor, created: false })); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts index ab69046bee4..8445d347a5d 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as lifecycle from 'vs/base/common/lifecycle'; import * as env from 'vs/base/common/platform'; @@ -95,7 +94,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.toggleExceptionWidget(); } - private getContextMenuActions(breakpoints: ReadonlyArray, uri: uri, lineNumber: number): TPromise<(IAction | ContextSubMenu)[]> { + private getContextMenuActions(breakpoints: ReadonlyArray, uri: uri, lineNumber: number): (IAction | ContextSubMenu)[] { const actions: (IAction | ContextSubMenu)[] = []; if (breakpoints.length === 1) { const breakpointType = breakpoints[0].logMessage ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint"); @@ -148,7 +147,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { nls.localize('addBreakpoint', "Add Breakpoint"), null, true, - () => this.debugService.addBreakpoints(uri, [{ lineNumber }]) + () => this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorContextMenu`) )); actions.push(new Action( 'addConditionalBreakpoint', @@ -166,7 +165,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { )); } - return Promise.resolve(actions); + return actions; } private registerListeners(): void { @@ -230,7 +229,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId())); } } else if (canSetBreakpoints) { - this.debugService.addBreakpoints(uri, [{ lineNumber }]); + this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorGutter`); } } })); @@ -321,7 +320,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { return EDITOR_CONTRIBUTION_ID; } - public showHover(range: Range, focus: boolean): TPromise { + public showHover(range: Range, focus: boolean): Promise { const sf = this.debugService.getViewModel().focusedStackFrame; const model = this.editor.getModel(); if (sf && model && sf.source.uri.toString() === model.uri.toString()) { @@ -524,14 +523,14 @@ export class DebugEditorContribution implements IDebugEditorContribution { if (this.configurationWidget) { this.configurationWidget.dispose(); } - if (model && LAUNCH_JSON_REGEX.test(model.uri.toString())) { + if (model && LAUNCH_JSON_REGEX.test(model.uri.toString()) && !this.editor.getConfiguration().readOnly) { this.configurationWidget = this.instantiationService.createInstance(FloatingClickWidget, this.editor, nls.localize('addConfiguration', "Add Configuration..."), null); this.configurationWidget.render(); this.toDispose.push(this.configurationWidget.onClick(() => this.addLaunchConfiguration())); } } - public addLaunchConfiguration(): TPromise { + public addLaunchConfiguration(): Promise { /* __GDPR__ "debug/addLaunchConfiguration" : {} */ @@ -561,7 +560,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { return Promise.resolve(undefined); } - const insertLine = (position: Position): TPromise => { + const insertLine = (position: Position): Promise => { // Check if there are more characters on a line after a "configurations": [, if yes enter a newline if (this.editor.getModel().getLineLastNonWhitespaceColumn(position.lineNumber) > position.column) { this.editor.setPosition(position); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts index 89c071e9019..d29320545c8 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as lifecycle from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import * as dom from 'vs/base/browser/dom'; @@ -140,7 +139,7 @@ export class DebugHoverWidget implements IContentWidget { return this.domNode; } - public showAt(range: Range, focus: boolean): TPromise { + public showAt(range: Range, focus: boolean): Promise { const pos = range.getStartPosition(); const session = this.debugService.getViewModel().focusedSession; @@ -152,7 +151,7 @@ export class DebugHoverWidget implements IContentWidget { return Promise.resolve(this.hide()); } - let promise: TPromise; + let promise: Promise; if (session.capabilities.supportsEvaluateForHovers) { const result = new Expression(matchingExpression); promise = result.evaluate(session, this.debugService.getViewModel().focusedStackFrame, 'hover').then(() => result); @@ -179,7 +178,7 @@ export class DebugHoverWidget implements IContentWidget { className: 'hoverHighlight' }); - private doFindExpression(container: IExpressionContainer, namesToFind: string[]): TPromise { + private doFindExpression(container: IExpressionContainer, namesToFind: string[]): Promise { if (!container) { return Promise.resolve(null); } @@ -199,7 +198,7 @@ export class DebugHoverWidget implements IContentWidget { }); } - private findExpressionInStackFrame(namesToFind: string[]): TPromise { + private findExpressionInStackFrame(namesToFind: string[]): Promise { return this.debugService.getViewModel().focusedStackFrame.getScopes() .then(scopes => scopes.filter(s => !s.expensive)) .then(scopes => Promise.all(scopes.map(scope => this.doFindExpression(scope, namesToFind)))) @@ -208,7 +207,7 @@ export class DebugHoverWidget implements IContentWidget { .then(expressions => (expressions.length > 0 && expressions.every(e => e.value === expressions[0].value)) ? expressions[0] : null); } - private doShow(position: Position, expression: IExpression, focus: boolean, forceValueHover = false): TPromise { + private doShow(position: Position, expression: IExpression, focus: boolean, forceValueHover = false): Thenable { if (!this.domNode) { this.create(); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 770f46f2ee4..aef141395cf 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -9,7 +9,6 @@ import { URI as uri } from 'vs/base/common/uri'; import { first, distinct } from 'vs/base/common/arrays'; import * as errors from 'vs/base/common/errors'; import severity from 'vs/base/common/severity'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IMarkerService } from 'vs/platform/markers/common/markers'; @@ -259,13 +258,13 @@ export class DebugService implements IDebugService { * main entry point * properly manages compounds, checks for errors and handles the initializing state. */ - startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug = false, unresolvedConfig?: IConfig, ): TPromise { + startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug = false, unresolvedConfig?: IConfig, ): Thenable { this.startInitializingState(); // make sure to save all files and that the configuration is up to date - return this.extensionService.activateByEvent('onDebug').then(() => - this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() => - this.extensionService.whenInstalledExtensionsRegistered().then(() => { + return this.extensionService.activateByEvent('onDebug').then(() => { + return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() => { + return this.extensionService.whenInstalledExtensionsRegistered().then(() => { let config: IConfig, compound: ICompound; if (!configOrName) { @@ -332,21 +331,22 @@ export class DebugService implements IDebugService { } return this.createSession(launch, config, unresolvedConfig, noDebug); - }) - ))).then(success => { - // make sure to get out of initializing state, and propagate the result - this.endInitializingState(); - return success; - }, err => { - this.endInitializingState(); - return Promise.reject(err); - }); + }); + })); + }).then(success => { + // make sure to get out of initializing state, and propagate the result + this.endInitializingState(); + return success; + }, err => { + this.endInitializingState(); + return Promise.reject(err); + }); } /** * gets the debugger for the type, resolves configurations by providers, substitutes variables and runs prelaunch tasks */ - private createSession(launch: ILaunch, config: IConfig, unresolvedConfig: IConfig, noDebug: boolean): TPromise { + private createSession(launch: ILaunch, config: IConfig, unresolvedConfig: IConfig, noDebug: boolean): Thenable { // We keep the debug type in a separate variable 'type' so that a no-folder config has no attributes. // Storing the type in the config would break extensions that assume that the no-folder case is indicated by an empty config. let type: string; @@ -420,10 +420,10 @@ export class DebugService implements IDebugService { /** * instantiates the new session, initializes the session, registers session listeners and reports telemetry */ - private doCreateSession(root: IWorkspaceFolder, configuration: { resolved: IConfig, unresolved: IConfig }): TPromise { + private doCreateSession(root: IWorkspaceFolder, configuration: { resolved: IConfig, unresolved: IConfig }): Thenable { const session = this.instantiationService.createInstance(DebugSession, configuration, root, this.model); - + this.model.addSession(session); // register listeners as the very first thing! this.registerSessionListeners(session); @@ -477,7 +477,7 @@ export class DebugService implements IDebugService { }); } - private launchOrAttachToSession(session: IDebugSession, focus = true): TPromise { + private launchOrAttachToSession(session: IDebugSession, focus = true): Thenable { const dbgr = this.configurationManager.getDebugger(session.configuration.type); return session.initialize(dbgr).then(() => { return session.launchOrAttach(session.configuration).then(() => { @@ -503,6 +503,9 @@ export class DebugService implements IDebugService { if (session.state === State.Running && this.viewModel.focusedSession === session) { sessionRunningScheduler.schedule(); } + if (session === this.viewModel.focusedSession) { + this.onStateChange(); + } })); this.toDispose.push(session.onDidEndAdapter(adapterExitEvent => { @@ -546,7 +549,7 @@ export class DebugService implements IDebugService { })); } - restartSession(session: IDebugSession, restartData?: any): TPromise { + restartSession(session: IDebugSession, restartData?: any): Thenable { return this.textFileService.saveAll().then(() => { const isAutoRestart = !!restartData; const runTasks: () => Thenable = () => { @@ -616,17 +619,21 @@ export class DebugService implements IDebugService { }); } - stopSession(session: IDebugSession): TPromise { + stopSession(session: IDebugSession): Promise { if (session) { return session.terminate(); } const sessions = this.model.getSessions(); + if (sessions.length === 0) { + this.endInitializingState(); + } + return Promise.all(sessions.map(s => s.terminate())); } - private substituteVariables(launch: ILaunch | undefined, config: IConfig): TPromise { + private substituteVariables(launch: ILaunch | undefined, config: IConfig): Thenable { const dbg = this.configurationManager.getDebugger(config.type); if (dbg) { let folder: IWorkspaceFolder | undefined = undefined; @@ -648,7 +655,7 @@ export class DebugService implements IDebugService { return Promise.resolve(config); } - private showError(message: string, actions: IAction[] = []): TPromise { + private showError(message: string, actions: IAction[] = []): Thenable { const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL); actions.push(configureAction); return this.dialogService.show(severity.Error, message, actions.map(a => a.label).concat(nls.localize('cancel', "Cancel")), { cancelId: actions.length }).then(choice => { @@ -662,7 +669,7 @@ export class DebugService implements IDebugService { //---- task management - private runTaskAndCheckErrors(root: IWorkspaceFolder, taskId: string | TaskIdentifier): TPromise { + private runTaskAndCheckErrors(root: IWorkspaceFolder, taskId: string | TaskIdentifier): Thenable { const debugAnywayAction = new Action('debug.debugAnyway', nls.localize('debugAnyway', "Debug Anyway"), undefined, true, () => Promise.resolve(TaskRunResult.Success)); return this.runTask(root, taskId).then((taskSummary: ITaskSummary) => { @@ -675,13 +682,14 @@ export class DebugService implements IDebugService { const taskLabel = typeof taskId === 'string' ? taskId : taskId.name; const message = errorCount > 1 - ? nls.localize('preLaunchTaskErrors', "Build errors have been detected during preLaunchTask '{0}'.", taskLabel) + ? nls.localize('preLaunchTaskErrors', "Errors exist after running preLaunchTask '{0}'.", taskLabel) : errorCount === 1 - ? nls.localize('preLaunchTaskError', "Build error has been detected during preLaunchTask '{0}'.", taskLabel) + ? nls.localize('preLaunchTaskError', "Error exists after running preLaunchTask '{0}'.", taskLabel) : nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary.exitCode); const showErrorsAction = new Action('debug.showErrors', nls.localize('showErrors', "Show Errors"), undefined, true, () => { - return this.panelService.openPanel(Constants.MARKERS_PANEL_ID).then(() => TaskRunResult.Failure); + this.panelService.openPanel(Constants.MARKERS_PANEL_ID); + return Promise.resolve(TaskRunResult.Failure); }); return this.showError(message, [debugAnywayAction, showErrorsAction]); @@ -690,7 +698,7 @@ export class DebugService implements IDebugService { }); } - private runTask(root: IWorkspaceFolder, taskId: string | TaskIdentifier): TPromise { + private runTask(root: IWorkspaceFolder, taskId: string | TaskIdentifier): Thenable { if (!taskId) { return Promise.resolve(null); } @@ -779,7 +787,7 @@ export class DebugService implements IDebugService { if (stackFrame) { stackFrame.openInEditor(this.editorService, true).then(undefined, errors.onUnexpectedError); - aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber)); + aria.alert(nls.localize('debuggingPaused', "Debugging paused {0}, {1} {2}", thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber)); } this.viewModel.setFocus(stackFrame, thread, session, explicit); @@ -806,7 +814,7 @@ export class DebugService implements IDebugService { //---- breakpoints - enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): TPromise { + enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): Promise { if (breakpoint) { this.model.setEnablement(breakpoint, enable); if (breakpoint instanceof Breakpoint) { @@ -822,9 +830,10 @@ export class DebugService implements IDebugService { return this.sendAllBreakpoints(); } - addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): TPromise { + addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[], context: string): Promise { const breakpoints = this.model.addBreakpoints(uri, rawBreakpoints); breakpoints.forEach(bp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", bp.lineNumber, uri.fsPath))); + breakpoints.forEach(bp => this.telemetryDebugAddBreakpoint(bp, context)); return this.sendBreakpoints(uri).then(() => breakpoints); } @@ -838,7 +847,7 @@ export class DebugService implements IDebugService { } } - removeBreakpoints(id?: string): TPromise { + removeBreakpoints(id?: string): Promise { const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id); toRemove.forEach(bp => aria.status(nls.localize('breakpointRemoved', "Removed breakpoint, line {0}, file {1}", bp.lineNumber, bp.uri.fsPath))); const urisToClear = distinct(toRemove, bp => bp.uri.toString()).map(bp => bp.uri); @@ -848,7 +857,7 @@ export class DebugService implements IDebugService { return Promise.all(urisToClear.map(uri => this.sendBreakpoints(uri))); } - setBreakpointsActivated(activated: boolean): TPromise { + setBreakpointsActivated(activated: boolean): Promise { this.model.setBreakpointsActivated(activated); return this.sendAllBreakpoints(); } @@ -858,24 +867,24 @@ export class DebugService implements IDebugService { this.viewModel.setSelectedFunctionBreakpoint(newFunctionBreakpoint); } - renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise { + renameFunctionBreakpoint(id: string, newFunctionName: string): Promise { this.model.renameFunctionBreakpoint(id, newFunctionName); return this.sendFunctionBreakpoints(); } - removeFunctionBreakpoints(id?: string): TPromise { + removeFunctionBreakpoints(id?: string): Promise { this.model.removeFunctionBreakpoints(id); return this.sendFunctionBreakpoints(); } - sendAllBreakpoints(session?: IDebugSession): TPromise { + sendAllBreakpoints(session?: IDebugSession): Promise { return Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session))) .then(() => this.sendFunctionBreakpoints(session)) // send exception breakpoints at the end since some debug adapters rely on the order .then(() => this.sendExceptionBreakpoints(session)); } - private sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): TPromise { + private sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { const breakpointsToSend = this.model.getBreakpoints({ uri: modelUri, enabledOnly: true }); @@ -884,7 +893,7 @@ export class DebugService implements IDebugService { ); } - private sendFunctionBreakpoints(session?: IDebugSession): TPromise { + private sendFunctionBreakpoints(session?: IDebugSession): Promise { const breakpointsToSend = this.model.getFunctionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated()); @@ -893,7 +902,7 @@ export class DebugService implements IDebugService { }); } - private sendExceptionBreakpoints(session?: IDebugSession): TPromise { + private sendExceptionBreakpoints(session?: IDebugSession): Promise { const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled); @@ -902,7 +911,7 @@ export class DebugService implements IDebugService { }); } - private sendToOneOrAllSessions(session: IDebugSession, send: (session: IDebugSession) => TPromise): TPromise { + private sendToOneOrAllSessions(session: IDebugSession, send: (session: IDebugSession) => Promise): Promise { if (session) { return send(session); } @@ -1006,7 +1015,7 @@ export class DebugService implements IDebugService { //---- telemetry - private telemetryDebugSessionStart(root: IWorkspaceFolder, type: string): TPromise { + private telemetryDebugSessionStart(root: IWorkspaceFolder, type: string): Thenable { const extension = this.configurationManager.getDebugger(type).extensionDescription; /* __GDPR__ "debugSessionStart" : { @@ -1030,7 +1039,7 @@ export class DebugService implements IDebugService { }); } - private telemetryDebugSessionStop(session: IDebugSession, adapterExitEvent: AdapterEndEvent): TPromise { + private telemetryDebugSessionStop(session: IDebugSession, adapterExitEvent: AdapterEndEvent): Thenable { const breakpoints = this.model.getBreakpoints(); @@ -1052,7 +1061,7 @@ export class DebugService implements IDebugService { }); } - private telemetryDebugMisconfiguration(debugType: string, message: string): TPromise { + private telemetryDebugMisconfiguration(debugType: string, message: string): Thenable { /* __GDPR__ "debugMisconfiguration" : { "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, @@ -1064,4 +1073,22 @@ export class DebugService implements IDebugService { error: message }); } + + private telemetryDebugAddBreakpoint(breakpoint: IBreakpoint, context: string): Thenable { + /* __GDPR__ + "debugAddBreakpoint" : { + "context": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "hasCondition": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "hasHitCondition": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "hasLogMessage": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + + return this.telemetryService.publicLog('debugAddBreakpoint', { + context: context, + hasCondition: !!breakpoint.condition, + hasHitCondition: !!breakpoint.hitCondition, + hasLogMessage: !!breakpoint.logMessage + }); + } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts index 76aa66c17c8..3f23d9b954d 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts @@ -8,7 +8,6 @@ import * as resources from 'vs/base/common/resources'; import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; import severity from 'vs/base/common/severity'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter } from 'vs/base/common/event'; import { CompletionItem, completionKindFromLegacyString } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; @@ -137,7 +136,7 @@ export class DebugSession implements IDebugSession { /** * create and initialize a new debug adapter for this session */ - initialize(dbgr: IDebugger): TPromise { + initialize(dbgr: IDebugger): Thenable { if (this.raw) { // if there was already a connection make sure to remove old listeners @@ -166,7 +165,6 @@ export class DebugSession implements IDebugSession { supportsRunInTerminalRequest: true, // #10574 locale: platform.locale }).then(() => { - this.model.addSession(this); this._onDidChangeState.fire(); this.model.setExceptionBreakpoints(this.raw.capabilities.exceptionBreakpointFilters); }); @@ -178,7 +176,7 @@ export class DebugSession implements IDebugSession { /** * launch or attach to the debuggee */ - launchOrAttach(config: IConfig): TPromise { + launchOrAttach(config: IConfig): Promise { if (this.raw) { // __sessionID only used for EH debugging (but we add it always for now...) @@ -194,7 +192,7 @@ export class DebugSession implements IDebugSession { /** * end the current debug adapter session */ - terminate(restart = false): TPromise { + terminate(restart = false): Promise { if (this.raw) { if (this.raw.capabilities.supportsTerminateRequest && this._configuration.resolved.request === 'launch') { return this.raw.terminate(restart).then(response => { @@ -211,7 +209,7 @@ export class DebugSession implements IDebugSession { /** * end the current debug adapter session */ - disconnect(restart = false): TPromise { + disconnect(restart = false): Promise { if (this.raw) { return this.raw.disconnect(restart).then(response => { return void 0; @@ -223,14 +221,14 @@ export class DebugSession implements IDebugSession { /** * restart debug adapter session */ - restart(): TPromise { + restart(): Promise { if (this.raw) { return this.raw.restart().then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - sendBreakpoints(modelUri: URI, breakpointsToSend: IBreakpoint[], sourceModified: boolean): TPromise { + sendBreakpoints(modelUri: URI, breakpointsToSend: IBreakpoint[], sourceModified: boolean): Promise { if (!this.raw) { return Promise.reject(new Error('no debug adapter')); @@ -272,7 +270,7 @@ export class DebugSession implements IDebugSession { }); } - sendFunctionBreakpoints(fbpts: IFunctionBreakpoint[]): TPromise { + sendFunctionBreakpoints(fbpts: IFunctionBreakpoint[]): Promise { if (this.raw) { if (this.raw.readyForBreakpoints) { return this.raw.setFunctionBreakpoints({ breakpoints: fbpts }).then(response => { @@ -286,13 +284,13 @@ export class DebugSession implements IDebugSession { }); } - return TPromise.as(undefined); + return Promise.resolve(undefined); } return Promise.reject(new Error('no debug adapter')); } - sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): TPromise { + sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise { if (this.raw) { if (this.raw.readyForBreakpoints) { return this.raw.setExceptionBreakpoints({ filters: exbpts.map(exb => exb.filter) }).then(() => undefined); @@ -302,21 +300,21 @@ export class DebugSession implements IDebugSession { return Promise.reject(new Error('no debug adapter')); } - customRequest(request: string, args: any): TPromise { + customRequest(request: string, args: any): Promise { if (this.raw) { return this.raw.custom(request, args); } return Promise.reject(new Error('no debug adapter')); } - stackTrace(threadId: number, startFrame: number, levels: number): TPromise { + stackTrace(threadId: number, startFrame: number, levels: number): Promise { if (this.raw) { return this.raw.stackTrace({ threadId, startFrame, levels }); } return Promise.reject(new Error('no debug adapter')); } - exceptionInfo(threadId: number): TPromise { + exceptionInfo(threadId: number): Promise { if (this.raw) { return this.raw.exceptionInfo({ threadId }).then(response => { if (response) { @@ -333,98 +331,98 @@ export class DebugSession implements IDebugSession { return Promise.reject(new Error('no debug adapter')); } - scopes(frameId: number): TPromise { + scopes(frameId: number): Promise { if (this.raw) { return this.raw.scopes({ frameId }); } return Promise.reject(new Error('no debug adapter')); } - variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): TPromise { + variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): Promise { if (this.raw) { return this.raw.variables({ variablesReference, filter, start, count }); } return Promise.resolve(undefined); } - evaluate(expression: string, frameId: number, context?: string): TPromise { + evaluate(expression: string, frameId: number, context?: string): Promise { if (this.raw) { return this.raw.evaluate({ expression, frameId, context }); } return Promise.reject(new Error('no debug adapter')); } - restartFrame(frameId: number, threadId: number): TPromise { + restartFrame(frameId: number, threadId: number): Promise { if (this.raw) { return this.raw.restartFrame({ frameId }, threadId).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - next(threadId: number): TPromise { + next(threadId: number): Promise { if (this.raw) { return this.raw.next({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - stepIn(threadId: number): TPromise { + stepIn(threadId: number): Promise { if (this.raw) { return this.raw.stepIn({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - stepOut(threadId: number): TPromise { + stepOut(threadId: number): Promise { if (this.raw) { return this.raw.stepOut({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - stepBack(threadId: number): TPromise { + stepBack(threadId: number): Promise { if (this.raw) { return this.raw.stepBack({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - continue(threadId: number): TPromise { + continue(threadId: number): Promise { if (this.raw) { return this.raw.continue({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - reverseContinue(threadId: number): TPromise { + reverseContinue(threadId: number): Promise { if (this.raw) { return this.raw.reverseContinue({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - pause(threadId: number): TPromise { + pause(threadId: number): Promise { if (this.raw) { return this.raw.pause({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - terminateThreads(threadIds?: number[]): TPromise { + terminateThreads(threadIds?: number[]): Promise { if (this.raw) { return this.raw.terminateThreads({ threadIds }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - setVariable(variablesReference: number, name: string, value: string): TPromise { + setVariable(variablesReference: number, name: string, value: string): Promise { if (this.raw) { return this.raw.setVariable({ variablesReference, name, value }); } return Promise.reject(new Error('no debug adapter')); } - loadSource(resource: URI): TPromise { + loadSource(resource: URI): Promise { if (!this.raw) { return Promise.reject(new Error('no debug adapter')); @@ -452,10 +450,14 @@ export class DebugSession implements IDebugSession { return this.raw.source({ sourceReference: rawSource.sourceReference, source: rawSource }); } - getLoadedSources(): TPromise { + getLoadedSources(): Promise { if (this.raw) { return this.raw.loadedSources({}).then(response => { - return response.body.sources.map(src => this.getSource(src)); + if (response.body && response.body.sources) { + return response.body.sources.map(src => this.getSource(src)); + } else { + return []; + } }, () => { return []; }); @@ -463,7 +465,7 @@ export class DebugSession implements IDebugSession { return Promise.reject(new Error('no debug adapter')); } - completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise { + completions(frameId: number, text: string, position: Position, overwriteBefore: number): Promise { if (this.raw) { return this.raw.completions({ frameId, @@ -560,7 +562,7 @@ export class DebugSession implements IDebugSession { } } - private fetchThreads(stoppedDetails?: IRawStoppedDetails): TPromise { + private fetchThreads(stoppedDetails?: IRawStoppedDetails): Promise { return this.raw ? this.raw.threads().then(response => { if (response && response.body && response.body.threads) { response.body.threads.forEach(thread => { @@ -572,7 +574,7 @@ export class DebugSession implements IDebugSession { }); }); } - }) : TPromise.as(undefined); + }) : Promise.resolve(undefined); } //---- private @@ -652,7 +654,7 @@ export class DebugSession implements IDebugSession { this._onDidChangeState.fire(); })); - let outputPromises: TPromise[] = []; + let outpuPromises: Promise[] = []; this.rawListeners.push(this.raw.onDidOutput(event => { if (!event.body) { return; @@ -671,7 +673,7 @@ export class DebugSession implements IDebugSession { } // Make sure to append output in the correct order by properly waiting on preivous promises #33822 - const waitFor = outputPromises.slice(); + const waitFor = outpuPromises.slice(); const source = event.body.source ? { lineNumber: event.body.line, column: event.body.column ? event.body.column : 1, @@ -679,7 +681,7 @@ export class DebugSession implements IDebugSession { } : undefined; if (event.body.variablesReference) { const container = new ExpressionContainer(this, event.body.variablesReference, generateUuid()); - outputPromises.push(container.getChildren().then(children => { + outpuPromises.push(container.getChildren().then(children => { return Promise.all(waitFor).then(() => children.forEach(child => { // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) child.name = null; @@ -689,7 +691,7 @@ export class DebugSession implements IDebugSession { } else if (typeof event.body.output === 'string') { Promise.all(waitFor).then(() => this.appendToRepl(event.body.output, outputSeverity, source)); } - Promise.all(outputPromises).then(() => outputPromises = []); + Promise.all(outpuPromises).then(() => outpuPromises = []); })); this.rawListeners.push(this.raw.onDidBreakpoint(event => { @@ -705,7 +707,7 @@ export class DebugSession implements IDebugSession { lineNumber: event.body.breakpoint.line, }], false); if (bps.length === 1) { - this.model.updateBreakpoints({ [bps[0].getId()]: event.body.breakpoint }); + this.model.setBreakpointSessionData(this.getId(), { [bps[0].getId()]: event.body.breakpoint }); } } @@ -796,7 +798,7 @@ export class DebugSession implements IDebugSession { this._onDidChangeREPLElements.fire(); } - addReplExpression(stackFrame: IStackFrame, name: string): TPromise { + addReplExpression(stackFrame: IStackFrame, name: string): Promise { const viewModel = this.debugService.getViewModel(); return this.repl.addReplExpression(stackFrame, name) .then(() => this._onDidChangeREPLElements.fire()) diff --git a/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts b/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts index 86e82c5349f..d2daa16c04f 100644 --- a/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts +++ b/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { removeAnsiEscapeCodes } from 'vs/base/common/strings'; import { Variable } from 'vs/workbench/parts/debug/common/debugModel'; @@ -22,7 +21,7 @@ export class CopyValueAction extends Action { this._enabled = typeof this.value === 'string' || (this.value instanceof Variable && !!this.value.evaluateName); } - public run(): TPromise { + public run(): Promise { if (this.value instanceof Variable) { const frameId = this.debugService.getViewModel().focusedStackFrame.frameId; const session = this.debugService.getViewModel().focusedSession; @@ -45,7 +44,7 @@ export class CopyEvaluatePathAction extends Action { this._enabled = this.value && !!this.value.evaluateName; } - public run(): TPromise { + public run(): Promise { clipboard.writeText(this.value.evaluateName); return Promise.resolve(undefined); } @@ -55,7 +54,7 @@ export class CopyAction extends Action { static readonly ID = 'workbench.debug.action.copy'; static LABEL = nls.localize('copy', "Copy"); - public run(): TPromise { + public run(): Promise { clipboard.writeText(window.getSelection().toString()); return Promise.resolve(undefined); } @@ -70,7 +69,7 @@ export class CopyAllAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Promise { let text = ''; const navigator = this.tree.getNavigator(); // skip first navigator element - the root node @@ -90,7 +89,7 @@ export class CopyStackTraceAction extends Action { static readonly ID = 'workbench.action.debug.copyStackTrace'; static LABEL = nls.localize('copyStackTrace', "Copy Call Stack"); - public run(frame: IStackFrame): TPromise { + public run(frame: IStackFrame): Promise { clipboard.writeText(frame.thread.getCallStack().map(sf => sf.toString()).join(lineDelimiter)); return Promise.resolve(undefined); } diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index 5c70a9266bc..888ce94a575 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -8,7 +8,6 @@ import { Event, Emitter } from 'vs/base/common/event'; import * as objects from 'vs/base/common/objects'; import { Action } from 'vs/base/common/actions'; import * as errors from 'vs/base/common/errors'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { formatPII } from 'vs/workbench/parts/debug/common/debugUtils'; import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from 'vs/workbench/parts/debug/common/debug'; @@ -204,7 +203,7 @@ export class RawDebugSession { /** * Starts the underlying debug adapter and tracks the session time for telemetry. */ - public start(): TPromise { + public start(): Promise { return this.debugAdapter.startSession().then(() => { this.startTime = new Date().getTime(); }, err => { @@ -215,7 +214,7 @@ export class RawDebugSession { /** * Send client capabilities to the debug adapter and receive DA capabilities in return. */ - public initialize(args: DebugProtocol.InitializeRequestArguments): TPromise { + public initialize(args: DebugProtocol.InitializeRequestArguments): Promise { return this.send('initialize', args).then((response: DebugProtocol.InitializeResponse) => { this.mergeCapabilities(response.body); return response; @@ -225,13 +224,13 @@ export class RawDebugSession { /** * Terminate the debuggee and shutdown the adapter */ - public disconnect(restart = false): TPromise { + public disconnect(restart = false): Promise { return this.shutdown(undefined, restart); } //---- DAP requests - public launchOrAttach(config: IConfig): TPromise { + public launchOrAttach(config: IConfig): Promise { return this.send(config.request, config).then(response => { this.mergeCapabilities(response.body); return response; @@ -241,7 +240,7 @@ export class RawDebugSession { /** * Try killing the debuggee softly... */ - public terminate(restart = false): TPromise { + public terminate(restart = false): Promise { if (this.capabilities.supportsTerminateRequest) { if (!this.terminated) { this.terminated = true; @@ -252,35 +251,35 @@ export class RawDebugSession { return Promise.reject(new Error('terminated not supported')); } - public restart(): TPromise { + public restart(): Promise { if (this.capabilities.supportsRestartRequest) { return this.send('restart', null); } return Promise.reject(new Error('restart not supported')); } - public next(args: DebugProtocol.NextArguments): TPromise { + public next(args: DebugProtocol.NextArguments): Promise { return this.send('next', args).then(response => { this.fireSimulatedContinuedEvent(args.threadId); return response; }); } - public stepIn(args: DebugProtocol.StepInArguments): TPromise { + public stepIn(args: DebugProtocol.StepInArguments): Promise { return this.send('stepIn', args).then(response => { this.fireSimulatedContinuedEvent(args.threadId); return response; }); } - public stepOut(args: DebugProtocol.StepOutArguments): TPromise { + public stepOut(args: DebugProtocol.StepOutArguments): Promise { return this.send('stepOut', args).then(response => { this.fireSimulatedContinuedEvent(args.threadId); return response; }); } - public continue(args: DebugProtocol.ContinueArguments): TPromise { + public continue(args: DebugProtocol.ContinueArguments): Promise { return this.send('continue', args).then(response => { if (response && response.body && response.body.allThreadsContinued !== undefined) { this.allThreadsContinued = response.body.allThreadsContinued; @@ -290,25 +289,25 @@ export class RawDebugSession { }); } - public pause(args: DebugProtocol.PauseArguments): TPromise { + public pause(args: DebugProtocol.PauseArguments): Promise { return this.send('pause', args); } - public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): TPromise { + public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): Promise { if (this.capabilities.supportsTerminateThreadsRequest) { return this.send('terminateThreads', args); } return Promise.reject(new Error('terminateThreads not supported')); } - public setVariable(args: DebugProtocol.SetVariableArguments): TPromise { + public setVariable(args: DebugProtocol.SetVariableArguments): Promise { if (this.capabilities.supportsSetVariable) { return this.send('setVariable', args); } return Promise.reject(new Error('setVariable not supported')); } - public restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): TPromise { + public restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): Promise { if (this.capabilities.supportsRestartFrame) { return this.send('restartFrame', args).then(response => { this.fireSimulatedContinuedEvent(threadId); @@ -318,74 +317,74 @@ export class RawDebugSession { return Promise.reject(new Error('restartFrame not supported')); } - public completions(args: DebugProtocol.CompletionsArguments): TPromise { + public completions(args: DebugProtocol.CompletionsArguments): Promise { if (this.capabilities.supportsCompletionsRequest) { return this.send('completions', args); } return Promise.reject(new Error('completions not supported')); } - public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): TPromise { + public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): Promise { return this.send('setBreakpoints', args); } - public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): TPromise { + public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): Promise { if (this.capabilities.supportsFunctionBreakpoints) { return this.send('setFunctionBreakpoints', args); } return Promise.reject(new Error('setFunctionBreakpoints not supported')); } - public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): TPromise { + public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise { return this.send('setExceptionBreakpoints', args); } - public configurationDone(): TPromise { + public configurationDone(): Promise { if (this.capabilities.supportsConfigurationDoneRequest) { return this.send('configurationDone', null); } return Promise.reject(new Error('configurationDone not supported')); } - public stackTrace(args: DebugProtocol.StackTraceArguments): TPromise { + public stackTrace(args: DebugProtocol.StackTraceArguments): Promise { return this.send('stackTrace', args); } - public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise { + public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise { if (this.capabilities.supportsExceptionInfoRequest) { return this.send('exceptionInfo', args); } return Promise.reject(new Error('exceptionInfo not supported')); } - public scopes(args: DebugProtocol.ScopesArguments): TPromise { + public scopes(args: DebugProtocol.ScopesArguments): Promise { return this.send('scopes', args); } - public variables(args: DebugProtocol.VariablesArguments): TPromise { + public variables(args: DebugProtocol.VariablesArguments): Promise { return this.send('variables', args); } - public source(args: DebugProtocol.SourceArguments): TPromise { + public source(args: DebugProtocol.SourceArguments): Promise { return this.send('source', args); } - public loadedSources(args: DebugProtocol.LoadedSourcesArguments): TPromise { + public loadedSources(args: DebugProtocol.LoadedSourcesArguments): Promise { if (this.capabilities.supportsLoadedSourcesRequest) { return this.send('loadedSources', args); } return Promise.reject(new Error('loadedSources not supported')); } - public threads(): TPromise { + public threads(): Promise { return this.send('threads', null); } - public evaluate(args: DebugProtocol.EvaluateArguments): TPromise { + public evaluate(args: DebugProtocol.EvaluateArguments): Promise { return this.send('evaluate', args); } - public stepBack(args: DebugProtocol.StepBackArguments): TPromise { + public stepBack(args: DebugProtocol.StepBackArguments): Promise { if (this.capabilities.supportsStepBack) { return this.send('stepBack', args).then(response => { if (response.body === undefined) { // TODO@AW why this check? @@ -397,7 +396,7 @@ export class RawDebugSession { return Promise.reject(new Error('stepBack not supported')); } - public reverseContinue(args: DebugProtocol.ReverseContinueArguments): TPromise { + public reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise { if (this.capabilities.supportsStepBack) { return this.send('reverseContinue', args).then(response => { if (response.body === undefined) { // TODO@AW why this check? @@ -409,14 +408,14 @@ export class RawDebugSession { return Promise.reject(new Error('reverseContinue not supported')); } - public custom(request: string, args: any): TPromise { + public custom(request: string, args: any): Promise { return this.send(request, args); } //---- private - private shutdown(error?: Error, restart = false): TPromise { + private shutdown(error?: Error, restart = false): Promise { if (!this.inShutdown) { this.inShutdown = true; if (this.debugAdapter) { @@ -432,7 +431,7 @@ export class RawDebugSession { return Promise.resolve(undefined); } - private stopAdapter(error?: Error): TPromise { + private stopAdapter(error?: Error): Promise { if (this.debugAdapter) { const da = this.debugAdapter; this.debugAdapter = null; diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 16249f43517..3315ff0af17 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -6,7 +6,6 @@ import 'vs/css!vs/workbench/parts/debug/browser/media/repl'; import * as nls from 'vs/nls'; import { URI as uri } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as errors from 'vs/base/common/errors'; import { IAction, IActionItem, Action } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; @@ -113,9 +112,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati private registerListeners(): void { this._register(this.debugService.getViewModel().onDidFocusSession(session => { - if (this.isVisible()) { - this.selectSession(session); - } + this.selectSession(session); })); this._register(this.debugService.onDidNewSession(() => this.updateTitleArea())); this._register(this.themeService.onThemeChange(() => { @@ -125,20 +122,16 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati })); } - setVisible(visible: boolean): Promise { + setVisible(visible: boolean): void { + super.setVisible(visible); if (!visible) { dispose(this.model); } else { this.model = this.modelService.createModel('', null, uri.parse(`${DEBUG_SCHEME}:replinput`), true); this.replInput.setModel(this.model); this.updateInputDecoration(); - const focusedSession = this.debugService.getViewModel().focusedSession; - if (focusedSession && this.tree.getInput() !== focusedSession) { - this.selectSession(focusedSession); - } + this.refreshReplElements(true); } - - return super.setVisible(visible); } get isReadonly(): boolean { @@ -171,10 +164,10 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati } selectSession(session: IDebugSession): void { - if (this.replElementsChangeListener) { - this.replElementsChangeListener.dispose(); - } if (session) { + if (this.replElementsChangeListener) { + this.replElementsChangeListener.dispose(); + } this.replElementsChangeListener = session.onDidChangeReplElements(() => { this.refreshReplElements(session.getReplElements().length === 0); }); @@ -282,8 +275,8 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati // --- Creation - async create(parent: HTMLElement): Promise { - await super.create(parent); + create(parent: HTMLElement): void { + super.create(parent); this.container = dom.append(parent, $('.repl')); this.treeContainer = dom.append(this.container, $('.repl-tree')); this.createReplInput(this.container); @@ -298,6 +291,12 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati accessibilityProvider: new ReplExpressionsAccessibilityProvider(), controller }, replTreeOptions); + + // Make sure to select the session if debugging is already active + const focusedSession = this.debugService.getViewModel().focusedSession; + if (focusedSession) { + this.selectSession(focusedSession); + } } private createReplInput(container: HTMLElement): void { @@ -395,7 +394,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati }, renderOptions: { after: { - contentText: nls.localize('startDebugFirst', "Please start a debug session to evaluate"), + contentText: nls.localize('startDebugFirst', "Please start a debug session to evaluate expressions"), color: transparent(editorForeground, 0.4)(this.themeService.getTheme()).toString() } } @@ -441,7 +440,7 @@ class AcceptReplInputAction extends EditorAction { }); } - run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { SuggestController.get(editor).acceptSelectedSuggestion(); accessor.get(IPrivateReplService).acceptReplInput(); } @@ -458,7 +457,7 @@ export class ReplCopyAllAction extends EditorAction { }); } - run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { clipboard.writeText(accessor.get(IPrivateReplService).getVisibleContent()); } } @@ -496,7 +495,7 @@ class SelectReplAction extends Action { super(id, label); } - run(sessionName: string): TPromise { + run(sessionName: string): Promise { const session = this.debugService.getModel().getSessions(true).filter(p => p.getLabel() === sessionName).pop(); // If session is already the focused session we need to manualy update the tree since view model will not send a focused change event if (session && session.state !== State.Inactive && session !== this.debugService.getViewModel().focusedSession) { @@ -520,7 +519,7 @@ class ClearReplAction extends Action { super(id, label, 'debug-action clear-repl'); } - public run(): TPromise { + public run(): Promise { this.replService.clearRepl(); aria.status(nls.localize('debugConsoleCleared', "Debug console was cleared")); diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index b4baba1d40d..cc821c5a40c 100644 --- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction, Action } from 'vs/base/common/actions'; import * as lifecycle from 'vs/base/common/lifecycle'; import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings'; @@ -38,7 +37,7 @@ export class ReplExpressionsDataSource implements IDataSource { return element instanceof DebugSession || (element).hasChildren; } - public getChildren(tree: ITree, element: any): TPromise { + public getChildren(tree: ITree, element: any): Promise { if (element instanceof DebugSession) { return Promise.resolve(element.getReplElements()); } @@ -52,7 +51,7 @@ export class ReplExpressionsDataSource implements IDataSource { return (element).getChildren(); } - public getParent(tree: ITree, element: any): TPromise { + public getParent(tree: ITree, element: any): Promise { return Promise.resolve(null); } } @@ -325,15 +324,15 @@ export class ReplExpressionsActionProvider implements IActionProvider { return false; } - public getActions(tree: ITree, element: any): TPromise { - return Promise.resolve([]); + public getActions(tree: ITree, element: any): IAction[] { + return []; } public hasSecondaryActions(tree: ITree, element: any): boolean { return true; } - public getSecondaryActions(tree: ITree, element: any): TPromise { + public getSecondaryActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL)); actions.push(new CopyAllAction(CopyAllAction.ID, CopyAllAction.LABEL, tree)); @@ -341,7 +340,7 @@ export class ReplExpressionsActionProvider implements IActionProvider { actions.push(new Separator()); actions.push(this.clearReplAction); - return Promise.resolve(actions); + return actions; } public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { diff --git a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts index 5203816513b..94ad0563156 100644 --- a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts +++ b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITerminalService, ITerminalInstance } from 'vs/workbench/parts/terminal/common/terminal'; import { ITerminalService as IExternalTerminalService } from 'vs/workbench/parts/execution/common/execution'; import { ITerminalLauncher, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug'; @@ -22,7 +21,7 @@ export class TerminalLauncher implements ITerminalLauncher { ) { } - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { if (args.kind === 'external') { return this.nativeTerminalService.runInTerminal(args.title, args.cwd, args.args, args.env || {}); diff --git a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts index bd2baae2fa9..ca53ecfe366 100644 --- a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts @@ -17,7 +17,6 @@ import { MenuId } from 'vs/platform/actions/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { twistiePixels, renderViewTree, IVariableTemplateData, BaseDebugController, renderRenameBox, renderVariable } from 'vs/workbench/parts/debug/browser/baseDebugView'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction, IActionItem } from 'vs/base/common/actions'; import { SetValueAction, AddToWatchExpressionsAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { CopyValueAction, CopyEvaluatePathAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions'; @@ -139,12 +138,11 @@ export class VariablesView extends TreeViewsViewletPanel { } } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { - if (visible && this.needsRefresh) { - this.onFocusStackFrameScheduler.schedule(); - } - }); + public setVisible(visible: boolean): void { + super.setVisible(visible); + if (visible && this.needsRefresh) { + this.onFocusStackFrameScheduler.schedule(); + } } } @@ -158,8 +156,8 @@ class VariablesActionProvider implements IActionProvider { return false; } - public getActions(tree: ITree, element: any): TPromise { - return Promise.resolve([]); + public getActions(tree: ITree, element: any): IAction[] { + return []; } public hasSecondaryActions(tree: ITree, element: any): boolean { @@ -167,7 +165,7 @@ class VariablesActionProvider implements IActionProvider { return element instanceof Variable && !!element.value; } - public getSecondaryActions(tree: ITree, element: any): TPromise { + public getSecondaryActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; const variable = element; actions.push(new SetValueAction(SetValueAction.ID, SetValueAction.LABEL, variable, this.debugService, this.keybindingService)); @@ -176,7 +174,7 @@ class VariablesActionProvider implements IActionProvider { actions.push(new Separator()); actions.push(new AddToWatchExpressionsAction(AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable, this.debugService, this.keybindingService)); - return Promise.resolve(actions); + return actions; } public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { @@ -199,7 +197,7 @@ export class VariablesDataSource implements IDataSource { return variable.hasChildren && !equalsIgnoreCase(variable.value, 'null'); } - public getChildren(tree: ITree, element: any): TPromise { + public getChildren(tree: ITree, element: any): Promise { if (element instanceof ViewModel) { const focusedStackFrame = (element).focusedStackFrame; return focusedStackFrame ? focusedStackFrame.getScopes() : Promise.resolve([]); @@ -209,7 +207,7 @@ export class VariablesDataSource implements IDataSource { return scope.getChildren(); } - public getParent(tree: ITree, element: any): TPromise { + public getParent(tree: ITree, element: any): Promise { return Promise.resolve(null); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 07748d02e2d..353df62e9b5 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as dom from 'vs/base/browser/dom'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider, IDragAndDropData, IDragOverReaction, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree'; import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { TreeViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; @@ -122,12 +121,11 @@ export class WatchExpressionsView extends TreeViewsViewletPanel { } } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { - if (visible && this.needsRefresh) { - this.onWatchExpressionsUpdatedScheduler.schedule(); - } - }); + public setVisible(visible: boolean): void { + super.setVisible(visible); + if (visible && this.needsRefresh) { + this.onWatchExpressionsUpdatedScheduler.schedule(); + } } } @@ -146,11 +144,11 @@ class WatchExpressionsActionProvider implements IActionProvider { return true; } - public getActions(tree: ITree, element: any): TPromise { - return Promise.resolve([]); + public getActions(tree: ITree, element: any): IAction[] { + return []; } - public getSecondaryActions(tree: ITree, element: any): TPromise { + public getSecondaryActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; if (element instanceof Expression) { const expression = element; @@ -175,7 +173,7 @@ class WatchExpressionsActionProvider implements IActionProvider { actions.push(new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService)); } - return Promise.resolve(actions); + return actions; } public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { @@ -202,7 +200,7 @@ class WatchExpressionsDataSource implements IDataSource { return watchExpression.hasChildren && !equalsIgnoreCase(watchExpression.value, 'null'); } - public getChildren(tree: ITree, element: any): TPromise { + public getChildren(tree: ITree, element: any): Promise { if (element instanceof DebugModel) { const viewModel = this.debugService.getViewModel(); return Promise.all(element.getWatchExpressions().map(we => @@ -213,7 +211,7 @@ class WatchExpressionsDataSource implements IDataSource { return expression.getChildren(); } - public getParent(tree: ITree, element: any): TPromise { + public getParent(tree: ITree, element: any): Promise { return Promise.resolve(null); } } diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts index 244fd3ea2a8..254f2e38277 100644 --- a/src/vs/workbench/parts/debug/node/debugAdapter.ts +++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts @@ -13,7 +13,6 @@ import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IOutputService } from 'vs/workbench/parts/output/common/output'; @@ -42,8 +41,8 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { this._onExit = new Emitter(); } - abstract startSession(): TPromise; - abstract stopSession(): TPromise; + abstract startSession(): Promise; + abstract stopSession(): Promise; public dispose(): void { } @@ -261,7 +260,7 @@ export class SocketDebugAdapter extends StreamDebugAdapter { super(); } - startSession(): TPromise { + startSession(): Promise { return new Promise((resolve, reject) => { let connected = false; this.socket = net.createConnection(this.adapterServer.port, this.adapterServer.host || '127.0.0.1', () => { @@ -286,7 +285,7 @@ export class SocketDebugAdapter extends StreamDebugAdapter { }); } - stopSession(): TPromise { + stopSession(): Promise { // Cancel all sent promises on disconnect so debug trees are not left in a broken state #3666. this.cancelPending(); @@ -310,7 +309,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { super(); } - startSession(): TPromise { + startSession(): Promise { return new Promise((resolve, reject) => { @@ -401,7 +400,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { }); } - stopSession(): TPromise { + stopSession(): Promise { // Cancel all sent promises on disconnect so debug trees are not left in a broken state #3666. this.cancelPending(); diff --git a/src/vs/workbench/parts/debug/node/debugger.ts b/src/vs/workbench/parts/debug/node/debugger.ts index 644722c03e4..53b50384cfc 100644 --- a/src/vs/workbench/parts/debug/node/debugger.ts +++ b/src/vs/workbench/parts/debug/node/debugger.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp'; import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; @@ -41,9 +40,8 @@ export class Debugger implements IDebugger { this.mergedExtensionDescriptions = [extensionDescription]; } - public hasConfigurationProvider = false; - public createDebugAdapter(session: IDebugSession, root: IWorkspaceFolder, config: IConfig, outputService: IOutputService): TPromise { + public createDebugAdapter(session: IDebugSession, root: IWorkspaceFolder, config: IConfig, outputService: IOutputService): Promise { if (this.inExtHost()) { return Promise.resolve(this.configurationManager.createDebugAdapter(session, root, config)); } else { @@ -60,7 +58,7 @@ export class Debugger implements IDebugger { } } - private getAdapterDescriptor(session: IDebugSession, root: IWorkspaceFolder, config: IConfig): TPromise { + private getAdapterDescriptor(session: IDebugSession, root: IWorkspaceFolder, config: IConfig): Promise { // a "debugServer" attribute in the launch config takes precedence if (typeof config.debugServer === 'number') { @@ -94,7 +92,7 @@ export class Debugger implements IDebugger { }); } - public substituteVariables(folder: IWorkspaceFolder, config: IConfig): TPromise { + public substituteVariables(folder: IWorkspaceFolder, config: IConfig): Thenable { if (this.inExtHost()) { return this.configurationManager.substituteVariables(this.type, folder, config).then(config => { return this.configurationResolverService.resolveWithCommands(folder, config, this.variables); @@ -104,7 +102,7 @@ export class Debugger implements IDebugger { } } - public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): TPromise { + public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise { const config = this.configurationService.getValue('terminal'); return this.configurationManager.runInTerminal(this.inExtHost() ? this.type : '*', args, config); } @@ -150,7 +148,11 @@ export class Debugger implements IDebugger { return !!this.debuggerContribution.initialConfigurations; } - public getInitialConfigurationContent(initialConfigs?: IConfig[]): TPromise { + public hasConfigurationProvider() { + this.configurationManager.hasDebugConfigurationProvider(this.type); + } + + public getInitialConfigurationContent(initialConfigs?: IConfig[]): Promise { // at this point we got some configs from the package.json and/or from registered DebugConfigurationProviders let initialConfigurations = this.debuggerContribution.initialConfigurations || []; if (initialConfigs) { @@ -183,7 +185,7 @@ export class Debugger implements IDebugger { } @memoize - public getCustomTelemetryService(): TPromise { + public getCustomTelemetryService(): Thenable { if (!this.debuggerContribution.aiKey) { return Promise.resolve(undefined); } diff --git a/src/vs/workbench/parts/debug/node/terminals.ts b/src/vs/workbench/parts/debug/node/terminals.ts index f6cc9c3ab77..74d04c9ed4f 100644 --- a/src/vs/workbench/parts/debug/node/terminals.ts +++ b/src/vs/workbench/parts/debug/node/terminals.ts @@ -8,7 +8,6 @@ import * as nls from 'vs/nls'; import * as env from 'vs/base/common/platform'; import * as pfs from 'vs/base/node/pfs'; import { assign } from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITerminalLauncher, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug'; import { getPathFromAmdModule } from 'vs/base/common/amd'; @@ -29,12 +28,12 @@ export function getTerminalLauncher() { return terminalLauncher; } -let _DEFAULT_TERMINAL_LINUX_READY: TPromise | null = null; -export function getDefaultTerminalLinuxReady(): TPromise { +let _DEFAULT_TERMINAL_LINUX_READY: Promise | null = null; +export function getDefaultTerminalLinuxReady(): Promise { if (!_DEFAULT_TERMINAL_LINUX_READY) { _DEFAULT_TERMINAL_LINUX_READY = new Promise(c => { if (env.isLinux) { - TPromise.join([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => { + Promise.all([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => { if (isDebian) { c('x-terminal-emulator'); } else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') { @@ -68,10 +67,10 @@ export function getDefaultTerminalWindows(): string { } abstract class TerminalLauncher implements ITerminalLauncher { - public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { return this.runInTerminal0(args.title, args.cwd, args.args, args.env || {}, config); } - runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, config): TPromise { + runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, config): Promise { return void 0; } } @@ -80,7 +79,7 @@ class WinTerminalService extends TerminalLauncher { private static readonly CMD = 'cmd.exe'; - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): TPromise { + public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const exec = configuration.external.windowsExec || getDefaultTerminalWindows(); @@ -118,7 +117,7 @@ class MacTerminalService extends TerminalLauncher { private static readonly DEFAULT_TERMINAL_OSX = 'Terminal.app'; private static readonly OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): TPromise { + public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const terminalApp = configuration.external.osxExec || MacTerminalService.DEFAULT_TERMINAL_OSX; @@ -185,7 +184,7 @@ class LinuxTerminalService extends TerminalLauncher { private static readonly WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue..."); - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): TPromise { + public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const terminalConfig = configuration.external; const execThenable: Thenable = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady(); diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts index fdab674b3b7..7982d4cde07 100644 --- a/src/vs/workbench/parts/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts @@ -5,7 +5,6 @@ import { URI as uri } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Position } from 'vs/editor/common/core/position'; import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug'; @@ -44,25 +43,25 @@ export class MockDebugService implements IDebugService { public focusStackFrame(focusedStackFrame: IStackFrame): void { } - sendAllBreakpoints(session?: IDebugSession): TPromise { + sendAllBreakpoints(session?: IDebugSession): Promise { return Promise.resolve(null); } - public addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): TPromise { + public addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): Promise { return Promise.resolve(null); } public updateBreakpoints(uri: uri, data: { [id: string]: IBreakpointUpdateData }, sendOnResourceSaved: boolean): void { } - public enableOrDisableBreakpoints(enabled: boolean): TPromise { + public enableOrDisableBreakpoints(enabled: boolean): Promise { return Promise.resolve(null); } - public setBreakpointsActivated(): TPromise { + public setBreakpointsActivated(): Promise { return Promise.resolve(null); } - public removeBreakpoints(): TPromise { + public removeBreakpoints(): Promise { return Promise.resolve(null); } @@ -70,39 +69,39 @@ export class MockDebugService implements IDebugService { public moveWatchExpression(id: string, position: number): void { } - public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise { + public renameFunctionBreakpoint(id: string, newFunctionName: string): Promise { return Promise.resolve(null); } - public removeFunctionBreakpoints(id?: string): TPromise { + public removeFunctionBreakpoints(id?: string): Promise { return Promise.resolve(null); } - public addReplExpression(name: string): TPromise { + public addReplExpression(name: string): Promise { return Promise.resolve(null); } public removeReplExpressions(): void { } - public addWatchExpression(name?: string): TPromise { + public addWatchExpression(name?: string): Promise { return Promise.resolve(null); } - public renameWatchExpression(id: string, newName: string): TPromise { + public renameWatchExpression(id: string, newName: string): Promise { return Promise.resolve(null); } public removeWatchExpressions(id?: string): void { } - public startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): TPromise { + public startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): Promise { return Promise.resolve(true); } - public restartSession(): TPromise { + public restartSession(): Promise { return Promise.resolve(null); } - public stopSession(): TPromise { + public stopSession(): Promise { return Promise.resolve(null); } @@ -118,7 +117,7 @@ export class MockDebugService implements IDebugService { public sourceIsNotAvailable(uri: uri): void { } - public tryToAutoFocusStackFrame(thread: IThread): TPromise { + public tryToAutoFocusStackFrame(thread: IThread): Promise { return Promise.resolve(null); } } @@ -133,8 +132,8 @@ export class MockSession implements IDebugSession { return null; } - addReplExpression(stackFrame: IStackFrame, name: string): TPromise { - return TPromise.as(void 0); + addReplExpression(stackFrame: IStackFrame, name: string): Promise { + return Promise.resolve(void 0); } appendToRepl(data: string | IExpression, severity: Severity, source?: IReplElementSource): void { } @@ -188,11 +187,11 @@ export class MockSession implements IDebugSession { return undefined; } - getLoadedSources(): TPromise { + getLoadedSources(): Promise { return Promise.resolve([]); } - completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise { + completions(frameId: number, text: string, position: Position, overwriteBefore: number): Promise { return Promise.resolve([]); } @@ -200,80 +199,80 @@ export class MockSession implements IDebugSession { rawUpdate(data: IRawModelUpdate): void { } - initialize(dbgr: IDebugger): TPromise { + initialize(dbgr: IDebugger): Thenable { throw new Error('Method not implemented.'); } - launchOrAttach(config: IConfig): TPromise { + launchOrAttach(config: IConfig): Promise { throw new Error('Method not implemented.'); } - restart(): TPromise { + restart(): Promise { throw new Error('Method not implemented.'); } - sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): TPromise { + sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise { throw new Error('Method not implemented.'); } - sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): TPromise { + sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise { throw new Error('Method not implemented.'); } - sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): TPromise { + sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise { throw new Error('Method not implemented.'); } - customRequest(request: string, args: any): TPromise { + customRequest(request: string, args: any): Promise { throw new Error('Method not implemented.'); } - stackTrace(threadId: number, startFrame: number, levels: number): TPromise { + stackTrace(threadId: number, startFrame: number, levels: number): Promise { throw new Error('Method not implemented.'); } - exceptionInfo(threadId: number): TPromise { + exceptionInfo(threadId: number): Promise { throw new Error('Method not implemented.'); } - scopes(frameId: number): TPromise { + scopes(frameId: number): Promise { throw new Error('Method not implemented.'); } - variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): TPromise { + variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): Promise { throw new Error('Method not implemented.'); } - evaluate(expression: string, frameId: number, context?: string): TPromise { + evaluate(expression: string, frameId: number, context?: string): Promise { throw new Error('Method not implemented.'); } - restartFrame(frameId: number, threadId: number): TPromise { + restartFrame(frameId: number, threadId: number): Promise { throw new Error('Method not implemented.'); } - next(threadId: number): TPromise { + next(threadId: number): Promise { throw new Error('Method not implemented.'); } - stepIn(threadId: number): TPromise { + stepIn(threadId: number): Promise { throw new Error('Method not implemented.'); } - stepOut(threadId: number): TPromise { + stepOut(threadId: number): Promise { throw new Error('Method not implemented.'); } - stepBack(threadId: number): TPromise { + stepBack(threadId: number): Promise { throw new Error('Method not implemented.'); } - continue(threadId: number): TPromise { + continue(threadId: number): Promise { throw new Error('Method not implemented.'); } - reverseContinue(threadId: number): TPromise { + reverseContinue(threadId: number): Promise { throw new Error('Method not implemented.'); } - pause(threadId: number): TPromise { + pause(threadId: number): Promise { throw new Error('Method not implemented.'); } - terminateThreads(threadIds: number[]): TPromise { + terminateThreads(threadIds: number[]): Promise { throw new Error('Method not implemented.'); } - setVariable(variablesReference: number, name: string, value: string): TPromise { + setVariable(variablesReference: number, name: string, value: string): Promise { throw new Error('Method not implemented.'); } - loadSource(resource: uri): TPromise { + loadSource(resource: uri): Promise { throw new Error('Method not implemented.'); } - terminate(restart = false): TPromise { + terminate(restart = false): Promise { throw new Error('Method not implemented.'); } - disconnect(restart = false): TPromise { + disconnect(restart = false): Promise { throw new Error('Method not implemented.'); } @@ -293,7 +292,7 @@ export class MockRawSession { return 100; } - public stackTrace(args: DebugProtocol.StackTraceArguments): TPromise { + public stackTrace(args: DebugProtocol.StackTraceArguments): Promise { return Promise.resolve({ seq: 1, type: 'response', @@ -311,103 +310,103 @@ export class MockRawSession { }); } - public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise { + public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise { return Promise.resolve(null); } - public launchOrAttach(args: IConfig): TPromise { + public launchOrAttach(args: IConfig): Promise { return Promise.resolve(null); } - public scopes(args: DebugProtocol.ScopesArguments): TPromise { + public scopes(args: DebugProtocol.ScopesArguments): Promise { return Promise.resolve(null); } - public variables(args: DebugProtocol.VariablesArguments): TPromise { + public variables(args: DebugProtocol.VariablesArguments): Promise { return Promise.resolve(null); } - evaluate(args: DebugProtocol.EvaluateArguments): TPromise { + evaluate(args: DebugProtocol.EvaluateArguments): Promise { return Promise.resolve(null); } - public custom(request: string, args: any): TPromise { + public custom(request: string, args: any): Promise { return Promise.resolve(null); } - public terminate(restart = false): TPromise { + public terminate(restart = false): Promise { return Promise.resolve(null); } - public disconnect(restart?: boolean): TPromise { + public disconnect(restart?: boolean): Promise { return Promise.resolve(null); } - public threads(): TPromise { + public threads(): Promise { return Promise.resolve(null); } - public stepIn(args: DebugProtocol.StepInArguments): TPromise { + public stepIn(args: DebugProtocol.StepInArguments): Promise { return Promise.resolve(null); } - public stepOut(args: DebugProtocol.StepOutArguments): TPromise { + public stepOut(args: DebugProtocol.StepOutArguments): Promise { return Promise.resolve(null); } - public stepBack(args: DebugProtocol.StepBackArguments): TPromise { + public stepBack(args: DebugProtocol.StepBackArguments): Promise { return Promise.resolve(null); } - public continue(args: DebugProtocol.ContinueArguments): TPromise { + public continue(args: DebugProtocol.ContinueArguments): Promise { return Promise.resolve(null); } - public reverseContinue(args: DebugProtocol.ReverseContinueArguments): TPromise { + public reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise { return Promise.resolve(null); } - public pause(args: DebugProtocol.PauseArguments): TPromise { + public pause(args: DebugProtocol.PauseArguments): Promise { return Promise.resolve(null); } - public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): TPromise { + public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): Promise { return Promise.resolve(null); } - public setVariable(args: DebugProtocol.SetVariableArguments): TPromise { + public setVariable(args: DebugProtocol.SetVariableArguments): Promise { return Promise.resolve(null); } - public restartFrame(args: DebugProtocol.RestartFrameArguments): TPromise { + public restartFrame(args: DebugProtocol.RestartFrameArguments): Promise { return Promise.resolve(null); } - public completions(args: DebugProtocol.CompletionsArguments): TPromise { + public completions(args: DebugProtocol.CompletionsArguments): Promise { return Promise.resolve(null); } - public next(args: DebugProtocol.NextArguments): TPromise { + public next(args: DebugProtocol.NextArguments): Promise { return Promise.resolve(null); } - public source(args: DebugProtocol.SourceArguments): TPromise { + public source(args: DebugProtocol.SourceArguments): Promise { return Promise.resolve(null); } - public loadedSources(args: DebugProtocol.LoadedSourcesArguments): TPromise { + public loadedSources(args: DebugProtocol.LoadedSourcesArguments): Promise { return Promise.resolve(null); } - public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): TPromise { + public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): Promise { return Promise.resolve(null); } - public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): TPromise { + public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): Promise { return Promise.resolve(null); } - public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): TPromise { + public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise { return Promise.resolve(null); } diff --git a/src/vs/workbench/parts/debug/test/node/debugger.test.ts b/src/vs/workbench/parts/debug/test/node/debugger.test.ts index a64cde0daf8..16d297aafbc 100644 --- a/src/vs/workbench/parts/debug/test/node/debugger.test.ts +++ b/src/vs/workbench/parts/debug/test/node/debugger.test.ts @@ -10,7 +10,6 @@ import { IDebugAdapterExecutable, IConfigurationManager, IConfig, IDebugSession import { Debugger } from 'vs/workbench/parts/debug/node/debugger'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExecutableDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; @@ -118,7 +117,7 @@ suite('Debug - Debugger', () => { const configurationManager = { - provideDebugAdapter(session: IDebugSession, folderUri: URI | undefined, config: IConfig): TPromise { + provideDebugAdapter(session: IDebugSession, folderUri: URI | undefined, config: IConfig): Promise { return Promise.resolve(undefined); } }; diff --git a/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts b/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts index b64af96857f..fbded03b34e 100644 --- a/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts +++ b/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts @@ -21,21 +21,20 @@ export interface IGrammarContributions { } export interface ILanguageIdentifierResolver { - getLanguageIdentifier(modeId: LanguageId): LanguageIdentifier; + getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier | null; } class GrammarContributions implements IGrammarContributions { - private static _grammars: ModeScopeMap | null = null; + private static _grammars: ModeScopeMap = {}; constructor(contributions: ExtensionPointContribution[]) { - if (GrammarContributions._grammars === null) { + if (!Object.keys(GrammarContributions._grammars).length) { this.fillModeScopeMap(contributions); } } private fillModeScopeMap(contributions: ExtensionPointContribution[]) { - GrammarContributions._grammars = {}; contributions.forEach((contribution) => { contribution.value.forEach((grammar) => { if (grammar.language && grammar.scopeName) { @@ -67,14 +66,14 @@ export abstract class EmmetEditorAction extends EditorAction { private _lastGrammarContributions: TPromise | null = null; private _lastExtensionService: IExtensionService | null = null; - private _withGrammarContributions(extensionService: IExtensionService): TPromise { + private _withGrammarContributions(extensionService: IExtensionService): TPromise { if (this._lastExtensionService !== extensionService) { this._lastExtensionService = extensionService; this._lastGrammarContributions = extensionService.readExtensionPointContributions(grammarsExtPoint).then((contributions) => { return new GrammarContributions(contributions); }); } - return this._lastGrammarContributions; + return this._lastGrammarContributions || TPromise.as(null); } public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { @@ -84,7 +83,7 @@ export abstract class EmmetEditorAction extends EditorAction { return this._withGrammarContributions(extensionService).then((grammarContributions) => { - if (this.id === 'editor.emmet.action.expandAbbreviation') { + if (this.id === 'editor.emmet.action.expandAbbreviation' && grammarContributions) { return commandService.executeCommand('emmet.expandAbbreviation', EmmetEditorAction.getLanguage(modeService, editor, grammarContributions)); } @@ -94,11 +93,23 @@ export abstract class EmmetEditorAction extends EditorAction { } public static getLanguage(languageIdentifierResolver: ILanguageIdentifierResolver, editor: ICodeEditor, grammars: IGrammarContributions) { - let position = editor.getSelection().getStartPosition(); - editor.getModel().tokenizeIfCheap(position.lineNumber); - let languageId = editor.getModel().getLanguageIdAtPosition(position.lineNumber, position.column); - let language = languageIdentifierResolver.getLanguageIdentifier(languageId).language; - let syntax = language.split('.').pop(); + const model = editor.getModel(); + const selection = editor.getSelection(); + + if (!model || !selection) { + return null; + } + + const position = selection.getStartPosition(); + model.tokenizeIfCheap(position.lineNumber); + const languageId = model.getLanguageIdAtPosition(position.lineNumber, position.column); + const languageIdentifier = languageIdentifierResolver.getLanguageIdentifier(languageId); + const language = languageIdentifier ? languageIdentifier.language : ''; + const syntax = language.split('.').pop(); + + if (!syntax) { + return null; + } let checkParentMode = (): string => { let languageGrammar = grammars.getGrammar(syntax); diff --git a/src/vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts b/src/vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts index 10c0127c08c..bd34d063500 100644 --- a/src/vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts +++ b/src/vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts @@ -53,11 +53,21 @@ suite('Emmet', () => { throw new Error('Unexpected'); } }; - editor.getModel().setMode(languageIdentifier); + const model = editor.getModel(); + if (!model) { + assert.fail('Editor model not found'); + return; + } + + model.setMode(languageIdentifier); let langOutput = EmmetEditorAction.getLanguage(languageIdentifierResolver, editor, new MockGrammarContributions(scopeName)); + if (!langOutput) { + assert.fail('langOutput not found'); + return; + } + assert.equal(langOutput.language, expectedLanguage); assert.equal(langOutput.parentMode, expectedParentLanguage); - } // syntaxes mapped using the scope name of the grammar diff --git a/src/vs/workbench/parts/execution/electron-browser/terminal.ts b/src/vs/workbench/parts/execution/electron-browser/terminal.ts index f08fd641f0a..525bd500305 100644 --- a/src/vs/workbench/parts/execution/electron-browser/terminal.ts +++ b/src/vs/workbench/parts/execution/electron-browser/terminal.ts @@ -11,7 +11,7 @@ export function getDefaultTerminalLinuxReady(): Promise { if (!_DEFAULT_TERMINAL_LINUX_READY) { _DEFAULT_TERMINAL_LINUX_READY = new Promise(c => { if (env.isLinux) { - Promise.all([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => { + Promise.all([pfs.exists('/etc/debian_version'), process.lazyEnv || Promise.resolve(void 0)]).then(([isDebian]) => { if (isDebian) { c('x-terminal-emulator'); } else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') { diff --git a/src/vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts b/src/vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts index d92206737f5..d5d27cda9f1 100644 --- a/src/vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts +++ b/src/vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts @@ -10,6 +10,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { language } from 'vs/base/common/platform'; export class ExperimentalPrompts extends Disposable implements IWorkbenchContribution { private _disposables: IDisposable[] = []; @@ -50,7 +51,8 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib }; const actionProperties = (experiment.action.properties); - if (!actionProperties || !actionProperties.promptText) { + const promptText = ExperimentalPrompts.getPromptText(actionProperties, language); + if (!actionProperties || !promptText) { return; } if (!actionProperties.commands) { @@ -80,7 +82,7 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib }; }); - this.notificationService.prompt(Severity.Info, actionProperties.promptText, choices, { + this.notificationService.prompt(Severity.Info, promptText, choices, { onCancel: () => { logTelemetry(); this.experimentService.markAsCompleted(experiment.id); @@ -91,4 +93,16 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib dispose() { this._disposables = dispose(this._disposables); } + + static getPromptText(actionProperties: IExperimentActionPromptProperties, displayLanguage: string): string { + if (typeof actionProperties.promptText === 'string') { + return actionProperties.promptText; + } + const msgInEnglish = actionProperties.promptText['en'] || actionProperties.promptText['en-us']; + displayLanguage = displayLanguage.toLowerCase(); + if (!actionProperties.promptText[displayLanguage] && displayLanguage.indexOf('-') === 2) { + displayLanguage = displayLanguage.substr(0, 2); + } + return actionProperties.promptText[displayLanguage] || msgInEnglish; + } } diff --git a/src/vs/workbench/parts/experiments/node/experimentService.ts b/src/vs/workbench/parts/experiments/node/experimentService.ts index d407611aa03..a3002d70b51 100644 --- a/src/vs/workbench/parts/experiments/node/experimentService.ts +++ b/src/vs/workbench/parts/experiments/node/experimentService.ts @@ -23,6 +23,7 @@ import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/co import { WorkspaceStats } from 'vs/workbench/parts/stats/node/workspaceStats'; import { CancellationToken } from 'vs/base/common/cancellation'; import { distinct } from 'vs/base/common/arrays'; +import { lastSessionDateStorageKey } from 'vs/platform/telemetry/node/workbenchCommonProperties'; interface IExperimentStorageState { enabled: boolean; @@ -43,6 +44,7 @@ interface IRawExperiment { enabled?: boolean; condition?: { insidersOnly?: boolean; + newUser?: boolean; displayLanguage?: string; installedExtensions?: { excludes?: string[]; @@ -76,7 +78,7 @@ export enum ExperimentActionType { } export interface IExperimentActionPromptProperties { - promptText: string; + promptText: string | { [key: string]: string }; commands: IExperimentActionPromptCommand[]; } @@ -333,6 +335,12 @@ export class ExperimentService extends Disposable implements IExperimentService return TPromise.wrap(ExperimentState.NoRun); } + const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); + if ((experiment.condition.newUser === true && !isNewUser) + || (experiment.condition.newUser === false && isNewUser)) { + return TPromise.wrap(ExperimentState.NoRun); + } + if (typeof experiment.condition.displayLanguage === 'string') { let localeToCheck = experiment.condition.displayLanguage.toLowerCase(); let displayLanguage = language.toLowerCase(); diff --git a/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts index 55472aabcf8..7db19c9eef7 100644 --- a/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/parts/experiments/node/experimentService'; +import { ExperimentService, ExperimentActionType, ExperimentState, IExperiment } from 'vs/workbench/parts/experiments/node/experimentService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices'; @@ -26,6 +26,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { assign } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { lastSessionDateStorageKey } from 'vs/platform/telemetry/node/workbenchCommonProperties'; let experimentData = { experiments: [] @@ -122,7 +123,7 @@ suite('Experiment Service', () => { }; testObject = instantiationService.createInstance(TestExperimentService); - const tests = []; + const tests: TPromise[] = []; tests.push(testObject.getExperimentById('experiment1')); tests.push(testObject.getExperimentById('experiment2')); tests.push(testObject.getExperimentById('experiment3')); @@ -173,6 +174,94 @@ suite('Experiment Service', () => { }); }); + test('NewUsers experiment shouldnt be enabled for old users', () => { + experimentData = { + experiments: [ + { + id: 'experiment1', + enabled: true, + condition: { + newUser: true + } + } + ] + }; + + instantiationService.stub(IStorageService, { + get: (a, b, c) => { + return a === lastSessionDateStorageKey ? 'some-date' : undefined; + }, + getBoolean: (a, b, c) => c, store: () => { }, remove: () => { } + }); + testObject = instantiationService.createInstance(TestExperimentService); + return testObject.getExperimentById('experiment1').then(result => { + assert.equal(result.enabled, true); + assert.equal(result.state, ExperimentState.NoRun); + }); + }); + + test('OldUsers experiment shouldnt be enabled for new users', () => { + experimentData = { + experiments: [ + { + id: 'experiment1', + enabled: true, + condition: { + newUser: false + } + } + ] + }; + + testObject = instantiationService.createInstance(TestExperimentService); + return testObject.getExperimentById('experiment1').then(result => { + assert.equal(result.enabled, true); + assert.equal(result.state, ExperimentState.NoRun); + }); + }); + + test('Experiment without NewUser condition should be enabled for old users', () => { + experimentData = { + experiments: [ + { + id: 'experiment1', + enabled: true, + condition: {} + } + ] + }; + + instantiationService.stub(IStorageService, { + get: (a, b, c) => { + return a === lastSessionDateStorageKey ? 'some-date' : undefined; + }, + getBoolean: (a, b, c) => c, store: () => { }, remove: () => { } + }); + testObject = instantiationService.createInstance(TestExperimentService); + return testObject.getExperimentById('experiment1').then(result => { + assert.equal(result.enabled, true); + assert.equal(result.state, ExperimentState.Run); + }); + }); + + test('Experiment without NewUser condition should be enabled for new users', () => { + experimentData = { + experiments: [ + { + id: 'experiment1', + enabled: true, + condition: {} + } + ] + }; + + testObject = instantiationService.createInstance(TestExperimentService); + return testObject.getExperimentById('experiment1').then(result => { + assert.equal(result.enabled, true); + assert.equal(result.state, ExperimentState.Run); + }); + }); + test('Experiment with no matching display language should be disabled', () => { experimentData = { experiments: [ @@ -546,7 +635,7 @@ suite('Experiment Service', () => { testObject = instantiationService.createInstance(TestExperimentService); - const tests = []; + const tests: TPromise[] = []; tests.push(testObject.getExperimentById('experiment1')); tests.push(testObject.getExperimentById('experiment2')); tests.push(testObject.getExperimentById('experiment3')); diff --git a/src/vs/workbench/parts/experiments/test/electron-browser/experimentalPrompts.test.ts b/src/vs/workbench/parts/experiments/test/electron-browser/experimentalPrompts.test.ts index 995f1498e45..cc1439dd8c2 100644 --- a/src/vs/workbench/parts/experiments/test/electron-browser/experimentalPrompts.test.ts +++ b/src/vs/workbench/parts/experiments/test/electron-browser/experimentalPrompts.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; -import { IExperiment, ExperimentActionType, IExperimentService, ExperimentState } from 'vs/workbench/parts/experiments/node/experimentService'; +import { IExperiment, ExperimentActionType, IExperimentService, ExperimentState, IExperimentActionPromptProperties } from 'vs/workbench/parts/experiments/node/experimentService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { Emitter } from 'vs/base/common/event'; @@ -157,4 +157,41 @@ suite('Experimental Prompts', () => { }); }); + + test('Test getPromptText', () => { + const simpleTextCase: IExperimentActionPromptProperties = { + promptText: 'My simple prompt', + commands: [] + }; + const multipleLocaleCase: IExperimentActionPromptProperties = { + promptText: { + en: 'My simple prompt for en', + de: 'My simple prompt for de', + 'en-au': 'My simple prompt for Austrailian English', + 'en-us': 'My simple prompt for US English' + }, + commands: [] + }; + const englishUSTextCase: IExperimentActionPromptProperties = { + promptText: { + 'en-us': 'My simple prompt for en' + }, + commands: [] + }; + const noEnglishTextCase: IExperimentActionPromptProperties = { + promptText: { + 'de-de': 'My simple prompt for German' + }, + commands: [] + }; + + assert.equal(ExperimentalPrompts.getPromptText(simpleTextCase, 'any-language'), simpleTextCase.promptText); + assert.equal(ExperimentalPrompts.getPromptText(multipleLocaleCase, 'en'), multipleLocaleCase.promptText['en']); + assert.equal(ExperimentalPrompts.getPromptText(multipleLocaleCase, 'de'), multipleLocaleCase.promptText['de']); + assert.equal(ExperimentalPrompts.getPromptText(multipleLocaleCase, 'en-au'), multipleLocaleCase.promptText['en-au']); + assert.equal(ExperimentalPrompts.getPromptText(multipleLocaleCase, 'en-gb'), multipleLocaleCase.promptText['en']); + assert.equal(ExperimentalPrompts.getPromptText(multipleLocaleCase, 'fr'), multipleLocaleCase.promptText['en']); + assert.equal(ExperimentalPrompts.getPromptText(englishUSTextCase, 'fr'), englishUSTextCase.promptText['en-us']); + assert.equal(!!ExperimentalPrompts.getPromptText(noEnglishTextCase, 'fr'), false); + }); }); \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 806a0d75107..ef06ad5475c 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -379,7 +379,7 @@ export class ExtensionEditor extends BaseEditor { reloadAction.extension = extension; this.extensionActionBar.clear(); - this.extensionActionBar.push([disabledStatusAction, reloadAction, updateAction, enableAction, disableAction, installAction, maliciousStatusAction], { icon: true, label: true }); + this.extensionActionBar.push([reloadAction, updateAction, enableAction, disableAction, installAction, maliciousStatusAction, disabledStatusAction], { icon: true, label: true }); this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, maliciousStatusAction, disabledStatusAction); const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 31ba8d61884..e3bc078e9ef 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -319,7 +319,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe const regEx = new RegExp(EXTENSION_IDENTIFIER_PATTERN); - const invalidExtensions = []; + const invalidExtensions: string[] = []; let message = ''; const regexFilter = (ids: string[]) => { 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 57ef2e8a6a6..63c0b8a7aa9 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -44,6 +44,7 @@ import { ExtensionHostProfileService } from 'vs/workbench/parts/extensions/elect import { RuntimeExtensionsInput } from 'vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput'; import { URI } from 'vs/base/common/uri'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ExtensionActivationProgress } from 'vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); @@ -56,6 +57,7 @@ workbenchRegistry.registerWorkbenchContribution(MaliciousExtensionChecker, Lifec workbenchRegistry.registerWorkbenchContribution(ConfigureRecommendedExtensionsCommandsContributor, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase.Running); workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Starting); +workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually); Registry.as(OutputExtensions.OutputChannels) .registerChannel({ id: ExtensionsChannelId, label: ExtensionsLabel, log: false }); @@ -165,7 +167,7 @@ const disabledActionDescriptor = new SyncActionDescriptor(ShowDisabledExtensions actionRegistry.registerWorkbenchAction(disabledActionDescriptor, 'Extensions: Show Disabled Extensions', ExtensionsLabel); const builtinActionDescriptor = new SyncActionDescriptor(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, ShowBuiltInExtensionsAction.LABEL); -actionRegistry.registerWorkbenchAction(builtinActionDescriptor, 'Extensions: Show Show Built-in Extensions', ExtensionsLabel); +actionRegistry.registerWorkbenchAction(builtinActionDescriptor, 'Extensions: Show Built-in Extensions', ExtensionsLabel); const updateAllActionDescriptor = new SyncActionDescriptor(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL); actionRegistry.registerWorkbenchAction(updateAllActionDescriptor, 'Extensions: Update All Extensions', ExtensionsLabel); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 91fab6acc7e..6fe1b4c60d0 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -16,7 +16,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate'; -import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionManagementServerService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; @@ -50,28 +50,34 @@ import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensions import product from 'vs/platform/node/product'; import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { clipboard } from 'electron'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { alert } from 'vs/base/browser/ui/aria/aria'; -const promptDownloadManually = (extension: IGalleryExtension, message: string, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService) => { - const downloadUrl = `${product.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; - notificationService.prompt(Severity.Error, message, [{ - label: localize('download', "Download Manually"), - run: () => openerService.open(URI.parse(downloadUrl)).then(() => { - notificationService.prompt( - Severity.Info, - localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', extension.identifier.id), - [{ - label: InstallVSIXAction.LABEL, - run: () => { - const action = instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); - action.run(); - action.dispose(); - } - }] - ); - }) - }]); +const promptDownloadManually = (extension: IGalleryExtension, message: string, error: Error, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService) => { + if (error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS) { + return Promise.reject(error); + } else { + const downloadUrl = `${product.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; + notificationService.prompt(Severity.Error, message, [{ + label: localize('download', "Download Manually"), + run: () => openerService.open(URI.parse(downloadUrl)).then(() => { + notificationService.prompt( + Severity.Info, + localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', extension.identifier.id), + [{ + label: InstallVSIXAction.LABEL, + run: () => { + const action = instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); + action.run(); + action.dispose(); + } + }] + ); + }) + }]); + return Promise.resolve(); + } }; export interface IExtensionAction extends IAction { @@ -147,7 +153,7 @@ export class InstallAction extends Action { console.error(err); - promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); }); } @@ -374,7 +380,7 @@ export class UpdateAction extends Action { console.error(err); - promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id), this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); }); } @@ -447,7 +453,7 @@ export class DropDownMenuActionItem extends ExtensionActionItem { const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(actions), + getActions: () => actions, actionRunner: this.actionRunner }); } @@ -515,6 +521,7 @@ export class ManageExtensionAction extends Action { this.instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL) ]); groups.push([this.instantiationService.createInstance(UninstallAction)]); + groups.push([this.instantiationService.createInstance(ExtensionInfoAction)]); return groups; } @@ -547,6 +554,36 @@ export class ManageExtensionAction extends Action { } } +export class ExtensionInfoAction extends Action implements IExtensionAction { + static readonly ID = 'extensions.extensionInfo'; + static readonly LABEL = localize('extensionInfoAction', "Copy Extension information"); + + private _extension: IExtension; + + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; } + + constructor() { + super(ExtensionInfoAction.ID, ExtensionInfoAction.LABEL); + } + + run(): Promise { + const { description, version, publisherDisplayName, id, displayName } = this.extension; + + const localizedExtension = localize('extensionInfoName', 'Name') + ': ' + displayName; + const localizedExtensionId = localize('extensionInfoId', 'Id') + ': ' + id; + const localizedDescription = localize('extensionInfoDescription', 'Description') + ': ' + description; + const localizedVersion = localize('extensionInfoVersion', 'Version') + ': ' + version; + const localizedPublisher = localize('extensionInfoPublisher', 'Publisher') + ': ' + publisherDisplayName; + const localizedVSMarketplaceLink = localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link') + ': https://marketplace.visualstudio.com/items?itemName=' + id; + + const clipboardStr = `${localizedExtension}\n${localizedExtensionId}\n${localizedDescription}\n${localizedVersion}\n${localizedPublisher}\n${localizedVSMarketplaceLink}`; + + clipboard.writeText(clipboardStr); + return Promise.resolve(null); + } +} + export class EnableForWorkspaceAction extends Action implements IExtensionAction { static readonly ID = 'extensions.enableForWorkspace'; @@ -572,7 +609,7 @@ export class EnableForWorkspaceAction extends Action implements IExtensionAction private update(): void { this.enabled = false; if (this.extension) { - this.enabled = (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); + this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -609,7 +646,7 @@ export class EnableGloballyAction extends Action implements IExtensionAction { private update(): void { this.enabled = false; if (this.extension) { - this.enabled = (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); + this.enabled = this.extension.state === ExtensionState.Installed && this.extension.enablementState === EnablementState.Disabled && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -626,13 +663,11 @@ export class EnableGloballyAction extends Action implements IExtensionAction { export class EnableAction extends Action { static readonly ID = 'extensions.enable'; - private static readonly EnabledClass = 'extension-action prominent enable'; + private static readonly EnabledClass = 'extension-action enable'; + private static readonly EnabledDropDownClass = 'extension-action dropdown enable'; private static readonly DisabledClass = `${EnableAction.EnabledClass} disabled`; private disposables: IDisposable[] = []; - - private _enableActions: IExtensionAction[]; - private _actionItem: DropDownMenuActionItem; get actionItem(): IActionItem { return this._actionItem; } @@ -640,18 +675,16 @@ export class EnableAction extends Action { get extension(): IExtension { return this._extension; } set extension(extension: IExtension) { this._extension = extension; this.update(); } - constructor( @IInstantiationService private instantiationService: IInstantiationService, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService ) { super(EnableAction.ID, localize('enableAction', "Enable"), EnableAction.DisabledClass, false); - this._enableActions = [ + this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [[ instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL), instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL) - ]; - this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this._enableActions], false); + ]], false); this.disposables.push(this._actionItem); this.disposables.push(this.extensionsWorkbenchService.onChange(extension => { @@ -673,18 +706,27 @@ export class EnableAction extends Action { } } - if (!this.extension) { - this.enabled = false; + const enabledActions = this._actionItem.getActions().filter(a => a.enabled); + this.enabled = enabledActions.length > 0; + if (this.enabled) { + if (enabledActions.length === 1) { + this.label = enabledActions[0].label; + this.class = EnableAction.EnabledClass; + } else { + this.class = EnableAction.EnabledDropDownClass; + } + } else { this.class = EnableAction.DisabledClass; - return; } - - this.enabled = this.extension.state === ExtensionState.Installed && this._enableActions.some(e => e.enabled); - this.class = this.enabled ? EnableAction.EnabledClass : EnableAction.DisabledClass; } public run(): Promise { - this._actionItem.showMenu(); + const enabledActions = this._actionItem.getActions().filter(a => a.enabled); + if (enabledActions.length === 1) { + enabledActions[0].run(); + } else { + this._actionItem.showMenu(); + } return Promise.resolve(null); } @@ -720,7 +762,7 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio private update(): void { this.enabled = false; if (this.extension && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { - this.enabled = (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); + this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -757,7 +799,7 @@ export class DisableGloballyAction extends Action implements IExtensionAction { private update(): void { this.enabled = false; if (this.extension) { - this.enabled = (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); + this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -776,10 +818,10 @@ export class DisableAction extends Action { static readonly ID = 'extensions.disable'; private static readonly EnabledClass = 'extension-action disable'; + private static readonly EnabledDropDownClass = 'extension-action dropdown enable'; private static readonly DisabledClass = `${DisableAction.EnabledClass} disabled`; private disposables: IDisposable[] = []; - private _disableActions: IExtensionAction[]; private _actionItem: DropDownMenuActionItem; get actionItem(): IActionItem { return this._actionItem; } @@ -787,17 +829,16 @@ export class DisableAction extends Action { get extension(): IExtension { return this._extension; } set extension(extension: IExtension) { this._extension = extension; this.update(); } - constructor( @IInstantiationService private instantiationService: IInstantiationService, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, ) { super(DisableAction.ID, localize('disableAction', "Disable"), DisableAction.DisabledClass, false); - this._disableActions = [ + + this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [[ instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL), instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL) - ]; - this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this._disableActions], false); + ]], false); this.disposables.push(this._actionItem); this.disposables.push(this.extensionsWorkbenchService.onChange(extension => { @@ -819,18 +860,27 @@ export class DisableAction extends Action { } } - if (!this.extension) { - this.enabled = false; + const enabledActions = this._actionItem.getActions().filter(a => a.enabled); + this.enabled = enabledActions.length > 0; + if (this.enabled) { + if (enabledActions.length === 1) { + this.label = enabledActions[0].label; + this.class = DisableAction.EnabledClass; + } else { + this.class = DisableAction.EnabledDropDownClass; + } + } else { this.class = DisableAction.DisabledClass; - return; } - - this.enabled = this.extension.state === ExtensionState.Installed && this._disableActions.some(a => a.enabled); - this.class = this.enabled ? DisableAction.EnabledClass : DisableAction.DisabledClass; } public run(): Promise { - this._actionItem.showMenu(); + const enabledActions = this._actionItem.getActions().filter(a => a.enabled); + if (enabledActions.length === 1) { + enabledActions[0].run(); + } else { + this._actionItem.showMenu(); + } return Promise.resolve(null); } @@ -984,7 +1034,7 @@ export class UpdateAllAction extends Action { console.error(err); - promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id), this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); }); } @@ -1044,7 +1094,12 @@ export class ReloadAction extends Action { .then(runningExtensions => this.computeReloadState(runningExtensions, installed)); }).then(() => { this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass; - this.label = this.useLongLabel ? this.tooltip : localize('reloadAction', "Reload"); + if (this.useLongLabel) { + this.label = this.tooltip; + this.tooltip = ''; + } else { + this.label = localize('reloadAction', "Reload"); + } }); } @@ -1070,9 +1125,8 @@ export class ReloadAction extends Action { } } else { const extensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location); - const localServer = this.extensionManagementServerService.getLocalExtensionManagementServer(); // Only extension from local server requires reload if it is not running on the server - if (extensionServer && extensionServer.authority === localServer.authority && !isDisabled) { + if (extensionServer && extensionServer.authority === this.extensionManagementServerService.localExtensionManagementServer.authority && !isDisabled) { // Requires reload to enable the extension this.enabled = true; this.tooltip = localize('postEnableTooltip', "Reload to Activate"); @@ -1129,7 +1183,7 @@ export class ShowEnabledExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1152,7 +1206,7 @@ export class ShowInstalledExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1175,7 +1229,7 @@ export class ShowDisabledExtensionsAction extends Action { super(id, label, 'null', true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1207,7 +1261,7 @@ export class ClearExtensionsInputAction extends Action { this.enabled = !!value; } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1234,7 +1288,7 @@ export class ShowBuiltInExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1257,7 +1311,7 @@ export class ShowOutdatedExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1280,7 +1334,7 @@ export class ShowPopularExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1303,7 +1357,7 @@ export class ShowRecommendedExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1336,7 +1390,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { this.recommendations = recommendations; } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1344,13 +1398,13 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { viewlet.focus(); const names = this.recommendations.map(({ extensionId }) => extensionId); return this.extensionWorkbenchService.queryGallery({ names, source: 'install-all-workspace-recommendations' }).then(pager => { - let installPromises = []; + let installPromises: Thenable[] = []; let model = new PagedModel(pager); for (let i = 0; i < pager.total; i++) { installPromises.push(model.resolve(i, CancellationToken.None).then(e => { return this.extensionWorkbenchService.install(e).then(null, err => { console.error(err); - promptDownloadManually(e.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", e.id), this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(e.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", e.id), err, this.instantiationService, this.notificationService, this.openerService); }); })); } @@ -1379,7 +1433,7 @@ export class InstallRecommendedExtensionAction extends Action { this.extensionId = extensionId; } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1392,7 +1446,7 @@ export class InstallRecommendedExtensionAction extends Action { return this.extensionWorkbenchService.install(extension) .then(() => null, err => { console.error(err); - promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); }); } return null; @@ -1475,7 +1529,7 @@ export class ShowRecommendedKeymapExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1498,7 +1552,7 @@ export class ShowLanguageExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1521,7 +1575,7 @@ export class ShowAzureExtensionsAction extends Action { super(id, label, null, true); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1560,7 +1614,7 @@ export class ChangeSortAction extends Action { this.enabled = value && this.query.isValid() && !this.query.equals(query); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -1598,7 +1652,7 @@ export class ChangeGroupAction extends Action { this.query = new Query(query.value, query.sortBy, this.groupBy || query.groupBy); } - run(): Promise { + run(): Thenable { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { @@ -2175,10 +2229,13 @@ export class DisabledStatusLabelAction extends Action { .then(runningExtensions => { this.class = `${DisabledStatusLabelAction.Class} hide`; this.tooltip = ''; - if (this.extension && !this.extension.isMalicious && !runningExtensions.some(e => e.id === this.extension.id)) { - if (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) { + if (this.extension && this.extension.local && !this.extension.isMalicious && !runningExtensions.some(e => e.id === this.extension.id)) { + if (this.extension.enablementState === EnablementState.Disabled) { this.class = `${DisabledStatusLabelAction.Class}`; - this.tooltip = this.extension.enablementState === EnablementState.Disabled ? localize('disabled globally', "Disabled") : localize('disabled workspace', "Disabled for this Workspace"); + this.label = localize('disabled globally', "Disabled for all Windows."); + } else if (this.extension.enablementState === EnablementState.WorkspaceDisabled) { + this.class = `${DisabledStatusLabelAction.Class}`; + this.label = localize('disabled workspace', "Disabled for this Workspace."); } } })); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts new file mode 100644 index 00000000000..644b4523b6f --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { localize } from 'vs/nls'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { timeout } from 'vs/base/common/async'; +import { ILogService } from 'vs/platform/log/common/log'; + +export class ExtensionActivationProgress implements IWorkbenchContribution { + + private readonly _listener: IDisposable; + + constructor( + @IExtensionService extensionService: IExtensionService, + @IProgressService2 progressService: IProgressService2, + @ILogService logService: ILogService, + ) { + + const options = { + location: ProgressLocation.Window, + title: localize('activation', "Activating Extensions...") + }; + + this._listener = extensionService.onWillActivateByEvent(e => { + logService.trace('onWillActivateByEvent: ', e.event); + progressService.withProgress(options, _ => Promise.race([e.activation, timeout(5000)])); + }); + } + + dispose(): void { + this._listener.dispose(); + } +} diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index 2f4900028e7..4628c0e8cf9 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -14,7 +14,7 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { once, Emitter, Event } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; -import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground, MaliciousStatusLabelAction, DisabledStatusLabelAction, ExtensionActionItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground, MaliciousStatusLabelAction, ExtensionActionItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { Label, RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -103,14 +103,13 @@ export class Renderer implements IPagedRenderer { const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, ratings, { small: true }); const maliciousStatusAction = this.instantiationService.createInstance(MaliciousStatusLabelAction, false); - const disabledStatusAction = this.instantiationService.createInstance(DisabledStatusLabelAction); const installAction = this.instantiationService.createInstance(InstallAction); const updateAction = this.instantiationService.createInstance(UpdateAction); const reloadAction = this.instantiationService.createInstance(ReloadAction, false); const manageAction = this.instantiationService.createInstance(ManageExtensionAction); - actionbar.push([updateAction, reloadAction, installAction, disabledStatusAction, maliciousStatusAction, manageAction], actionOptions); - const disposables = [versionWidget, installCountWidget, ratingsWidget, maliciousStatusAction, disabledStatusAction, updateAction, installAction, reloadAction, manageAction, actionbar, bookmarkStyler]; + actionbar.push([updateAction, reloadAction, installAction, maliciousStatusAction, manageAction], actionOptions); + const disposables = [versionWidget, installCountWidget, ratingsWidget, maliciousStatusAction, updateAction, installAction, reloadAction, manageAction, actionbar, bookmarkStyler]; return { root, element, icon, name, installCount, ratings, author, description, disposables, actionbar, @@ -120,7 +119,6 @@ export class Renderer implements IPagedRenderer { installCountWidget.extension = extension; ratingsWidget.extension = extension; maliciousStatusAction.extension = extension; - disabledStatusAction.extension = extension; installAction.extension = extension; updateAction.extension = extension; reloadAction.extension = extension; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index a557912381b..826e074323b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -55,6 +55,7 @@ import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery'; import { SuggestEnabledInput, attachSuggestEnabledInputBoxStyler } from 'vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; interface SearchInputEvent extends Event { target: HTMLInputElement; @@ -90,7 +91,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio } private registerViews(): void { - let viewDescriptors = []; + let viewDescriptors: IViewDescriptor[] = []; viewDescriptors.push(this.createMarketPlaceExtensionsListViewDescriptor()); viewDescriptors.push(this.createEnabledExtensionsListViewDescriptor()); viewDescriptors.push(this.createDisabledExtensionsListViewDescriptor()); @@ -102,10 +103,9 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push(this.createOtherRecommendedExtensionsListViewDescriptor()); viewDescriptors.push(this.createWorkspaceRecommendedExtensionsListViewDescriptor()); - if (this.extensionManagementServerService.extensionManagementServers.length > 1) { - for (const extensionManagementServer of this.extensionManagementServerService.extensionManagementServers) { - viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(extensionManagementServer)); - } + if (this.extensionManagementServerService.otherExtensionManagementServer) { + viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.localExtensionManagementServer)); + viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.otherExtensionManagementServer)); } ViewsRegistry.registerViews(viewDescriptors); @@ -325,7 +325,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio }, this, this.disposables); } - create(parent: HTMLElement): Promise { + create(parent: HTMLElement): void { addClass(parent, 'extensions-viewlet'); this.root = parent; @@ -358,18 +358,17 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.searchBox.onShouldFocusResults(() => this.focusListView(), this, this.disposables); this.extensionsBox = append(this.root, $('.extensions')); - return super.create(this.extensionsBox); + super.create(this.extensionsBox); } - setVisible(visible: boolean): Promise { + setVisible(visible: boolean): void { const isVisibilityChanged = this.isVisible() !== visible; - return super.setVisible(visible).then(() => { - if (isVisibilityChanged) { - if (visible) { - this.searchBox.focus(); - } + super.setVisible(visible); + if (isVisibilityChanged) { + if (visible) { + this.searchBox.focus(); } - }); + } } focus(): void { @@ -416,7 +415,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Sort By: Rating"), this.onSearchChange, 'rating'), this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Sort By: Name"), this.onSearchChange, 'name'), new Separator(), - ...(this.extensionManagementServerService.extensionManagementServers.length > 1 ? [this.groupByServerAction, new Separator()] : []), + ...(this.extensionManagementServerService.otherExtensionManagementServer ? [this.groupByServerAction, new Separator()] : []), this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL), ...(this.configurationService.getValue(AutoUpdateConfigurationKey) ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]), this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL), @@ -494,8 +493,10 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio } protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel { - for (const extensionManagementServer of this.extensionManagementServerService.extensionManagementServers) { - if (viewDescriptor.id === `server.extensionsList.${extensionManagementServer.authority}`) { + if (this.extensionManagementServerService.otherExtensionManagementServer) { + const extensionManagementServer = viewDescriptor.id === `server.extensionsList.${this.extensionManagementServerService.localExtensionManagementServer.authority}` ? this.extensionManagementServerService.localExtensionManagementServer + : viewDescriptor.id === `server.extensionsList.${this.extensionManagementServerService.otherExtensionManagementServer.authority}` ? this.extensionManagementServerService.otherExtensionManagementServer : null; + if (extensionManagementServer) { const servicesCollection: ServiceCollection = new ServiceCollection(); servicesCollection.set(IExtensionManagementServerService, new SingleServerExtensionManagementServerService(extensionManagementServer)); servicesCollection.set(IExtensionManagementService, extensionManagementServer.extensionManagementService); @@ -608,9 +609,12 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { @IExtensionManagementService private extensionsManagementService: IExtensionManagementService, @IWindowService private windowService: IWindowService, @ILogService private logService: ILogService, - @INotificationService private notificationService: INotificationService + @INotificationService private notificationService: INotificationService, + @IEnvironmentService private environmentService: IEnvironmentService ) { - this.loopCheckForMaliciousExtensions(); + if (!this.environmentService.disableExtensions) { + this.loopCheckForMaliciousExtensions(); + } } private loopCheckForMaliciousExtensions(): void { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 615297a9210..4f07b8c30af 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -9,7 +9,7 @@ import { assign } from 'vs/base/common/objects'; import { chain, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; -import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService, EnablementState, IExtensionRecommendation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService, IExtensionRecommendation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -125,9 +125,6 @@ export class ExtensionsListView extends ViewletPanel { case 'rating': options = assign(options, { sortBy: SortBy.WeightedRating }); break; case 'name': options = assign(options, { sortBy: SortBy.Title }); break; } - if (!query || !query.trim()) { - options.sortBy = SortBy.InstallCount; - } const successCallback = model => { this.setModel(model); @@ -171,7 +168,7 @@ export class ExtensionsListView extends ViewletPanel { if (manageExtensionAction.enabled) { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => Promise.resolve(manageExtensionAction.actionItem.getActions()) + getActions: () => manageExtensionAction.actionItem.getActions() }); } } @@ -286,15 +283,14 @@ export class ExtensionsListView extends ViewletPanel { if (/@enabled/i.test(value)) { value = value ? value.replace(/@enabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : ''; - const local = await this.extensionsWorkbenchService.queryLocal(); + const local = (await this.extensionsWorkbenchService.queryLocal()).filter(e => e.type === LocalExtensionType.User); + const runningExtensions = await this.extensionService.getExtensions(); - let result = local + const result = local .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) - .filter(e => e.type === LocalExtensionType.User && - (e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled) && - (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && - (!categories.length || categories.some(category => (e.local.manifest.categories || []).some(c => c.toLowerCase() === category))) - ); + .filter(e => runningExtensions.some(r => areSameExtensions(r, e)) + && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) + && (!categories.length || categories.some(category => (e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); return this.getPagedModel(this.sortExtensions(result, options)); } @@ -303,6 +299,11 @@ export class ExtensionsListView extends ViewletPanel { } private async queryGallery(query: Query, options: IQueryOptions): Promise> { + const hasUserDefinedSortOrder = options.sortBy !== undefined; + if (!hasUserDefinedSortOrder && !query.value.trim()) { + options.sortBy = SortBy.InstallCount; + } + let value = query.value; const idRegex = /@id:(([a-z0-9A-Z][a-z0-9\-A-Z]*)\.([a-z0-9A-Z][a-z0-9\-A-Z]*))/g; @@ -359,11 +360,13 @@ export class ExtensionsListView extends ViewletPanel { let preferredResults: string[] = []; if (text) { options = assign(options, { text: text.substr(0, 350), source: 'searchText' }); - for (let i = 0; i < this.searchExperiments.length; i++) { - if (text.toLowerCase() === this.searchExperiments[i].action.properties['searchText'] && Array.isArray(this.searchExperiments[i].action.properties['preferredResults'])) { - preferredResults = this.searchExperiments[i].action.properties['preferredResults']; - options.source += `-experiment-${this.searchExperiments[i].id}`; - break; + if (!hasUserDefinedSortOrder) { + for (let i = 0; i < this.searchExperiments.length; i++) { + if (text.toLowerCase() === this.searchExperiments[i].action.properties['searchText'] && Array.isArray(this.searchExperiments[i].action.properties['preferredResults'])) { + preferredResults = this.searchExperiments[i].action.properties['preferredResults']; + options.source += `-experiment-${this.searchExperiments[i].id}`; + break; + } } } } else { @@ -850,4 +853,4 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { return this.tipsService.getWorkspaceRecommendations() .then(recommendations => recommendations.filter(({ extensionId }) => !this.extensionsWorkbenchService.local.some(i => areSameExtensions({ id: extensionId }, { id: i.id })))); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css index 89c71289a0a..12f1b6f98ce 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css @@ -20,8 +20,8 @@ .monaco-action-bar .action-item .action-label.extension-action.multiserver.install:after, .monaco-action-bar .action-item .action-label.extension-action.multiserver.update:after, -.monaco-action-bar .action-item .action-label.extension-action.enable:after, -.monaco-action-bar .action-item .action-label.extension-action.disable:after { +.monaco-action-bar .action-item .action-label.extension-action.enable.dropdown:after, +.monaco-action-bar .action-item .action-label.extension-action.disable.dropdown:after { content: '▼'; padding-left: 2px; font-size: 80%; @@ -54,6 +54,11 @@ font-weight: normal; } +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.disable-status:hover, +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status:hover { + opacity: 0.9; +} + .extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.extension-action.manage.hide { display: none; } diff --git a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts index 7084c892586..314b5b3924b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -254,6 +254,9 @@ export class RuntimeExtensionsEditor extends BaseEditor { msgIcon: HTMLElement; msgLabel: HTMLElement; + msgIcon2: HTMLElement; + msgLabel2: HTMLElement; + actionbar: ActionBar; disposables: IDisposable[]; elementDisposables: IDisposable[]; @@ -271,6 +274,9 @@ export class RuntimeExtensionsEditor extends BaseEditor { const msgIcon = append(msgContainer, $('.')); const msgLabel = append(msgContainer, $('span.msg-label')); + const msgIcon2 = append(msgContainer, $('.')); + const msgLabel2 = append(msgContainer, $('span.msg-label')); + const timeContainer = append(element, $('.time')); const activationTime = append(timeContainer, $('div.activation-time')); const profileTime = append(timeContainer, $('div.profile-time')); @@ -295,6 +301,8 @@ export class RuntimeExtensionsEditor extends BaseEditor { profileTimeline, msgIcon, msgLabel, + msgIcon2, + msgLabel2, disposables, elementDisposables: [] }; @@ -365,6 +373,14 @@ export class RuntimeExtensionsEditor extends BaseEditor { data.msgLabel.textContent = ''; } + if (element.description.extensionLocation.scheme !== 'file') { + data.msgIcon2.className = 'octicon octicon-rss'; + data.msgLabel2.textContent = element.description.extensionLocation.authority; + } else { + data.msgIcon2.className = ''; + data.msgLabel2.textContent = ''; + } + if (this._profileInfo) { data.profileTime.textContent = `Profile: ${(element.profileInfo.totalTime / 1000).toFixed(2)}ms`; const elementSegments = element.profileInfo.segments; @@ -426,7 +442,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { this._contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => Promise.resolve(actions) + getActions: () => actions }); }); } diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index fac339e0e34..7fb58618c97 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -29,7 +29,7 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; import product from 'vs/platform/node/product'; import { ILogService } from 'vs/platform/log/common/log'; -import { IProgressService2, ProgressLocation } from 'vs/workbench/services/progress/common/progress'; +import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { groupBy } from 'vs/base/common/collections'; @@ -615,7 +615,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } private syncWithGallery(): Promise { - const ids = [], names = []; + const ids: string[] = [], names: string[] = []; for (const installed of this.installed) { if (installed.type === LocalExtensionType.User) { if (installed.uuid) { @@ -626,7 +626,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } } - const promises = []; + const promises: Promise>[] = []; if (ids.length) { promises.push(this.queryGallery({ ids, pageSize: ids.length })); } @@ -956,7 +956,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, private onEnablementChanged(identifier: IExtensionIdentifier) { const [extension] = this.local.filter(e => areSameExtensions(e, identifier)); - if (extension) { + if (extension && extension.local) { const enablementState = this.extensionEnablementService.getEnablementState(extension.local); if (enablementState !== extension.enablementState) { extension.enablementState = enablementState; diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts index 36baa5b1c7d..0778b7c9b2b 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -622,7 +622,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { testObject.extension = extensions[0]; - assert.ok(testObject.enabled); + assert.ok(!testObject.enabled); }); }); }); @@ -653,7 +653,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { testObject.extension = extensions[0]; - assert.ok(testObject.enabled); + assert.ok(!testObject.enabled); }); }); }); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts index 452cd8865bb..334db276af2 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts @@ -12,7 +12,7 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/commo import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, IQueryOptions, - DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, IExtensionManagementServer, EnablementState, ExtensionRecommendationReason + DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, IExtensionManagementServer, EnablementState, ExtensionRecommendationReason, SortBy } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService'; @@ -152,6 +152,33 @@ suite('ExtensionsListView Tests', () => { assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@outdated searchText'), true); }); + test('Test empty query equates to sort by install count', () => { + const target = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); + return testableView.show('').then(() => { + assert.ok(target.calledOnce); + const options: IQueryOptions = target.args[0][0]; + assert.equal(options.sortBy, SortBy.InstallCount); + }); + }); + + test('Test non empty query without sort doesnt use sortBy', () => { + const target = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); + return testableView.show('some extension').then(() => { + assert.ok(target.calledOnce); + const options: IQueryOptions = target.args[0][0]; + assert.equal(options.sortBy, undefined); + }); + }); + + test('Test query with sort uses sortBy', () => { + const target = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); + return testableView.show('some extension @sort:rating').then(() => { + assert.ok(target.calledOnce); + const options: IQueryOptions = target.args[0][0]; + assert.equal(options.sortBy, SortBy.WeightedRating); + }); + }); + test('Test installed query results', () => { const allInstalledCheck = testableView.show('@installed').then(result => { assert.equal(result.length, 5, 'Unexpected number of results for @installed query'); @@ -441,6 +468,49 @@ suite('ExtensionsListView Tests', () => { }); }); + test('Skip preferred search experiment when user defines sort order', () => { + const searchText = 'search-me'; + const realResults = [ + fileBasedRecommendationA, + workspaceRecommendationA, + otherRecommendationA, + workspaceRecommendationB + ]; + + const queryTarget = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...realResults)); + const experimentTarget = instantiationService.stubPromise(IExperimentService, 'getExperimentsByType', [{ + id: 'someId', + enabled: true, + state: ExperimentState.Run, + action: { + type: ExperimentActionType.ExtensionSearchResults, + properties: { + searchText: 'search-me', + preferredResults: [ + workspaceRecommendationA.identifier.id, + 'something-that-wasnt-in-first-page', + workspaceRecommendationB.identifier.id + ] + } + } + }]); + + testableView.dispose(); + testableView = instantiationService.createInstance(ExtensionsListView, {}); + + return testableView.show('search-me @sort:installs').then(result => { + const options: IQueryOptions = queryTarget.args[0][0]; + + assert.ok(experimentTarget.calledOnce); + assert.ok(queryTarget.calledOnce); + assert.equal(options.text, searchText); + assert.equal(result.length, realResults.length); + for (let i = 0; i < realResults.length; i++) { + assert.equal(result.get(i).id, realResults[i].identifier.id); + } + }); + }); + function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}, type: LocalExtensionType = LocalExtensionType.User): ILocalExtension { const localExtension = Object.create({ manifest: {} }); assign(localExtension, { type, manifest: {}, location: URI.file(`pub.${name}`) }, properties); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index d22f8f3a694..fe907d5a032 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -30,7 +30,7 @@ import { TestContextService, TestWindowService } from 'vs/workbench/test/workben import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IWindowService } from 'vs/platform/windows/common/windows'; -import { IProgressService2 } from 'vs/workbench/services/progress/common/progress'; +import { IProgressService2 } from 'vs/platform/progress/common/progress'; import { ProgressService2 } from 'vs/workbench/services/progress/browser/progressService2'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { URLService } from 'vs/platform/url/common/urlService'; diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts index 5408ffe986d..5e50f7ea658 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts @@ -236,7 +236,7 @@ export class FeedbackDropdown extends Dropdown { // Remaining Characters const remainingCharacterCountContainer = dom.append(this.feedbackForm, dom.$('h3')); - remainingCharacterCountContainer.textContent = nls.localize("tell us why?", "Tell us why?"); + remainingCharacterCountContainer.textContent = nls.localize("tell us why", "Tell us why?"); this.remainingCharacterCount = dom.append(remainingCharacterCountContainer, dom.$('span.char-counter')); this.remainingCharacterCount.textContent = this.getCharCountText(0); diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts index 28ae869f96a..67e4b743813 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts @@ -110,7 +110,7 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { this.contextMenuService.showContextMenu({ getAnchor: () => this.container, - getActions: () => Promise.resolve([this.hideAction]) + getActions: () => [this.hideAction] }); })); diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index b98dd326616..36660df4223 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -13,14 +13,14 @@ import { ITextModelContentProvider } from 'vs/editor/common/services/resolverSer import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModel } from 'vs/editor/common/model'; -import { IMode } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { InputFocusedContextKey } from 'vs/platform/workbench/common/contextkeys'; import { Registry } from 'vs/platform/registry/common/platform'; import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer } from 'vs/workbench/common/views'; +import { Schemas } from 'vs/base/common/network'; /** * Explorer viewlet id. @@ -156,7 +156,7 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider { } provideTextContent(resource: URI): TPromise { - const fileOnDiskResource = URI.file(resource.fsPath); + const fileOnDiskResource = resource.with({ scheme: Schemas.file }); // Make sure our file from disk is resolved up to date return this.resolveEditorModel(resource).then(codeEditorModel => { @@ -180,7 +180,7 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider { } private resolveEditorModel(resource: URI, createAsNeeded = true): TPromise { - const fileOnDiskResource = URI.file(resource.fsPath); + const fileOnDiskResource = resource.with({ scheme: Schemas.file }); return this.textFileService.resolveTextContent(fileOnDiskResource).then(content => { let codeEditorModel = this.modelService.getModel(resource); @@ -189,14 +189,14 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider { } else if (createAsNeeded) { const fileOnDiskModel = this.modelService.getModel(fileOnDiskResource); - let mode: Promise; + let languageSelector: ILanguageSelection; if (fileOnDiskModel) { - mode = this.modeService.getOrCreateMode(fileOnDiskModel.getModeId()); + languageSelector = this.modeService.create(fileOnDiskModel.getModeId()); } else { - mode = this.modeService.getOrCreateModeByFilepathOrFirstLine(fileOnDiskResource.fsPath); + languageSelector = this.modeService.createByFilepathOrFirstLine(fileOnDiskResource.fsPath); } - codeEditorModel = this.modelService.createModel(content.value, mode, resource); + codeEditorModel = this.modelService.createModel(content.value, languageSelector, resource); } return codeEditorModel; diff --git a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts index 29bec3f381f..7f8b7605406 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts @@ -60,7 +60,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor private registerViews(): void { const viewDescriptors = ViewsRegistry.getViews(VIEW_CONTAINER); - let viewDescriptorsToRegister = []; + let viewDescriptorsToRegister: IViewDescriptor[] = []; let viewDescriptorsToDeregister: string[] = []; const openEditorsViewDescriptor = this.createOpenEditorsViewDescriptor(); @@ -175,10 +175,9 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi this._register(this.contextService.onDidChangeWorkspaceName(e => this.updateTitleArea())); } - create(parent: HTMLElement): Promise { - return super.create(parent).then(() => { - DOM.addClass(parent, 'explorer-viewlet'); - }); + create(parent: HTMLElement): void { + super.create(parent); + DOM.addClass(parent, 'explorer-viewlet'); } protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel { @@ -235,9 +234,9 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi return this.getView(EmptyView.ID); } - public setVisible(visible: boolean): Promise { + public setVisible(visible: boolean): void { this.viewletVisibleContextKey.set(visible); - return super.setVisible(visible); + super.setVisible(visible); } public getActionRunner(): IActionRunner { diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 8cdac0809a1..a77c906d968 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -1546,7 +1546,7 @@ class ClipboardContentProvider implements ITextModelContentProvider { ) { } provideTextContent(resource: URI): TPromise { - const model = this.modelService.createModel(this.clipboardService.readText(), this.modeService.getOrCreateMode('text/plain'), resource); + const model = this.modelService.createModel(this.clipboardService.readText(), this.modeService.create('text/plain'), resource); return Promise.resolve(model); } diff --git a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts index ec20c823dd0..edf211fb9d2 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts @@ -437,7 +437,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: EditorContextKeys.focus.toNegated(), primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_C, win: { - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C) }, id: COPY_RELATIVE_PATH_COMMAND_ID, handler: (accessor, resource: URI | object) => { diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index b6a14d0f087..f857f474d90 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -336,7 +336,7 @@ configurationRegistry.registerConfiguration({ 'editor.formatOnSave': { 'type': 'boolean', 'default': false, - 'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be auto-saved, and editor must not be shutting down."), + 'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down."), 'overridable': true, 'scope': ConfigurationScope.RESOURCE }, diff --git a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css index eb562776b4e..c3fc2504f70 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -104,18 +104,15 @@ .explorer-viewlet .explorer-item .monaco-inputbox { width: 100%; line-height: normal; - margin-left: -3px; } .monaco-workbench.linux .explorer-viewlet .explorer-item .monaco-inputbox, .monaco-workbench.mac .explorer-viewlet .explorer-item .monaco-inputbox { height: 22px; - margin-left: -1px; } .explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input { padding: 1px 2px; - height: 19px; } .monaco-workbench.linux .explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input, diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index 201f26cebf9..a4ae0916074 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -6,6 +6,7 @@ import * as nls from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; +import * as perf from 'vs/base/common/performance'; import { ThrottledDelayer, Delayer } from 'vs/base/common/async'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; @@ -172,7 +173,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView this.onConfigurationUpdated(configuration); // Load and Fill Viewer - let targetsToExpand = []; + let targetsToExpand: URI[] = []; if (this.viewState[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES]) { targetsToExpand = this.viewState[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES].map((e: string) => URI.parse(e)); } @@ -322,57 +323,54 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { + public setVisible(visible: boolean): void { + super.setVisible(visible); - // Show - if (visible) { + // Show + if (visible) { - // If a refresh was requested and we are now visible, run it - let refreshPromise: Thenable = Promise.resolve(null); - if (this.shouldRefresh) { - refreshPromise = this.doRefresh(); - this.shouldRefresh = false; // Reset flag - } - - if (!this.autoReveal) { - return refreshPromise; // do not react to setVisible call if autoReveal === false - } - - // Always select the current navigated file in explorer if input is file editor input - // unless autoReveal is set to false - const activeFile = this.getActiveFile(); - if (activeFile) { - return refreshPromise.then(() => { - return this.select(activeFile); - }); - } - - // Return now if the workbench has not yet been created - in this case the workbench takes care of restoring last used editors - if (!this.partService.isCreated()) { - return Promise.resolve(null); - } - - // Otherwise restore last used file: By lastActiveFileResource - let lastActiveFileResource: URI; - if (this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) { - lastActiveFileResource = URI.parse(this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]); - } - - if (lastActiveFileResource && this.isCreated && this.model.findClosest(lastActiveFileResource)) { - this.editorService.openEditor({ resource: lastActiveFileResource, options: { revealIfVisible: true } }); - - return refreshPromise; - } - - // Otherwise restore last used file: By Explorer selection - return refreshPromise.then(() => { - this.openFocusedElement(); - }); + // If a refresh was requested and we are now visible, run it + let refreshPromise: Thenable = TPromise.as(null); + if (this.shouldRefresh) { + refreshPromise = this.doRefresh(); + this.shouldRefresh = false; // Reset flag } - return void 0; - }); + if (!this.autoReveal) { + return; // do not react to setVisible call if autoReveal === false + } + + // Always select the current navigated file in explorer if input is file editor input + // unless autoReveal is set to false + const activeFile = this.getActiveFile(); + if (activeFile) { + refreshPromise.then(() => { + this.select(activeFile); + }); + return; + } + + // Return now if the workbench has not yet been created - in this case the workbench takes care of restoring last used editors + if (!this.partService.isCreated()) { + return; + } + + // Otherwise restore last used file: By lastActiveFileResource + let lastActiveFileResource: URI; + if (this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) { + lastActiveFileResource = URI.parse(this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]); + } + + if (lastActiveFileResource && this.isCreated && this.model.findClosest(lastActiveFileResource)) { + this.editorService.openEditor({ resource: lastActiveFileResource, options: { revealIfVisible: true } }); + return; + } + + // Otherwise restore last used file: By Explorer selection + refreshPromise.then(() => { + this.openFocusedElement(); + }); + } } private openFocusedElement(preserveFocus?: boolean): void { @@ -473,7 +471,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView public getOptimalWidth(): number { const parentNode = this.explorerViewer.getHTMLElement(); - const childNodes = [].slice.call(parentNode.querySelectorAll('.explorer-item .label-name')); // select all file labels + const childNodes = ([] as Element[]).slice.call(parentNode.querySelectorAll('.explorer-item .label-name')); // select all file labels return DOM.getLargestChildWidth(parentNode, childNodes); } @@ -806,6 +804,9 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView // Display roots only when multi folder workspace let input = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? this.model.roots[0] : this.model; + if (input !== this.explorerViewer.getInput()) { + perf.mark('willResolveExplorer'); + } const errorRoot = (resource: URI, root: ExplorerItem) => { if (input === this.model.roots[0]) { @@ -828,7 +829,8 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView statsToExpand = this.model.roots.concat(statsToExpand); } - return this.explorerViewer.setInput(input).then(() => this.explorerViewer.expandAll(statsToExpand)); + return this.explorerViewer.setInput(input).then(() => this.explorerViewer.expandAll(statsToExpand)) + .then(() => perf.mark('didResolveExplorer')); }; if (targetsToResolve.every(t => t.root.resource.scheme === 'file')) { diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts index 05f13e4656a..b1324496560 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -543,7 +543,7 @@ export class FileController extends WorkbenchTreeController implements IDisposab getActions: () => { const actions: IAction[] = []; fillInContextMenuActions(this.contributedContextMenu, { arg: stat instanceof ExplorerItem ? stat.resource : {}, shouldForwardArgs: true }, actions, this.contextMenuService); - return Promise.resolve(actions); + return actions; }, onHide: (wasCancelled?: boolean) => { if (wasCancelled) { diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index b6a1ed7cd8b..d57a8dd1fd7 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -297,13 +297,12 @@ export class OpenEditorsView extends ViewletPanel { } } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { - this.updateListVisibility(visible && this.isExpanded()); - if (visible && this.needsRefresh) { - this.listRefreshScheduler.schedule(0); - } - }); + public setVisible(visible: boolean): void { + super.setVisible(visible); + this.updateListVisibility(visible && this.isExpanded()); + if (visible && this.needsRefresh) { + this.listRefreshScheduler.schedule(0); + } } public focus(): void { @@ -393,7 +392,7 @@ export class OpenEditorsView extends ViewletPanel { getActions: () => { const actions: IAction[] = []; fillInContextMenuActions(this.contributedContextMenu, { shouldForwardArgs: true, arg: element instanceof OpenEditor ? element.editor.getResource() : {} }, actions, this.contextMenuService); - return Promise.resolve(actions); + return actions; }, getActionsContext: () => element instanceof OpenEditor ? { groupId: element.groupId, editorIndex: element.editorIndex } : { groupId: element.id } }); @@ -468,7 +467,7 @@ export class OpenEditorsView extends ViewletPanel { public getOptimalWidth(): number { let parentNode = this.list.getHTMLElement(); - let childNodes = [].slice.call(parentNode.querySelectorAll('.open-editor > a')); + let childNodes: HTMLElement[] = [].slice.call(parentNode.querySelectorAll('.open-editor > a')); return dom.getLargestChildWidth(parentNode, childNodes); } diff --git a/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts index 26825ffc794..96e890bad6a 100644 --- a/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModel } from 'vs/editor/common/model'; import { Disposable, IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; import { EditorOptions, EditorInput, IEditorMemento } from 'vs/workbench/common/editor'; @@ -179,7 +178,7 @@ export class HtmlPreviewPart extends BaseWebviewEditor { public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable { if (this.input && this.input.matches(input) && this._hasValidModel() && this.input instanceof HtmlInput && input instanceof HtmlInput && areHtmlInputOptionsEqual(this.input.options, input.options)) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } let oldOptions: HtmlInputOptions | undefined = undefined; @@ -197,7 +196,7 @@ export class HtmlPreviewPart extends BaseWebviewEditor { this._modelChangeSubscription.dispose(); if (!(input instanceof HtmlInput)) { - return TPromise.wrapError(new Error('Invalid input')); + return Promise.reject(new Error('Invalid input')); } return super.setInput(input, options, token).then(() => { @@ -213,7 +212,7 @@ export class HtmlPreviewPart extends BaseWebviewEditor { } if (!this.model) { - return TPromise.wrapError(new Error(localize('html.voidInput', "Invalid editor input."))); + return Promise.reject(new Error(localize('html.voidInput', "Invalid editor input."))); } if (oldOptions && !areHtmlInputOptionsEqual(oldOptions, input.options)) { diff --git a/src/vs/workbench/parts/logs/electron-browser/logsActions.ts b/src/vs/workbench/parts/logs/electron-browser/logsActions.ts index bfaf241b326..d0f67b0ace2 100644 --- a/src/vs/workbench/parts/logs/electron-browser/logsActions.ts +++ b/src/vs/workbench/parts/logs/electron-browser/logsActions.ts @@ -60,7 +60,7 @@ export class SetLogLevelAction extends Action { }); } - private getDescription(level: LogLevel, current: LogLevel): string { + private getDescription(level: LogLevel, current: LogLevel): string | undefined { if (DEFAULT_LOG_LEVEL === level && current === level) { return nls.localize('default and current', "Default & Current"); } diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.ts b/src/vs/workbench/parts/markers/electron-browser/markers.ts index 0ff5025eb6e..e0c98d0c1b1 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.ts @@ -65,4 +65,4 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb } } -registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService); \ No newline at end of file +registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService, true); diff --git a/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts b/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts index 62d121cab32..13b739e57b7 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts @@ -27,12 +27,12 @@ class MarkersDecorationsProvider implements IDecorationsProvider { this.onDidChange = _markerService.onMarkerChanged; } - provideDecorations(resource: URI): IDecorationData { + provideDecorations(resource: URI): IDecorationData | undefined { let markers = this._markerService.read({ resource, severities: MarkerSeverity.Error | MarkerSeverity.Warning }); - let first: IMarker; + let first: IMarker | undefined; for (const marker of markers) { if (!first || marker.severity > first.severity) { first = marker; diff --git a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts index 68eeff1f910..a3e554d21df 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts @@ -11,6 +11,7 @@ import { groupBy, flatten, isFalsyOrEmpty } from 'vs/base/common/arrays'; import { values } from 'vs/base/common/map'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; +import { Hasher } from 'vs/base/common/hash'; function compareUris(a: URI, b: URI) { const astr = a.toString(); @@ -48,6 +49,13 @@ export class ResourceMarkers { @memoize get name(): string { return paths.basename(this.resource.fsPath); } + @memoize + get hash(): string { + const hasher = new Hasher(); + hasher.hash(this.resource.toString()); + return `${hasher.value}`; + } + constructor(readonly resource: URI, readonly markers: Marker[]) { } } @@ -56,6 +64,17 @@ export class Marker { get resource(): URI { return this.marker.resource; } get range(): IRange { return this.marker; } + @memoize + get hash(): string { + const hasher = new Hasher(); + hasher.hash(this.resource.toString()); + hasher.hash(this.marker.startLineNumber); + hasher.hash(this.marker.startColumn); + hasher.hash(this.marker.endLineNumber); + hasher.hash(this.marker.endColumn); + return `${hasher.value}`; + } + constructor( readonly marker: IMarker, readonly relatedInformation: RelatedInformation[] = [] @@ -72,7 +91,27 @@ export class Marker { export class RelatedInformation { - constructor(readonly raw: IRelatedInformation) { } + @memoize + get hash(): string { + const hasher = new Hasher(); + hasher.hash(this.resource.toString()); + hasher.hash(this.marker.startLineNumber); + hasher.hash(this.marker.startColumn); + hasher.hash(this.marker.endLineNumber); + hasher.hash(this.marker.endColumn); + hasher.hash(this.raw.resource.toString()); + hasher.hash(this.raw.startLineNumber); + hasher.hash(this.raw.startColumn); + hasher.hash(this.raw.endLineNumber); + hasher.hash(this.raw.endColumn); + return `${hasher.value}`; + } + + constructor( + private resource: URI, + private marker: IMarker, + readonly raw: IRelatedInformation + ) { } } export class MarkersModel { @@ -110,7 +149,7 @@ export class MarkersModel { if (rawMarker.relatedInformation) { const groupedByResource = groupBy(rawMarker.relatedInformation, compareMarkersByUri); groupedByResource.sort((a, b) => compareUris(a[0].resource, b[0].resource)); - relatedInformation = flatten(groupedByResource).map(r => new RelatedInformation(r)); + relatedInformation = flatten(groupedByResource).map(r => new RelatedInformation(resource, rawMarker, r)); } return new Marker(rawMarker, relatedInformation); diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 1a9f0656dd7..0e53bca9dc2 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -108,7 +108,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.setCurrentActiveEditor(); } - public create(parent: HTMLElement): Promise { + public create(parent: HTMLElement): void { super.create(parent); this.rangeHighlightDecorations = this._register(this.instantiationService.createInstance(RangeHighlightDecorations)); @@ -129,8 +129,6 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.onDidBlur(() => this.panelFoucusContextKey.set(false)); this.render(); - - return Promise.resolve(null); } public getTitle(): string { @@ -159,18 +157,16 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } } - public setVisible(visible: boolean): Promise { + public setVisible(visible: boolean): void { const wasVisible = this.isVisible(); - return super.setVisible(visible) - .then(() => { - if (this.isVisible()) { - if (!wasVisible) { - this.refreshPanel(); - } - } else { - this.rangeHighlightDecorations.removeHighlightRange(); - } - }); + super.setVisible(visible); + if (this.isVisible()) { + if (!wasVisible) { + this.refreshPanel(); + } + } else { + this.rangeHighlightDecorations.removeHighlightRange(); + } } public getActions(): IAction[] { @@ -225,6 +221,9 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.filter.options = new FilterOptions(this.filterAction.filterText, excludeExpression); this.tree.refilter(); this._onDidFilter.fire(); + + const { total, filtered } = this.getFilterStats(); + dom.toggleClass(this.treeContainer, 'hidden', total > 0 && filtered === 0); this.renderMessage(); } @@ -298,7 +297,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { renderers, { filter: this.filter, - accessibilityProvider + accessibilityProvider, + identityProvider: (element: TreeElement) => element.hash } ) as any as WorkbenchObjectTree; @@ -552,21 +552,23 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { e.browserEvent.preventDefault(); e.browserEvent.stopPropagation(); - this.contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, - getActions: () => TPromise.wrap(this._getMenuActions(e.element.element)), - getActionItem: (action) => { - const keybinding = this.keybindingService.lookupKeybinding(action.id); - if (keybinding) { - return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + this._getMenuActions(e.element.element).then(actions => { + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => actions, + getActionItem: (action) => { + const keybinding = this.keybindingService.lookupKeybinding(action.id); + if (keybinding) { + return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + } + return null; + }, + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + this.tree.domFocus(); + } } - return null; - }, - onHide: (wasCancelled?: boolean) => { - if (wasCancelled) { - this.tree.domFocus(); - } - } + }); }); } @@ -653,4 +655,4 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { super.dispose(); this.tree.dispose(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts index b91f4ed2702..3cd2f27c7ce 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Action, IAction, IActionChangeEvent } from 'vs/base/common/actions'; import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import Messages from 'vs/workbench/parts/markers/electron-browser/messages'; @@ -70,7 +70,8 @@ export class ShowProblemsPanelAction extends Action { } public run(): TPromise { - return this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true); + this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true); + return Promise.resolve(null); } } @@ -147,7 +148,7 @@ export class MarkersFilterActionItem extends BaseActionItem { ) { super(null, action); this.focusContextKey = Constants.MarkerPanelFilterFocusContextKey.bindTo(contextKeyService); - this.delayedFilterUpdate = new Delayer(500); + this.delayedFilterUpdate = new Delayer(200); this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); } @@ -189,9 +190,9 @@ export class MarkersFilterActionItem extends BaseActionItem { this.filterInputBox.value = this.action.filterText; } })); - this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, 'keydown', (keyboardEvent) => this.onInputKeyDown(keyboardEvent, this.filterInputBox))); - this._register(DOM.addStandardDisposableListener(container, 'keydown', this.handleKeyboardEvent)); - this._register(DOM.addStandardDisposableListener(container, 'keyup', this.handleKeyboardEvent)); + this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.KEY_DOWN, e => this.onInputKeyDown(e, this.filterInputBox))); + this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_DOWN, this.handleKeyboardEvent)); + this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_UP, this.handleKeyboardEvent)); const focusTracker = this._register(DOM.trackFocus(this.filterInputBox.inputElement)); this._register(focusTracker.onDidFocus(() => this.focusContextKey.set(true))); @@ -260,28 +261,25 @@ export class MarkersFilterActionItem extends BaseActionItem { } // Action toolbar is swallowing some keys for action items which should not be for an input box - private handleKeyboardEvent(e: IKeyboardEvent) { - switch (e.keyCode) { - case KeyCode.Space: - case KeyCode.LeftArrow: - case KeyCode.RightArrow: - case KeyCode.Escape: - e.stopPropagation(); - break; + private handleKeyboardEvent(event: StandardKeyboardEvent) { + if (event.equals(KeyCode.Space) + || event.equals(KeyCode.LeftArrow) + || event.equals(KeyCode.RightArrow) + || event.equals(KeyCode.Escape) + ) { + event.stopPropagation(); } } - private onInputKeyDown(keyboardEvent: IKeyboardEvent, filterInputBox: HistoryInputBox) { + private onInputKeyDown(event: StandardKeyboardEvent, filterInputBox: HistoryInputBox) { let handled = false; - switch (keyboardEvent.keyCode) { - case KeyCode.Escape: - filterInputBox.value = ''; - handled = true; - break; + if (event.equals(KeyCode.Escape)) { + filterInputBox.value = ''; + handled = true; } if (handled) { - keyboardEvent.stopPropagation(); - keyboardEvent.preventDefault(); + event.stopPropagation(); + event.preventDefault(); } } @@ -412,10 +410,11 @@ export class QuickFixActionItem extends ActionItem { public onClick(event: DOM.EventLike): void { DOM.EventHelper.stop(event, true); const elementPosition = DOM.getDomNodePagePosition(this.element); - this.contextMenuService.showContextMenu({ - getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height }), - getActions: () => TPromise.wrap((this.getAction()).getQuickFixActions()), + (this.getAction()).getQuickFixActions().then(actions => { + this.contextMenuService.showContextMenu({ + getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height }), + getActions: () => actions + }); }); } - -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index f45304ded7f..3558f4b9c81 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -221,9 +221,9 @@ export class MarkerRenderer implements ITreeRenderer { options = new FilterOptions(); - filter(element: TreeElement): TreeFilterResult { + filter(element: TreeElement, parentVisibility: TreeVisibility): TreeFilterResult { if (element instanceof ResourceMarkers) { return this.filterResourceMarkers(element); } else if (element instanceof Marker) { - return this.filterMarker(element); + return this.filterMarker(element, parentVisibility); } else { - return this.filterRelatedInformation(element); + return this.filterRelatedInformation(element, parentVisibility); } } @@ -347,20 +347,20 @@ export class Filter implements ITreeFilter { return false; } - if (this.options.includePattern && this.options.includePattern(resourceMarkers.resource.fsPath)) { - return true; - } - const uriMatches = FilterOptions._filter(this.options.textFilter, paths.basename(resourceMarkers.resource.fsPath)); if (this.options.textFilter && uriMatches) { return { visibility: true, data: { type: FilterDataType.ResourceMarkers, uriMatches } }; } + if (this.options.includePattern && this.options.includePattern(resourceMarkers.resource.fsPath)) { + return true; + } + return TreeVisibility.Recurse; } - private filterMarker(marker: Marker): TreeFilterResult { + private filterMarker(marker: Marker, parentVisibility: TreeVisibility): TreeFilterResult { if (this.options.filterErrors && MarkerSeverity.Error === marker.marker.severity) { return true; } @@ -385,10 +385,10 @@ export class Filter implements ITreeFilter { return { visibility: true, data: { type: FilterDataType.Marker, messageMatches: messageMatches || [], sourceMatches: sourceMatches || [], codeMatches: codeMatches || [] } }; } - return TreeVisibility.Recurse; + return parentVisibility; } - private filterRelatedInformation(relatedInformation: RelatedInformation): TreeFilterResult { + private filterRelatedInformation(relatedInformation: RelatedInformation, parentVisibility: TreeVisibility): TreeFilterResult { if (!this.options.textFilter) { return true; } @@ -400,6 +400,6 @@ export class Filter implements ITreeFilter { return { visibility: true, data: { type: FilterDataType.RelatedInformation, uriMatches: uriMatches || [], messageMatches: messageMatches || [] } }; } - return false; + return parentVisibility; } } \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/electron-browser/messages.ts b/src/vs/workbench/parts/markers/electron-browser/messages.ts index 50e2086b586..93cf9007d64 100644 --- a/src/vs/workbench/parts/markers/electron-browser/messages.ts +++ b/src/vs/workbench/parts/markers/electron-browser/messages.ts @@ -18,7 +18,6 @@ export default class Messages { public static PROBLEMS_PANEL_CONFIGURATION_AUTO_REVEAL: string = nls.localize('problems.panel.configuration.autoreveal', "Controls whether Problems view should automatically reveal files when opening them."); public static MARKERS_PANEL_TITLE_PROBLEMS: string = nls.localize('markers.panel.title.problems', "Problems"); - public static MARKERS_PANEL_ARIA_LABEL_PROBLEMS_TREE: string = nls.localize('markers.panel.aria.label.problems.tree', "Problems grouped by files"); public static MARKERS_PANEL_NO_PROBLEMS_BUILT: string = nls.localize('markers.panel.no.problems.build', "No problems have been detected in the workspace so far."); public static MARKERS_PANEL_NO_PROBLEMS_FILTERS: string = nls.localize('markers.panel.no.problems.filters', "No results found with provided filter criteria."); diff --git a/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts b/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts index 4f5cc884e91..0b6b6ce606f 100644 --- a/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts +++ b/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts @@ -120,7 +120,7 @@ suite('MarkersModel Test', () => { const testObject = new Marker(marker, null); // hack - (testObject as any).relatedInformation = marker.relatedInformation.map(r => new RelatedInformation(r)); + (testObject as any).relatedInformation = marker.relatedInformation.map(r => new RelatedInformation(marker.resource, marker, r)); assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path, relatedInformation: marker.relatedInformation.map(r => ({ ...r, resource: r.resource.path })) }, null, '\t'), testObject.toString()); }); diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index 1f7629cf220..a47b0dd36fa 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -21,7 +21,6 @@ import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LRUCache } from 'vs/base/common/map'; import { escape } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import 'vs/css!./outlinePanel'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -31,7 +30,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; -import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; +import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry'; import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -383,12 +382,12 @@ export class OutlinePanel extends ViewletPanel { } } - setVisible(visible: boolean): TPromise { + setVisible(visible: boolean): void { if (visible && this.isExpanded() && !this._requestOracle) { // workaround for https://github.com/Microsoft/vscode/issues/60011 this.setExpanded(true); } - return super.setVisible(visible); + super.setVisible(visible); } setExpanded(expanded: boolean): void { @@ -700,7 +699,13 @@ export class OutlinePanel extends ViewletPanel { lineNumber: selection.selectionStartLineNumber, column: selection.selectionStartColumn }, first instanceof OutlineElement ? first : undefined); - if (item) { + if (!item) { + // nothing to reveal + return; + } + let top = this._tree.getRelativeTop(item); + if (top < 0 || top > 1) { + // only when outside view port await this._tree.reveal(item, .5); this._tree.setFocus(item, this); this._tree.setSelection([item], this); diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts index 54abf9b78b9..287425bc6db 100644 --- a/src/vs/workbench/parts/output/browser/outputActions.ts +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -122,7 +122,7 @@ export class SwitchOutputActionItem extends SelectActionItem { @IThemeService themeService: IThemeService, @IContextViewService contextViewService: IContextViewService ) { - super(null, action, [], 0, contextViewService, { ariaLabel: nls.localize('outputs', 'Outputs') }); + super(null, action, [], 0, contextViewService, { ariaLabel: nls.localize('outputChannels', 'Output Channels.') }); let outputChannelRegistry = Registry.as(OutputExt.OutputChannels); this.toDispose.push(outputChannelRegistry.onDidRegisterChannel(() => this.updateOtions(this.outputService.getActiveChannel().id))); diff --git a/src/vs/workbench/parts/output/common/outputLinkComputer.ts b/src/vs/workbench/parts/output/common/outputLinkComputer.ts index 081ab8a0da7..72f5ed65a19 100644 --- a/src/vs/workbench/parts/output/common/outputLinkComputer.ts +++ b/src/vs/workbench/parts/output/common/outputLinkComputer.ts @@ -17,7 +17,7 @@ export interface ICreateData { } export interface IResourceCreator { - toResource: (folderRelativePath: string) => URI; + toResource: (folderRelativePath: string) => URI | null; } export class OutputLinkComputer { @@ -43,7 +43,7 @@ export class OutputLinkComputer { }); } - private getModel(uri: string): IMirrorModel { + private getModel(uri: string): IMirrorModel | null { const models = this.ctx.getMirrorModels(); for (let i = 0; i < models.length; i++) { const model = models[i]; @@ -58,7 +58,7 @@ export class OutputLinkComputer { public computeLinks(uri: string): Promise { const model = this.getModel(uri); if (!model) { - return void 0; + return Promise.resolve([]); } const links: ILink[] = []; @@ -67,7 +67,7 @@ export class OutputLinkComputer { // For each workspace root patterns this.patterns.forEach((folderPatterns, folderUri) => { const resourceCreator: IResourceCreator = { - toResource: (folderRelativePath: string): URI => { + toResource: (folderRelativePath: string): URI | null => { if (typeof folderRelativePath === 'string') { return resources.joinPath(folderUri, folderRelativePath); } @@ -130,15 +130,18 @@ export class OutputLinkComputer { patterns.forEach(pattern => { pattern.lastIndex = 0; // the holy grail of software development - let match: RegExpExecArray; + let match: RegExpExecArray | null; let offset = 0; while ((match = pattern.exec(line)) !== null) { // Convert the relative path information to a resource that we can use in links const folderRelativePath = strings.rtrim(match[1], '.').replace(/\\/g, '/'); // remove trailing "." that likely indicate end of sentence - let resource: string; + let resourceString: string | undefined; try { - resource = resourceCreator.toResource(folderRelativePath).toString(); + const resource = resourceCreator.toResource(folderRelativePath); + if (resource) { + resourceString = resource.toString(); + } } catch (error) { continue; // we might find an invalid URI and then we dont want to loose all other links } @@ -149,9 +152,9 @@ export class OutputLinkComputer { if (match[5]) { const columnNumber = match[5]; - resource = strings.format('{0}#{1},{2}', resource, lineNumber, columnNumber); + resourceString = strings.format('{0}#{1},{2}', resourceString, lineNumber, columnNumber); } else { - resource = strings.format('{0}#{1}', resource, lineNumber); + resourceString = strings.format('{0}#{1}', resourceString, lineNumber); } } @@ -173,7 +176,7 @@ export class OutputLinkComputer { links.push({ range: linkRange, - url: resource + url: resourceString }); } }); diff --git a/src/vs/workbench/parts/output/common/outputLinkProvider.ts b/src/vs/workbench/parts/output/common/outputLinkProvider.ts index de9ae22b121..63437034c96 100644 --- a/src/vs/workbench/parts/output/common/outputLinkProvider.ts +++ b/src/vs/workbench/parts/output/common/outputLinkProvider.ts @@ -17,7 +17,7 @@ export class OutputLinkProvider { private static readonly DISPOSE_WORKER_TIME = 3 * 60 * 1000; // dispose worker after 3 minutes of inactivity - private worker: MonacoWebWorker; + private worker?: MonacoWebWorker; private disposeWorkerScheduler: RunOnceScheduler; private linkProviderRegistration: IDisposable; @@ -83,7 +83,7 @@ export class OutputLinkProvider { private disposeWorker(): void { if (this.worker) { this.worker.dispose(); - this.worker = null; + this.worker = undefined; } } } diff --git a/src/vs/workbench/parts/output/electron-browser/outputServices.ts b/src/vs/workbench/parts/output/electron-browser/outputServices.ts index f7575b08745..b5d296a91b2 100644 --- a/src/vs/workbench/parts/output/electron-browser/outputServices.ts +++ b/src/vs/workbench/parts/output/electron-browser/outputServices.ts @@ -131,7 +131,7 @@ abstract class AbstractFileOutputChannel extends Disposable implements OutputCha if (this.model) { this.model.setValue(content); } else { - this.model = this.modelService.createModel(content, this.modeService.getOrCreateMode(this.mimeType), this.modelUri); + this.model = this.modelService.createModel(content, this.modeService.create(this.mimeType), this.modelUri); this.onModelCreated(this.model); const disposables: IDisposable[] = []; disposables.push(this.model.onWillDispose(() => { @@ -461,7 +461,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo } this._register(registry.onDidRegisterChannel(this.onDidRegisterChannel, this)); - this._register(panelService.onDidPanelOpen(this.onDidPanelOpen, this)); + this._register(panelService.onDidPanelOpen(({ panel, focus }) => this.onDidPanelOpen(panel, !focus), this)); this._register(panelService.onDidPanelClose(this.onDidPanelClose, this)); // Set active channel to first channel if not set @@ -492,12 +492,12 @@ export class OutputService extends Disposable implements IOutputService, ITextMo } this.activeChannel = channel; - let promise: TPromise = TPromise.as(null); + let promise: TPromise; if (this.isPanelShown()) { - this.doShowChannel(channel, preserveFocus); + promise = this.doShowChannel(channel, preserveFocus); } else { - promise = this.panelService.openPanel(OUTPUT_PANEL_ID) - .then(() => this.doShowChannel(this.activeChannel, preserveFocus)); + this.panelService.openPanel(OUTPUT_PANEL_ID); + promise = this.doShowChannel(this.activeChannel, preserveFocus); } return promise.then(() => this._onActiveOutputChannel.fire(id)); } @@ -519,16 +519,16 @@ export class OutputService extends Disposable implements IOutputService, ITextMo this.channels.set(channelId, channel); if (this.activeChannelIdInStorage === channelId) { this.activeChannel = channel; - this.onDidPanelOpen(this.panelService.getActivePanel()) + this.onDidPanelOpen(this.panelService.getActivePanel(), true) .then(() => this._onActiveOutputChannel.fire(channelId)); } } - private onDidPanelOpen(panel: IPanel): Thenable { + private onDidPanelOpen(panel: IPanel, preserveFocus: boolean): Thenable { if (panel && panel.getId() === OUTPUT_PANEL_ID) { this._outputPanel = this.panelService.getActivePanel(); if (this.activeChannel) { - return this.doShowChannel(this.activeChannel, true); + return this.doShowChannel(this.activeChannel, preserveFocus); } } return TPromise.as(null); @@ -744,7 +744,7 @@ class BufferredOutputChannel extends Disposable implements OutputChannel { } private createModel(content: string): ITextModel { - const model = this.modelService.createModel(content, this.modeService.getOrCreateMode(OUTPUT_MIME), URI.from({ scheme: OUTPUT_SCHEME, path: this.id })); + const model = this.modelService.createModel(content, this.modeService.create(OUTPUT_MIME), URI.from({ scheme: OUTPUT_SCHEME, path: this.id })); const disposables: IDisposable[] = []; disposables.push(model.onWillDispose(() => { this.model = null; diff --git a/src/vs/workbench/parts/performance/electron-browser/actions.ts b/src/vs/workbench/parts/performance/electron-browser/actions.ts index 66a220c34a2..ca7faa0975a 100644 --- a/src/vs/workbench/parts/performance/electron-browser/actions.ts +++ b/src/vs/workbench/parts/performance/electron-browser/actions.ts @@ -33,6 +33,9 @@ class Info { table['window.loadUrl() => begin to require(workbench.main.js)'] = new Info(metrics.timers.ellapsedWindowLoadToRequire, '[main->renderer]', StartupKindToString(metrics.windowKind)); table['require(workbench.main.js)'] = new Info(metrics.timers.ellapsedRequire, '[renderer]', `cached data: ${(metrics.didUseCachedData ? 'YES' : 'NO')}${nodeModuleLoadTime ? `, node_modules took ${nodeModuleLoadTime}ms` : ''}`); + table['require workspace storage'] = new Info(metrics.timers.ellapsedWorkspaceStorageRequire, '[renderer]'); + table['require & init workspace storage'] = new Info(metrics.timers.ellapsedWorkspaceStorageInit, '[renderer]'); + table['register extensions & spawn extension host'] = new Info(metrics.timers.ellapsedExtensions, '[renderer]'); table['restore viewlet'] = new Info(metrics.timers.ellapsedViewletRestore, '[renderer]', metrics.viewletId); table['restore panel'] = new Info(metrics.timers.ellapsedPanelRestore, '[renderer]', metrics.panelId); @@ -262,29 +265,27 @@ export class ReportPerformanceIssueAction extends Action { } - const osVersion = `${os.type()} ${os.arch()} ${os.release()} `; + const osVersion = `${os.type()} ${os.arch()} ${os.release()}`; const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&'; const body = encodeURIComponent( `- VSCode Version: ${name} ${version} ${isPure ? '' : ' **[Unsupported]**'} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'}) - - OS Version: ${ osVersion} - - CPUs: ${ metrics.cpus.model} (${metrics.cpus.count} x ${metrics.cpus.speed}) - - Memory(System): ${ (metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)} GB(${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free) < /code> - - Memory(Process): ${ (metrics.meminfo.workingSetSize / 1024).toFixed(2)} MB working set(${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared) < /code> - - Load(avg): ${ metrics.loadavg.map(l => Math.round(l)).join(', ')} - - VM: ${ metrics.isVMLikelyhood}% - - Initial Startup: ${ metrics.initialStartup ? 'yes' : 'no'} - - Screen Reader: ${ metrics.hasAccessibilitySupport ? 'yes' : 'no'} - - Empty Workspace: ${ metrics.emptyWorkbench ? 'yes' : 'no'} - - Timings: - -${ this.generatePerformanceTable(metrics, nodeModuleLoadTime)} +- OS Version: ${ osVersion} +- CPUs: ${ metrics.cpus.model} (${metrics.cpus.count} x ${metrics.cpus.speed}) +- Memory(System): ${ (metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)} GB(${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free) +- Memory(Process): ${ (metrics.meminfo.workingSetSize / 1024).toFixed(2)} MB working set(${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared) +- Load(avg): ${ metrics.loadavg.map(l => Math.round(l)).join(', ')} +- VM: ${ metrics.isVMLikelyhood}% +- Initial Startup: ${ metrics.initialStartup ? 'yes' : 'no'} +- Screen Reader: ${ metrics.hasAccessibilitySupport ? 'yes' : 'no'} +- Empty Workspace: ${ metrics.emptyWorkbench ? 'yes' : 'no'} +- Timings: +${this.generatePerformanceTable(metrics, nodeModuleLoadTime)} --- - ${ appendix} ` - ); +${appendix}`); - return `${baseUrl} ${queryStringPrefix} body = ${body} `; + return `${baseUrl}${queryStringPrefix}body=${body}`; } private generatePerformanceTable(metrics: IStartupMetrics, nodeModuleLoadTime?: number): string { 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 ee89b788990..9fbd7234073 100644 --- a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts +++ b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts @@ -5,5 +5,4 @@ import './startupProfiler'; import './startupTimings'; -import './startupTimingsAppender'; import './stats'; diff --git a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts index baccbedf783..2a1d9bd4a3d 100644 --- a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts @@ -17,6 +17,10 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ITimerService, didUseCachedData } from 'vs/workbench/services/timer/electron-browser/timerService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import product from 'vs/platform/node/product'; +import { timeout, nfcall } from 'vs/base/common/async'; +import { appendFile } from 'fs'; class StartupTimings implements IWorkbenchContribution { @@ -30,13 +34,21 @@ class StartupTimings implements IWorkbenchContribution { @ITelemetryService private readonly _telemetryService: ITelemetryService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @IUpdateService private readonly _updateService: IUpdateService, + @IEnvironmentService private readonly _envService: IEnvironmentService, ) { - - this._reportVariedStartupTimes().then(undefined, onUnexpectedError); - this._reportStandardStartupTimes().then(undefined, onUnexpectedError); + // + this._report().catch(onUnexpectedError); } - private async _reportVariedStartupTimes(): Promise { + private async _report() { + const isStandardStartup = await this._isStandardStartup(); + this._reportStartupTimes(isStandardStartup).catch(onUnexpectedError); + this._appendStartupTimes(isStandardStartup).catch(onUnexpectedError); + } + + private async _reportStartupTimes(isStandardStartup: boolean): Promise { + const metrics = await this._timerService.startupMetrics; + /* __GDPR__ "startupTimeVaried" : { "${include}": [ @@ -44,45 +56,7 @@ class StartupTimings implements IWorkbenchContribution { ] } */ - this._telemetryService.publicLog('startupTimeVaried', await this._timerService.startupMetrics); - } - - private async _reportStandardStartupTimes(): Promise { - // check for standard startup: - // * new window (no reload) - // * just one window - // * explorer viewlet visible - // * one text editor (not multiple, not webview, welcome etc...) - // * cached data present (not rejected, not created) - if (this._lifecycleService.startupKind !== StartupKind.NewWindow) { - this._logService.info('no standard startup: not a new window'); - return; - } - if (await this._windowsService.getWindowCount() !== 1) { - this._logService.info('no standard startup: not just one window'); - return; - } - if (!this._viewletService.getActiveViewlet() || this._viewletService.getActiveViewlet().getId() !== files.VIEWLET_ID) { - this._logService.info('no standard startup: not the explorer viewlet'); - return; - } - const visibleControls = this._editorService.visibleControls; - if (visibleControls.length !== 1 || !isCodeEditor(visibleControls[0].getControl())) { - this._logService.info('no standard startup: not just one text editor'); - return; - } - if (this._panelService.getActivePanel()) { - this._logService.info('no standard startup: panel is active'); - return; - } - if (!didUseCachedData()) { - this._logService.info('no standard startup: not using cached data'); - return; - } - if (!await this._updateService.isLatestVersion()) { - this._logService.info('no standard startup: not running latest version'); - return; - } + this._telemetryService.publicLog('startupTimeVaried', metrics); /* __GDPR__ "startupTime" : { @@ -91,11 +65,76 @@ class StartupTimings implements IWorkbenchContribution { ] } */ - const metrics = await this._timerService.startupMetrics; this._telemetryService.publicLog('startupTime', metrics); - this._logService.info('standard startup', metrics); + } + + private async _appendStartupTimes(isStandardStartup: boolean) { + let appendTo = this._envService.args['prof-append-timers']; + if (!appendTo) { + // nothing to do + return; + } + + const waitWhenNoCachedData: () => Promise = () => { + // wait 15s for cached data to be produced + return !didUseCachedData() + ? timeout(15000) + : Promise.resolve(); + }; + + Promise.all([ + this._timerService.startupMetrics, + waitWhenNoCachedData(), + ]).then(([startupMetrics]) => { + return nfcall(appendFile, appendTo, `${startupMetrics.ellapsed}\t${product.nameLong}\t${product.commit || '0000000'}\t${isStandardStartup ? 'standard_start' : 'NOT_standard_start'}\n`); + }).then(() => { + this._windowsService.quit(); + }).catch(err => { + console.error(err); + this._windowsService.quit(); + }); + } + + private async _isStandardStartup(): Promise { + // check for standard startup: + // * new window (no reload) + // * just one window + // * explorer viewlet visible + // * one text editor (not multiple, not webview, welcome etc...) + // * cached data present (not rejected, not created) + if (this._lifecycleService.startupKind !== StartupKind.NewWindow) { + this._logService.info('no standard startup: not a new window'); + return false; + } + if (await this._windowsService.getWindowCount() !== 1) { + this._logService.info('no standard startup: not just one window'); + return false; + } + if (!this._viewletService.getActiveViewlet() || this._viewletService.getActiveViewlet().getId() !== files.VIEWLET_ID) { + this._logService.info('no standard startup: not the explorer viewlet'); + return false; + } + const visibleControls = this._editorService.visibleControls; + if (visibleControls.length !== 1 || !isCodeEditor(visibleControls[0].getControl())) { + this._logService.info('no standard startup: not just one text editor'); + return false; + } + if (this._panelService.getActivePanel()) { + this._logService.info('no standard startup: panel is active'); + return false; + } + if (!didUseCachedData()) { + this._logService.info('no standard startup: not using cached data'); + return false; + } + if (!await this._updateService.isLatestVersion()) { + this._logService.info('no standard startup: not running latest version'); + return false; + } + this._logService.info('standard startup'); + return true; } } const registry = Registry.as(Extensions.Workbench); -registry.registerWorkbenchContribution(StartupTimings, LifecyclePhase.Running); +registry.registerWorkbenchContribution(StartupTimings, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/parts/performance/electron-browser/startupTimingsAppender.ts b/src/vs/workbench/parts/performance/electron-browser/startupTimingsAppender.ts deleted file mode 100644 index 4b70f807595..00000000000 --- a/src/vs/workbench/parts/performance/electron-browser/startupTimingsAppender.ts +++ /dev/null @@ -1,53 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions } from 'vs/workbench/common/contributions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ITimerService, didUseCachedData } from 'vs/workbench/services/timer/electron-browser/timerService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { nfcall, timeout } from 'vs/base/common/async'; -import { appendFile, } from 'fs'; -import product from 'vs/platform/node/product'; - -class StartupTimingsAppender implements IWorkbenchContribution { - - constructor( - @ITimerService timerService: ITimerService, - @IWindowsService windowsService: IWindowsService, - @ILifecycleService lifecycleService: ILifecycleService, - @IEnvironmentService environmentService: IEnvironmentService, - ) { - - let appendTo = environmentService.args['prof-append-timers']; - if (!appendTo) { - // nothing to do - return; - } - - Promise.all([ - timerService.startupMetrics, - this._waitWhenNoCachedData(), - ]).then(([startupMetrics]) => { - return nfcall(appendFile, appendTo, `${startupMetrics.ellapsed}\t${product.nameLong}\t${product.commit || '0000000'}\n`); - }).then(() => { - windowsService.quit(); - }).catch(err => { - console.error(err); - windowsService.quit(); - }); - } - - private _waitWhenNoCachedData(): Promise { - // wait 15s for cached data to be produced - return !didUseCachedData() - ? timeout(15000) - : Promise.resolve(); - } -} - -const registry = Registry.as(Extensions.Workbench); -registry.registerWorkbenchContribution(StartupTimingsAppender, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts b/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts index 189020b8c0c..b975a47e9bd 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts @@ -6,7 +6,6 @@ import 'vs/css!./media/keybindings'; import * as nls from 'vs/nls'; import { OS } from 'vs/base/common/platform'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Disposable, dispose, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; @@ -180,9 +179,9 @@ export class DefineKeybindingWidget extends Widget { return this._domNode.domNode; } - define(): TPromise { + define(): Promise { this._keybindingInputWidget.clear(); - return new TPromise((c, e) => { + return new Promise((c) => { if (!this._isVisible) { this._isVisible = true; this._domNode.setDisplay('block'); @@ -331,7 +330,7 @@ export class DefineKeybindingOverlayWidget extends Disposable implements IOverla super.dispose(); } - public start(): TPromise { + public start(): Promise { this._editor.revealPositionInCenterIfOutsideViewport(this._editor.getPosition(), ScrollType.Smooth); const layoutInfo = this._editor.getLayoutInfo(); this._widget.layout(new dom.Dimension(layoutInfo.width, layoutInfo.height)); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index 021e1fb525d..d6dc44341ec 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -45,6 +45,7 @@ import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/common import { CancellationToken } from 'vs/base/common/cancellation'; import { attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; let $ = DOM.$; @@ -91,7 +92,8 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor @IClipboardService private clipboardService: IClipboardService, @IInstantiationService private instantiationService: IInstantiationService, @IEditorService private editorService: IEditorService, - @IStorageService storageService: IStorageService + @IStorageService storageService: IStorageService, + @IPreferencesService private preferencesService: IPreferencesService ) { super(KeybindingsEditor.ID, telemetryService, themeService, storageService); this.delayedFiltering = new Delayer(300); @@ -346,6 +348,8 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor })); this.actionBar.push([this.recordKeysAction, this.sortByPrecedenceAction, clearInputAction], { label: false, icon: true }); + + this.createOpenKeybindingsElement(this.headerContainer); } private createRecordingBadge(container: HTMLElement): HTMLElement { @@ -365,6 +369,24 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor return recordingBadge; } + private createOpenKeybindingsElement(parent: HTMLElement): void { + const openKeybindingsContainer = DOM.append(parent, $('.open-keybindings-container')); + DOM.append(openKeybindingsContainer, $('', null, localize('header-message', "For advanced customizations open and edit"))); + const fileElement = DOM.append(openKeybindingsContainer, $('.file-name', null, localize('keybindings-file-name', "keybindings.json"))); + fileElement.tabIndex = 0; + this._register(DOM.addDisposableListener(fileElement, DOM.EventType.CLICK, () => this.preferencesService.openGlobalKeybindingSettings(true))); + this._register(DOM.addDisposableListener(fileElement, DOM.EventType.KEY_UP, e => { + let keyboardEvent = new StandardKeyboardEvent(e); + switch (keyboardEvent.keyCode) { + case KeyCode.Enter: + this.preferencesService.openGlobalKeybindingSettings(true); + keyboardEvent.preventDefault(); + keyboardEvent.stopPropagation(); + return; + } + })); + } + private layoutSearchWidget(dimension: DOM.Dimension): void { this.searchWidget.layout(dimension); DOM.toggleClass(this.headerContainer, 'small', dimension.width < 400); @@ -538,7 +560,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.selectEntry(e.element); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => TPromise.as([ + getActions: () => [ this.createCopyAction(e.element), this.createCopyCommandAction(e.element), new Separator(), @@ -546,7 +568,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.createRemoveAction(e.element), this.createResetAction(e.element), new Separator(), - this.createShowConflictsAction(e.element)]) + this.createShowConflictsAction(e.element)] }); } } @@ -787,7 +809,7 @@ class ActionsColumn extends Column { render(keybindingItemEntry: IKeybindingItemEntry): void { this.actionBar.clear(); - const actions = []; + const actions: IAction[] = []; if (keybindingItemEntry.keybindingItem.keybinding) { actions.push(this.createEditAction(keybindingItemEntry)); } else { @@ -841,15 +863,15 @@ class CommandColumn extends Column { this.commandColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); let commandLabel: HighlightedLabel; if (keybindingItem.commandLabel) { - commandLabel = new HighlightedLabel(this.commandColumn); + commandLabel = new HighlightedLabel(this.commandColumn, false); commandLabel.set(keybindingItem.commandLabel, keybindingItemEntry.commandLabelMatches); } if (keybindingItemEntry.commandDefaultLabelMatches) { - commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.command-default-label'))); + commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.command-default-label')), false); commandLabel.set(keybindingItem.commandDefaultLabel, keybindingItemEntry.commandDefaultLabelMatches); } if (keybindingItemEntry.commandIdMatches || !keybindingItem.commandLabel) { - commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.code'))); + commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.code')), false); commandLabel.set(keybindingItem.command, keybindingItemEntry.commandIdMatches); } if (commandLabel) { @@ -896,7 +918,7 @@ class SourceColumn extends Column { render(keybindingItemEntry: IKeybindingItemEntry): void { DOM.clearNode(this.sourceColumn); this.sourceColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); - new HighlightedLabel(this.sourceColumn).set(keybindingItemEntry.keybindingItem.source, keybindingItemEntry.sourceMatches); + new HighlightedLabel(this.sourceColumn, false).set(keybindingItemEntry.keybindingItem.source, keybindingItemEntry.sourceMatches); } private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { @@ -920,7 +942,7 @@ class WhenColumn extends Column { DOM.toggleClass(this.whenColumn, 'code', !!keybindingItemEntry.keybindingItem.when); DOM.toggleClass(this.whenColumn, 'empty', !keybindingItemEntry.keybindingItem.when); if (keybindingItemEntry.keybindingItem.when) { - const whenLabel = new HighlightedLabel(this.whenColumn); + const whenLabel = new HighlightedLabel(this.whenColumn, false); whenLabel.set(keybindingItemEntry.keybindingItem.when, keybindingItemEntry.whenMatches); this.whenColumn.title = keybindingItemEntry.keybindingItem.when; whenLabel.element.title = keybindingItemEntry.keybindingItem.when; diff --git a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts index db35ad97128..660f311f5dd 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -6,7 +6,7 @@ import { Action } from 'vs/base/common/actions'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import * as nls from 'vs/nls'; @@ -14,7 +14,6 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; -import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; export class OpenRawDefaultSettingsAction extends Action { @@ -30,7 +29,7 @@ export class OpenRawDefaultSettingsAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openRawDefaultSettings(); } } @@ -48,7 +47,7 @@ export class OpenSettings2Action extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openSettings(false); } } @@ -66,7 +65,7 @@ export class OpenSettingsAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openSettings(); } } @@ -84,7 +83,7 @@ export class OpenSettingsJsonAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openSettings(true); } } @@ -102,7 +101,7 @@ export class OpenGlobalSettingsAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openGlobalSettings(); } } @@ -120,7 +119,7 @@ export class OpenGlobalKeybindingsAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openGlobalKeybindingSettings(false); } } @@ -138,7 +137,7 @@ export class OpenGlobalKeybindingsFileAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openGlobalKeybindingSettings(true); } } @@ -156,7 +155,7 @@ export class OpenDefaultKeybindingsFileAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openDefaultKeybindingsFile(); } } @@ -183,7 +182,7 @@ export class OpenWorkspaceSettingsAction extends Action { this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY; } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openWorkspaceSettings(); } @@ -220,7 +219,7 @@ export class OpenFolderSettingsAction extends Action { this.enabled = this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.workspaceContextService.getWorkspace().folders.length > 0; } - public run(): TPromise { + public run(): Thenable { return this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID) .then(workspaceFolder => { if (workspaceFolder) { @@ -253,7 +252,7 @@ export class ConfigureLanguageBasedSettingsAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { const languages = this.modeService.getRegisteredLanguageNames(); const picks: IQuickPickItem[] = languages.sort().map((lang, index) => { let description: string = nls.localize('languageDescriptionConfigured', "({0})", this.modeService.getModeIdForLanguageName(lang.toLowerCase())); @@ -278,8 +277,8 @@ export class ConfigureLanguageBasedSettingsAction extends Action { return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language") }) .then(pick => { if (pick) { - return this.modeService.getOrCreateModeByLanguageName(pick.label) - .then(mode => this.preferencesService.configureSettingsForLanguage(mode.getLanguageIdentifier().language)); + const modeId = this.modeService.getModeIdForLanguageName(pick.label.toLowerCase()); + return this.preferencesService.configureSettingsForLanguage(modeId); } return undefined; }); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 3a18ea81a29..c5db416d90c 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -16,7 +16,6 @@ import { ArrayNavigator } from 'vs/base/common/iterator'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorExtensionsRegistry, IEditorContributionCtor, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -48,7 +47,7 @@ import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorMo import { PREFERENCES_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; import { DefaultSettingsRenderer, FolderSettingsRenderer, IPreferencesRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers'; import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/parts/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider, CONTEXT_SETTINGS_JSON_EDITOR } from 'vs/workbench/parts/preferences/common/preferences'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { IFilterResult, IPreferencesService, ISearchResult, ISetting, ISettingsEditorModel, ISettingsGroup, SettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; @@ -62,6 +61,7 @@ export class PreferencesEditor extends BaseEditor { public static readonly ID: string = PREFERENCES_EDITOR_ID; private defaultSettingsEditorContextKey: IContextKey; + private defaultSettingsJSONEditorContextKey: IContextKey; private searchFocusContextKey: IContextKey; private headerContainer: HTMLElement; private searchWidget: SearchWidget; @@ -99,6 +99,7 @@ export class PreferencesEditor extends BaseEditor { ) { super(PreferencesEditor.ID, telemetryService, themeService, storageService); this.defaultSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(this.contextKeyService); + this.defaultSettingsJSONEditorContextKey = CONTEXT_SETTINGS_JSON_EDITOR.bindTo(this.contextKeyService); this.searchFocusContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(this.contextKeyService); this.delayedFilterLogging = new Delayer(1000); this.localSearchDelayer = new Delayer(100); @@ -155,6 +156,7 @@ export class PreferencesEditor extends BaseEditor { public setInput(newInput: PreferencesEditorInput, options: SettingsEditorOptions, token: CancellationToken): Thenable { this.defaultSettingsEditorContextKey.set(true); + this.defaultSettingsJSONEditorContextKey.set(true); if (options && options.query) { this.focusSearch(options.query); } @@ -194,6 +196,7 @@ export class PreferencesEditor extends BaseEditor { public clearInput(): void { this.defaultSettingsEditorContextKey.set(false); + this.defaultSettingsJSONEditorContextKey.set(false); this.sideBySidePreferencesWidget.clearInput(); this.preferencesRenderers.onHidden(); super.clearInput(); @@ -204,7 +207,7 @@ export class PreferencesEditor extends BaseEditor { super.setEditorVisible(visible, group); } - private updateInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): TPromise { + private updateInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): Promise { return this.sideBySidePreferencesWidget.setInput(newInput.details, newInput.master, options, token).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { if (token.isCancellationRequested) { return void 0; @@ -230,12 +233,12 @@ export class PreferencesEditor extends BaseEditor { }); } - private triggerSearch(query: string): TPromise { + private triggerSearch(query: string): Promise { if (query) { - return TPromise.join([ + return Promise.all([ this.localSearchDelayer.trigger(() => this.preferencesRenderers.localFilterPreferences(query).then(() => { })), - this.remoteSearchThrottle.trigger(() => TPromise.wrap(this.progressService.showWhile(this.preferencesRenderers.remoteSearchPreferences(query), 500))) - ]) as TPromise; + this.remoteSearchThrottle.trigger(() => Promise.resolve(this.progressService.showWhile(this.preferencesRenderers.remoteSearchPreferences(query), 500))) + ]).then(() => { }); } else { // When clearing the input, update immediately to clear it this.localSearchDelayer.cancel(); @@ -251,8 +254,8 @@ export class PreferencesEditor extends BaseEditor { if (this.editorService.activeControl !== this) { this.focus(); } - const promise = this.input && this.input.isDirty() ? this.input.save() : TPromise.as(true); - promise.then(value => { + const promise: Thenable = this.input && this.input.isDirty() ? this.input.save() : Promise.resolve(true); + promise.then(() => { if (target === ConfigurationTarget.USER) { this.preferencesService.switchSettings(ConfigurationTarget.USER, this.preferencesService.userSettingsResource, true); } else if (target === ConfigurationTarget.WORKSPACE) { @@ -269,14 +272,14 @@ export class PreferencesEditor extends BaseEditor { this.sideBySidePreferencesWidget.setResultCount(count.target, count.count); } else if (this.searchWidget.getValue()) { if (countValue === 0) { - this.searchWidget.showMessage(nls.localize('noSettingsFound', "No Results"), countValue); + this.searchWidget.showMessage(nls.localize('noSettingsFound', "No Settings Found")); } else if (countValue === 1) { - this.searchWidget.showMessage(nls.localize('oneSettingFound', "1 Setting Found"), countValue); + this.searchWidget.showMessage(nls.localize('oneSettingFound', "1 Setting Found")); } else { - this.searchWidget.showMessage(nls.localize('settingsFound', "{0} Settings Found", countValue), countValue); + this.searchWidget.showMessage(nls.localize('settingsFound', "{0} Settings Found", countValue)); } } else { - this.searchWidget.showMessage(nls.localize('totalSettingsMessage', "Total {0} Settings", countValue), countValue); + this.searchWidget.showMessage(nls.localize('totalSettingsMessage', "Total {0} Settings", countValue)); } } @@ -438,7 +441,7 @@ class PreferencesRenderersController extends Disposable { this._prefsModelsForSearch = new Map(); } - remoteSearchPreferences(query: string, updateCurrentResults?: boolean): TPromise { + remoteSearchPreferences(query: string, updateCurrentResults?: boolean): Promise { if (this.lastFilterResult && this.lastFilterResult.exactMatch) { // Skip and clear remote search query = ''; @@ -467,7 +470,7 @@ class PreferencesRenderersController extends Disposable { }); } - localFilterPreferences(query: string, updateCurrentResults?: boolean): TPromise { + localFilterPreferences(query: string, updateCurrentResults?: boolean): Promise { if (this._settingsNavigator) { this._settingsNavigator.reset(); } @@ -476,10 +479,10 @@ class PreferencesRenderersController extends Disposable { return this.filterOrSearchPreferences(query, this._currentLocalSearchProvider, 'filterResult', nls.localize('filterResult', "Filtered Results"), 0, undefined, updateCurrentResults); } - private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken, editableContentOnly?: boolean): TPromise { + private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken, editableContentOnly?: boolean): Promise { this._lastQuery = query; - const filterPs: TPromise[] = [this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder, token)]; + const filterPs: Promise[] = [this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder, token)]; if (!editableContentOnly) { filterPs.push( this._filterOrSearchPreferences(query, this.defaultPreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder, token)); @@ -487,7 +490,7 @@ class PreferencesRenderersController extends Disposable { this.searchAllSettingsTargets(query, searchProvider, groupId, groupLabel, groupOrder, token).then(() => null)); } - return TPromise.join(filterPs).then(results => { + return Promise.all(filterPs).then(results => { let [editableFilterResult, defaultFilterResult] = results; if (!defaultFilterResult && editableContentOnly) { @@ -501,7 +504,7 @@ class PreferencesRenderersController extends Disposable { }); } - private searchAllSettingsTargets(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): TPromise { + private searchAllSettingsTargets(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { const searchPs = [ this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.WORKSPACE, groupId, groupLabel, groupOrder, token), this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.USER, groupId, groupLabel, groupOrder, token) @@ -513,7 +516,7 @@ class PreferencesRenderersController extends Disposable { } - return TPromise.join(searchPs).then(() => { }); + return Promise.all(searchPs).then(() => { }); } private searchSettingsTarget(query: string, provider: ISearchProvider, target: SettingsTarget, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { @@ -530,7 +533,7 @@ class PreferencesRenderersController extends Disposable { this._onDidFilterResultsCountChange.fire({ target, count }); }, err => { if (!isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } return null; @@ -582,9 +585,9 @@ class PreferencesRenderersController extends Disposable { } } - private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): TPromise { + private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { if (!preferencesRenderer) { - return TPromise.wrap(null); + return Promise.resolve(null); } const model = preferencesRenderer.preferencesModel; @@ -594,12 +597,12 @@ class PreferencesRenderersController extends Disposable { }); } - private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): TPromise { - const searchP = provider ? provider.searchModel(model, token) : TPromise.wrap(null); + private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { + const searchP = provider ? provider.searchModel(model, token) : Promise.resolve(null); return searchP .then(null, err => { if (isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } else { /* __GDPR__ "defaultSettings.searchError" : { @@ -845,10 +848,10 @@ class SideBySidePreferencesWidget extends Widget { this._register(focusTracker.onDidFocus(() => this._onFocus.fire())); } - public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions, token: CancellationToken): TPromise<{ defaultPreferencesRenderer?: IPreferencesRenderer, editablePreferencesRenderer?: IPreferencesRenderer }> { + public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions, token: CancellationToken): Promise<{ defaultPreferencesRenderer?: IPreferencesRenderer, editablePreferencesRenderer?: IPreferencesRenderer }> { this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput); this.settingsTargetsWidget.settingsTarget = this.getSettingsTarget(editablePreferencesEditorInput.getResource()); - return TPromise.join([ + return Promise.all([ this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.getResource(), options, token), this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options, token) ]) @@ -1076,13 +1079,13 @@ export class DefaultPreferencesEditor extends BaseTextEditor { interface ISettingsEditorContribution extends editorCommon.IEditorContribution { - updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise>; + updatePreferencesRenderer(associatedPreferencesModelUri: URI): Thenable>; } abstract class AbstractSettingsEditorContribution extends Disposable implements ISettingsEditorContribution { - private preferencesRendererCreationPromise: TPromise>; + private preferencesRendererCreationPromise: Thenable>; constructor(protected editor: ICodeEditor, @IInstantiationService protected instantiationService: IInstantiationService, @@ -1093,7 +1096,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements this._register(this.editor.onDidChangeModel(() => this._onModelChanged())); } - updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise> { + updatePreferencesRenderer(associatedPreferencesModelUri: URI): Thenable> { if (!this.preferencesRendererCreationPromise) { this.preferencesRendererCreationPromise = this._createPreferencesRenderer(); } @@ -1103,7 +1106,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements .then(changed => changed ? this._updatePreferencesRenderer(associatedPreferencesModelUri) : this.preferencesRendererCreationPromise); } - return TPromise.as(null); + return Promise.resolve(null); } protected _onModelChanged(): void { @@ -1114,13 +1117,13 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements } } - private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): TPromise { + private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): Thenable { return this.preferencesRendererCreationPromise.then(preferencesRenderer => { return !(preferencesRenderer && preferencesRenderer.getAssociatedPreferencesModel() && preferencesRenderer.getAssociatedPreferencesModel().uri.toString() === associatedPreferencesModelUri.toString()); }); } - private _updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise> { + private _updatePreferencesRenderer(associatedPreferencesModelUri: URI): Thenable> { return this.preferencesService.createPreferencesEditorModel(associatedPreferencesModelUri) .then(associatedPreferencesEditorModel => { return this.preferencesRendererCreationPromise.then(preferencesRenderer => { @@ -1148,7 +1151,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements preferencesRenderer.dispose(); } }); - this.preferencesRendererCreationPromise = TPromise.as(null); + this.preferencesRendererCreationPromise = Promise.resolve(null); } } @@ -1157,7 +1160,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements super.dispose(); } - protected abstract _createPreferencesRenderer(): TPromise>; + protected abstract _createPreferencesRenderer(): Thenable>; abstract getId(): string; } @@ -1169,7 +1172,7 @@ class DefaultSettingsEditorContribution extends AbstractSettingsEditorContributi return DefaultSettingsEditorContribution.ID; } - protected _createPreferencesRenderer(): TPromise> { + protected _createPreferencesRenderer(): Thenable> { return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri) .then(editorModel => { if (editorModel instanceof DefaultSettingsEditorModel && this.editor.getModel()) { @@ -1199,7 +1202,7 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl return SettingsEditorContribution.ID; } - protected _createPreferencesRenderer(): TPromise> { + protected _createPreferencesRenderer(): Thenable> { if (this.isSettingsModel()) { return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri) .then(settingsModel => { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 3abe2ca6ab9..5f11ee73262 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -10,7 +10,6 @@ import { Delayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { Position } from 'vs/editor/common/core/position'; @@ -828,7 +827,7 @@ class EditSettingRenderer extends Disposable { : editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => TPromise.wrap(actions) + getActions: () => actions }); } @@ -843,7 +842,7 @@ class EditSettingRenderer extends Disposable { const actions = this.getActions(this.editPreferenceWidgetForMouseMove.preferences[0], this.getConfigurationsMap()[this.editPreferenceWidgetForMouseMove.preferences[0].key]); this.contextMenuService.showContextMenu({ getAnchor: () => this.toAbsoluteCoords(new Position(startLine, 1)), - getActions: () => TPromise.wrap(actions) + getActions: () => actions }); return true; diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index 0cf3f2e83f2..97c1dd8b223 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -14,7 +14,6 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; import { ICodeEditor, IEditorMouseEvent, IViewZone, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; @@ -25,7 +24,7 @@ import { ConfigurationTarget } from 'vs/platform/configuration/common/configurat import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { activeContrastBorder, badgeBackground, badgeForeground, contrastBorder, errorForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { activeContrastBorder, badgeBackground, badgeForeground, contrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -172,6 +171,11 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { } public render() { + if (!this.settingsGroup.range) { + // #61352 + return; + } + this._afterLineNumber = this.settingsGroup.range.startLineNumber - 2; this.editor.changeViewZones(accessor => { this.id = accessor.addZone(this); @@ -411,7 +415,7 @@ export class FolderSettingsActionItem extends BaseActionItem { private showMenu(): void { this.contextMenuService.showContextMenu({ getAnchor: () => this.container, - getActions: () => TPromise.as(this.getDropdownMenuActions()), + getActions: () => this.getDropdownMenuActions(), getActionItem: () => null, onHide: () => { this.anchorElement.blur(); @@ -544,13 +548,13 @@ export class SettingsTargetsWidget extends Widget { } } - public updateTarget(settingsTarget: SettingsTarget): TPromise { + public updateTarget(settingsTarget: SettingsTarget): Promise { const isSameTarget = this.settingsTarget === settingsTarget || settingsTarget instanceof URI && this.settingsTarget instanceof URI && this.settingsTarget.toString() === settingsTarget.toString(); if (!isSameTarget) { this.settingsTarget = settingsTarget; this._onDidTargetChange.fire(this.settingsTarget); } - return TPromise.as(null); + return Promise.resolve(null); } private update(): void { @@ -609,7 +613,8 @@ export class SearchWidget extends Widget { this.countElement.style.borderStyle = border ? 'solid' : null; this.countElement.style.borderColor = border; - this.styleCountElementForeground(); + const color = this.themeService.getTheme().getColor(badgeForeground); + this.countElement.style.color = color ? color.toString() : null; })); } @@ -640,23 +645,15 @@ export class SearchWidget extends Widget { return box; } - public showMessage(message: string, count: number): void { + public showMessage(message: string): void { // Avoid setting the aria-label unnecessarily, the screenreader will read the count every time it's set, since it's aria-live:assertive. #50968 if (this.countElement && message !== this.countElement.textContent) { this.countElement.textContent = message; this.inputBox.inputElement.setAttribute('aria-label', message); - DOM.toggleClass(this.countElement, 'no-results', count === 0); this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px'; - this.styleCountElementForeground(); } } - private styleCountElementForeground() { - const colorId = DOM.hasClass(this.countElement, 'no-results') ? errorForeground : badgeForeground; - const color = this.themeService.getTheme().getColor(colorId); - this.countElement.style.color = color ? color.toString() : null; - } - public layout(dimension: DOM.Dimension) { if (dimension.width < 400) { if (this.countElement) { diff --git a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts index e98adfcb7bb..2dc95fd5a3c 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts @@ -127,7 +127,7 @@ export const tocData: ITOCEntry = { { id: 'features/search', label: localize('search', "Search"), - settings: ['search.*'] + settings: ['search.*', 'searchRipgrep.*'] } , { diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 6de41e6defb..3c136ead81d 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -23,7 +23,6 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { escapeRegExpCharacters, startsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAccessibilityProvider, IDataSource, IFilter, IRenderer as ITreeRenderer, ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree'; import { DefaultTreestyler } from 'vs/base/parts/tree/browser/treeDefaults'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; @@ -186,8 +185,8 @@ export class SettingsDataSource implements IDataSource { return false; } - getChildren(tree: ITree, element: SettingsTreeElement): TPromise { - return TPromise.as(this._getChildren(element)); + getChildren(tree: ITree, element: SettingsTreeElement): Promise { + return Promise.resolve(this._getChildren(element)); } private _getChildren(element: SettingsTreeElement): SettingsTreeElement[] { @@ -199,8 +198,8 @@ export class SettingsDataSource implements IDataSource { } } - getParent(tree: ITree, element: SettingsTreeElement): TPromise { - return TPromise.wrap(element && element.parent); + getParent(tree: ITree, element: SettingsTreeElement): Promise { + return Promise.resolve(element && element.parent); } shouldAutoexpand(): boolean { @@ -241,7 +240,7 @@ export class SimplePagedDataSource implements IDataSource { return this.realDataSource.hasChildren(tree, element); } - getChildren(tree: ITree, element: SettingsTreeGroupElement): TPromise { + getChildren(tree: ITree, element: SettingsTreeGroupElement): Thenable { return this.realDataSource.getChildren(tree, element).then(realChildren => { return this._getChildren(realChildren); }); @@ -258,7 +257,7 @@ export class SimplePagedDataSource implements IDataSource { } } - getParent(tree: ITree, element: any): TPromise { + getParent(tree: ITree, element: any): Thenable { return this.realDataSource.getParent(tree, element); } @@ -400,7 +399,7 @@ export class SettingsRenderer implements ITreeRenderer { this._onDidChangeSetting.fire({ key: context.setting.key, value: undefined, type: context.setting.type as SettingValueType }); } - return TPromise.wrap(null); + return Promise.resolve(null); }), new Separator(), this.instantiationService.createInstance(CopySettingIdAction), @@ -413,7 +412,7 @@ export class SettingsRenderer implements ITreeRenderer { const toolbarElement: HTMLElement = settingDOMElement.querySelector('.toolbar-toggle-more'); if (toolbarElement) { this.contextMenuService.showContextMenu({ - getActions: () => TPromise.wrap(this.settingActions), + getActions: () => this.settingActions, getAnchor: () => toolbarElement, getActionsContext: () => element }); @@ -598,7 +597,7 @@ export class SettingsRenderer implements ITreeRenderer { private renderGroupTitleTemplate(container: HTMLElement): IGroupTitleTemplate { DOM.addClass(container, 'group-title'); - const toDispose = []; + const toDispose: IDisposable[] = []; const template: IGroupTitleTemplate = { parent: container, toDispose @@ -624,7 +623,7 @@ export class SettingsRenderer implements ITreeRenderer { const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); - const toDispose = []; + const toDispose: IDisposable[] = []; const toolbarContainer = DOM.append(container, $('.setting-toolbar-container')); const toolbar = this.renderSettingToolbar(toolbarContainer); @@ -775,7 +774,7 @@ export class SettingsRenderer implements ITreeRenderer { const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); - const toDispose = []; + const toDispose: IDisposable[] = []; const checkbox = new Checkbox({ actionClassName: 'setting-value-checkbox', isChecked: true, title: '', inputActiveOptionBorder: null }); controlElement.appendChild(checkbox.domNode); toDispose.push(checkbox); @@ -966,7 +965,7 @@ export class SettingsRenderer implements ITreeRenderer { } private renderNewExtensionsTemplate(container: HTMLElement): ISettingNewExtensionsTemplate { - const toDispose = []; + const toDispose: IDisposable[] = []; container.classList.add('setting-item-new-extensions'); @@ -1436,12 +1435,12 @@ export class SettingsAccessibilityProvider implements IAccessibilityProvider { } class NonExpandableOrSelectableTree extends Tree { - expand(): TPromise { - return TPromise.wrap(null); + expand(): Promise { + return Promise.resolve(null); } - collapse(): TPromise { - return TPromise.wrap(null); + collapse(): Promise { + return Promise.resolve(null); } public setFocus(element?: any, eventPayload?: any): void { @@ -1624,12 +1623,12 @@ class CopySettingIdAction extends Action { super(CopySettingIdAction.ID, CopySettingIdAction.LABEL); } - run(context: SettingsTreeSettingElement): TPromise { + run(context: SettingsTreeSettingElement): Promise { if (context) { this.clipboardService.writeText(context.setting.key); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -1643,12 +1642,12 @@ class CopySettingAsJSONAction extends Action { super(CopySettingAsJSONAction.ID, CopySettingAsJSONAction.LABEL); } - run(context: SettingsTreeSettingElement): TPromise { + run(context: SettingsTreeSettingElement): Promise { if (context) { const jsonResult = `"${context.setting.key}": ${JSON.stringify(context.value, undefined, ' ')}`; this.clipboardService.writeText(jsonResult); } - return TPromise.as(null); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts index a449db5aa11..fe8eff8f9d8 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts @@ -12,8 +12,8 @@ import { ConfigurationScope } from 'vs/platform/configuration/common/configurati import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { ITOCEntry, knownAcronyms } from 'vs/workbench/parts/preferences/browser/settingsLayout'; import { IExtensionSetting, ISearchResult, ISetting, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; +import { MODIFIED_SETTING_TAG } from 'vs/workbench/parts/preferences/common/preferences'; -export const MODIFIED_SETTING_TAG = 'modified'; export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices'; export interface ISettingsEditorViewState { @@ -137,7 +137,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { const { isConfigured, inspected, targetSelector } = inspectResult; const displayValue = isConfigured ? inspected[targetSelector] : inspected.default; - const overriddenScopeList = []; + const overriddenScopeList: string[] = []; if (targetSelector === 'user' && typeof inspected.workspace !== 'undefined') { overriddenScopeList.push(localize('workspace', "Workspace")); } @@ -281,7 +281,7 @@ export class SettingsTreeModel { element.parent = parent; element.level = this.getDepth(element); - const children = []; + const children: SettingsTreeGroupChild[] = []; if (tocEntry.settings) { const settingChildren = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(s, element)) .filter(el => el.setting.deprecationMessage ? el.isConfigured : true); diff --git a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts index 645523b757b..eaf3cdf400e 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts @@ -16,7 +16,7 @@ import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./media/settingsWidgets'; import { localize } from 'vs/nls'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground, textPreformatForeground, editorWidgetBorder } from 'vs/platform/theme/common/colorRegistry'; +import { foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground, textPreformatForeground, editorWidgetBorder, textLinkActiveForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { disposableTimeout } from 'vs/base/common/async'; @@ -69,6 +69,14 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a > code { color: ${link}; }`); } + const activeLink = theme.getColor(textLinkActiveForeground); + if (activeLink) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:hover, .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:active { color: ${activeLink}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:hover > code, .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:active > code { color: ${activeLink}; }`); + collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:hover, .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:active { color: ${activeLink}; }`); + collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:hover > code, .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:active > code { color: ${activeLink}; }`); + } + const headerForegroundColor = theme.getColor(settingsHeaderForeground); if (headerForegroundColor) { collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label.checked { color: ${headerForegroundColor}; border-bottom-color: ${headerForegroundColor}; }`); diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index f281c9d3033..97fb24420a7 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -5,7 +5,6 @@ import * as DOM from 'vs/base/browser/dom'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IRenderer, ITree, ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree'; import { DefaultTreestyler } from 'vs/base/parts/tree/browser/treeDefaults'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -106,8 +105,8 @@ export class TOCDataSource implements IDataSource { return false; } - getChildren(tree: ITree, element: TOCTreeElement): TPromise { - return TPromise.as(this._getChildren(element)); + getChildren(tree: ITree, element: TOCTreeElement): Promise { + return Promise.resolve(this._getChildren(element)); } private _getChildren(element: TOCTreeElement): SettingsTreeElement[] { @@ -115,8 +114,8 @@ export class TOCDataSource implements IDataSource { .filter(child => child instanceof SettingsTreeGroupElement); } - getParent(tree: ITree, element: TOCTreeElement): TPromise { - return TPromise.wrap(element instanceof SettingsTreeGroupElement && element.parent); + getParent(tree: ITree, element: TOCTreeElement): Promise { + return Promise.resolve(element instanceof SettingsTreeGroupElement && element.parent); } } diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 434b91a2929..10a020ce49f 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -41,7 +41,7 @@ export interface IPreferencesSearchService { } export interface ISearchProvider { - searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): TPromise; + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise; } export interface IKeybindingsEditor extends IEditor { @@ -63,6 +63,7 @@ export interface IKeybindingsEditor extends IEditor { } export const CONTEXT_SETTINGS_EDITOR = new RawContextKey('inSettingsEditor', false); +export const CONTEXT_SETTINGS_JSON_EDITOR = new RawContextKey('inSettingsJSONEditor', false); export const CONTEXT_SETTINGS_SEARCH_FOCUS = new RawContextKey('inSettingsSearch', false); export const CONTEXT_TOC_ROW_FOCUS = new RawContextKey('settingsTocRowFocus', false); export const CONTEXT_KEYBINDINGS_EDITOR = new RawContextKey('inKeybindings', false); @@ -79,6 +80,10 @@ export const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH = 'settings.acti export const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST = 'settings.action.focusSettingsList'; export const SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU = 'settings.action.showContextMenu'; +export const SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON = 'settings.switchToJSON'; +export const SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED = 'settings.filterByModified'; +export const SETTINGS_EDITOR_COMMAND_FILTER_ONLINE = 'settings.filterByOnline'; + export const KEYBINDINGS_EDITOR_COMMAND_SEARCH = 'keybindings.editor.searchKeybindings'; export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'keybindings.editor.clearSearchResults'; export const KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS = 'keybindings.editor.recordSearchKeys'; @@ -96,3 +101,5 @@ export const KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS = 'keybindings.editor.show export const FOLDER_SETTINGS_PATH = join('.vscode', 'settings.json'); export const DEFAULT_SETTINGS_EDITOR_SETTING = 'workbench.settings.openDefaultSettings'; + +export const MODIFIED_SETTING_TAG = 'modified'; \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/common/preferencesContribution.ts b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts index 51e42f47265..28d4324c3bd 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts @@ -6,7 +6,6 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModel } from 'vs/editor/common/model'; import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -109,14 +108,14 @@ export class PreferencesContribution implements IWorkbenchContribution { private start(): void { this.textModelResolverService.registerTextModelContentProvider('vscode', { - provideTextContent: (uri: URI): TPromise => { + provideTextContent: (uri: URI): Thenable => { if (uri.scheme !== 'vscode') { return null; } if (uri.authority === 'schemas') { const schemaModel = this.getSchemaModel(uri); if (schemaModel) { - return TPromise.as(schemaModel); + return Promise.resolve(schemaModel); } } return this.preferencesService.resolveModel(uri); @@ -128,10 +127,10 @@ export class PreferencesContribution implements IWorkbenchContribution { let schema = schemaRegistry.getSchemaContributions().schemas[uri.toString()]; if (schema) { const modelContent = JSON.stringify(schema); - const mode = this.modeService.getOrCreateMode('jsonc'); - const model = this.modelService.createModel(modelContent, mode, uri); + const languageSelection = this.modeService.create('jsonc'); + const model = this.modelService.createModel(modelContent, languageSelection, uri); - let disposables = []; + let disposables: IDisposable[] = []; disposables.push(schemaRegistry.onDidChangeSchema(schemaUri => { if (schemaUri === uri.toString()) { schema = schemaRegistry.getSchemaContributions().schemas[uri.toString()]; diff --git a/src/vs/workbench/parts/preferences/electron-browser/media/edit-json-inverse.svg b/src/vs/workbench/parts/preferences/electron-browser/media/edit-json-inverse.svg new file mode 100644 index 00000000000..6dc96a9e122 --- /dev/null +++ b/src/vs/workbench/parts/preferences/electron-browser/media/edit-json-inverse.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/workbench/parts/preferences/electron-browser/media/edit-json.svg b/src/vs/workbench/parts/preferences/electron-browser/media/edit-json.svg new file mode 100644 index 00000000000..747e2706bc0 --- /dev/null +++ b/src/vs/workbench/parts/preferences/electron-browser/media/edit-json.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css index a484bdf40ba..7f7bed3a256 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css @@ -355,12 +355,16 @@ opacity: 0.9; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message, -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message { margin-top: 3px; user-select: text; } +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description { + margin-top: -1px; + user-select: text; +} + .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message { position: absolute; } @@ -446,7 +450,6 @@ border-radius: 3px; margin-right: 9px; margin-left: 0px; - margin-top: 4px; padding: 0px; background-size: 16px !important; } diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts index 1b1c737047b..3367d19696f 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts @@ -3,40 +3,35 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!../browser/media/preferences'; -import * as nls from 'vs/nls'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { URI } from 'vs/base/common/uri'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { EditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; -import { SettingsEditor2 } from 'vs/workbench/parts/preferences/electron-browser/settingsEditor2'; -import { DefaultPreferencesEditorInput, PreferencesEditorInput, KeybindingsEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; -import { KeybindingsEditor } from 'vs/workbench/parts/preferences/browser/keybindingsEditor'; -import { OpenDefaultKeybindingsFileAction, OpenRawDefaultSettingsAction, OpenSettingsAction, OpenGlobalSettingsAction, OpenGlobalKeybindingsFileAction, OpenWorkspaceSettingsAction, OpenFolderSettingsAction, ConfigureLanguageBasedSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND, OpenGlobalKeybindingsAction, OpenSettings2Action, OpenSettingsJsonAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; -import { - IKeybindingsEditor, IPreferencesSearchService, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, - KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SEARCH, CONTEXT_SETTINGS_EDITOR, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, - CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, CONTEXT_TOC_ROW_FOCUS, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST, - SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS -} from 'vs/workbench/parts/preferences/common/preferences'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { PreferencesContribution } from 'vs/workbench/parts/preferences/common/preferencesContribution'; -import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { PreferencesSearchService } from 'vs/workbench/parts/preferences/electron-browser/preferencesSearch'; -import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import 'vs/css!../browser/media/preferences'; import { Command } from 'vs/editor/browser/editorExtensions'; import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest'; +import * as nls from 'vs/nls'; +import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; +import { Extensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { EditorInput, Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; +import { KeybindingsEditor } from 'vs/workbench/parts/preferences/browser/keybindingsEditor'; +import { ConfigureLanguageBasedSettingsAction, OpenDefaultKeybindingsFileAction, OpenFolderSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenGlobalSettingsAction, OpenRawDefaultSettingsAction, OpenSettings2Action, OpenSettingsAction, OpenSettingsJsonAction, OpenWorkspaceSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND } from 'vs/workbench/parts/preferences/browser/preferencesActions'; +import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; +import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IKeybindingsEditor, IPreferencesSearchService, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON } from 'vs/workbench/parts/preferences/common/preferences'; +import { PreferencesContribution } from 'vs/workbench/parts/preferences/common/preferencesContribution'; +import { PreferencesSearchService } from 'vs/workbench/parts/preferences/electron-browser/preferencesSearch'; +import { SettingsEditor2 } from 'vs/workbench/parts/preferences/electron-browser/settingsEditor2'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; registerSingleton(IPreferencesSearchService, PreferencesSearchService); @@ -394,8 +389,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { id: OpenGlobalKeybindingsFileAction.ID, title: OpenGlobalKeybindingsFileAction.LABEL, iconLocation: { - light: URI.parse(require.toUrl(`vs/workbench/parts/preferences/browser/media/open-file.svg`)), - dark: URI.parse(require.toUrl(`vs/workbench/parts/preferences/browser/media/open-file-inverse.svg`)) + light: URI.parse(require.toUrl(`vs/workbench/parts/preferences/electron-browser/media/edit-json.svg`)), + dark: URI.parse(require.toUrl(`vs/workbench/parts/preferences/electron-browser/media/edit-json-inverse.svg`)) } }, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR), @@ -582,6 +577,29 @@ const showContextMenuCommand = new ShowContextMenuCommand({ }); showContextMenuCommand.register(); +CommandsRegistry.registerCommand(SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, serviceAccessor => { + const control = serviceAccessor.get(IEditorService).activeControl as SettingsEditor2; + if (control instanceof SettingsEditor2) { + return control.switchToSettingsFile(); + } + + return Promise.resolve(null); +}); + +CommandsRegistry.registerCommand(SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, serviceAccessor => { + const control = serviceAccessor.get(IEditorService).activeControl as SettingsEditor2; + if (control instanceof SettingsEditor2) { + control.focusSearch(`@${MODIFIED_SETTING_TAG}`); + } +}); + +CommandsRegistry.registerCommand(SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, serviceAccessor => { + const control = serviceAccessor.get(IEditorService).activeControl as SettingsEditor2; + if (control instanceof SettingsEditor2) { + control.focusSearch(`@tag:usesOnlineServices`); + } +}); + // Preferences menu MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { @@ -601,3 +619,48 @@ MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { }, order: 1 }); + +// Editor tool items + +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, + title: nls.localize('openSettingsJson', "Open Settings (JSON)"), + iconLocation: { + dark: URI.parse(require.toUrl('vs/workbench/parts/preferences/electron-browser/media/edit-json-inverse.svg')), + light: URI.parse(require.toUrl('vs/workbench/parts/preferences/electron-browser/media/edit-json.svg')) + } + }, + group: 'navigation', + order: 1, + when: ContextKeyExpr.and( + CONTEXT_SETTINGS_EDITOR, + CONTEXT_SETTINGS_JSON_EDITOR.toNegated() + ) +}); + +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, + title: nls.localize('filterModifiedLabel', "Show modified settings") + }, + group: '1_filter', + order: 1, + when: ContextKeyExpr.and( + CONTEXT_SETTINGS_EDITOR, + CONTEXT_SETTINGS_JSON_EDITOR.toNegated() + ) +}); + +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, + title: nls.localize('filterOnlineServicesLabel', "Show settings for online services"), + }, + group: '1_filter', + order: 2, + when: ContextKeyExpr.and( + CONTEXT_SETTINGS_EDITOR, + CONTEXT_SETTINGS_JSON_EDITOR.toNegated() + ) +}); diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts index 9ff55ccdc9c..76c7f4a101c 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { ISettingsEditorModel, ISetting, ISettingsGroup, IFilterMetadata, ISearchResult, IGroupFilter, ISettingMatcher, IScoredResults, ISettingMatch, IRemoteSetting, IExtensionSetting } from 'vs/workbench/services/preferences/common/preferences'; import { IRange } from 'vs/editor/common/core/range'; import { distinct, top } from 'vs/base/common/arrays'; @@ -32,7 +31,7 @@ export interface IEndpointDetails { export class PreferencesSearchService extends Disposable implements IPreferencesSearchService { _serviceBrand: any; - private _installedExtensions: TPromise; + private _installedExtensions: Promise; constructor( @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, @@ -104,9 +103,9 @@ export class LocalSearchProvider implements ISearchProvider { .trim(); } - searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): TPromise { + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise { if (!this._filter) { - return TPromise.wrap(null); + return Promise.resolve(null); } let orderedScore = LocalSearchProvider.START_SCORE; // Sort is not stable @@ -126,12 +125,12 @@ export class LocalSearchProvider implements ISearchProvider { const filterMatches = preferencesModel.filterSettings(this._filter, this.getGroupFilter(this._filter), settingMatcher); if (filterMatches[0] && filterMatches[0].score === LocalSearchProvider.EXACT_MATCH_SCORE) { - return TPromise.wrap({ + return Promise.resolve({ filterMatches: filterMatches.slice(0, 1), exactMatch: true }); } else { - return TPromise.wrap({ + return Promise.resolve({ filterMatches }); } @@ -164,19 +163,19 @@ class RemoteSearchProvider implements ISearchProvider { private static readonly MAX_REQUESTS = 10; private static readonly NEW_EXTENSIONS_MIN_SCORE = 1; - private _remoteSearchP: TPromise; + private _remoteSearchP: Promise; - constructor(private options: IRemoteSearchProviderOptions, private installedExtensions: TPromise, + constructor(private options: IRemoteSearchProviderOptions, private installedExtensions: Promise, @IEnvironmentService private environmentService: IEnvironmentService, @IRequestService private requestService: IRequestService, @ILogService private logService: ILogService ) { this._remoteSearchP = this.options.filter ? - TPromise.wrap(this.getSettingsForFilter(this.options.filter)) : - TPromise.wrap(null); + Promise.resolve(this.getSettingsForFilter(this.options.filter)) : + Promise.resolve(null); } - searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): TPromise { + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise { return this._remoteSearchP.then(remoteResult => { if (!remoteResult) { return null; @@ -239,7 +238,7 @@ class RemoteSearchProvider implements ISearchProvider { } } - return TPromise.join(allRequestDetails.map(details => this.getSettingsFromBing(details))).then(allResponses => { + return Promise.all(allRequestDetails.map(details => this.getSettingsFromBing(details))).then(allResponses => { // Merge all IFilterMetadata const metadata = allResponses[0]; metadata.requestCount = 1; @@ -253,7 +252,7 @@ class RemoteSearchProvider implements ISearchProvider { }); } - private getSettingsFromBing(details: IBingRequestDetails): TPromise { + private getSettingsFromBing(details: IBingRequestDetails): Promise { this.logService.debug(`Searching settings via ${details.url}`); if (details.body) { this.logService.debug(`Body: ${details.body}`); diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index a5abd5e68ad..1d7b8db7f0c 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -4,25 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { Action } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; -import { isArray } from 'vs/base/common/types'; import { Delayer, ThrottledDelayer } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as collections from 'vs/base/common/collections'; import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; +import { isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { collapseAll, expandAll } from 'vs/base/parts/tree/browser/treeUtils'; import 'vs/css!./media/settingsEditor2'; import { localize } from 'vs/nls'; -import { ConfigurationTarget, IConfigurationOverrides, IConfigurationService, ConfigurationTargetToString } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, ConfigurationTargetToString, IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; @@ -36,18 +32,17 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditor, IEditorMemento } from 'vs/workbench/common/editor'; import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput'; -import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { commonlyUsedData, tocData } from 'vs/workbench/parts/preferences/browser/settingsLayout'; -import { ISettingLinkClickEvent, resolveExtensionsSettings, resolveSettingsTree, SettingsDataSource, SettingsRenderer, SettingsTree, SimplePagedDataSource, ISettingOverrideClickEvent } from 'vs/workbench/parts/preferences/browser/settingsTree'; -import { ISettingsEditorViewState, MODIFIED_SETTING_TAG, ONLINE_SERVICES_SETTING_TAG, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; +import { ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveExtensionsSettings, resolveSettingsTree, SettingsDataSource, SettingsRenderer, SettingsTree, SimplePagedDataSource } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { parseQuery, ISettingsEditorViewState, SearchResultIdx, SearchResultModel, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; import { settingsTextInputBorder } from 'vs/workbench/parts/preferences/browser/settingsWidgets'; import { TOCRenderer, TOCTree, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/parts/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/parts/preferences/common/preferences'; +import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingsEditorOptions, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; -import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; const $ = DOM.$; @@ -60,7 +55,7 @@ export class SettingsEditor2 extends BaseEditor { private static SETTING_UPDATE_SLOW_DEBOUNCE: number = 1000; private static readonly SUGGESTIONS: string[] = [ - '@modified', '@tag:usesOnlineServices' + `@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices' ]; private static shouldSettingUpdateFast(type: SettingValueType | SettingValueType[]): boolean { @@ -129,7 +124,6 @@ export class SettingsEditor2 extends BaseEditor { @IInstantiationService private instantiationService: IInstantiationService, @IPreferencesSearchService private preferencesSearchService: IPreferencesSearchService, @ILogService private logService: ILogService, - @IEnvironmentService private environmentService: IEnvironmentService, @IContextKeyService contextKeyService: IContextKeyService, @IContextMenuService private contextMenuService: IContextMenuService, @IStorageService private storageService: IStorageService, @@ -211,7 +205,7 @@ export class SettingsEditor2 extends BaseEditor { } this._setOptions(options); - this.render(token); + return this.render(token); }) .then(() => { // Init TOC selection @@ -416,31 +410,6 @@ export class SettingsEditor2 extends BaseEditor { actionRunner: this.actionRunner })); - const actions: Action[] = [ - this.instantiationService.createInstance(FilterByTagAction, - localize('filterModifiedLabel', "Show modified settings"), - MODIFIED_SETTING_TAG, - this) - ]; - if (this.environmentService.appQuality !== 'stable') { - actions.push( - this.instantiationService.createInstance( - FilterByTagAction, - localize('filterOnlineServicesLabel', "Show settings for online services"), - ONLINE_SERVICES_SETTING_TAG, - this)); - actions.push(new Separator()); - } - actions.push(new Action('settings.openSettingsJson', localize('openSettingsJsonLabel', "Open settings.json"), undefined, undefined, () => { - return this.openSettingsFile().then(editor => { - const currentQuery = parseQuery(this.searchWidget.getValue()); - if (editor instanceof PreferencesEditor && currentQuery) { - editor.focusSearch(currentQuery.query); - } - }); - })); - - this.toolbar.setActions([], actions)(); this.toolbar.context = { target: this.settingsTargetsWidget.settingsTarget }; } @@ -471,7 +440,12 @@ export class SettingsEditor2 extends BaseEditor { } } - private openSettingsFile(query?: string): TPromise { + public switchToSettingsFile(): Thenable { + const query = parseQuery(this.searchWidget.getValue()); + return this.openSettingsFile(query.query); + } + + private openSettingsFile(query?: string): Thenable { const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget; const options: ISettingsEditorOptions = { query }; @@ -592,7 +566,7 @@ export class SettingsEditor2 extends BaseEditor { } if (element && (!e.payload || !e.payload.fromScroll)) { - let refreshP = TPromise.wrap(null); + let refreshP: Thenable = Promise.resolve(null); if (this.settingsTreeDataSource.pageTo(element.index, true)) { refreshP = this.renderTree(); } @@ -724,7 +698,7 @@ export class SettingsEditor2 extends BaseEditor { } } - private updateChangedSetting(key: string, value: any): TPromise { + private updateChangedSetting(key: string, value: any): Thenable { // ConfigurationService displays the error if this fails. // Force a render afterwards because onDidConfigurationUpdate doesn't fire if the update doesn't result in an effective setting value change const settingsTarget = this.settingsTargetsWidget.settingsTarget; @@ -813,32 +787,9 @@ export class SettingsEditor2 extends BaseEditor { } */ this.telemetryService.publicLog('settingsEditor.settingModified', data); - - const data2 = { - key: props.key, - groupId, - nlpIndex, - displayIndex, - showConfiguredOnly: props.showConfiguredOnly, - isReset: props.isReset, - target: reportedTarget - }; - - /* __GDPR__ - "settingsEditor.settingModified" : { - "key" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "groupId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "nlpIndex" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "displayIndex" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "showConfiguredOnly" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "isReset" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "target" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('settingsEditor.settingModified2', data2); } - private render(token: CancellationToken): TPromise { + private render(token: CancellationToken): Thenable { if (this.input) { return this.input.resolve() .then((model: Settings2EditorModel) => { @@ -851,7 +802,7 @@ export class SettingsEditor2 extends BaseEditor { return this.onConfigUpdate(); }); } - return TPromise.as(null); + return Promise.resolve(null); } private onSearchModeToggled(): void { @@ -886,7 +837,7 @@ export class SettingsEditor2 extends BaseEditor { }); } - private onConfigUpdate(keys?: string[], forceRefresh = false): TPromise { + private onConfigUpdate(keys?: string[], forceRefresh = false): Thenable { if (keys && this.settingsTreeModel) { return this.updateElementsByKey(keys); } @@ -898,7 +849,7 @@ export class SettingsEditor2 extends BaseEditor { // Warn for settings not included in layout if (settingsResult.leftoverSettings.size && !this.hasWarnedMissingSettings) { - const settingKeyList = []; + const settingKeyList: string[] = []; settingsResult.leftoverSettings.forEach(s => { settingKeyList.push(s.key); }); @@ -939,10 +890,10 @@ export class SettingsEditor2 extends BaseEditor { } } - return TPromise.wrap(null); + return Promise.resolve(null); } - private updateElementsByKey(keys: string[]): TPromise { + private updateElementsByKey(keys: string[]): Thenable { if (keys.length) { if (this.searchResultModel) { keys.forEach(key => this.searchResultModel.updateElementsByName(key)); @@ -952,7 +903,7 @@ export class SettingsEditor2 extends BaseEditor { keys.forEach(key => this.settingsTreeModel.updateElementsByName(key)); } - return TPromise.join( + return Promise.all( keys.map(key => this.renderTree(key))) .then(() => { }); } else { @@ -966,10 +917,10 @@ export class SettingsEditor2 extends BaseEditor { null; } - private renderTree(key?: string, force = false): TPromise { + private renderTree(key?: string, force = false): Thenable { if (!force && key && this.scheduledRefreshes.has(key)) { this.updateModifiedLabelForKey(key); - return TPromise.wrap(null); + return Promise.resolve(null); } // If a setting control is currently focused, schedule a refresh for later @@ -983,24 +934,24 @@ export class SettingsEditor2 extends BaseEditor { this.updateModifiedLabelForKey(key); this.scheduleRefresh(focusedSetting, key); - return TPromise.wrap(null); + return Promise.resolve(null); } } else { this.scheduleRefresh(focusedSetting); - return TPromise.wrap(null); + return Promise.resolve(null); } } - let refreshP: TPromise; + let refreshP: Thenable; if (key) { const elements = this.currentSettingsModel.getElementsByName(key); if (elements && elements.length) { // TODO https://github.com/Microsoft/vscode/issues/57360 - // refreshP = TPromise.join(elements.map(e => this.settingsTree.refresh(e))); + // refreshP = Promise.join(elements.map(e => this.settingsTree.refresh(e))); refreshP = this.settingsTree.refresh(); } else { // Refresh requested for a key that we don't know about - return TPromise.wrap(null); + return Promise.resolve(null); } } else { refreshP = this.settingsTree.refresh(); @@ -1042,7 +993,7 @@ export class SettingsEditor2 extends BaseEditor { return match && match[1]; } - private triggerSearch(query: string): TPromise { + private triggerSearch(query: string): Thenable { this.viewState.tagFilters = new Set(); if (query) { const parsedQuery = parseQuery(query); @@ -1147,25 +1098,9 @@ export class SettingsEditor2 extends BaseEditor { } */ this.telemetryService.publicLog('settingsEditor.filter', data); - - const data2 = { - durations, - counts, - requestCount - }; - - /* __GDPR__ - "settingsEditor.filter" : { - "durations.nlpResult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "counts.nlpResult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "counts.filterResult" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "requestCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('settingsEditor.filter2', data2); } - private triggerFilterPreferences(query: string): TPromise { + private triggerFilterPreferences(query: string): Thenable { if (this.searchInProgress) { this.searchInProgress.cancel(); this.searchInProgress = null; @@ -1180,26 +1115,26 @@ export class SettingsEditor2 extends BaseEditor { this.remoteSearchThrottle.trigger(() => { return searchInProgress && !searchInProgress.token.isCancellationRequested ? this.remoteSearchPreferences(query, this.searchInProgress.token) : - TPromise.wrap(null); + Promise.resolve(null); }); } }); } else { - return TPromise.wrap(null); + return Promise.resolve(null); } }); } - private localFilterPreferences(query: string, token?: CancellationToken): TPromise { + private localFilterPreferences(query: string, token?: CancellationToken): Thenable { const localSearchProvider = this.preferencesSearchService.getLocalSearchProvider(query); return this.filterOrSearchPreferences(query, SearchResultIdx.Local, localSearchProvider, token); } - private remoteSearchPreferences(query: string, token?: CancellationToken): TPromise { + private remoteSearchPreferences(query: string, token?: CancellationToken): Thenable { const remoteSearchProvider = this.preferencesSearchService.getRemoteSearchProvider(query); const newExtSearchProvider = this.preferencesSearchService.getRemoteSearchProvider(query, true); - return TPromise.join([ + return Promise.all([ this.filterOrSearchPreferences(query, SearchResultIdx.Remote, remoteSearchProvider, token), this.filterOrSearchPreferences(query, SearchResultIdx.NewExtensions, newExtSearchProvider, token) ]).then(() => { @@ -1207,7 +1142,7 @@ export class SettingsEditor2 extends BaseEditor { }); } - private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider: ISearchProvider, token?: CancellationToken): TPromise { + private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider: ISearchProvider, token?: CancellationToken): Thenable { return this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider, token).then(result => { if (token && token.isCancellationRequested) { // Handle cancellation like this because cancellation is lost inside the search provider due to async/await @@ -1254,12 +1189,12 @@ export class SettingsEditor2 extends BaseEditor { } } - private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, token?: CancellationToken): TPromise { - const searchP = provider ? provider.searchModel(model, token) : TPromise.wrap(null); + private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, token?: CancellationToken): Thenable { + const searchP = provider ? provider.searchModel(model, token) : Promise.resolve(null); return searchP .then(null, err => { if (isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } else { /* __GDPR__ "settingsEditor.searchError" : { @@ -1311,20 +1246,3 @@ interface ISettingsEditor2State { interface ISettingsToolbarContext { target: SettingsTarget; } - -class FilterByTagAction extends Action { - static readonly ID = 'settings.filterByTag'; - - constructor( - label: string, - private tag: string, - private settingsEditor: SettingsEditor2 - ) { - super(FilterByTagAction.ID, label, 'toggle-filter-tag'); - } - - run(): TPromise { - this.settingsEditor.focusSearch(this.tag === MODIFIED_SETTING_TAG ? `@${this.tag} ` : `@tag:${this.tag} `, false); - return TPromise.as(null); - } -} diff --git a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts index a2e4eccb86a..39f65feba63 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { IEntryRunContext, Mode, IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; @@ -38,7 +37,7 @@ export class GotoLineAction extends QuickOpenAction { super(actionId, actionLabel, GOTO_LINE_PREFIX, _quickOpenService); } - run(): TPromise { + run(): Promise { let activeTextEditorWidget = this.editorService.activeTextEditorWidget; if (isDiffEditor(activeTextEditorWidget)) { @@ -217,7 +216,7 @@ export class GotoLineHandler extends QuickOpenHandler { return nls.localize('gotoLineHandlerAriaLabel', "Type a line number to navigate to."); } - getResults(searchValue: string, token: CancellationToken): TPromise { + getResults(searchValue: string, token: CancellationToken): Promise { searchValue = searchValue.trim(); // Remember view state to be able to restore on cancel @@ -226,7 +225,7 @@ export class GotoLineHandler extends QuickOpenHandler { this.lastKnownEditorViewState = activeTextEditorWidget.saveViewState(); } - return TPromise.as(new QuickOpenModel([new GotoLineEntry(searchValue, this.editorService, this)])); + return Promise.resolve(new QuickOpenModel([new GotoLineEntry(searchValue, this.editorService, this)])); } canRun(): boolean | string { diff --git a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts index eb66494ac57..1f6017b0945 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!vs/editor/contrib/documentSymbols/media/symbol-icons'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import * as strings from 'vs/base/common/strings'; @@ -369,7 +368,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { private rangeHighlightDecorationId: IEditorLineDecoration; private lastKnownEditorViewState: IEditorViewState; - private cachedOutlineRequest: TPromise; + private cachedOutlineRequest: Promise; private pendingOutlineRequest: CancellationTokenSource; constructor( @@ -391,7 +390,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { this.rangeHighlightDecorationId = void 0; } - getResults(searchValue: string, token: CancellationToken): TPromise { + getResults(searchValue: string, token: CancellationToken): Promise { searchValue = searchValue.trim(); // Support to cancel pending outline requests @@ -483,7 +482,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { return results; } - private getOutline(): TPromise { + private getOutline(): Promise { if (!this.cachedOutlineRequest) { this.cachedOutlineRequest = this.doGetActiveOutline(); } @@ -491,7 +490,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { return this.cachedOutlineRequest; } - private doGetActiveOutline(): TPromise { + private doGetActiveOutline(): Promise { const activeTextEditorWidget = this.editorService.activeTextEditorWidget; if (activeTextEditorWidget) { let model = activeTextEditorWidget.getModel(); @@ -500,13 +499,13 @@ export class GotoSymbolHandler extends QuickOpenHandler { } if (model && types.isFunction((model).getLanguageIdentifier)) { - return TPromise.wrap(asThenable(() => getDocumentSymbols(model, true, this.pendingOutlineRequest.token)).then(entries => { + return Promise.resolve(asThenable(() => getDocumentSymbols(model, true, this.pendingOutlineRequest.token)).then(entries => { return new OutlineModel(this.toQuickOpenEntries(entries)); })); } } - return TPromise.wrap(null); + return Promise.resolve(null); } decorateOutline(fullRange: IRange, startRange: IRange, editor: IEditor, group: IEditorGroup): void { diff --git a/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts index 16596d153e0..2524fa81a27 100644 --- a/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts @@ -102,6 +102,13 @@ export class ViewPickerHandler extends QuickOpenHandler { return true; }); + const entryToCategory = {}; + entries.forEach(e => { + if (!entryToCategory[e.getLabel()]) { + entryToCategory[e.getLabel()] = e.getCategory(); + } + }); + let lastCategory: string; entries.forEach((e, index) => { if (lastCategory !== e.getCategory()) { @@ -109,6 +116,11 @@ export class ViewPickerHandler extends QuickOpenHandler { e.setShowBorder(index > 0); e.setGroupLabel(lastCategory); + + // When the entry category has a parent category, set group label as Parent / Child. For example, Views / Explorer. + if (entryToCategory[lastCategory]) { + e.setGroupLabel(`${entryToCategory[lastCategory]} / ${lastCategory}`); + } } else { e.setShowBorder(false); e.setGroupLabel(void 0); @@ -136,11 +148,11 @@ export class ViewPickerHandler extends QuickOpenHandler { // Viewlets const viewlets = this.viewletService.getViewlets(); - viewlets.forEach((viewlet, index) => viewEntries.push(new ViewEntry(viewlet.name, nls.localize('views', "Views"), () => this.viewletService.openViewlet(viewlet.id, true)))); + viewlets.forEach((viewlet, index) => viewEntries.push(new ViewEntry(viewlet.name, nls.localize('views', "Side Bar"), () => this.viewletService.openViewlet(viewlet.id, true)))); // Panels const panels = this.panelService.getPanels(); - panels.forEach((panel, index) => viewEntries.push(new ViewEntry(panel.name, nls.localize('panels', "Panels"), () => this.panelService.openPanel(panel.id, true)))); + panels.forEach((panel, index) => viewEntries.push(new ViewEntry(panel.name, nls.localize('panels', "Panel"), () => this.panelService.openPanel(panel.id, true)))); // Viewlet Views viewlets.forEach((viewlet, index) => { @@ -170,7 +182,7 @@ export class ViewPickerHandler extends QuickOpenHandler { const channels = this.outputService.getChannelDescriptors(); channels.forEach((channel, index) => { const outputCategory = nls.localize('channels', "Output"); - const entry = new ViewEntry(channel.label, outputCategory, () => this.outputService.showChannel(channel.id)); + const entry = new ViewEntry(channel.log ? nls.localize('logChannel', "Log ({0})", channel.label) : channel.label, outputCategory, () => this.outputService.showChannel(channel.id)); viewEntries.push(entry); }); diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts index 76c20e0769a..c5e863ef7c5 100644 --- a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -15,7 +15,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { RunOnceScheduler } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { isEqual } from 'vs/base/common/resources'; -import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { equals } from 'vs/base/common/objects'; @@ -24,7 +24,7 @@ interface IConfiguration extends IWindowsConfiguration { update: { channel: string; }; telemetry: { enableCrashReporter: boolean }; keyboard: { touchbar: { enabled: boolean } }; - workbench: { tree: { horizontalScrolling: boolean } }; + workbench: { tree: { horizontalScrolling: boolean }, enableLegacyStorage: boolean }; files: { useExperimentalFileWatcher: boolean, watcherExclude: object }; } @@ -32,13 +32,16 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private titleBarStyle: 'native' | 'custom'; private nativeTabs: boolean; + private nativeFullScreen: boolean; private clickThroughInactive: boolean; private updateChannel: string; private enableCrashReporter: boolean; private touchbarEnabled: boolean; private treeHorizontalScrolling: boolean; + private windowsSmoothScrollingWorkaround: boolean; private experimentalFileWatcher: boolean; private fileWatcherExclude: object; + private legacyStorage: boolean; private firstFolderResource: URI; private extensionHostRestarter: RunOnceScheduler; @@ -86,6 +89,12 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo changed = true; } + // macOS: Native fullscreen + if (isMacintosh && config.window && typeof config.window.nativeFullScreen === 'boolean' && config.window.nativeFullScreen !== this.nativeFullScreen) { + this.nativeFullScreen = config.window.nativeFullScreen; + changed = true; + } + // macOS: Click through (accept first mouse) if (isMacintosh && config.window && typeof config.window.clickThroughInactive === 'boolean' && config.window.clickThroughInactive !== this.clickThroughInactive) { this.clickThroughInactive = config.window.clickThroughInactive; @@ -130,6 +139,17 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo changed = true; } + // Legacy Workspace Storage + if (config.workbench && typeof config.workbench.enableLegacyStorage === 'boolean' && config.workbench.enableLegacyStorage !== this.legacyStorage) { + this.legacyStorage = config.workbench.enableLegacyStorage; + changed = true; + } + // Windows: smooth scrolling workaround + if (isWindows && config.window && typeof config.window.smoothScrollingWorkaround === 'boolean' && config.window.smoothScrollingWorkaround !== this.windowsSmoothScrollingWorkaround) { + this.windowsSmoothScrollingWorkaround = config.window.smoothScrollingWorkaround; + changed = true; + } + // Notify only when changed and we are the focused window (avoids notification spam across windows) if (notify && changed) { this.doConfirm( diff --git a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css index 0a15ee8d540..84ad51a4b7b 100644 --- a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css +++ b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css @@ -137,12 +137,12 @@ } .scm-viewlet .scm-editor { + box-sizing: border-box; padding: 5px 9px 5px 16px; } -.scm-viewlet .scm-editor { - box-sizing: border-box; - padding: 5px 9px 5px 16px; +.scm-viewlet .scm-editor.hidden { + display: none; } .scm-viewlet .scm-editor > .monaco-inputbox { diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 7f1782b2979..b944b153252 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -11,7 +11,7 @@ import { domEvent, stop } from 'vs/base/browser/event'; import { basename } from 'vs/base/common/paths'; import { IDisposable, dispose, combinedDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { PanelViewlet, ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; -import { append, $, addClass, toggleClass, trackFocus, Dimension, addDisposableListener } from 'vs/base/browser/dom'; +import { append, $, addClass, toggleClass, trackFocus, Dimension, addDisposableListener, removeClass } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; @@ -316,7 +316,7 @@ class MainPanel extends ViewletPanel { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => TPromise.as(secondary), + getActions: () => secondary, getActionsContext: () => repository.provider }); } @@ -799,12 +799,12 @@ export class RepositoryPanel extends ViewletPanel { this.contextMenuService.showContextMenu({ getAnchor: () => ({ x: event.posx, y: event.posy }), - getActions: () => TPromise.as([{ + getActions: () => [{ id: `scm.hideRepository`, label: localize('hideRepository', "Hide"), enabled: true, run: () => this.viewModel.hide(this.repository) - }]), + }], }); } @@ -865,6 +865,10 @@ export class RepositoryPanel extends ViewletPanel { this.updateInputBox(); + // Input box visibility + this.repository.input.onDidChangeVisibility(this.updateInputBoxVisibility, this, this.disposables); + this.updateInputBoxVisibility(); + // List this.listContainer = append(container, $('.scm-status.show-file-icons')); @@ -918,21 +922,35 @@ export class RepositoryPanel extends ViewletPanel { } this.cachedHeight = height; - this.inputBox.layout(); - const editorHeight = this.inputBox.height; - const listHeight = height - (editorHeight + 12 /* margin */); - this.listContainer.style.height = `${listHeight}px`; - this.list.layout(listHeight); + if (this.repository.input.visible) { + removeClass(this.inputBoxContainer, 'hidden'); + this.inputBox.layout(); - toggleClass(this.inputBoxContainer, 'scroll', editorHeight >= 134); + const editorHeight = this.inputBox.height; + const listHeight = height - (editorHeight + 12 /* margin */); + this.listContainer.style.height = `${listHeight}px`; + this.list.layout(listHeight); + + toggleClass(this.inputBoxContainer, 'scroll', editorHeight >= 134); + } else { + addClass(this.inputBoxContainer, 'hidden'); + removeClass(this.inputBoxContainer, 'scroll'); + + this.listContainer.style.height = `${height}px`; + this.list.layout(height); + } } focus(): void { super.focus(); if (this.isExpanded()) { - this.inputBox.focus(); + if (this.repository.input.visible) { + this.inputBox.focus(); + } else { + this.list.domFocus(); + } } } @@ -979,7 +997,7 @@ export class RepositoryPanel extends ViewletPanel { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => TPromise.as(actions), + getActions: () => actions, getActionsContext: () => element, actionRunner: new MultipleSelectionActionRunner(() => this.getSelectedResources()) }); @@ -991,13 +1009,19 @@ export class RepositoryPanel extends ViewletPanel { } private updateInputBox(): void { - if (typeof this.repository.provider.commitTemplate === 'undefined' || this.inputBox.value) { + if (typeof this.repository.provider.commitTemplate === 'undefined' || !this.repository.input.visible || this.inputBox.value) { return; } this.inputBox.value = this.repository.provider.commitTemplate; } + private updateInputBoxVisibility(): void { + if (this.cachedHeight) { + this.layoutBody(this.cachedHeight); + } + } + dispose(): void { this.visibilityDisposables = dispose(this.visibilityDisposables); super.dispose(); @@ -1067,37 +1091,36 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle this.disposables.push(this.contributedViews); } - create(parent: HTMLElement): Promise { - return super.create(parent).then(() => { - this.el = parent; - addClass(this.el, 'scm-viewlet'); - addClass(this.el, 'empty'); - append(parent, $('div.empty-message', null, localize('no open repo', "No source control providers registered."))); + create(parent: HTMLElement): void { + super.create(parent); + this.el = parent; + addClass(this.el, 'scm-viewlet'); + addClass(this.el, 'empty'); + append(parent, $('div.empty-message', null, localize('no open repo', "No source control providers registered."))); - this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); - this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); - this.scmService.repositories.forEach(r => this.onDidAddRepository(r)); + this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); + this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); + this.scmService.repositories.forEach(r => this.onDidAddRepository(r)); - const onDidUpdateConfiguration = filterEvent(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.alwaysShowProviders')); - onDidUpdateConfiguration(this.onDidChangeRepositories, this, this.disposables); + const onDidUpdateConfiguration = filterEvent(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.alwaysShowProviders')); + onDidUpdateConfiguration(this.onDidChangeRepositories, this, this.disposables); - this.onDidChangeRepositories(); + this.onDidChangeRepositories(); - this.contributedViews.onDidAdd(this.onDidAddContributedViews, this, this.disposables); - this.contributedViews.onDidRemove(this.onDidRemoveContributedViews, this, this.disposables); + this.contributedViews.onDidAdd(this.onDidAddContributedViews, this, this.disposables); + this.contributedViews.onDidRemove(this.onDidRemoveContributedViews, this, this.disposables); - let index = this.getContributedViewsStartIndex(); - const contributedViews: IAddedViewDescriptorRef[] = this.contributedViews.visibleViewDescriptors.map(viewDescriptor => { - const size = this.contributedViews.getSize(viewDescriptor.id); - const collapsed = this.contributedViews.isCollapsed(viewDescriptor.id); - return { viewDescriptor, index: index++, size, collapsed }; - }); - if (contributedViews.length) { - this.onDidAddContributedViews(contributedViews); - } - - this.onDidSashChange(this.saveContributedViewSizes, this, this.disposables); + let index = this.getContributedViewsStartIndex(); + const contributedViews: IAddedViewDescriptorRef[] = this.contributedViews.visibleViewDescriptors.map(viewDescriptor => { + const size = this.contributedViews.getSize(viewDescriptor.id); + const collapsed = this.contributedViews.isCollapsed(viewDescriptor.id); + return { viewDescriptor, index: index++, size, collapsed }; }); + if (contributedViews.length) { + this.onDidAddContributedViews(contributedViews); + } + + this.onDidSashChange(this.saveContributedViewSizes, this, this.disposables); } private onDidAddRepository(repository: ISCMRepository): void { @@ -1130,6 +1153,12 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle private onDidChangeRepositories(): void { toggleClass(this.el, 'empty', this.scmService.repositories.length === 0); + if (this.scmService.repositories.length === 0) { + this.el.tabIndex = 0; + } else { + this.el.removeAttribute('tabIndex'); + } + const shouldMainPanelAlwaysBeVisible = this.configurationService.getValue('scm.alwaysShowProviders'); const shouldMainPanelBeVisible = shouldMainPanelAlwaysBeVisible || this.scmService.repositories.length > 1; @@ -1161,9 +1190,16 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle return (this.mainPanel ? 1 : 0) + this.repositoryPanels.length; } - setVisible(visible: boolean): Promise { - const promises: TPromise[] = []; - promises.push(super.setVisible(visible)); + focus(): void { + if (this.scmService.repositories.length === 0) { + this.el.focus(); + } else { + super.focus(); + } + } + + setVisible(visible: boolean): void { + super.setVisible(visible); if (!visible) { this.cachedMainPanelHeight = this.getPanelSize(this.mainPanel); @@ -1175,10 +1211,8 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle for (let i = 0; i < this.contributedViews.visibleViewDescriptors.length; i++) { const panel = this.panels[start + i] as ViewletPanel; - promises.push(panel.setVisible(visible)); + panel.setVisible(visible); } - - return Promise.all(promises).then(() => null); } getOptimalWidth(): number { @@ -1372,7 +1406,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => TPromise.as(actions) + getActions: () => actions }); } @@ -1443,7 +1477,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle return super.isSingleView() && this.repositoryPanels.length + this.contributedViews.visibleViewDescriptors.length === 1; } - openView(id: string, focus?: boolean): TPromise { + openView(id: string, focus?: boolean): IView { if (focus) { this.focus(); } @@ -1453,8 +1487,10 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle } panel = this.panels.filter(panel => panel instanceof ViewletPanel && panel.id === id)[0]; panel.setExpanded(true); - panel.focus(); - return TPromise.as(panel); + if (focus) { + panel.focus(); + } + return panel; } hide(repository: ISCMRepository): void { diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index a7c98574b4c..757fe864db8 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -121,7 +121,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { } // Combine results. - const mergedResults = [].concat(...results.map(r => r.entries)); + const mergedResults: QuickOpenEntry[] = [].concat(...results.map(r => r.entries)); // Sort const compare = (elementA: QuickOpenEntry, elementB: QuickOpenEntry) => compareItemsByScore(elementA, elementB, query, true, QuickOpenItemAccessor, this.scorerCache); diff --git a/src/vs/workbench/parts/search/browser/openFileHandler.ts b/src/vs/workbench/parts/search/browser/openFileHandler.ts index 408b144a5e1..ec191af0f51 100644 --- a/src/vs/workbench/parts/search/browser/openFileHandler.ts +++ b/src/vs/workbench/parts/search/browser/openFileHandler.ts @@ -19,12 +19,12 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler, EditorQuickOpenEntry } from 'vs/workbench/browser/quickopen'; -import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { QueryBuilder, IFileQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IQueryOptions, ISearchService, IFileSearchStats, ISearchQuery, ISearchComplete } from 'vs/platform/search/common/search'; +import { ISearchService, IFileSearchStats, IFileQuery, ISearchComplete } from 'vs/platform/search/common/search'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IRange } from 'vs/editor/common/core/range'; @@ -171,7 +171,7 @@ export class OpenFileHandler extends QuickOpenHandler { return TPromise.wrap({ results: [{ resource: result }] }); } - return this.searchService.search(this.queryBuilder.file(this.contextService.getWorkspace().folders.map(folder => folder.uri), queryOptions), token); + return this.searchService.fileSearch(this.queryBuilder.file(this.contextService.getWorkspace().folders.map(folder => folder.uri), queryOptions), token); }).then(complete => { const results: QuickOpenEntry[] = []; @@ -200,8 +200,9 @@ export class OpenFileHandler extends QuickOpenHandler { return TPromise.as(null); } - private doResolveQueryOptions(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): IQueryOptions { - const queryOptions: IQueryOptions = { + private doResolveQueryOptions(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): IFileQueryBuilderOptions { + const queryOptions: IFileQueryBuilderOptions = { + _reason: 'openFileHandler', extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService), filePattern: query.value, cacheKey @@ -220,12 +221,13 @@ export class OpenFileHandler extends QuickOpenHandler { } onOpen(): void { - this.cacheState = new CacheState(cacheKey => this.cacheQuery(cacheKey), query => this.searchService.search(query), cacheKey => this.searchService.clearCache(cacheKey), this.cacheState); + this.cacheState = new CacheState(cacheKey => this.cacheQuery(cacheKey), query => this.searchService.fileSearch(query), cacheKey => this.searchService.clearCache(cacheKey), this.cacheState); this.cacheState.load(); } - private cacheQuery(cacheKey: string): ISearchQuery { - const options: IQueryOptions = { + private cacheQuery(cacheKey: string): IFileQuery { + const options: IFileQueryBuilderOptions = { + _reason: 'openFileHandler', extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService), filePattern: '', cacheKey: cacheKey, @@ -268,12 +270,12 @@ enum LoadingPhase { export class CacheState { private _cacheKey = defaultGenerator.nextId(); - private query: ISearchQuery; + private query: IFileQuery; private loadingPhase = LoadingPhase.Created; private promise: TPromise; - constructor(cacheQuery: (cacheKey: string) => ISearchQuery, private doLoad: (query: ISearchQuery) => TPromise, private doDispose: (cacheKey: string) => TPromise, private previous: CacheState) { + constructor(cacheQuery: (cacheKey: string) => IFileQuery, private doLoad: (query: IFileQuery) => TPromise, private doDispose: (cacheKey: string) => TPromise, private previous: CacheState) { this.query = cacheQuery(this._cacheKey); if (this.previous) { const current = objects.assign({}, this.query, { cacheKey: null }); diff --git a/src/vs/workbench/parts/search/browser/openSymbolHandler.ts b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts index 6f504eb2685..db2517c2614 100644 --- a/src/vs/workbench/parts/search/browser/openSymbolHandler.ts +++ b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts @@ -85,7 +85,9 @@ class SymbolEntry extends EditorQuickOpenEntry { TPromise.as(this.bearingResolve).then(() => { const scheme = this.bearing.location.uri ? this.bearing.location.uri.scheme : void 0; if (scheme === Schemas.http || scheme === Schemas.https) { - this.openerService.open(this.bearing.location.uri); // support http/https resources (https://github.com/Microsoft/vscode/issues/58924)) + if (mode === Mode.OPEN || mode === Mode.OPEN_IN_BACKGROUND) { + this.openerService.open(this.bearing.location.uri); // support http/https resources (https://github.com/Microsoft/vscode/issues/58924)) + } } else { super.run(mode, context); } diff --git a/src/vs/workbench/parts/search/browser/patternInputWidget.ts b/src/vs/workbench/parts/search/browser/patternInputWidget.ts index ced2a9bbba2..354d942ac0b 100644 --- a/src/vs/workbench/parts/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/parts/search/browser/patternInputWidget.ts @@ -31,7 +31,6 @@ export class PatternInputWidget extends Widget { public inputFocusTracker: dom.IFocusTracker; - protected onOptionChange: (event: Event) => void; private width: number; private placeholder: string; private ariaLabel: string; @@ -50,7 +49,6 @@ export class PatternInputWidget extends Widget { @IContextKeyService private contextKeyService: IContextKeyService ) { super(); - this.onOptionChange = null; this.width = options.width || 100; this.placeholder = options.placeholder || ''; this.ariaLabel = options.ariaLabel || nls.localize('defaultLabel', "input"); @@ -70,19 +68,6 @@ export class PatternInputWidget extends Widget { } } - public on(eventType: string, handler: (event: Event) => void): PatternInputWidget { - switch (eventType) { - case 'keydown': - case 'keyup': - this._register(dom.addDisposableListener(this.inputBox.inputElement, eventType, handler)); - break; - case PatternInputWidget.OPTION_CHANGE: - this.onOptionChange = handler; - break; - } - return this; - } - public setWidth(newWidth: number): void { this.width = newWidth; this.domNode.style.width = this.width + 'px'; @@ -218,7 +203,6 @@ export class ExcludePatternInputWidget extends PatternInputWidget { isChecked: true, })); this._register(this.useExcludesAndIgnoreFilesBox.onChange(viaKeyboard => { - this.onOptionChange(null); if (!viaKeyboard) { this.inputBox.focus(); } diff --git a/src/vs/workbench/parts/search/browser/replaceContributions.ts b/src/vs/workbench/parts/search/browser/replaceContributions.ts index 2a0f463fa68..85865f63594 100644 --- a/src/vs/workbench/parts/search/browser/replaceContributions.ts +++ b/src/vs/workbench/parts/search/browser/replaceContributions.ts @@ -10,6 +10,6 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; export function registerContributions(): void { - registerSingleton(IReplaceService, ReplaceService); + registerSingleton(IReplaceService, ReplaceService, true); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ReplacePreviewContentProvider, LifecyclePhase.Starting); } diff --git a/src/vs/workbench/parts/search/browser/replaceService.ts b/src/vs/workbench/parts/search/browser/replaceService.ts index 92aa7ab0116..0287f28a507 100644 --- a/src/vs/workbench/parts/search/browser/replaceService.ts +++ b/src/vs/workbench/parts/search/browser/replaceService.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as errors from 'vs/base/common/errors'; -import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; import * as network from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -18,7 +17,7 @@ import { IProgressRunner } from 'vs/platform/progress/common/progress'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ScrollType } from 'vs/editor/common/editorCommon'; -import { ITextModel } from 'vs/editor/common/model'; +import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResourceTextEdit } from 'vs/editor/common/modes'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; @@ -47,7 +46,7 @@ export class ReplacePreviewContentProvider implements ITextModelContentProvider, this.textModelResolverService.registerTextModelContentProvider(network.Schemas.internal, this); } - public provideTextContent(uri: URI): TPromise { + public provideTextContent(uri: URI): Thenable { if (uri.fragment === REPLACE_PREVIEW) { return this.instantiationService.createInstance(ReplacePreviewModel).resolve(uri); } @@ -66,14 +65,14 @@ class ReplacePreviewModel extends Disposable { super(); } - resolve(replacePreviewUri: URI): TPromise { + resolve(replacePreviewUri: URI): Thenable { const fileResource = toFileResource(replacePreviewUri); const fileMatch = this.searchWorkbenchService.searchModel.searchResult.matches().filter(match => match.resource().toString() === fileResource.toString())[0]; return this.textModelResolverService.createModelReference(fileResource).then(ref => { ref = this._register(ref); const sourceModel = ref.object.textEditorModel; const sourceModelModeId = sourceModel.getLanguageIdentifier().language; - const replacePreviewModel = this.modelService.createModel(createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), this.modeService.getOrCreateMode(sourceModelModeId), replacePreviewUri); + const replacePreviewModel = this.modelService.createModel(createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), this.modeService.create(sourceModelModeId), replacePreviewUri); this._register(fileMatch.onChange(modelChange => this.update(sourceModel, replacePreviewModel, fileMatch, modelChange))); this._register(this.searchWorkbenchService.searchModel.onReplaceTermChanged(() => this.update(sourceModel, replacePreviewModel, fileMatch))); this._register(fileMatch.onDispose(() => replacePreviewModel.dispose())); // TODO@Sandeep we should not dispose a model directly but rather the reference (depends on https://github.com/Microsoft/vscode/issues/17073) @@ -101,17 +100,17 @@ export class ReplaceService implements IReplaceService { @IBulkEditService private bulkEditorService: IBulkEditService ) { } - public replace(match: Match): TPromise; - public replace(files: FileMatch[], progress?: IProgressRunner): TPromise; - public replace(match: FileMatchOrMatch, progress?: IProgressRunner, resource?: URI): TPromise; - public replace(arg: any, progress: IProgressRunner | null = null, resource: URI | null = null): TPromise { + public replace(match: Match): Promise; + public replace(files: FileMatch[], progress?: IProgressRunner): Promise; + public replace(match: FileMatchOrMatch, progress?: IProgressRunner, resource?: URI): Promise; + public replace(arg: any, progress: IProgressRunner | null = null, resource: URI | null = null): Promise { const edits: ResourceTextEdit[] = this.createEdits(arg, resource); return this.bulkEditorService.apply({ edits }, { progress }).then(() => this.textFileService.saveAll(edits.map(e => e.resource))); } - public openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + public openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable { const fileMatch = element instanceof Match ? element.parent() : element; return this.editorService.openEditor({ @@ -139,13 +138,13 @@ export class ReplaceService implements IReplaceService { }, errors.onUnexpectedError); } - public updateReplacePreview(fileMatch: FileMatch, override: boolean = false): TPromise { + public updateReplacePreview(fileMatch: FileMatch, override: boolean = false): Promise { const replacePreviewUri = toReplaceResource(fileMatch.resource()); - return TPromise.join([this.textModelResolverService.createModelReference(fileMatch.resource()), this.textModelResolverService.createModelReference(replacePreviewUri)]) + return Promise.all([this.textModelResolverService.createModelReference(fileMatch.resource()), this.textModelResolverService.createModelReference(replacePreviewUri)]) .then(([sourceModelRef, replaceModelRef]) => { const sourceModel = sourceModelRef.object.textEditorModel; const replaceModel = replaceModelRef.object.textEditorModel; - let returnValue = TPromise.wrap(null); + let returnValue = Promise.resolve(null); // If model is disposed do not update if (sourceModel && replaceModel) { if (override) { @@ -164,7 +163,7 @@ export class ReplaceService implements IReplaceService { private applyEditsToPreview(fileMatch: FileMatch, replaceModel: ITextModel): void { const resourceEdits = this.createEdits(fileMatch, replaceModel.uri); - const modelEdits = []; + const modelEdits: IIdentifiedSingleEditOperation[] = []; for (const resourceEdit of resourceEdits) { for (const edit of resourceEdit.edits) { const range = Range.lift(edit.range); diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index 6921c98e215..dc23bd390ff 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -11,7 +11,6 @@ import { normalizeDriveLetter } from 'vs/base/common/labels'; import { Schemas } from 'vs/base/common/network'; import { isWindows, OS } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import * as nls from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -45,12 +44,12 @@ export function appendKeyBindingLabel(label: string, keyBinding: number | Resolv } } -export function openSearchView(viewletService: IViewletService, panelService: IPanelService, focus?: boolean): TPromise { +export function openSearchView(viewletService: IViewletService, panelService: IPanelService, focus?: boolean): Thenable { if (viewletService.getViewlets().filter(v => v.id === VIEW_ID).length) { return viewletService.openViewlet(VIEW_ID, focus).then(viewlet => viewlet); } - return panelService.openPanel(VIEW_ID, focus).then(panel => panel); + return Promise.resolve(panelService.openPanel(VIEW_ID, focus) as SearchView); } export function getSearchView(viewletService: IViewletService, panelService: IPanelService): SearchView { @@ -97,10 +96,10 @@ export class FocusNextInputAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); searchView.focusNextInputBox(); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -115,10 +114,10 @@ export class FocusPreviousInputAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); searchView.focusPreviousInputBox(); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -130,7 +129,7 @@ export abstract class FindOrReplaceInFilesAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { return openSearchView(this.viewletService, this.panelService, false).then(openedView => { const searchAndReplaceWidget = openedView.searchAndReplaceWidget; searchAndReplaceWidget.toggleReplace(this.expandSearchReplaceWidget); @@ -165,7 +164,7 @@ export class OpenSearchViewletAction extends FindOrReplaceInFilesAction { super(id, label, viewletService, panelService, /*expandSearchReplaceWidget=*/false); } - public run(): TPromise { + public run(): Thenable { // Pass focus to viewlet if not open or focused if (this.otherViewletShowing() || !isSearchViewFocused(this.viewletService, this.panelService)) { @@ -175,7 +174,7 @@ export class OpenSearchViewletAction extends FindOrReplaceInFilesAction { // Otherwise pass focus to editor group this.editorGroupService.activeGroup.focus(); - return TPromise.as(true); + return Promise.resolve(true); } private otherViewletShowing(): boolean { @@ -205,11 +204,11 @@ export class CloseReplaceAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); searchView.searchAndReplaceWidget.toggleReplace(false); searchView.searchAndReplaceWidget.focus(); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -231,12 +230,12 @@ export class RefreshAction extends Action { this.enabled = searchView && searchView.isSearchSubmitted(); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); if (searchView) { searchView.onQueryChanged(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -258,12 +257,12 @@ export class CollapseDeepestExpandedLevelAction extends Action { this.enabled = searchView && searchView.hasSearchResults(); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); if (searchView) { const viewer = searchView.getControl(); if (viewer.getHighlight()) { - return TPromise.as(null); // Global action disabled if user is in edit mode from another action + return Promise.resolve(null); // Global action disabled if user is in edit mode from another action } /** @@ -299,7 +298,7 @@ export class CollapseDeepestExpandedLevelAction extends Action { viewer.domFocus(); viewer.focusFirst(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -321,12 +320,12 @@ export class ClearSearchResultsAction extends Action { this.enabled = searchView && searchView.isSearchSubmitted(); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); if (searchView) { searchView.clearSearchResults(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -348,13 +347,13 @@ export class CancelSearchAction extends Action { this.enabled = searchView && searchView.isSearching(); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); if (searchView) { searchView.cancelSearch(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -369,7 +368,7 @@ export class FocusNextSearchResultAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { return openSearchView(this.viewletService, this.panelService).then(searchView => { searchView.selectNextMatch(); }); @@ -387,7 +386,7 @@ export class FocusPreviousSearchResultAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { return openSearchView(this.viewletService, this.panelService).then(searchView => { searchView.selectPreviousMatch(); }); @@ -470,7 +469,7 @@ export class RemoveAction extends AbstractSearchAndReplaceAction { super('remove', RemoveAction.LABEL, 'action-remove'); } - public run(): TPromise { + public run(): Thenable { const currentFocusElement = this.viewer.getFocus(); const nextFocusElement = !currentFocusElement || currentFocusElement instanceof SearchResult || elementIsEqualOrParent(currentFocusElement, this.element) ? this.getElementToFocusAfterRemoved(this.viewer, this.element) : @@ -521,7 +520,7 @@ export class ReplaceAllAction extends AbstractSearchAndReplaceAction { super(Constants.ReplaceAllInFileActionId, appendKeyBindingLabel(ReplaceAllAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFileActionId), keyBindingService), 'action-replace-all'); } - public run(): TPromise { + public run(): Thenable { let nextFocusElement = this.getElementToFocusAfterRemoved(this.viewer, this.fileMatch); return this.fileMatch.parent().replace(this.fileMatch).then(() => { if (nextFocusElement) { @@ -543,7 +542,7 @@ export class ReplaceAllInFolderAction extends AbstractSearchAndReplaceAction { super(Constants.ReplaceAllInFolderActionId, appendKeyBindingLabel(ReplaceAllInFolderAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFolderActionId), keyBindingService), 'action-replace-all'); } - public run(): TPromise { + public run(): Thenable { let nextFocusElement = this.getElementToFocusAfterRemoved(this.viewer, this.folderMatch); return this.folderMatch.replaceAll() .then(() => { @@ -567,7 +566,7 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction { super(Constants.ReplaceActionId, appendKeyBindingLabel(ReplaceAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceActionId), keyBindingService), 'action-replace'); } - public run(): TPromise { + public run(): Thenable { this.enabled = false; return this.element.parent().replace(this.element).then(() => { diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 28982417ce1..7ab243e91ae 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -10,7 +10,6 @@ import { IAction } from 'vs/base/common/actions'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ContextMenuEvent, IAccessibilityProvider, IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; import * as nls from 'vs/nls'; import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; @@ -80,15 +79,15 @@ export class SearchDataSource implements IDataSource { return []; } - public getChildren(tree: ITree, element: any): TPromise { - return TPromise.as(this._getChildren(element)); + public getChildren(tree: ITree, element: any): Thenable { + return Promise.resolve(this._getChildren(element)); } public hasChildren(tree: ITree, element: any): boolean { return element instanceof FileMatch || element instanceof FolderMatch || element instanceof SearchResult; } - public getParent(tree: ITree, element: any): TPromise { + public getParent(tree: ITree, element: any): Thenable { let value: any = null; if (element instanceof Match) { @@ -99,7 +98,7 @@ export class SearchDataSource implements IDataSource { value = element.parent(); } - return TPromise.as(value); + return Promise.resolve(value); } public shouldAutoexpand(tree: ITree, element: any): boolean { @@ -425,7 +424,7 @@ export class SearchTreeController extends WorkbenchTreeController { getActions: () => { const actions: IAction[] = []; fillInContextMenuActions(this.contextMenu, { shouldForwardArgs: true }, actions, this.contextMenuService); - return TPromise.as(actions); + return actions; }, getActionsContext: () => element diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index a00c799ff6b..4c1e61a907c 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -18,7 +18,6 @@ import * as paths from 'vs/base/common/paths'; import * as env from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import 'vs/css!./media/searchview'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -31,9 +30,9 @@ import { IConfirmation, IDialogService } from 'vs/platform/dialogs/common/dialog import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TreeResourceNavigator, WorkbenchTree } from 'vs/platform/list/browser/listService'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IPatternInfo, IQueryOptions, ISearchComplete, ISearchConfiguration, ISearchHistoryService, ISearchProgressItem, ISearchQuery, VIEW_ID, ISearchHistoryValues } from 'vs/platform/search/common/search'; +import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchHistoryService, ISearchHistoryValues, ISearchProgressItem, ITextQuery, VIEW_ID, SearchErrorCode } from 'vs/platform/search/common/search'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, editorFindMatchHighlight, editorFindMatchHighlightBorder, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -42,6 +41,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { SimpleFileResourceDragAndDrop } from 'vs/workbench/browser/dnd'; import { Viewlet } from 'vs/workbench/browser/viewlet'; +import { IEditor } from 'vs/workbench/common/editor'; import { IPanel } from 'vs/workbench/common/panel'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/parts/search/browser/patternInputWidget'; @@ -49,7 +49,7 @@ import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLe import { SearchAccessibilityProvider, SearchDataSource, SearchFilter, SearchRenderer, SearchSorter, SearchTreeController } from 'vs/workbench/parts/search/browser/searchResultsView'; import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/parts/search/browser/searchWidget'; import * as Constants from 'vs/workbench/parts/search/common/constants'; -import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/common/search'; import { FileMatch, FileMatchOrMatch, FolderMatch, IChangeEvent, ISearchWorkbenchService, Match, SearchModel } from 'vs/workbench/parts/search/common/searchModel'; @@ -169,7 +169,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } } - public create(parent: HTMLElement): Promise { + public create(parent: HTMLElement): void { super.create(parent); this.viewModel = this._register(this.searchWorkbenchService.searchModel); @@ -273,8 +273,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this._register(this.onDidFocus(() => this.viewletFocused.set(true))); this._register(this.onDidBlur(() => this.viewletFocused.set(false))); - - return Promise.resolve(null); } public get searchAndReplaceWidget(): SearchWidget { @@ -369,23 +367,23 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.layout(this.size); } - private onSearchResultsChanged(event?: IChangeEvent): TPromise { + private onSearchResultsChanged(event?: IChangeEvent): Thenable { if (this.isVisible()) { return this.refreshAndUpdateCount(event); } else { this.changedWhileHidden = true; - return TPromise.wrap(null); + return Promise.resolve(null); } } - private refreshAndUpdateCount(event?: IChangeEvent): TPromise { + private refreshAndUpdateCount(event?: IChangeEvent): Thenable { return this.refreshTree(event).then(() => { this.searchWidget.setReplaceAllActionState(!this.viewModel.searchResult.isEmpty()); this.updateSearchResultCount(); }); } - private refreshTree(event?: IChangeEvent): TPromise { + private refreshTree(event?: IChangeEvent): Thenable { if (!event || event.added || event.removed) { return this.tree.refresh(this.viewModel.searchResult); } else { @@ -653,8 +651,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } } - public setVisible(visible: boolean): Promise { - let promise: Promise; + public setVisible(visible: boolean): void { this.viewletVisible.set(visible); if (visible) { if (this.changedWhileHidden) { @@ -663,11 +660,11 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.changedWhileHidden = false; } - promise = super.setVisible(visible); + super.setVisible(visible); this.tree.onVisible(); } else { this.tree.onHidden(); - promise = super.setVisible(visible); + super.setVisible(visible); } // Enable highlights if there are searchresults @@ -682,8 +679,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.onFocus(focus, true); } } - - return promise; } public moveFocusToResults(): void { @@ -1054,7 +1049,8 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { const charsPerLine = content.isRegExp ? 10000 : 250; - const options: IQueryOptions = { + const options: ITextQueryBuilderOptions = { + _reason: 'searchView', extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService), maxResults: SearchView.MAX_TEXT_RESULTS, disregardIgnoreFiles: !useExcludesAndIgnoreFiles, @@ -1073,7 +1069,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.viewModel.searchResult.clear(); }; - let query: ISearchQuery; + let query: ITextQuery; try { query = this.queryBuilder.text(content, folderResources.map(folder => folder.uri), options); } catch (err) { @@ -1090,14 +1086,14 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { }, onQueryValidationError); } - private validateQuery(query: ISearchQuery): TPromise { + private validateQuery(query: ITextQuery): Thenable { // Validate folderQueries const folderQueriesExistP = query.folderQueries.map(fq => { return this.fileService.existsFile(fq.folder); }); - return TPromise.join(folderQueriesExistP).then(existResults => { + return Promise.resolve(folderQueriesExistP).then(existResults => { // If no folders exist, show an error message about the first one const existingFolderQueries = query.folderQueries.filter((folderQuery, i) => existResults[i]); if (!query.folderQueries.length || existingFolderQueries.length) { @@ -1105,14 +1101,14 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } else { const nonExistantPath = query.folderQueries[0].folder.fsPath; const searchPathNotFoundError = nls.localize('searchPathNotFoundError', "Search path not found: {0}", nonExistantPath); - return TPromise.wrapError(new Error(searchPathNotFoundError)); + return Promise.reject(new Error(searchPathNotFoundError)); } return undefined; }); } - private onQueryTriggered(query: ISearchQuery, excludePatternText: string, includePatternText: string): void { + private onQueryTriggered(query: ITextQuery, excludePatternText: string, includePatternText: string): void { this.inputPatternExcludes.onSearchSubmit(); this.inputPatternIncludes.onSearchSubmit(); @@ -1217,25 +1213,14 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { })); } else { const openSettingsLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('openSettings.message', "Open Settings"))); - this.messageDisposables.push(dom.addDisposableListener(openSettingsLink, dom.EventType.CLICK, (e: MouseEvent) => { - dom.EventHelper.stop(e, false); - - const options: ISettingsEditorOptions = { query: '.exclude' }; - this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? - this.preferencesService.openWorkspaceSettings(undefined, options) : - this.preferencesService.openGlobalSettings(undefined, options); - })); + this.addClickEvents(openSettingsLink, this.onOpenSettings); } if (completed) { dom.append(p, $('span', undefined, ' - ')); const learnMoreLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('openSettings.learnMore', "Learn More"))); - this.messageDisposables.push(dom.addDisposableListener(learnMoreLink, dom.EventType.CLICK, (e: MouseEvent) => { - dom.EventHelper.stop(e, false); - - window.open('https://go.microsoft.com/fwlink/?linkid=853977'); - })); + this.addClickEvents(learnMoreLink, this.onLearnMore); } if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { @@ -1258,6 +1243,21 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { progressRunner.done(); this.searchWidget.searchInput.showMessage({ content: e.message, type: MessageType.ERROR }); this.viewModel.searchResult.clear(); + + if (e.code === SearchErrorCode.unknownEncoding && !this.configurationService.getValue('search.useLegacySearch')) { + this.notificationService.prompt(Severity.Info, nls.localize('otherEncodingWarning', "You can enable \"search.useLegacySearch\" to search non-standard file encodings."), + [{ + label: nls.localize('otherEncodingWarning.openSettingsLabel', "Open Settings"), + run: () => this.openSettings('search.useLegacySearch') + }]); + } else if (e.code === SearchErrorCode.regexParseError && !this.configurationService.getValue('search.usePCRE2')) { + // If the regex parsed in JS but not rg, it likely uses features that are supported in JS and PCRE2 but not Rust + this.notificationService.prompt(Severity.Info, nls.localize('rgRegexError', "You can enable \"search.usePCRE2\" to enable some extra regex features like lookbehind and backreferences."), + [{ + label: nls.localize('otherEncodingWarning.openSettingsLabel', "Open Settings"), + run: () => this.openSettings('search.usePCRE2') + }]); + } } }; @@ -1323,6 +1323,44 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.viewModel.search(query, onProgress).then(onComplete, onError); } + private addClickEvents = (element: HTMLElement, handler: (event: any) => void): void => { + this.messageDisposables.push(dom.addDisposableListener(element, dom.EventType.CLICK, handler)); + this.messageDisposables.push(dom.addDisposableListener(element, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + let event = new StandardKeyboardEvent(e as KeyboardEvent); + let eventHandled = true; + + if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { + handler(e); + } else { + eventHandled = false; + } + + if (eventHandled) { + event.preventDefault(); + event.stopPropagation(); + } + })); + } + + private onOpenSettings = (e: dom.EventLike): void => { + dom.EventHelper.stop(e, false); + + this.openSettings('.exclude'); + } + + private openSettings(query: string): Thenable { + const options: ISettingsEditorOptions = { query }; + return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? + this.preferencesService.openWorkspaceSettings(undefined, options) : + this.preferencesService.openGlobalSettings(undefined, options); + } + + private onLearnMore = (e: MouseEvent): void => { + dom.EventHelper.stop(e, false); + + window.open('https://go.microsoft.com/fwlink/?linkid=853977'); + } + private updateSearchResultCount(): void { const fileCount = this.viewModel.searchResult.fileCount(); this.hasSearchResultsKey.set(fileCount > 0); @@ -1388,10 +1426,10 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.currentSelectedFileMatch = null; } - private onFocus(lineMatch: any, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + private onFocus(lineMatch: any, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable { if (!(lineMatch instanceof Match)) { this.viewModel.searchResult.rangeHighlightDecorations.removeHighlightRange(); - return TPromise.as(true); + return Promise.resolve(true); } const useReplacePreview = this.configurationService.getValue().search.useReplacePreview; @@ -1400,7 +1438,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.open(lineMatch, preserveFocus, sideBySide, pinned); } - public open(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + public open(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable { const selection = this.getSelectionFrom(element); const resource = element instanceof Match ? element.parent().resource() : (element).resource(); return this.editorService.openEditor({ @@ -1424,7 +1462,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { if (editor) { return this.editorGroupsService.activateGroup(editor.group); } else { - return TPromise.wrap(null); + return Promise.resolve(null); } }, errors.onUnexpectedError); } diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index b56f1a72fae..806ce9f553e 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -17,7 +17,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as env from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CONTEXT_FIND_WIDGET_NOT_VISIBLE } from 'vs/editor/contrib/find/findModel'; import * as nls from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -67,11 +66,11 @@ class ReplaceAllAction extends Action { this._searchWidget = searchWidget; } - run(): TPromise { + run(): Promise { if (this._searchWidget) { return this._searchWidget.triggerReplaceAll(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -307,7 +306,7 @@ export class SearchWidget extends Widget { this._register(this.searchInputFocusTracker.onDidFocus(() => { this.searchInputBoxFocused.set(true); - const useGlobalFindBuffer = this.configurationService.getValue('search').globalFindClipboard; + const useGlobalFindBuffer = this.searchConfiguration.globalFindClipboard; if (!this.ignoreGlobalFindBufferOnNextFocus && useGlobalFindBuffer) { const globalBufferText = this.clipboardServce.readFindText(); if (this.previousGlobalFindBufferValue !== globalBufferText) { @@ -349,9 +348,9 @@ export class SearchWidget extends Widget { this._register(this.replaceInputFocusTracker.onDidBlur(() => this.replaceInputBoxFocused.set(false))); } - triggerReplaceAll(): TPromise { + triggerReplaceAll(): Promise { this._onReplaceAll.fire(); - return TPromise.as(null); + return Promise.resolve(null); } private onToggleReplaceButton(): void { @@ -398,7 +397,9 @@ export class SearchWidget extends Widget { } if (strings.regExpContainsBackreference(value)) { - return { content: nls.localize('regexp.backreferenceValidationFailure', "Backreferences are not supported") }; + if (!this.searchConfiguration.usePCRE2) { + return { content: nls.localize('regexp.backreferenceValidationFailure', "Backreferences are not supported") }; + } } return null; @@ -475,8 +476,13 @@ export class SearchWidget extends Widget { } private submitSearch(): void { + this.searchInput.validate(); + if (!this.searchInput.inputBox.isInputValid()) { + return; + } + const value = this.searchInput.getValue(); - const useGlobalFindBuffer = this.configurationService.getValue('search').globalFindClipboard; + const useGlobalFindBuffer = this.searchConfiguration.globalFindClipboard; if (value) { if (useGlobalFindBuffer) { this.clipboardServce.writeFindText(value); @@ -492,6 +498,10 @@ export class SearchWidget extends Widget { this.replaceActionBar = null; super.dispose(); } + + private get searchConfiguration(): ISearchConfigurationProperties { + return this.configurationService.getValue('search'); + } } export function registerContributions() { diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index 370b0d4cb72..61641c5ac7c 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import * as arrays from 'vs/base/common/arrays'; -import * as objects from 'vs/base/common/objects'; import * as collections from 'vs/base/common/collections'; -import * as strings from 'vs/base/common/strings'; import * as glob from 'vs/base/common/glob'; +import { untildify } from 'vs/base/common/labels'; +import * as objects from 'vs/base/common/objects'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; +import * as strings from 'vs/base/common/strings'; import { URI as uri } from 'vs/base/common/uri'; -import { untildify } from 'vs/base/common/labels'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IPatternInfo, IQueryOptions, IFolderQuery, ISearchQuery, QueryType, ISearchConfiguration, getExcludes, pathIncludedInQuery } from 'vs/platform/search/common/search'; +import { isMultilineRegexSource } from 'vs/editor/common/model/textModelSearch'; +import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { isMultilineRegexSource } from 'vs/editor/common/model/textModelSearch'; +import { getExcludes, ICommonQueryProps, IFileQuery, IFolderQuery, IPatternInfo, ISearchConfiguration, ITextQuery, ITextSearchPreviewOptions, pathIncludedInQuery, QueryType } from 'vs/platform/search/common/search'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; export interface ISearchPathPattern { searchPath: uri; @@ -29,6 +29,35 @@ export interface ISearchPathsResult { pattern?: glob.IExpression; } +export interface ICommonQueryBuilderOptions { + _reason?: string; + excludePattern?: string; + includePattern?: string; + extraFileResources?: uri[]; + + maxResults?: number; + maxFileSize?: number; + useRipgrep?: boolean; + disregardIgnoreFiles?: boolean; + disregardGlobalIgnoreFiles?: boolean; + disregardExcludeSettings?: boolean; + ignoreSymlinks?: boolean; +} + +export interface IFileQueryBuilderOptions extends ICommonQueryBuilderOptions { + filePattern?: string; + exists?: boolean; + sortByScore?: boolean; + cacheKey?: string; +} + +export interface ITextQueryBuilderOptions extends ICommonQueryBuilderOptions { + previewOptions?: ITextSearchPreviewOptions; + fileEncoding?: string; + beforeContext?: number; + afterContext?: number; +} + export class QueryBuilder { constructor( @@ -37,81 +66,75 @@ export class QueryBuilder { @IEnvironmentService private environmentService: IEnvironmentService ) { } - public text(contentPattern: IPatternInfo, folderResources?: uri[], options?: IQueryOptions): ISearchQuery { - return this.query(QueryType.Text, contentPattern, folderResources, options); + text(contentPattern: IPatternInfo, folderResources?: uri[], options: ITextQueryBuilderOptions = {}): ITextQuery { + contentPattern.isCaseSensitive = this.isCaseSensitive(contentPattern); + contentPattern.isMultiline = this.isMultiline(contentPattern); + const searchConfig = this.configurationService.getValue(); + contentPattern.wordSeparators = searchConfig.editor.wordSeparators; + + const fallbackToPCRE = folderResources && folderResources.some(folder => { + const folderConfig = this.configurationService.getValue({ resource: folder }); + return !folderConfig.search.useRipgrep; + }); + + const commonQuery = this.commonQuery(folderResources, options); + return { + ...commonQuery, + type: QueryType.Text, + contentPattern, + previewOptions: options.previewOptions, + maxFileSize: options.maxFileSize, + usePCRE2: searchConfig.search.usePCRE2 || fallbackToPCRE || false, + beforeContext: options.beforeContext, + afterContext: options.afterContext + }; } - public file(folderResources?: uri[], options?: IQueryOptions): ISearchQuery { - return this.query(QueryType.File, null, folderResources, options); - } - - private query(type: QueryType, contentPattern?: IPatternInfo, folderResources?: uri[], options: IQueryOptions = {}): ISearchQuery { - let { searchPaths, pattern: includePattern } = this.parseSearchPaths(options.includePattern); - let excludePattern = this.parseExcludePattern(options.excludePattern); - - // Build folderQueries from searchPaths, if given, otherwise folderResources - let folderQueries = folderResources && folderResources.map(uri => this.getFolderQueryForRoot(uri, type === QueryType.File, options)); - if (searchPaths && searchPaths.length) { - const allRootExcludes = folderQueries && this.mergeExcludesFromFolderQueries(folderQueries); - folderQueries = searchPaths.map(searchPath => this.getFolderQueryForSearchPath(searchPath)); - if (allRootExcludes) { - excludePattern = objects.mixin(excludePattern || Object.create(null), allRootExcludes); - } - } - - // TODO@rob - see #37998 - const useIgnoreFiles = !folderResources || folderResources.every(folder => { - const folderConfig = this.configurationService.getValue({ resource: folder }); - return folderConfig.search.useIgnoreFiles; - }); - - const useGlobalIgnoreFiles = !folderResources || folderResources.every(folder => { - const folderConfig = this.configurationService.getValue({ resource: folder }); - return folderConfig.search.useGlobalIgnoreFiles; - }); - - const useRipgrep = !folderResources || folderResources.every(folder => { - const folderConfig = this.configurationService.getValue({ resource: folder }); - return folderConfig.search.useRipgrep; - }); - - const ignoreSymlinks = !this.configurationService.getValue().search.followSymlinks; - - if (contentPattern) { - contentPattern.isCaseSensitive = this.isCaseSensitive(contentPattern); - contentPattern.isMultiline = this.isMultiline(contentPattern); - - contentPattern.wordSeparators = this.configurationService.getValue().editor.wordSeparators; - } - - const query: ISearchQuery = { - type, - folderQueries, - usingSearchPaths: !!(searchPaths && searchPaths.length), - extraFileResources: options.extraFileResources, + file(folderResources: uri[] | undefined, options: IFileQueryBuilderOptions = {}): IFileQuery { + const commonQuery = this.commonQuery(folderResources, options); + return { + ...commonQuery, + type: QueryType.File, filePattern: options.filePattern ? options.filePattern.trim() : options.filePattern, + exists: options.exists, + sortByScore: options.sortByScore, + cacheKey: options.cacheKey + }; + } + + private commonQuery(folderResources?: uri[], options: ICommonQueryBuilderOptions = {}): ICommonQueryProps { + let { searchPaths, pattern: includePattern } = this.parseSearchPaths(options.includePattern || ''); + let excludePattern = this.parseExcludePattern(options.excludePattern || ''); + + // Build folderQueries from searchPaths, if given, otherwise folderResources + const folderQueries = searchPaths && searchPaths.length ? + searchPaths.map(searchPath => this.getFolderQueryForSearchPath(searchPath, options)) : + folderResources && folderResources.map(uri => this.getFolderQueryForRoot(uri, options)); + + const useRipgrep = !folderResources || folderResources.every(folder => { + const folderConfig = this.configurationService.getValue({ resource: folder }); + return !folderConfig.search.useLegacySearch; + }); + + const queryProps: ICommonQueryProps = { + _reason: options._reason, + folderQueries: folderQueries || [], + usingSearchPaths: !!(searchPaths && searchPaths.length), + extraFileResources: options.extraFileResources, + excludePattern, includePattern, maxResults: options.maxResults, - sortByScore: options.sortByScore, - cacheKey: options.cacheKey, - contentPattern, - useRipgrep, - disregardIgnoreFiles: options.disregardIgnoreFiles || !useIgnoreFiles, - disregardGlobalIgnoreFiles: options.disregardGlobalIgnoreFiles || !useGlobalIgnoreFiles, - disregardExcludeSettings: options.disregardExcludeSettings, - ignoreSymlinks, - previewOptions: options.previewOptions, - exists: options.exists + useRipgrep }; // Filter extraFileResources against global include/exclude patterns - they are already expected to not belong to a workspace - let extraFileResources = options.extraFileResources && options.extraFileResources.filter(extraFile => pathIncludedInQuery(query, extraFile.fsPath)); - query.extraFileResources = extraFileResources && extraFileResources.length ? extraFileResources : undefined; + let extraFileResources = options.extraFileResources && options.extraFileResources.filter(extraFile => pathIncludedInQuery(queryProps, extraFile.fsPath)); + queryProps.extraFileResources = extraFileResources && extraFileResources.length ? extraFileResources : undefined; - return query; + return queryProps; } /** @@ -129,7 +152,7 @@ export class QueryBuilder { } } - return contentPattern.isCaseSensitive; + return !!contentPattern.isCaseSensitive; } private isMultiline(contentPattern: IPatternInfo): boolean { @@ -210,32 +233,7 @@ export class QueryBuilder { return Object.keys(excludeExpression).length ? excludeExpression : undefined; } - private mergeExcludesFromFolderQueries(folderQueries: IFolderQuery[]): glob.IExpression | undefined { - const mergedExcludes = folderQueries.reduce((merged: glob.IExpression, fq: IFolderQuery) => { - if (fq.excludePattern) { - objects.mixin(merged, this.getAbsoluteIExpression(fq.excludePattern, fq.folder.fsPath)); - } - - return merged; - }, Object.create(null)); - - // Don't return an empty IExpression - return Object.keys(mergedExcludes).length ? mergedExcludes : undefined; - } - - private getAbsoluteIExpression(expr: glob.IExpression, root: string): glob.IExpression { - return Object.keys(expr) - .reduce((absExpr: glob.IExpression, key: string) => { - if (expr[key] && !paths.isAbsolute(key)) { - const absPattern = paths.join(root, key); - absExpr[absPattern] = expr[key]; - } - - return absExpr; - }, Object.create(null)); - } - - private getExcludesForFolder(folderConfig: ISearchConfiguration, options: IQueryOptions): glob.IExpression | undefined { + private getExcludesForFolder(folderConfig: ISearchConfiguration, options: ICommonQueryBuilderOptions): glob.IExpression | undefined { return options.disregardExcludeSettings ? undefined : getExcludes(folderConfig); @@ -303,24 +301,50 @@ export class QueryBuilder { return []; } - private getFolderQueryForSearchPath(searchPath: ISearchPathPattern): IFolderQuery { - const folder = searchPath.searchPath; - const folderConfig = this.configurationService.getValue({ resource: folder }); - return { - folder, - includePattern: searchPath.pattern && patternListToIExpression([searchPath.pattern]), - fileEncoding: folderConfig.files && folderConfig.files.encoding + private getFolderQueryForSearchPath(searchPath: ISearchPathPattern, options: ICommonQueryBuilderOptions): IFolderQuery { + const searchPathWorkspaceFolder = this.workspaceContextService.getWorkspaceFolder(searchPath.searchPath); + const searchPathRelativePath = searchPathWorkspaceFolder && searchPath.searchPath.path.substr(searchPathWorkspaceFolder.uri.path.length + 1); + + const rootConfig = this.getFolderQueryForRoot(searchPath.searchPath, options); + let resolvedExcludes: glob.IExpression = {}; + if (searchPathWorkspaceFolder && rootConfig.excludePattern) { + // Resolve excludes relative to the search path + for (let excludePattern in rootConfig.excludePattern) { + const { pathPortion, globPortion } = splitSimpleGlob(excludePattern); + if (!pathPortion) { // **/foo + if (globPortion) { + resolvedExcludes[globPortion] = rootConfig.excludePattern[excludePattern]; + } + } else if (strings.startsWith(pathPortion, searchPathRelativePath)) { // searchPathRelativePath/something/**/foo + // Strip `searchPathRelativePath/` + const resolvedPathPortion = pathPortion.substr(searchPathRelativePath.length + 1); + const resolvedPattern = globPortion ? + resolvedPathPortion + globPortion : + resolvedPathPortion; + + resolvedExcludes[resolvedPattern] = rootConfig.excludePattern[excludePattern]; + } + } + } + + return { + ...rootConfig, + ...{ + includePattern: searchPath.pattern ? patternListToIExpression([searchPath.pattern]) : undefined, + excludePattern: Object.keys(resolvedExcludes).length ? resolvedExcludes : undefined + } }; } - private getFolderQueryForRoot(folder: uri, perFolderUseIgnoreFiles: boolean, options?: IQueryOptions): IFolderQuery { + private getFolderQueryForRoot(folder: uri, options: ICommonQueryBuilderOptions): IFolderQuery { const folderConfig = this.configurationService.getValue({ resource: folder }); return { folder, excludePattern: this.getExcludesForFolder(folderConfig, options), fileEncoding: folderConfig.files && folderConfig.files.encoding, - disregardIgnoreFiles: perFolderUseIgnoreFiles ? !folderConfig.search.useIgnoreFiles : undefined, - disregardGlobalIgnoreFiles: perFolderUseIgnoreFiles ? !folderConfig.search.useGlobalIgnoreFiles : undefined + disregardIgnoreFiles: typeof options.disregardIgnoreFiles === 'boolean' ? options.disregardIgnoreFiles : !folderConfig.search.useIgnoreFiles, + disregardGlobalIgnoreFiles: typeof options.disregardGlobalIgnoreFiles === 'boolean' ? options.disregardGlobalIgnoreFiles : !folderConfig.search.useGlobalIgnoreFiles, + ignoreSymlinks: typeof options.ignoreSymlinks === 'boolean' ? options.ignoreSymlinks : !folderConfig.search.followSymlinks, }; } } @@ -333,13 +357,13 @@ function splitGlobFromPath(searchPath: string): { pathPortion: string, globPorti if (lastSlashMatch) { let pathPortion = searchPath.substr(0, lastSlashMatch.index); if (!pathPortion.match(/[/\\]/)) { - // If the last slash was the only slash, then we now have '' or 'C:'. Append a slash. + // If the last slash was the only slash, then we now have '' or 'C:' or '.'. Append a slash. pathPortion += '/'; } return { pathPortion, - globPortion: searchPath.substr(lastSlashMatch.index + 1) + globPortion: searchPath.substr((lastSlashMatch.index || 0) + 1) }; } } @@ -350,6 +374,22 @@ function splitGlobFromPath(searchPath: string): { pathPortion: string, globPorti }; } +function splitSimpleGlob(searchPath: string): { pathPortion: string, globPortion?: string } { + const globCharMatch = searchPath.match(/[\*\{\}\(\)\[\]\?]/); + if (globCharMatch) { + const globCharIdx = globCharMatch.index || 0; + return { + pathPortion: searchPath.substr(0, globCharIdx), + globPortion: searchPath.substr(globCharIdx) + }; + } + + // No glob char + return { + pathPortion: searchPath + }; +} + function patternListToIExpression(patterns: string[]): glob.IExpression { return patterns.length ? patterns.reduce((glob, cur) => { glob[cur] = true; return glob; }, Object.create(null)) : diff --git a/src/vs/workbench/parts/search/common/replace.ts b/src/vs/workbench/parts/search/common/replace.ts index 953a4704eda..8f2d2e9026d 100644 --- a/src/vs/workbench/parts/search/common/replace.ts +++ b/src/vs/workbench/parts/search/common/replace.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Match, FileMatch, FileMatchOrMatch } from 'vs/workbench/parts/search/common/searchModel'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgressRunner } from 'vs/platform/progress/common/progress'; @@ -17,22 +16,22 @@ export interface IReplaceService { /** * Replaces the given match in the file that match belongs to */ - replace(match: Match): TPromise; + replace(match: Match): Thenable; /** * Replace all the matches from the given file matches in the files * You can also pass the progress runner to update the progress of replacing. */ - replace(files: FileMatch[], progress?: IProgressRunner): TPromise; + replace(files: FileMatch[], progress?: IProgressRunner): Thenable; /** * Opens the replace preview for given file match or match */ - openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise; + openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable; /** * Update the replace preview for the given file. * If `override` is `true`, then replace preview is constructed from source model */ - updateReplacePreview(file: FileMatch, override?: boolean): TPromise; + updateReplacePreview(file: FileMatch, override?: boolean): Thenable; } diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index 095a46e2f52..c70002daa0b 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -13,7 +13,6 @@ import { ResourceMap, TernarySearchTree, values } from 'vs/base/common/map'; import * as objects from 'vs/base/common/objects'; import { lcut } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Range } from 'vs/editor/common/core/range'; import { FindMatch, IModelDeltaDecoration, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -21,11 +20,12 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgressRunner } from 'vs/platform/progress/common/progress'; import { ReplacePattern } from 'vs/platform/search/common/replace'; -import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, TextSearchResult } from 'vs/platform/search/common/search'; +import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchService, ITextQuery, ITextSearchPreviewOptions, ITextSearchMatch, ITextSearchStats, resultIsMatch, ISearchRange, OneLineRange } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; +import { editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; export class Match { @@ -36,18 +36,22 @@ export class Match { private _previewText: string; private _rangeInPreviewText: Range; - constructor(private _parent: FileMatch, _result: ITextSearchResult) { + constructor(private _parent: FileMatch, _result: ITextSearchMatch) { + if (Array.isArray(_result.ranges) || Array.isArray(_result.preview.matches)) { + throw new Error('A Match can only be built from a single search result'); + } + this._range = new Range( - _result.range.startLineNumber + 1, - _result.range.startColumn + 1, - _result.range.endLineNumber + 1, - _result.range.endColumn + 1); + _result.ranges.startLineNumber + 1, + _result.ranges.startColumn + 1, + _result.ranges.endLineNumber + 1, + _result.ranges.endColumn + 1); this._rangeInPreviewText = new Range( - _result.preview.match.startLineNumber + 1, - _result.preview.match.startColumn + 1, - _result.preview.match.endLineNumber + 1, - _result.preview.match.endColumn + 1); + _result.preview.matches.startLineNumber + 1, + _result.preview.matches.startColumn + 1, + _result.preview.matches.endLineNumber + 1, + _result.preview.matches.endColumn + 1); this._previewText = _result.preview.text; this._id = this._parent.id() + '>' + this._range + this.getMatchString(); @@ -170,10 +174,12 @@ export class FileMatch extends Disposable { this.bindModel(model); this.updateMatchesForModel(); } else { - this.rawMatch.matches.forEach(rawMatch => { - let match = new Match(this, rawMatch); - this.add(match); - }); + this.rawMatch.results + .filter(resultIsMatch) + .forEach(rawMatch => { + textSearchResultToMatches(rawMatch, this) + .forEach(m => this.add(m)); + }); } } @@ -237,16 +243,16 @@ export class FileMatch extends Disposable { } private updateMatches(matches: FindMatch[], modelChange: boolean) { - matches.forEach(m => { - const textSearchResult = editorMatchToTextSearchResult(m, this._model, this._previewOptions); - const match = new Match(this, textSearchResult); - - if (!this._removedMatches.has(match.id())) { - this.add(match); - if (this.isMatchSelected(match)) { - this._selectedMatch = match; + const textSearchResults = editorMatchesToTextSearchResults(matches, this._model, this._previewOptions); + textSearchResults.forEach(textSearchResult => { + textSearchResultToMatches(textSearchResult, this).forEach(match => { + if (!this._removedMatches.has(match.id())) { + this.add(match); + if (this.isMatchSelected(match)) { + this._selectedMatch = match; + } } - } + }); }); this._onChange.fire(modelChange); @@ -286,7 +292,7 @@ export class FileMatch extends Disposable { this._onChange.fire(false); } - public replace(toReplace: Match): TPromise { + public replace(toReplace: Match): Thenable { return this.replaceService.replace(toReplace) .then(() => this.updatesMatchesForLineAfterReplace(toReplace.range().startLineNumber, false)); } @@ -366,7 +372,7 @@ export class FolderMatch extends Disposable { private _unDisposedFileMatches: ResourceMap; private _replacingAll: boolean = false; - constructor(private _resource: URI | null, private _id: string, private _index: number, private _query: ISearchQuery, private _parent: SearchResult, private _searchModel: SearchModel, @IReplaceService private replaceService: IReplaceService, + constructor(private _resource: URI | null, private _id: string, private _index: number, private _query: ITextQuery, private _parent: SearchResult, private _searchModel: SearchModel, @IReplaceService private replaceService: IReplaceService, @IInstantiationService private instantiationService: IInstantiationService) { super(); this._fileMatches = new ResourceMap(); @@ -415,10 +421,13 @@ export class FolderMatch extends Disposable { raw.forEach((rawFileMatch) => { if (this._fileMatches.has(rawFileMatch.resource)) { const existingFileMatch = this._fileMatches.get(rawFileMatch.resource); - rawFileMatch.matches.forEach(m => { - let match = new Match(existingFileMatch, m); - existingFileMatch.add(match); - }); + rawFileMatch + .results + .filter(resultIsMatch) + .forEach(m => { + textSearchResultToMatches(m, existingFileMatch) + .forEach(m => existingFileMatch.add(m)); + }); updated.push(existingFileMatch); } else { const fileMatch = this.instantiationService.createInstance(FileMatch, this._query.contentPattern, this._query.previewOptions, this._query.maxResults, this, rawFileMatch); @@ -445,13 +454,13 @@ export class FolderMatch extends Disposable { this.doRemove(match); } - public replace(match: FileMatch): TPromise { + public replace(match: FileMatch): Thenable { return this.replaceService.replace([match]).then(() => { this.doRemove(match, false, true); }); } - public replaceAll(): TPromise { + public replaceAll(): Thenable { const matches = this.matches(); return this.replaceService.replace(matches).then(() => { matches.forEach(match => this.doRemove(match, false, true)); @@ -563,7 +572,7 @@ export class SearchResult extends Disposable { this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); } - public set query(query: ISearchQuery) { + public set query(query: ITextQuery) { // When updating the query we could change the roots, so ensure we clean up the old roots first. this.clear(); this._folderMatches = (query.folderQueries || []) @@ -574,7 +583,7 @@ export class SearchResult extends Disposable { this._otherFilesMatch = this.createFolderMatch(null, 'otherFiles', this._folderMatches.length + 1, query); } - private createFolderMatch(resource: URI | null, id: string, index: number, query: ISearchQuery): FolderMatch { + private createFolderMatch(resource: URI | null, id: string, index: number, query: ITextQuery): FolderMatch { const folderMatch = this.instantiationService.createInstance(FolderMatch, resource, id, index, query, this, this._searchModel); const disposable = folderMatch.onChange((event) => this._onChange.fire(event)); folderMatch.onDispose(() => disposable.dispose()); @@ -631,11 +640,11 @@ export class SearchResult extends Disposable { } } - public replace(match: FileMatch): TPromise { + public replace(match: FileMatch): Thenable { return this.getFolderMatch(match.resource()).replace(match); } - public replaceAll(progressRunner: IProgressRunner): TPromise { + public replaceAll(progressRunner: IProgressRunner): Thenable { this.replacingAll = true; const promise = this.replaceService.replace(this.matches(), progressRunner); @@ -744,7 +753,7 @@ export class SearchResult extends Disposable { export class SearchModel extends Disposable { private _searchResult: SearchResult; - private _searchQuery: ISearchQuery | null = null; + private _searchQuery: ITextQuery | null = null; private _replaceActive: boolean = false; private _replaceString: string | null = null; private _replacePattern: ReplacePattern | null = null; @@ -787,7 +796,7 @@ export class SearchModel extends Disposable { return this._searchResult; } - public search(query: ISearchQuery, onProgress?: (result: ISearchProgressItem) => void): TPromise { + public search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Thenable { this.cancelSearch(); this._searchQuery = query; @@ -798,7 +807,7 @@ export class SearchModel extends Disposable { this._replacePattern = new ReplacePattern(this._replaceString, this._searchQuery.contentPattern); const tokenSource = this.currentCancelTokenSource = new CancellationTokenSource(); - const currentRequest = this.searchService.search(this._searchQuery, this.currentCancelTokenSource.token, p => { + const currentRequest = this.searchService.textSearch(this._searchQuery, this.currentCancelTokenSource.token, p => { progressEmitter.fire(); this.onSearchProgress(p); @@ -1005,9 +1014,46 @@ export class RangeHighlightDecorations implements IDisposable { }); } -export function editorMatchToTextSearchResult(match: FindMatch, model: ITextModel, previewOptions: ITextSearchPreviewOptions): TextSearchResult { - return new TextSearchResult( - model.getLineContent(match.range.startLineNumber), - new Range(match.range.startLineNumber - 1, match.range.startColumn - 1, match.range.endLineNumber - 1, match.range.endColumn - 1), - previewOptions); +function textSearchResultToMatches(rawMatch: ITextSearchMatch, fileMatch: FileMatch): Match[] { + if (Array.isArray(rawMatch.ranges)) { + const previewLines = rawMatch.preview.text.split('\n'); + return rawMatch.ranges.map((r, i) => { + const previewRange: ISearchRange = rawMatch.preview.matches[i]; + const matchText = previewLines[previewRange.startLineNumber]; + const adjustedEndCol = previewRange.startLineNumber === previewRange.endLineNumber ? + previewRange.endColumn : + matchText.length; + const adjustedRange = new OneLineRange(0, previewRange.startColumn, adjustedEndCol); + + return new Match(fileMatch, { + uri: rawMatch.uri, + ranges: r, + preview: { + text: matchText, + matches: adjustedRange + } + }); + }); + } else { + const firstNewlineIdx = rawMatch.preview.text.indexOf('\n'); + const matchText = firstNewlineIdx >= 0 ? + rawMatch.preview.text.slice(0, firstNewlineIdx) : + rawMatch.preview.text; + const previewRange = rawMatch.preview.matches; + const adjustedEndCol = previewRange.startLineNumber === previewRange.endLineNumber ? + previewRange.endColumn : + matchText.length; + const adjustedRange = new OneLineRange(0, previewRange.startColumn, adjustedEndCol); + + const adjustedMatch: ITextSearchMatch = { + preview: { + text: matchText, + matches: adjustedRange + }, + ranges: rawMatch.ranges + }; + + let match = new Match(fileMatch, adjustedMatch); + return [match]; + } } diff --git a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts index 62af8ecc289..b38067dd9f3 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -9,7 +9,6 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; @@ -58,7 +57,7 @@ import { SearchViewLocationUpdater } from 'vs/workbench/parts/search/browser/sea import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -registerSingleton(ISearchWorkbenchService, SearchWorkbenchService); +registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); replaceContributions(); searchWidgetContributions(); @@ -430,7 +429,7 @@ class ShowAllSymbolsAction extends Action { this.enabled = !!this.quickOpenService; } - public run(context?: any): TPromise { + public run(context?: any): Promise { let prefix = ShowAllSymbolsAction.ALL_SYMBOLS_PREFIX; let inputSelection: { start: number; end: number; } = void 0; @@ -443,7 +442,7 @@ class ShowAllSymbolsAction extends Action { this.quickOpenService.show(prefix, { inputSelection }); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -586,9 +585,15 @@ configurationRegistry.registerConfiguration({ }, 'search.useRipgrep': { type: 'boolean', - description: nls.localize('useRipgrep', "Controls whether to use ripgrep in text and file search."), + description: nls.localize('useRipgrep', "This setting is deprecated and now falls back on \"search.usePCRE2\"."), + deprecationMessage: nls.localize('useRipgrepDeprecated', "Deprecated. Consider \"search.usePCRE2\" for advanced regex feature support."), default: true }, + 'search.useLegacySearch': { + type: 'boolean', + description: nls.localize('useLegacySearch', "Controls whether to use the deprecated legacy mode for text and file search. It supports some text encodings that are not supported by the standard ripgrep-based search."), + default: false + }, 'search.useIgnoreFiles': { type: 'boolean', markdownDescription: nls.localize('useIgnoreFiles', "Controls whether to use `.gitignore` and `.ignore` files when searching for files."), @@ -663,7 +668,12 @@ configurationRegistry.registerConfiguration({ 'search.runInExtensionHost': { type: 'boolean', default: false, - description: nls.localize('search.searchRipgrepEnable', "Whether to run search in the extension host") + description: nls.localize('search.runInExtensionHost', "Whether to run search in the extension host. Requires a restart to take effect.") + }, + 'search.usePCRE2': { + type: 'boolean', + default: false, + description: nls.localize('search.usePCRE2', "Whether to use the PCRE2 regex engine in text search. This enables using some advanced regex features like lookbehind and backreferences. However, not all PCRE2 features are supported - only features that are also supported by JavaScript.") } } }); @@ -696,4 +706,4 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { title: nls.localize({ key: 'miGotoSymbolInWorkspace', comment: ['&& denotes a mnemonic'] }, "Go to Symbol in &&Workspace...") }, order: 3 -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts b/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts index 95b77c6a04f..ced5dcf2132 100644 --- a/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts +++ b/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts @@ -6,14 +6,13 @@ import * as assert from 'assert'; import * as errors from 'vs/base/common/errors'; import * as objects from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CacheState } from 'vs/workbench/parts/search/browser/openFileHandler'; -import { DeferredTPromise } from 'vs/base/test/common/utils'; -import { QueryType, ISearchQuery } from 'vs/platform/search/common/search'; +import { DeferredPromise } from 'vs/base/test/common/utils'; +import { QueryType, IFileQuery } from 'vs/platform/search/common/search'; suite('CacheState', () => { - test('reuse old cacheKey until new cache is loaded', function () { + test('reuse old cacheKey until new cache is loaded', async function () { const cache = new MockCache(); @@ -26,7 +25,7 @@ suite('CacheState', () => { assert.strictEqual(first.isLoaded, false); assert.strictEqual(first.isUpdating, true); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); assert.strictEqual(first.isLoaded, true); assert.strictEqual(first.isUpdating, false); @@ -34,18 +33,18 @@ suite('CacheState', () => { second.load(); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, true); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); assert.strictEqual(second.cacheKey, firstKey); // still using old cacheKey const secondKey = cache.cacheKeys[1]; - cache.loading[secondKey].complete(null); + await cache.loading[secondKey].complete(null); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); assert.strictEqual(second.cacheKey, secondKey); }); - test('do not spawn additional load if previous is still loading', function () { + test('do not spawn additional load if previous is still loading', async function () { const cache = new MockCache(); @@ -64,29 +63,29 @@ suite('CacheState', () => { assert.strictEqual(Object.keys(cache.loading).length, 1); // still only one loading assert.strictEqual(second.cacheKey, firstKey); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); }); - test('do not use previous cacheKey if query changed', function () { + test('do not use previous cacheKey if query changed', async function () { const cache = new MockCache(); const first = createCacheState(cache); const firstKey = first.cacheKey; first.load(); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); assert.strictEqual(first.isLoaded, true); assert.strictEqual(first.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); cache.baseQuery.excludePattern = { '**/node_modules': true }; const second = createCacheState(cache, first); assert.strictEqual(second.isLoaded, false); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); second.load(); assert.strictEqual(second.isLoaded, false); @@ -95,40 +94,40 @@ suite('CacheState', () => { const secondKey = cache.cacheKeys[1]; assert.strictEqual(second.cacheKey, secondKey); - cache.loading[secondKey].complete(null); + await cache.loading[secondKey].complete(null); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); }); - test('dispose propagates', function () { + test('dispose propagates', async function () { const cache = new MockCache(); const first = createCacheState(cache); const firstKey = first.cacheKey; first.load(); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); const second = createCacheState(cache, first); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); second.dispose(); assert.strictEqual(second.isLoaded, false); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); assert.ok(cache.disposing[firstKey]); }); - test('keep using old cacheKey when loading fails', function () { + test('keep using old cacheKey when loading fails', async function () { const cache = new MockCache(); const first = createCacheState(cache); const firstKey = first.cacheKey; first.load(); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); const second = createCacheState(cache, first); second.load(); @@ -136,14 +135,14 @@ suite('CacheState', () => { const origErrorHandler = errors.errorHandler.getUnexpectedErrorHandler(); try { errors.setUnexpectedErrorHandler(() => null); - cache.loading[secondKey].error('loading failed'); + await cache.loading[secondKey].error('loading failed'); } finally { errors.setUnexpectedErrorHandler(origErrorHandler); } assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); assert.strictEqual(Object.keys(cache.loading).length, 2); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); assert.strictEqual(second.cacheKey, firstKey); // keep using old cacheKey const third = createCacheState(cache, second); @@ -151,15 +150,15 @@ suite('CacheState', () => { assert.strictEqual(third.isLoaded, true); assert.strictEqual(third.isUpdating, true); assert.strictEqual(Object.keys(cache.loading).length, 3); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); assert.strictEqual(third.cacheKey, firstKey); const thirdKey = cache.cacheKeys[2]; - cache.loading[thirdKey].complete(null); + await cache.loading[thirdKey].complete(null); assert.strictEqual(third.isLoaded, true); assert.strictEqual(third.isUpdating, false); assert.strictEqual(Object.keys(cache.loading).length, 3); - assert.strictEqual(Object.keys(cache.disposing).length, 2); + await cache.awaitDisposal(2); assert.strictEqual(third.cacheKey, thirdKey); // recover with next successful load }); @@ -175,28 +174,45 @@ suite('CacheState', () => { class MockCache { public cacheKeys: string[] = []; - public loading: { [cacheKey: string]: DeferredTPromise } = {}; - public disposing: { [cacheKey: string]: DeferredTPromise } = {}; + public loading: { [cacheKey: string]: DeferredPromise } = {}; + public disposing: { [cacheKey: string]: DeferredPromise } = {}; - public baseQuery: ISearchQuery = { + private _awaitDisposal: (() => void)[][] = []; + + public baseQuery: IFileQuery = { type: QueryType.File }; - public query(cacheKey: string): ISearchQuery { + public query(cacheKey: string): IFileQuery { this.cacheKeys.push(cacheKey); return objects.assign({ cacheKey: cacheKey }, this.baseQuery); } - public load(query: ISearchQuery): TPromise { - const promise = new DeferredTPromise(); + public load(query: IFileQuery): Promise { + const promise = new DeferredPromise(); this.loading[query.cacheKey] = promise; - return promise; + return promise.p; } - public dispose(cacheKey: string): TPromise { - const promise = new DeferredTPromise(); + public dispose(cacheKey: string): Promise { + const promise = new DeferredPromise(); this.disposing[cacheKey] = promise; - return promise; + const n = Object.keys(this.disposing).length; + for (const done of this._awaitDisposal[n] || []) { + done(); + } + delete this._awaitDisposal[n]; + return promise.p; + } + + public awaitDisposal(n: number) { + return new Promise(resolve => { + if (n === Object.keys(this.disposing).length) { + resolve(); + } else { + (this._awaitDisposal[n] || (this._awaitDisposal[n] = [])).push(resolve); + } + }); } } }); diff --git a/src/vs/workbench/parts/search/test/browser/searchActions.test.ts b/src/vs/workbench/parts/search/test/browser/searchActions.test.ts index e1624978573..9151227995d 100644 --- a/src/vs/workbench/parts/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/parts/search/test/browser/searchActions.test.ts @@ -127,14 +127,14 @@ suite('Search Actions', () => { function aFileMatch(): FileMatch { let rawMatch: IFileMatch = { resource: URI.file('somepath' + ++counter), - matches: [] + results: [] }; return instantiationService.createInstance(FileMatch, null, null, null, null, rawMatch); } function aMatch(fileMatch: FileMatch): Match { const line = ++counter; - const range = { + const ranges = { startLineNumber: line, startColumn: 0, endLineNumber: line, @@ -143,9 +143,9 @@ suite('Search Actions', () => { let match = new Match(fileMatch, { preview: { text: 'some match', - match: range + matches: ranges }, - range + ranges }); fileMatch.add(match); return match; diff --git a/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts index b64fd255d57..bfb7643abf6 100644 --- a/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts @@ -7,7 +7,7 @@ import { URI as uri } from 'vs/base/common/uri'; import { Match, FileMatch, SearchResult } from 'vs/workbench/parts/search/common/searchModel'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { SearchDataSource, SearchSorter } from 'vs/workbench/parts/search/browser/searchResultsView'; -import { IFileMatch, TextSearchResult, OneLineRange, ITextSearchResult } from 'vs/platform/search/common/search'; +import { IFileMatch, TextSearchMatch, OneLineRange, ITextSearchMatch, QueryType } from 'vs/platform/search/common/search'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; @@ -28,9 +28,15 @@ suite('Search - Viewlet', () => { test('Data Source', function () { let ds = instantiation.createInstance(SearchDataSource); let result: SearchResult = instantiation.createInstance(SearchResult, null); - result.query = { type: 1, folderQueries: [{ folder: uri.parse('file://c:/') }] }; + result.query = { + type: QueryType.Text, + contentPattern: { pattern: 'foo' }, + folderQueries: [{ + folder: uri.parse('file://c:/') + }] + }; - const range = { + const ranges = { startLineNumber: 1, startColumn: 0, endLineNumber: 1, @@ -38,12 +44,12 @@ suite('Search - Viewlet', () => { }; result.add([{ resource: uri.parse('file:///c:/foo'), - matches: [{ + results: [{ preview: { text: 'bar', - match: range + matches: ranges }, - range + ranges }] }]); @@ -51,8 +57,8 @@ suite('Search - Viewlet', () => { let lineMatch = fileMatch.matches()[0]; assert.equal(ds.getId(null, result), 'root'); - assert.equal(ds.getId(null, fileMatch), 'file:///c:/foo'); - assert.equal(ds.getId(null, lineMatch), 'file:///c:/foo>[2,1 -> 2,2]b'); + assert.equal(ds.getId(null, fileMatch), 'file:///c%3A/foo'); + assert.equal(ds.getId(null, lineMatch), 'file:///c%3A/foo>[2,1 -> 2,2]b'); assert(!ds.hasChildren(null, 'foo')); assert(ds.hasChildren(null, result)); @@ -64,9 +70,9 @@ suite('Search - Viewlet', () => { let fileMatch1 = aFileMatch('C:\\foo'); let fileMatch2 = aFileMatch('C:\\with\\path'); let fileMatch3 = aFileMatch('C:\\with\\path\\foo'); - let lineMatch1 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(0, 1, 1))); - let lineMatch2 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(2, 1, 1))); - let lineMatch3 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(2, 1, 1))); + let lineMatch1 = new Match(fileMatch1, new TextSearchMatch('bar', new OneLineRange(0, 1, 1))); + let lineMatch2 = new Match(fileMatch1, new TextSearchMatch('bar', new OneLineRange(2, 1, 1))); + let lineMatch3 = new Match(fileMatch1, new TextSearchMatch('bar', new OneLineRange(2, 1, 1))); let s = new SearchSorter(); @@ -80,10 +86,10 @@ suite('Search - Viewlet', () => { assert(s.compare(null, lineMatch2, lineMatch3) === 0); }); - function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchResult[]): FileMatch { + function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchMatch[]): FileMatch { let rawMatch: IFileMatch = { resource: uri.file('C:\\' + path), - matches: lineMatches + results: lineMatches }; return instantiation.createInstance(FileMatch, null, null, null, searchResult, rawMatch); } diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index 880edef3f12..994c38f7232 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -10,14 +10,15 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IFolderQuery, IPatternInfo, ISearchQuery, QueryType } from 'vs/platform/search/common/search'; +import { IFolderQuery, IPatternInfo, QueryType, ITextQuery, IFileQuery } from 'vs/platform/search/common/search'; import { IWorkspaceContextService, toWorkspaceFolders, Workspace } from 'vs/platform/workspace/common/workspace'; import { ISearchPathsResult, QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; import { TestContextService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; const DEFAULT_EDITOR_CONFIG = {}; const DEFAULT_USER_CONFIG = { useRipgrep: true, useIgnoreFiles: true, useGlobalIgnoreFiles: true }; -const DEFAULT_QUERY_PROPS = { useRipgrep: true, disregardIgnoreFiles: false, disregardGlobalIgnoreFiles: false }; +const DEFAULT_QUERY_PROPS = { useRipgrep: true }; +const DEFAULT_TEXT_QUERY_PROPS = { usePCRE2: false }; suite('QueryBuilder', () => { const PATTERN_INFO: IPatternInfo = { pattern: 'a' }; @@ -49,21 +50,22 @@ suite('QueryBuilder', () => { }); test('simple text pattern', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text(PATTERN_INFO), - { + { + folderQueries: [], contentPattern: PATTERN_INFO, type: QueryType.Text }); }); test('folderResources', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI] ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI }], type: QueryType.Text @@ -81,12 +83,12 @@ suite('QueryBuilder', () => { } }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI] ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI, @@ -102,13 +104,13 @@ suite('QueryBuilder', () => { }); test('simple include', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { includePattern: './bar' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: getUri(fixPath(paths.join(ROOT_1, 'bar'))) @@ -116,13 +118,13 @@ suite('QueryBuilder', () => { type: QueryType.Text }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { includePattern: '.\\bar' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: getUri(fixPath(paths.join(ROOT_1, 'bar'))) @@ -142,23 +144,20 @@ suite('QueryBuilder', () => { } }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { includePattern: './foo' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ - folder: getUri(paths.join(ROOT_1, 'foo')) - }], - excludePattern: { - [paths.join(ROOT_1, 'foo/**/*.js')]: true, - [paths.join(ROOT_1, 'bar/**')]: { - 'when': '$(basename).ts' + folder: getUri(paths.join(ROOT_1, 'foo')), + excludePattern: { + ['**/*.js']: true } - }, + }], type: QueryType.Text }); }); @@ -182,12 +181,12 @@ suite('QueryBuilder', () => { }, ROOT_2_URI); // There are 3 roots, the first two have search.exclude settings, test that the correct basic query is returned - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI, ROOT_2_URI, ROOT_3_URI] ), - { + { contentPattern: PATTERN_INFO, folderQueries: [ { folder: ROOT_1_URI, excludePattern: patternsToIExpression('foo/**/*.js') }, @@ -199,31 +198,30 @@ suite('QueryBuilder', () => { ); // Now test that it merges the root excludes when an 'include' is used - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI, ROOT_2_URI, ROOT_3_URI], { includePattern: './root2/src' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [ { folder: getUri(paths.join(ROOT_2, 'src')) } ], - excludePattern: patternsToIExpression(paths.join(ROOT_1, 'foo/**/*.js'), paths.join(ROOT_2, 'bar')), type: QueryType.Text } ); }); test('simple exclude input pattern', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { excludePattern: 'foo' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -236,26 +234,25 @@ suite('QueryBuilder', () => { test('file pattern trimming', () => { const content = 'content'; assertEqualQueries( - queryBuilder.text( - PATTERN_INFO, + queryBuilder.file( undefined, { filePattern: ` ${content} ` } ), - { - contentPattern: PATTERN_INFO, + { + folderQueries: [], filePattern: content, - type: QueryType.Text + type: QueryType.File }); }); test('exclude ./ syntax', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { excludePattern: './bar' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -264,13 +261,13 @@ suite('QueryBuilder', () => { type: QueryType.Text }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { excludePattern: './bar/**/*.ts' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -279,13 +276,13 @@ suite('QueryBuilder', () => { type: QueryType.Text }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { excludePattern: '.\\bar\\**\\*.ts' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -296,13 +293,13 @@ suite('QueryBuilder', () => { }); test('extraFileResources', () => { - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], { extraFileResources: [getUri('/foo/bar.js')] } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -311,7 +308,7 @@ suite('QueryBuilder', () => { type: QueryType.Text }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], @@ -320,7 +317,7 @@ suite('QueryBuilder', () => { excludePattern: '*.js' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -329,7 +326,7 @@ suite('QueryBuilder', () => { type: QueryType.Text }); - assertEqualQueries( + assertEqualTextQueries( queryBuilder.text( PATTERN_INFO, [ROOT_1_URI], @@ -338,7 +335,7 @@ suite('QueryBuilder', () => { includePattern: '*.txt' } ), - { + { contentPattern: PATTERN_INFO, folderQueries: [{ folder: ROOT_1_URI @@ -764,7 +761,16 @@ suite('QueryBuilder', () => { }); }); -function assertEqualQueries(actual: ISearchQuery, expected: ISearchQuery): void { +function assertEqualTextQueries(actual: ITextQuery, expected: ITextQuery): void { + expected = { + ...DEFAULT_TEXT_QUERY_PROPS, + ...expected + }; + + return assertEqualQueries(actual, expected); +} + +function assertEqualQueries(actual: ITextQuery | IFileQuery, expected: ITextQuery | IFileQuery): void { expected = { ...DEFAULT_QUERY_PROPS, ...expected @@ -779,8 +785,6 @@ function assertEqualQueries(actual: ISearchQuery, expected: ISearchQuery): void }; }; - delete actual.ignoreSymlinks; - // Avoid comparing URI objects, not a good idea if (expected.folderQueries) { assert.deepEqual(actual.folderQueries.map(folderQueryToCompareObject), expected.folderQueries.map(folderQueryToCompareObject)); diff --git a/src/vs/workbench/parts/search/test/common/searchModel.test.ts b/src/vs/workbench/parts/search/test/common/searchModel.test.ts index c54dad1e84f..ff45b9468f0 100644 --- a/src/vs/workbench/parts/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchModel.test.ts @@ -5,20 +5,19 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { timeout } from 'vs/base/common/async'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { DeferredTPromise } from 'vs/base/test/common/utils'; +import { DeferredPromise } from 'vs/base/test/common/utils'; import { Range } from 'vs/editor/common/core/range'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IFileMatch, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchResult, TextSearchResult, OneLineRange } from 'vs/platform/search/common/search'; +import { IFileMatch, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchMatch, OneLineRange, TextSearchMatch } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; const nullEvent = new class { @@ -71,7 +70,7 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModelService, stubModelService(instantiationService)); instantiationService.stub(ISearchService, {}); - instantiationService.stub(ISearchService, 'search', TPromise.as({ results: [] })); + instantiationService.stub(ISearchService, 'textSearch', Promise.resolve({ results: [] })); }); teardown(() => { @@ -82,8 +81,8 @@ suite('SearchModel', () => { function searchServiceWithResults(results: IFileMatch[], complete: ISearchComplete | null = null): ISearchService { return { - search(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { - return new TPromise(resolve => { + textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Thenable { + return new Promise(resolve => { process.nextTick(() => { results.forEach(onProgress); resolve(complete); @@ -95,8 +94,8 @@ suite('SearchModel', () => { function searchServiceWithError(error: Error): ISearchService { return { - search(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { - return new TPromise((resolve, reject) => { + textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Thenable { + return new Promise((resolve, reject) => { reject(error); }); } @@ -105,12 +104,12 @@ suite('SearchModel', () => { function canceleableSearchService(tokenSource: CancellationTokenSource): ISearchService { return { - search(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { + textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Thenable { if (token) { token.onCancellationRequested(() => tokenSource.cancel()); } - return new TPromise(resolve => { + return new Promise(resolve => { process.nextTick(() => { resolve({}); }); @@ -122,9 +121,9 @@ suite('SearchModel', () => { test('Search Model: Search adds to results', async () => { let results = [ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), - aRawMatch('file://c:/2', new TextSearchResult('preview 2', lineOneRange))]; + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11))), + aRawMatch('file://c:/2', new TextSearchMatch('preview 2', lineOneRange))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); @@ -152,10 +151,10 @@ suite('SearchModel', () => { let target = instantiationService.spy(ITelemetryService, 'publicLog'); let results = [ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11))), aRawMatch('file://c:/2', - new TextSearchResult('preview 2', lineOneRange))]; + new TextSearchMatch('preview 2', lineOneRange))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); @@ -193,7 +192,7 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, 'publicLog', target1); instantiationService.stub(ISearchService, searchServiceWithResults( - [aRawMatch('file://c:/1', new TextSearchResult('some preview', lineOneRange))], + [aRawMatch('file://c:/1', new TextSearchMatch('some preview', lineOneRange))], { results: [], stats: testSearchStats })); let testObject = instantiationService.createInstance(SearchModel); @@ -236,13 +235,13 @@ suite('SearchModel', () => { let target1 = sinon.stub().returns(nullEvent); instantiationService.stub(ITelemetryService, 'publicLog', target1); - let promise = new DeferredTPromise(); - instantiationService.stub(ISearchService, 'search', promise); + let deferredPromise = new DeferredPromise(); + instantiationService.stub(ISearchService, 'textSearch', deferredPromise.p); let testObject = instantiationService.createInstance(SearchModel); let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); - promise.cancel(); + deferredPromise.cancel(); return timeout(1).then(() => { return result.then(() => { }, () => { @@ -256,10 +255,10 @@ suite('SearchModel', () => { test('Search Model: Search results are cleared during search', async () => { let results = [ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11))), aRawMatch('file://c:/2', - new TextSearchResult('preview 2', lineOneRange))]; + new TextSearchMatch('preview 2', lineOneRange))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); @@ -286,8 +285,8 @@ suite('SearchModel', () => { test('getReplaceString returns proper replace string for regExpressions', async () => { let results = [ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11)))]; + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11)))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); @@ -314,8 +313,8 @@ suite('SearchModel', () => { assert.equal('helloe', match.replaceString); }); - function aRawMatch(resource: string, ...matches: ITextSearchResult[]): IFileMatch { - return { resource: URI.parse(resource), matches }; + function aRawMatch(resource: string, ...results: ITextSearchMatch[]): IFileMatch { + return { resource: URI.parse(resource), results }; } function stub(arg1: any, arg2: any, arg3: any): sinon.SinonStub { diff --git a/src/vs/workbench/parts/search/test/common/searchResult.test.ts b/src/vs/workbench/parts/search/test/common/searchResult.test.ts index 882b52066fc..f6312851b77 100644 --- a/src/vs/workbench/parts/search/test/common/searchResult.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchResult.test.ts @@ -7,7 +7,7 @@ import * as sinon from 'sinon'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { Match, FileMatch, SearchResult, SearchModel } from 'vs/workbench/parts/search/common/searchModel'; import { URI } from 'vs/base/common/uri'; -import { IFileMatch, TextSearchResult, OneLineRange, ITextSearchResult } from 'vs/platform/search/common/search'; +import { IFileMatch, TextSearchMatch, OneLineRange, ITextSearchMatch } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { Range } from 'vs/editor/common/core/range'; @@ -33,7 +33,7 @@ suite('SearchResult', () => { test('Line Match', function () { let fileMatch = aFileMatch('folder/file.txt', null); - let lineMatch = new Match(fileMatch, new TextSearchResult('foo bar', new OneLineRange(1, 0, 3))); + let lineMatch = new Match(fileMatch, new TextSearchMatch('foo bar', new OneLineRange(1, 0, 3))); assert.equal(lineMatch.text(), 'foo bar'); assert.equal(lineMatch.range().startLineNumber, 2); assert.equal(lineMatch.range().endLineNumber, 2); @@ -43,7 +43,7 @@ suite('SearchResult', () => { }); test('Line Match - Remove', function () { - let fileMatch = aFileMatch('folder/file.txt', aSearchResult(), new TextSearchResult('foo bar', new OneLineRange(1, 0, 3))); + let fileMatch = aFileMatch('folder/file.txt', aSearchResult(), new TextSearchMatch('foo bar', new OneLineRange(1, 0, 3))); let lineMatch = fileMatch.matches()[0]; fileMatch.remove(lineMatch); assert.equal(fileMatch.matches().length, 0); @@ -65,8 +65,8 @@ suite('SearchResult', () => { let testObject = aFileMatch( 'folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(testObject.matches()[0]); @@ -77,8 +77,8 @@ suite('SearchResult', () => { let testObject = aFileMatch( 'folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); let target = testObject.matches()[0]; testObject.remove(target); @@ -91,8 +91,8 @@ suite('SearchResult', () => { let testObject = aFileMatch( 'folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); let target = testObject.matches()[0]; testObject.setSelectedMatch(target); @@ -102,8 +102,8 @@ suite('SearchResult', () => { test('File Match: isSelected return false for un-selected match', function () { let testObject = aFileMatch('folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(testObject.matches()[0]); assert.ok(!testObject.isMatchSelected(testObject.matches()[1])); }); @@ -112,8 +112,8 @@ suite('SearchResult', () => { let testObject = aFileMatch( 'folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(testObject.matches()[0]); testObject.setSelectedMatch(null); @@ -124,8 +124,8 @@ suite('SearchResult', () => { let testObject = aFileMatch( 'folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(null); assert.equal(null, testObject.getSelectedMatch()); @@ -134,7 +134,7 @@ suite('SearchResult', () => { test('Alle Drei Zusammen', function () { let searchResult = instantiationService.createInstance(SearchResult, null); let fileMatch = aFileMatch('far/boo', searchResult); - let lineMatch = new Match(fileMatch, new TextSearchResult('foo bar', new OneLineRange(1, 0, 3))); + let lineMatch = new Match(fileMatch, new TextSearchMatch('foo bar', new OneLineRange(1, 0, 3))); assert(lineMatch.parent() === fileMatch); assert(fileMatch.parent() === searchResult); @@ -143,9 +143,9 @@ suite('SearchResult', () => { test('Adding a raw match will add a file match with line matches', function () { let testObject = aSearchResult(); let target = [aRawMatch('file://c:/', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11)), - new TextSearchResult('preview 2', lineOneRange))]; + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11)), + new TextSearchMatch('preview 2', lineOneRange))]; testObject.add(target); @@ -172,10 +172,10 @@ suite('SearchResult', () => { let testObject = aSearchResult(); let target = [ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11))), aRawMatch('file://c:/2', - new TextSearchResult('preview 2', lineOneRange))]; + new TextSearchMatch('preview 2', lineOneRange))]; testObject.add(target); @@ -205,9 +205,9 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange)), + new TextSearchMatch('preview 1', lineOneRange)), aRawMatch('file://c:/2', - new TextSearchResult('preview 2', lineOneRange))]); + new TextSearchMatch('preview 2', lineOneRange))]); testObject.matches()[0].onDispose(target1); testObject.matches()[1].onDispose(target2); @@ -224,7 +224,7 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange))]); + new TextSearchMatch('preview 1', lineOneRange))]); let objectRoRemove = testObject.matches()[0]; testObject.onChange(target); @@ -239,7 +239,7 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange))]); + new TextSearchMatch('preview 1', lineOneRange))]); let objectRoRemove = testObject.matches()[0]; testObject.onChange(target); @@ -253,7 +253,7 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange))]); + new TextSearchMatch('preview 1', lineOneRange))]); let target = testObject.matches()[0]; let matchToRemove = target.matches()[0]; target.remove(matchToRemove); @@ -270,7 +270,7 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange))]); + new TextSearchMatch('preview 1', lineOneRange))]); testObject.replace(testObject.matches()[0]); @@ -283,7 +283,7 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange))]); + new TextSearchMatch('preview 1', lineOneRange))]); testObject.onChange(target); let objectRoRemove = testObject.matches()[0]; @@ -298,9 +298,9 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange)), + new TextSearchMatch('preview 1', lineOneRange)), aRawMatch('file://c:/2', - new TextSearchResult('preview 2', lineOneRange))]); + new TextSearchMatch('preview 2', lineOneRange))]); testObject.replaceAll(null); @@ -351,10 +351,10 @@ suite('SearchResult', () => { // lineHasNoDecoration(oneModel, 2); //}); - function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchResult[]): FileMatch { + function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchMatch[]): FileMatch { let rawMatch: IFileMatch = { resource: URI.file('/' + path), - matches: lineMatches + results: lineMatches }; return instantiationService.createInstance(FileMatch, null, null, null, searchResult, rawMatch); } @@ -365,8 +365,8 @@ suite('SearchResult', () => { return searchModel.searchResult; } - function aRawMatch(resource: string, ...matches: ITextSearchResult[]): IFileMatch { - return { resource: URI.parse(resource), matches }; + function aRawMatch(resource: string, ...results: ITextSearchMatch[]): IFileMatch { + return { resource: URI.parse(resource), results }; } function stubModelService(instantiationService: TestInstantiationService): IModelService { diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts index fafe8a563e7..82bd65b22fa 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { MarkdownString } from 'vs/base/common/htmlContent'; import { compare } from 'vs/base/common/strings'; import { Position } from 'vs/editor/common/core/position'; @@ -88,6 +86,8 @@ export class SnippetCompletionProvider implements CompletionItemProvider { let pos = { lineNumber: position.lineNumber, column: 1 }; let lineOffsets: number[] = []; let linePrefixLow = model.getLineContent(position.lineNumber).substr(0, position.column - 1).toLowerCase(); + let endsInWhitespace = linePrefixLow.match(/\s$/); + while (pos.column < position.column) { let word = model.getWordAtPosition(pos); if (word) { @@ -108,22 +108,26 @@ export class SnippetCompletionProvider implements CompletionItemProvider { pos.column += 1; } } - if (lineOffsets.length === 0) { - // no interesting spans found -> pick all snippets - suggestions = snippets.map(snippet => new SnippetCompletion(snippet, Range.fromPositions(position))); - } - else { - let consumed = new Set(); - suggestions = []; - for (let start of lineOffsets) { - for (const snippet of snippets) { - if (!consumed.has(snippet) && matches(linePrefixLow, start, snippet.prefixLow, 0)) { - suggestions.push(new SnippetCompletion(snippet, Range.fromPositions(position.delta(0, -(linePrefixLow.length - start)), position))); - consumed.add(snippet); - } + + let availableSnippets = new Set(); + snippets.forEach(availableSnippets.add, availableSnippets); + suggestions = []; + for (let start of lineOffsets) { + availableSnippets.forEach(snippet => { + if (matches(linePrefixLow, start, snippet.prefixLow, 0)) { + suggestions.push(new SnippetCompletion(snippet, Range.fromPositions(position.delta(0, -(linePrefixLow.length - start)), position))); + availableSnippets.delete(snippet); } - } + }); } + if (endsInWhitespace || lineOffsets.length === 0) { + // add remaing snippets when the current prefix ends in whitespace or when no + // interesting positions have been found + availableSnippets.forEach(snippet => { + suggestions.push(new SnippetCompletion(snippet, Range.fromPositions(position))); + }); + } + // dismbiguate suggestions with same labels suggestions.sort(SnippetCompletion.compareByLabel); diff --git a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts index 750898ee040..508d61e4563 100644 --- a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts +++ b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts @@ -11,6 +11,7 @@ import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ISnippetsService } from 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; import { Snippet, SnippetSource } from 'vs/workbench/parts/snippets/electron-browser/snippetsFile'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; class SimpleSnippetService implements ISnippetsService { _serviceBrand: any; @@ -327,4 +328,47 @@ suite('SnippetsService', function () { assert.equal(result.suggestions.length, 1); }); + + test('issue #61296: VS code freezes when editing CSS file with emoji', async function () { + let toDispose = LanguageConfigurationRegistry.register(modeService.getLanguageIdentifier('fooLang'), { + wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]*(?=[^,{;]*[,{]))|(([@#.!])?[\w-?]+%?|[@#!.])/g + }); + snippetService = new SimpleSnippetService([new Snippet( + ['fooLang'], + 'bug', + '-a-bug', + '', + 'second', + '', + SnippetSource.User + )]); + + const provider = new SnippetCompletionProvider(modeService, snippetService); + + let model = TextModel.createFromString('.🐷-a-b', undefined, modeService.getLanguageIdentifier('fooLang')); + let result = await provider.provideCompletionItems(model, new Position(1, 8)); + + assert.equal(result.suggestions.length, 1); + + toDispose.dispose(); + }); + + test('No snippets shown when triggering completions at whitespace on line that already has text #62335', async function () { + snippetService = new SimpleSnippetService([new Snippet( + ['fooLang'], + 'bug', + 'bug', + '', + 'second', + '', + SnippetSource.User + )]); + + const provider = new SnippetCompletionProvider(modeService, snippetService); + + let model = TextModel.createFromString('a ', undefined, modeService.getLanguageIdentifier('fooLang')); + let result = await provider.provideCompletionItems(model, new Position(1, 3)); + + assert.equal(result.suggestions.length, 1); + }); }); diff --git a/src/vs/workbench/parts/stats/node/workspaceStats.ts b/src/vs/workbench/parts/stats/node/workspaceStats.ts index e6380eace34..16dabafd261 100644 --- a/src/vs/workbench/parts/stats/node/workspaceStats.ts +++ b/src/vs/workbench/parts/stats/node/workspaceStats.ts @@ -54,11 +54,15 @@ const ModulesToLookFor = [ 'react', 'react-native', '@angular/core', + '@ionic', 'vue', + 'tns-core-modules', // Other interesting packages 'aws-sdk', + 'aws-amplify', 'azure', 'azure-storage', + 'firebase', '@google-cloud/common', 'heroku-cli' ]; @@ -224,6 +228,8 @@ export class WorkspaceStats implements IWorkbenchContribution { // Cloud Stats this.reportCloudStats(); + + this.reportProxyStats(); } private static searchArray(arr: string[], regEx: RegExp): boolean { @@ -259,9 +265,11 @@ export class WorkspaceStats implements IWorkbenchContribution { "workspace.npm.@angular/core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.vue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.aws-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.aws-amplify-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.azure-storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@google-cloud/common" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.firebase" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.heroku-cli" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.bower" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.yeoman.code.ext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -271,6 +279,8 @@ export class WorkspaceStats implements IWorkbenchContribution { "workspace.xamarin.ios" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.android.cpp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.reactNative" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.ionic" : { "classification" : "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": "true" }, + "workspace.nativeScript" : { "classification" : "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": "true" }, "workspace.py.requirements" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.requirements.star" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.Pipfile" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -385,6 +395,14 @@ export class WorkspaceStats implements IWorkbenchContribution { } } + if (tags['workspace.config.xml'] && + !tags['workspace.language.cs'] && !tags['workspace.language.vb'] && !tags['workspace.language.aspx']) { + + if (nameSet.has('ionic.config.json')) { + tags['workspace.ionic'] = true; + } + } + if (mainActivity && properties && resources) { tags['workspace.xamarin.android'] = true; } @@ -461,6 +479,10 @@ export class WorkspaceStats implements IWorkbenchContribution { if (packageJsonContents['dependencies'][module]) { tags['workspace.reactNative'] = true; } + } else if ('tns-core-modules' === module) { + if (packageJsonContents['dependencies'][module]) { + tags['workspace.nativescript'] = true; + } } else { if (packageJsonContents['dependencies'][module]) { tags['workspace.npm.' + module] = true; @@ -675,4 +697,20 @@ export class WorkspaceStats implements IWorkbenchContribution { this.reportAzure(uris); } } -} \ No newline at end of file + + private reportProxyStats() { + this.windowService.resolveProxy('https://www.example.com/') + .then(proxy => { + let type = proxy ? String(proxy).trim().split(/\s+/, 1)[0] : 'EMPTY'; + if (['DIRECT', 'PROXY', 'HTTPS', 'SOCKS', 'EMPTY'].indexOf(type) === -1) { + type = 'UNKNOWN'; + } + /* __GDPR__ + "resolveProxy.stats" : { + "type": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + this.telemetryService.publicLog('resolveProxy.stats', { type }); + }).then(null, onUnexpectedError); + } +} diff --git a/src/vs/workbench/parts/tasks/common/problemCollectors.ts b/src/vs/workbench/parts/tasks/common/problemCollectors.ts index 98c2c309454..08b3b3d7b0a 100644 --- a/src/vs/workbench/parts/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/parts/tasks/common/problemCollectors.ts @@ -36,9 +36,9 @@ export interface IProblemMatcher { export class AbstractProblemCollector implements IDisposable { private matchers: INumberDictionary; - private activeMatcher: ILineMatcher; + private activeMatcher: ILineMatcher | null; private _numberOfMatches: number; - private _maxMarkerSeverity: MarkerSeverity; + private _maxMarkerSeverity?: MarkerSeverity; private buffer: string[]; private bufferLength: number; private openModels: IStringDictionary; @@ -111,11 +111,11 @@ export class AbstractProblemCollector implements IDisposable { return this._numberOfMatches; } - public get maxMarkerSeverity(): MarkerSeverity { + public get maxMarkerSeverity(): MarkerSeverity | undefined { return this._maxMarkerSeverity; } - protected tryFindMarker(line: string): ProblemMatch { + protected tryFindMarker(line: string): ProblemMatch | null { let result: ProblemMatch | null = null; if (this.activeMatcher) { result = this.activeMatcher.next(line); @@ -163,7 +163,7 @@ export class AbstractProblemCollector implements IDisposable { return ApplyToKind.allDocuments; } - private tryMatchers(): ProblemMatch { + private tryMatchers(): ProblemMatch | null { this.activeMatcher = null; let length = this.buffer.length; for (let startIndex = 0; startIndex < length; startIndex++) { @@ -384,8 +384,8 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement private _activeBackgroundMatchers: Set; // Current State - private currentOwner: string; - private currentResource: string; + private currentOwner: string | null; + private currentResource: string | null; constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService) { super(problemMatchers, markerService, modelService); @@ -458,7 +458,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement this.cleanMarkerCaches(); this.resetCurrentResource(); let owner = background.matcher.owner; - let file = matches[background.begin.file]; + let file = matches[background.begin.file!]; if (file) { let resource = getResource(file, background.matcher); this.recordResourceToClean(owner, resource); diff --git a/src/vs/workbench/parts/tasks/common/problemMatcher.ts b/src/vs/workbench/parts/tasks/common/problemMatcher.ts index 6bf21c96387..188cc53270e 100644 --- a/src/vs/workbench/parts/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/parts/tasks/common/problemMatcher.ts @@ -14,7 +14,6 @@ import * as UUID from 'vs/base/common/uuid'; import * as Platform from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ValidationStatus, ValidationState, IProblemReporter, Parser } from 'vs/base/common/parsers'; import { IStringDictionary } from 'vs/base/common/collections'; @@ -29,7 +28,7 @@ export enum FileLocationKind { } export module FileLocationKind { - export function fromString(value: string): FileLocationKind { + export function fromString(value: string): FileLocationKind | undefined { value = value.toLowerCase(); if (value === 'absolute') { return FileLocationKind.Absolute; @@ -41,14 +40,13 @@ export module FileLocationKind { } } - export enum ProblemLocationKind { File, Location } export module ProblemLocationKind { - export function fromString(value: string): ProblemLocationKind { + export function fromString(value: string): ProblemLocationKind | undefined { value = value.toLowerCase(); if (value === 'file') { return ProblemLocationKind.File; @@ -110,7 +108,7 @@ export enum ApplyToKind { } export module ApplyToKind { - export function fromString(value: string): ApplyToKind { + export function fromString(value: string): ApplyToKind | undefined { value = value.toLowerCase(); if (value === 'alldocuments') { return ApplyToKind.allDocuments; @@ -179,18 +177,21 @@ export interface ProblemMatch { } export interface HandleResult { - match: ProblemMatch; + match: ProblemMatch | null; continue: boolean; } export function getResource(filename: string, matcher: ProblemMatcher): URI { let kind = matcher.fileLocation; - let fullPath: string; + let fullPath: string | undefined; if (kind === FileLocationKind.Absolute) { fullPath = filename; - } else if (kind === FileLocationKind.Relative) { + } else if ((kind === FileLocationKind.Relative) && matcher.filePrefix) { fullPath = Paths.join(matcher.filePrefix, filename); } + if (fullPath === void 0) { + throw new Error('FileLocationKind is not actionable. Does the matcher have a filePrefix? This should never happen.'); + } fullPath = fullPath.replace(/\\/g, '/'); if (fullPath[0] !== '/') { fullPath = '/' + fullPath; @@ -204,7 +205,7 @@ export function getResource(filename: string, matcher: ProblemMatcher): URI { export interface ILineMatcher { matchLength: number; - next(line: string): ProblemMatch; + next(line: string): ProblemMatch | null; handle(lines: string[], start?: number): HandleResult; } @@ -230,50 +231,57 @@ abstract class AbstractLineMatcher implements ILineMatcher { return { match: null, continue: false }; } - public next(line: string): ProblemMatch { + public next(line: string): ProblemMatch | null { return null; } public abstract get matchLength(): number; - protected fillProblemData(data: ProblemData, pattern: ProblemPattern, matches: RegExpExecArray): void { - this.fillProperty(data, 'file', pattern, matches, true); - this.appendProperty(data, 'message', pattern, matches, true); - this.fillProperty(data, 'code', pattern, matches, true); - this.fillProperty(data, 'severity', pattern, matches, true); - this.fillProperty(data, 'location', pattern, matches, true); - this.fillProperty(data, 'line', pattern, matches); - this.fillProperty(data, 'character', pattern, matches); - this.fillProperty(data, 'endLine', pattern, matches); - this.fillProperty(data, 'endCharacter', pattern, matches); + protected fillProblemData(data: ProblemData | null, pattern: ProblemPattern, matches: RegExpExecArray): data is ProblemData { + if (data) { + this.fillProperty(data, 'file', pattern, matches, true); + this.appendProperty(data, 'message', pattern, matches, true); + this.fillProperty(data, 'code', pattern, matches, true); + this.fillProperty(data, 'severity', pattern, matches, true); + this.fillProperty(data, 'location', pattern, matches, true); + this.fillProperty(data, 'line', pattern, matches); + this.fillProperty(data, 'character', pattern, matches); + this.fillProperty(data, 'endLine', pattern, matches); + this.fillProperty(data, 'endCharacter', pattern, matches); + return true; + } else { + return false; + } } private appendProperty(data: ProblemData, property: keyof ProblemData, pattern: ProblemPattern, matches: RegExpExecArray, trim: boolean = false): void { + const patternProperty = pattern[property]; if (Types.isUndefined(data[property])) { this.fillProperty(data, property, pattern, matches, trim); } - else if (!Types.isUndefined(pattern[property]) && pattern[property] < matches.length) { - let value = matches[pattern[property]]; + else if (!Types.isUndefined(patternProperty) && patternProperty < matches.length) { + let value = matches[patternProperty]; if (trim) { - value = Strings.trim(value); + value = Strings.trim(value)!; } data[property] += endOfLine + value; } } private fillProperty(data: ProblemData, property: keyof ProblemData, pattern: ProblemPattern, matches: RegExpExecArray, trim: boolean = false): void { - if (Types.isUndefined(data[property]) && !Types.isUndefined(pattern[property]) && pattern[property] < matches.length) { - let value = matches[pattern[property]]; + const patternAtProperty = pattern[property]; + if (Types.isUndefined(data[property]) && !Types.isUndefined(patternAtProperty) && patternAtProperty < matches.length) { + let value = matches[patternAtProperty]; if (value !== void 0) { if (trim) { - value = Strings.trim(value); + value = Strings.trim(value)!; } data[property] = value; } } } - protected getMarkerMatch(data: ProblemData): ProblemMatch { + protected getMarkerMatch(data: ProblemData): ProblemMatch | undefined { try { let location = this.getLocation(data); if (data.file && location && data.message) { @@ -307,7 +315,7 @@ abstract class AbstractLineMatcher implements ILineMatcher { return getResource(filename, this.matcher); } - private getLocation(data: ProblemData): Location { + private getLocation(data: ProblemData): Location | null { if (data.kind === ProblemLocationKind.File) { return this.createLocation(0, 0, 0, 0); } @@ -324,7 +332,7 @@ abstract class AbstractLineMatcher implements ILineMatcher { return this.createLocation(startLine, startColumn, endLine, endColumn); } - private parseLocationInfo(value: string): Location { + private parseLocationInfo(value: string): Location | null { if (!value || !value.match(/(\d+|\d+,\d+|\d+,\d+,\d+,\d+)/)) { return null; } @@ -338,7 +346,7 @@ abstract class AbstractLineMatcher implements ILineMatcher { } } - private createLocation(startLine: number, startColumn: number, endLine: number, endColumn: number): Location { + private createLocation(startLine: number, startColumn: number | undefined, endLine: number | undefined, endColumn: number | undefined): Location { if (startLine && startColumn && endColumn) { return { startLineNumber: startLine, startCharacter: startColumn, endLineNumber: endLine || startLine, endCharacter: endColumn }; } @@ -406,7 +414,7 @@ class SingleLineMatcher extends AbstractLineMatcher { return { match: null, continue: false }; } - public next(line: string): ProblemMatch { + public next(line: string): ProblemMatch | null { return null; } } @@ -414,7 +422,7 @@ class SingleLineMatcher extends AbstractLineMatcher { class MultiLineMatcher extends AbstractLineMatcher { private patterns: ProblemPattern[]; - private data: ProblemData; + private data: ProblemData | null; constructor(matcher: ProblemMatcher) { super(matcher); @@ -428,7 +436,7 @@ class MultiLineMatcher extends AbstractLineMatcher { public handle(lines: string[], start: number = 0): HandleResult { Assert.ok(lines.length - start === this.patterns.length); this.data = Object.create(null); - let data = this.data; + let data = this.data!; data.kind = this.patterns[0].kind; for (let i = 0; i < this.patterns.length; i++) { let pattern = this.patterns[i]; @@ -443,14 +451,15 @@ class MultiLineMatcher extends AbstractLineMatcher { this.fillProblemData(data, pattern, matches); } } - let loop = this.patterns[this.patterns.length - 1].loop; + let loop = !!this.patterns[this.patterns.length - 1].loop; if (!loop) { this.data = null; } - return { match: this.getMarkerMatch(data), continue: loop }; + const markerMatch = data ? this.getMarkerMatch(data) : null; + return { match: markerMatch ? markerMatch : null, continue: loop }; } - public next(line: string): ProblemMatch { + public next(line: string): ProblemMatch | null { let pattern = this.patterns[this.patterns.length - 1]; Assert.ok(pattern.loop === true && this.data !== null); let matches = pattern.regexp.exec(line); @@ -459,8 +468,11 @@ class MultiLineMatcher extends AbstractLineMatcher { return null; } let data = Objects.deepClone(this.data); - this.fillProblemData(data, pattern, matches); - return this.getMarkerMatch(data); + let problemMatch: ProblemMatch | undefined; + if (this.fillProblemData(data, pattern, matches)) { + problemMatch = this.getMarkerMatch(data); + } + return problemMatch ? problemMatch : null; } } @@ -552,6 +564,21 @@ export namespace Config { loop?: boolean; } + export interface CheckedProblemPattern extends ProblemPattern { + /** + * The regular expression to find a problem in the console output of an + * executed task. + */ + regexp: string; + } + + export namespace CheckedProblemPattern { + export function is(value: any): value is CheckedProblemPattern { + let candidate: ProblemPattern = value as ProblemPattern; + return candidate && Types.isString(candidate.regexp); + } + } + export interface NamedProblemPattern extends ProblemPattern { /** * The name of the problem pattern. @@ -565,12 +592,27 @@ export namespace Config { } export namespace NamedProblemPattern { - export function is(value: ProblemPattern): value is NamedProblemPattern { + export function is(value: any): value is NamedProblemPattern { let candidate: NamedProblemPattern = value as NamedProblemPattern; return candidate && Types.isString(candidate.name); } } + export interface NamedCheckedProblemPattern extends NamedProblemPattern { + /** + * The regular expression to find a problem in the console output of an + * executed task. + */ + regexp: string; + } + + export namespace NamedCheckedProblemPattern { + export function is(value: any): value is NamedCheckedProblemPattern { + let candidate: NamedProblemPattern = value as NamedProblemPattern; + return candidate && NamedProblemPattern.is(candidate) && Types.isString(candidate.regexp); + } + } + export type MultiLineProblemPattern = ProblemPattern[]; export namespace MultiLineProblemPattern { @@ -579,7 +621,24 @@ export namespace Config { } } - export interface NamedMultiLineProblemPattern { + export type MultiLineCheckedProblemPattern = CheckedProblemPattern[]; + + export namespace MultiLineCheckedProblemPattern { + export function is(value: any): value is MultiLineCheckedProblemPattern { + let is = false; + if (value && Types.isArray(value)) { + is = true; + value.forEach(element => { + if (!Config.CheckedProblemPattern.is(element)) { + is = false; + } + }); + } + return is; + } + } + + export interface NamedMultiLineCheckedProblemPattern { /** * The name of the problem pattern. */ @@ -593,17 +652,17 @@ export namespace Config { /** * The actual patterns */ - patterns: MultiLineProblemPattern; + patterns: MultiLineCheckedProblemPattern; } - export namespace NamedMultiLineProblemPattern { - export function is(value: any): value is NamedMultiLineProblemPattern { - let candidate = value as NamedMultiLineProblemPattern; - return candidate && Types.isString(candidate.name) && Types.isArray(candidate.patterns); + export namespace NamedMultiLineCheckedProblemPattern { + export function is(value: any): value is NamedMultiLineCheckedProblemPattern { + let candidate = value as NamedMultiLineCheckedProblemPattern; + return candidate && Types.isString(candidate.name) && Types.isArray(candidate.patterns) && MultiLineCheckedProblemPattern.is(candidate.patterns); } } - export type NamedProblemPatterns = (Config.NamedProblemPattern | Config.NamedMultiLineProblemPattern)[]; + export type NamedProblemPatterns = (Config.NamedProblemPattern | Config.NamedMultiLineCheckedProblemPattern)[]; /** * A watching pattern @@ -737,10 +796,10 @@ export namespace Config { export interface NamedProblemMatcher extends ProblemMatcher { /** - * An optional name. This name can be used to refer to the + * This name can be used to refer to the * problem matcher from within a task. */ - name?: string; + name: string; /** * A human readable label. @@ -762,24 +821,29 @@ export class ProblemPatternParser extends Parser { public parse(value: Config.ProblemPattern): ProblemPattern; public parse(value: Config.MultiLineProblemPattern): MultiLineProblemPattern; public parse(value: Config.NamedProblemPattern): NamedProblemPattern; - public parse(value: Config.NamedMultiLineProblemPattern): NamedMultiLineProblemPattern; - public parse(value: Config.ProblemPattern | Config.MultiLineProblemPattern | Config.NamedProblemPattern | Config.NamedMultiLineProblemPattern): any { - if (Config.NamedMultiLineProblemPattern.is(value)) { + public parse(value: Config.NamedMultiLineCheckedProblemPattern): NamedMultiLineProblemPattern; + public parse(value: Config.ProblemPattern | Config.MultiLineProblemPattern | Config.NamedProblemPattern | Config.NamedMultiLineCheckedProblemPattern): any { + if ((Config.MultiLineProblemPattern.is(value) && !Config.MultiLineCheckedProblemPattern.is(value)) || + (!Config.MultiLineProblemPattern.is(value) && !Config.CheckedProblemPattern.is(value))) { + this.error(localize('ProblemPatternParser.problemPattern.missingRegExp', 'The problem pattern is missing a regular expression.')); + } + + if (Config.NamedMultiLineCheckedProblemPattern.is(value)) { return this.createNamedMultiLineProblemPattern(value); - } else if (Config.MultiLineProblemPattern.is(value)) { + } else if (Config.MultiLineCheckedProblemPattern.is(value)) { return this.createMultiLineProblemPattern(value); - } else if (Config.NamedProblemPattern.is(value)) { + } else if (Config.NamedCheckedProblemPattern.is(value)) { let result = this.createSingleProblemPattern(value) as NamedProblemPattern; result.name = value.name; return result; - } else if (value) { + } else if (Config.CheckedProblemPattern.is(value)) { return this.createSingleProblemPattern(value); } else { return null; } } - private createSingleProblemPattern(value: Config.ProblemPattern): ProblemPattern { + private createSingleProblemPattern(value: Config.CheckedProblemPattern): ProblemPattern | null { let result = this.doCreateSingleProblemPattern(value, true); if (result.kind === undefined) { result.kind = ProblemLocationKind.Location; @@ -787,16 +851,20 @@ export class ProblemPatternParser extends Parser { return this.validateProblemPattern([result]) ? result : null; } - private createNamedMultiLineProblemPattern(value: Config.NamedMultiLineProblemPattern): NamedMultiLineProblemPattern { + private createNamedMultiLineProblemPattern(value: Config.NamedMultiLineCheckedProblemPattern): NamedMultiLineProblemPattern | null { + const validPatterns = this.createMultiLineProblemPattern(value.patterns); + if (!validPatterns) { + return null; + } let result = { name: value.name, label: value.label ? value.label : value.name, - patterns: this.createMultiLineProblemPattern(value.patterns) + patterns: validPatterns }; - return result.patterns ? result : null; + return result; } - private createMultiLineProblemPattern(values: Config.MultiLineProblemPattern): MultiLineProblemPattern { + private createMultiLineProblemPattern(values: Config.MultiLineCheckedProblemPattern): MultiLineProblemPattern | null { let result: MultiLineProblemPattern = []; for (let i = 0; i < values.length; i++) { let pattern = this.doCreateSingleProblemPattern(values[i], false); @@ -814,10 +882,12 @@ export class ProblemPatternParser extends Parser { return this.validateProblemPattern(result) ? result : null; } - private doCreateSingleProblemPattern(value: Config.ProblemPattern, setDefaults: boolean): ProblemPattern { - let result: ProblemPattern = { - regexp: this.createRegularExpression(value.regexp), - }; + private doCreateSingleProblemPattern(value: Config.CheckedProblemPattern, setDefaults: boolean): ProblemPattern { + const regexp = this.createRegularExpression(value.regexp); + if (regexp === void 0) { + throw new Error('Invalid regular expression'); + } + let result: ProblemPattern = { regexp }; if (value.kind) { result.kind = ProblemLocationKind.fromString(value.kind); } @@ -861,8 +931,7 @@ export class ProblemPatternParser extends Parser { } private validateProblemPattern(values: ProblemPattern[]): boolean { - let file: boolean, message: boolean, location: boolean, line: boolean; - let regexp: number = 0; + let file: boolean = false, message: boolean = false, location: boolean = false, line: boolean = false; let locationKind = (values[0].kind === undefined) ? ProblemLocationKind.Location : values[0].kind; values.forEach((pattern, i) => { @@ -873,14 +942,7 @@ export class ProblemPatternParser extends Parser { message = message || !Types.isUndefined(pattern.message); location = location || !Types.isUndefined(pattern.location); line = line || !Types.isUndefined(pattern.line); - if (pattern.regexp) { - regexp++; - } }); - if (regexp !== values.length) { - this.error(localize('ProblemPatternParser.problemPattern.missingRegExp', 'The problem pattern is missing a regular expression.')); - return false; - } if (!(file && message)) { this.error(localize('ProblemPatternParser.problemPattern.missingProperty', 'The problem pattern is invalid. It must have at least have a file and a message.')); return false; @@ -892,11 +954,8 @@ export class ProblemPatternParser extends Parser { return true; } - private createRegularExpression(value: string): RegExp { - let result: RegExp | null = null; - if (!value) { - return result; - } + private createRegularExpression(value: string): RegExp | undefined { + let result: RegExp | undefined; try { result = new RegExp(value); } catch (err) { @@ -999,13 +1058,12 @@ export namespace Schemas { }; export const NamedProblemPattern: IJSONSchema = Objects.deepClone(ProblemPattern); - NamedProblemPattern.properties = Objects.deepClone(NamedProblemPattern.properties); + NamedProblemPattern.properties = Objects.deepClone(NamedProblemPattern.properties) || {}; NamedProblemPattern.properties['name'] = { type: 'string', description: localize('NamedProblemPatternSchema.name', 'The name of the problem pattern.') }; - export const MultiLineProblemPattern: IJSONSchema = { type: 'array', items: ProblemPattern @@ -1040,7 +1098,7 @@ let problemPatternExtPoint = ExtensionsRegistry.registerExtensionPoint; + onReady(): Promise; get(key: string): ProblemPattern | MultiLineProblemPattern; } @@ -1048,12 +1106,12 @@ export interface IProblemPatternRegistry { class ProblemPatternRegistryImpl implements IProblemPatternRegistry { private patterns: IStringDictionary; - private readyPromise: TPromise; + private readyPromise: Promise; constructor() { this.patterns = Object.create(null); this.fillDefaults(); - this.readyPromise = new TPromise((resolve, reject) => { + this.readyPromise = new Promise((resolve, reject) => { problemPatternExtPoint.setHandler((extensions) => { // We get all statically know extension during startup in one batch try { @@ -1061,7 +1119,7 @@ class ProblemPatternRegistryImpl implements IProblemPatternRegistry { let problemPatterns = extension.value as Config.NamedProblemPatterns; let parser = new ProblemPatternParser(new ExtensionRegistryReporter(extension.collector)); for (let pattern of problemPatterns) { - if (Config.NamedMultiLineProblemPattern.is(pattern)) { + if (Config.NamedMultiLineCheckedProblemPattern.is(pattern)) { let result = parser.parse(pattern); if (parser.problemReporter.status.state < ValidationState.Error) { this.add(result.name, result.patterns); @@ -1090,7 +1148,7 @@ class ProblemPatternRegistryImpl implements IProblemPatternRegistry { }); } - public onReady(): TPromise { + public onReady(): Promise { return this.readyPromise; } @@ -1225,7 +1283,7 @@ export class ProblemMatcherParser extends Parser { super(logger); } - public parse(json: Config.ProblemMatcher): ProblemMatcher { + public parse(json: Config.ProblemMatcher): ProblemMatcher | null { let result = this.createProblemMatcher(json); if (!this.checkProblemMatcherValid(json, result)) { return null; @@ -1235,7 +1293,7 @@ export class ProblemMatcherParser extends Parser { return result; } - private checkProblemMatcherValid(externalProblemMatcher: Config.ProblemMatcher, problemMatcher: ProblemMatcher): boolean { + private checkProblemMatcherValid(externalProblemMatcher: Config.ProblemMatcher, problemMatcher: ProblemMatcher | null): problemMatcher is ProblemMatcher { if (!problemMatcher) { this.error(localize('ProblemMatcherParser.noProblemMatcher', 'Error: the description can\'t be converted into a problem matcher:\n{0}\n', JSON.stringify(externalProblemMatcher, null, 4))); return false; @@ -1255,10 +1313,10 @@ export class ProblemMatcherParser extends Parser { return true; } - private createProblemMatcher(description: Config.ProblemMatcher): ProblemMatcher { + private createProblemMatcher(description: Config.ProblemMatcher): ProblemMatcher | null { let result: ProblemMatcher | null = null; - let owner = description.owner ? description.owner : UUID.generateUuid(); + let owner = Types.isString(description.owner) ? description.owner : UUID.generateUuid(); let source = Types.isString(description.source) ? description.source : undefined; let applyTo = Types.isString(description.applyTo) ? ApplyToKind.fromString(description.applyTo) : ApplyToKind.allDocuments; if (!applyTo) { @@ -1267,7 +1325,7 @@ export class ProblemMatcherParser extends Parser { let fileLocation: FileLocationKind | undefined = undefined; let filePrefix: string | undefined = undefined; - let kind: FileLocationKind; + let kind: FileLocationKind | undefined; if (Types.isUndefined(description.fileLocation)) { fileLocation = FileLocationKind.Relative; filePrefix = '${workspaceFolder}'; @@ -1306,30 +1364,28 @@ export class ProblemMatcherParser extends Parser { let base = ProblemMatcherRegistry.get(variableName.substring(1)); if (base) { result = Objects.deepClone(base); - if (description.owner) { + if (description.owner !== void 0 && owner !== void 0) { result.owner = owner; } - if (source) { + if (description.source !== void 0 && source !== void 0) { result.source = source; } - if (fileLocation) { + if (description.fileLocation !== void 0 && fileLocation !== void 0) { result.fileLocation = fileLocation; - } - if (filePrefix) { result.filePrefix = filePrefix; } - if (description.pattern) { + if (description.pattern !== void 0 && pattern !== void 0 && pattern !== null) { result.pattern = pattern; } - if (description.severity) { + if (description.severity !== void 0 && severity !== void 0) { result.severity = severity; } - if (description.applyTo) { + if (description.applyTo !== void 0 && applyTo !== void 0) { result.applyTo = applyTo; } } } - } else if (fileLocation) { + } else if (fileLocation && pattern) { result = { owner: owner, applyTo: applyTo, @@ -1353,7 +1409,7 @@ export class ProblemMatcherParser extends Parser { return result; } - private createProblemPattern(value: string | Config.ProblemPattern | Config.MultiLineProblemPattern): ProblemPattern | ProblemPattern[] { + private createProblemPattern(value: string | Config.ProblemPattern | Config.MultiLineProblemPattern): ProblemPattern | ProblemPattern[] | null { if (Types.isString(value)) { let variableName: string = value; if (variableName.length > 1 && variableName[0] === '$') { @@ -1395,8 +1451,8 @@ export class ProblemMatcherParser extends Parser { if (Types.isUndefinedOrNull(backgroundMonitor)) { return; } - let begins: WatchingPattern = this.createWatchingPattern(backgroundMonitor.beginsPattern); - let ends: WatchingPattern = this.createWatchingPattern(backgroundMonitor.endsPattern); + let begins: WatchingPattern | null = this.createWatchingPattern(backgroundMonitor.beginsPattern); + let ends: WatchingPattern | null = this.createWatchingPattern(backgroundMonitor.endsPattern); if (begins && ends) { internal.watching = { activeOnStart: Types.isBoolean(backgroundMonitor.activeOnStart) ? backgroundMonitor.activeOnStart : false, @@ -1410,12 +1466,12 @@ export class ProblemMatcherParser extends Parser { } } - private createWatchingPattern(external: string | Config.WatchingPattern): WatchingPattern { + private createWatchingPattern(external: string | Config.WatchingPattern | undefined): WatchingPattern | null { if (Types.isUndefinedOrNull(external)) { return null; } - let regexp: RegExp; - let file: number; + let regexp: RegExp | null; + let file: number | undefined; if (Types.isString(external)) { regexp = this.createRegularExpression(external); } else { @@ -1430,7 +1486,7 @@ export class ProblemMatcherParser extends Parser { return file ? { regexp, file } : { regexp, file: 1 }; } - private createRegularExpression(value: string): RegExp { + private createRegularExpression(value: string | undefined): RegExp | null { let result: RegExp | null = null; if (!value) { return result; @@ -1579,7 +1635,7 @@ export namespace Schemas { }; export const LegacyProblemMatcher: IJSONSchema = Objects.deepClone(ProblemMatcher); - LegacyProblemMatcher.properties = Objects.deepClone(LegacyProblemMatcher.properties); + LegacyProblemMatcher.properties = Objects.deepClone(LegacyProblemMatcher.properties) || {}; LegacyProblemMatcher.properties['watchedTaskBeginsRegExp'] = { type: 'string', deprecationMessage: localize('LegacyProblemMatcherSchema.watchedBegin.deprecated', 'This property is deprecated. Use the watching property instead.'), @@ -1592,7 +1648,7 @@ export namespace Schemas { }; export const NamedProblemMatcher: IJSONSchema = Objects.deepClone(ProblemMatcher); - NamedProblemMatcher.properties = Objects.deepClone(NamedProblemMatcher.properties); + NamedProblemMatcher.properties = Objects.deepClone(NamedProblemMatcher.properties) || {}; NamedProblemMatcher.properties.name = { type: 'string', description: localize('NamedProblemMatcherSchema.name', 'The name of the problem matcher used to refer to it.') @@ -1610,7 +1666,7 @@ let problemMatchersExtPoint = ExtensionsRegistry.registerExtensionPoint; + onReady(): Promise; get(name: string): NamedProblemMatcher; keys(): string[]; } @@ -1618,12 +1674,12 @@ export interface IProblemMatcherRegistry { class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry { private matchers: IStringDictionary; - private readyPromise: TPromise; + private readyPromise: Promise; constructor() { this.matchers = Object.create(null); this.fillDefaults(); - this.readyPromise = new TPromise((resolve, reject) => { + this.readyPromise = new Promise((resolve, reject) => { problemMatchersExtPoint.setHandler((extensions) => { try { extensions.forEach(extension => { @@ -1647,7 +1703,7 @@ class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry { }); } - public onReady(): TPromise { + public onReady(): Promise { ProblemPatternRegistry.onReady(); return this.readyPromise; } diff --git a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts index 1da2c74561f..57fa3aba3cc 100644 --- a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { IStringDictionary } from 'vs/base/common/collections'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as Types from 'vs/base/common/types'; import * as Objects from 'vs/base/common/objects'; @@ -46,7 +45,7 @@ namespace Configuration { properties?: IJSONSchemaMap; } - export function from(value: TaskDefinition, extensionId: string, messageCollector: ExtensionMessageCollector): Tasks.TaskDefinition { + export function from(value: TaskDefinition, extensionId: string, messageCollector: ExtensionMessageCollector): Tasks.TaskDefinition | undefined { if (!value) { return undefined; } @@ -75,7 +74,7 @@ const taskDefinitionsExtPoint = ExtensionsRegistry.registerExtensionPoint; + onReady(): Promise; get(key: string): Tasks.TaskDefinition; all(): Tasks.TaskDefinition[]; @@ -85,12 +84,12 @@ export interface ITaskDefinitionRegistry { class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { private taskTypes: IStringDictionary; - private readyPromise: TPromise; + private readyPromise: Promise; private _schema: IJSONSchema; constructor() { this.taskTypes = Object.create(null); - this.readyPromise = new TPromise((resolve, reject) => { + this.readyPromise = new Promise((resolve, reject) => { taskDefinitionsExtPoint.setHandler((extensions) => { try { for (let extension of extensions) { @@ -109,7 +108,7 @@ class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { }); } - public onReady(): TPromise { + public onReady(): Promise { return this.readyPromise; } @@ -137,7 +136,7 @@ class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { } else { schema.properties = Object.create(null); } - schema.properties.type = { + schema.properties!.type = { type: 'string', enum: [definition.taskType] }; diff --git a/src/vs/workbench/parts/tasks/common/taskService.ts b/src/vs/workbench/parts/tasks/common/taskService.ts index 316d7878209..6eed335a670 100644 --- a/src/vs/workbench/parts/tasks/common/taskService.ts +++ b/src/vs/workbench/parts/tasks/common/taskService.ts @@ -8,6 +8,7 @@ import { Action } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; import { LinkedMap } from 'vs/base/common/map'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Task, ContributedTask, CustomTask, TaskSet, TaskSorter, TaskEvent, TaskIdentifier } from 'vs/workbench/parts/tasks/common/tasks'; @@ -66,8 +67,7 @@ export interface ITaskService { customize(task: ContributedTask | CustomTask, properties?: {}, openConfig?: boolean): TPromise; openConfig(task: CustomTask): TPromise; - registerTaskProvider(handle: number, taskProvider: ITaskProvider): void; - unregisterTaskProvider(handle: number): boolean; + registerTaskProvider(taskProvider: ITaskProvider): IDisposable; registerTaskSystem(scheme: string, taskSystemInfo: TaskSystemInfo): void; } diff --git a/src/vs/workbench/parts/tasks/common/taskSystem.ts b/src/vs/workbench/parts/tasks/common/taskSystem.ts index b565fea8166..a23db094065 100644 --- a/src/vs/workbench/parts/tasks/common/taskSystem.ts +++ b/src/vs/workbench/parts/tasks/common/taskSystem.ts @@ -102,11 +102,25 @@ export interface TaskTerminateResponse extends TerminateResponse { task: Task | undefined; } +export interface ResolveSet { + process?: { + name: string; + cwd?: string; + path?: string; + }; + variables: Set; +} + +export interface ResolvedVariables { + process?: string; + variables: Map; +} + export interface TaskSystemInfo { platform: Platform; context: any; uriProvider: (this: void, path: string) => URI; - resolveVariables(workspaceFolder: IWorkspaceFolder, variables: Set): TPromise>; + resolveVariables(workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet): TPromise; } export interface TaskSystemInfoResovler { diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index c6a303d57c4..5d8f1f88557 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -32,6 +32,8 @@ export enum ShellQuoting { Weak = 3, } +export const CUSTOMIZED_TASK_TYPE = '$customized'; + export namespace ShellQuoting { export function from(this: void, value: string): ShellQuoting { if (!value) { @@ -203,6 +205,11 @@ export interface PresentationOptions { * Controls whether to show the "Terminal will be reused by tasks, press any key to close it" message. */ showReuseMessage: boolean; + + /** + * Controls whether to clear the terminal before executing the task. + */ + clear: boolean; } export enum RuntimeType { @@ -431,7 +438,7 @@ export interface CommonTask { export interface CustomTask extends CommonTask, ConfigurationProperties { - type: 'custom'; + type: '$customized'; // CUSTOMIZED_TASK_TYPE /** * Indicated the source of the task (e.g tasks.json or extension) @@ -453,7 +460,7 @@ export interface CustomTask extends CommonTask, ConfigurationProperties { export namespace CustomTask { export function is(value: any): value is CustomTask { let candidate: CustomTask = value; - return candidate && candidate.type === 'custom'; + return candidate && candidate.type === CUSTOMIZED_TASK_TYPE; } export function getDefinition(task: CustomTask): KeyedTaskIdentifier { let type: string; @@ -469,7 +476,7 @@ export namespace CustomTask { }; return result; } - export function customizes(task: CustomTask): KeyedTaskIdentifier { + export function customizes(task: CustomTask): KeyedTaskIdentifier | undefined { if (task._source && task._source.customizes) { return task._source.customizes; } @@ -559,7 +566,7 @@ export namespace Task { if (!workspaceFolder) { return undefined; } - let key: CustomKey = { type: 'custom', folder: workspaceFolder.uri.toString(), id: task.identifier }; + let key: CustomKey = { type: CUSTOMIZED_TASK_TYPE, folder: workspaceFolder.uri.toString(), id: task.identifier }; return JSON.stringify(key); } if (ContributedTask.is(task)) { @@ -636,7 +643,7 @@ export namespace Task { } } - export function getTaskDefinition(task: Task, useSource: boolean = false): KeyedTaskIdentifier { + export function getTaskDefinition(task: Task, useSource: boolean = false): KeyedTaskIdentifier | undefined { if (ContributedTask.is(task)) { return task.defines; } else if (CustomTask.is(task)) { @@ -764,8 +771,8 @@ export namespace TaskEvent { taskName: task.name, runType: task.isBackground ? TaskRunType.Background : TaskRunType.SingleRun, group: task.group, - processId: undefined, - exitCode: undefined, + processId: undefined as number | undefined, + exitCode: undefined as number | undefined, __task: task, }; if (kind === TaskEventKind.ProcessStarted) { diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts index f03e3aa5c9a..bef22d20af1 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts @@ -82,7 +82,8 @@ const presentation: IJSONSchema = { reveal: 'always', focus: false, panel: 'shared', - showReuseMessage: true + showReuseMessage: true, + clear: false, }, description: nls.localize('JsonSchema.tasks.presentation', 'Configures the panel that is used to present the task\'s ouput and reads its input.'), additionalProperties: false, @@ -118,6 +119,11 @@ const presentation: IJSONSchema = { type: 'boolean', default: true, description: nls.localize('JsonSchema.tasks.presentation.showReuseMessage', 'Controls whether to show the `Terminal will be reused by tasks, press any key to close it` message.') + }, + clear: { + type: 'boolean', + default: false, + description: nls.localize('JsonSchema.tasks.presentation.clear', 'Controls whether the terminal is cleared before executing the task.') } } }; diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index 10629efcf19..7fe4376b8a0 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -41,7 +41,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ProblemMatcherRegistry, NamedProblemMatcher } from 'vs/workbench/parts/tasks/common/problemMatcher'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IProgressService2, IProgressOptions, ProgressLocation } from 'vs/workbench/services/progress/common/progress'; +import { IProgressService2, IProgressOptions, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IWindowService } from 'vs/platform/windows/common/windows'; @@ -444,6 +444,8 @@ class TaskService extends Disposable implements ITaskService { public static OutputChannelId: string = 'tasks'; public static OutputChannelLabel: string = nls.localize('tasks', "Tasks"); + private static nextHandle: number = 0; + private _configHasErrors: boolean; private _schemaVersion: JsonSchemaVersion; private _executionEngine: ExecutionEngine; @@ -675,15 +677,19 @@ class TaskService extends Disposable implements ITaskService { } } - public registerTaskProvider(handle: number, provider: ITaskProvider): void { + public registerTaskProvider(provider: ITaskProvider): IDisposable { if (!provider) { - return; + return { + dispose: () => { } + }; } + let handle = TaskService.nextHandle++; this._providers.set(handle, provider); - } - - public unregisterTaskProvider(handle: number): boolean { - return this._providers.delete(handle); + return { + dispose: () => { + this._providers.delete(handle); + } + }; } public registerTaskSystem(key: string, info: TaskSystemInfo): void { @@ -1801,7 +1807,7 @@ class TaskService extends Disposable implements ITaskService { return true; } - private createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false): TaskQuickPickEntry[] { + private createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry): TaskQuickPickEntry[] { if (tasks === void 0 || tasks === null || tasks.length === 0) { return []; } @@ -1822,7 +1828,11 @@ class TaskService extends Disposable implements ITaskService { for (let task of tasks) { let entry: TaskQuickPickEntry = TaskQuickPickEntry(task); entry.buttons = [{ iconClass: 'quick-open-task-configure', tooltip: nls.localize('configureTask', "Configure Task") }]; - entries.push(entry); + if (selectedEntry && (task === selectedEntry.task)) { + entries.unshift(selectedEntry); + } else { + entries.push(entry); + } } } let entries: TaskQuickPickEntry[]; @@ -1875,16 +1885,16 @@ class TaskService extends Disposable implements ITaskService { return entries; } - private showQuickPick(tasks: TPromise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false): TPromise { + private showQuickPick(tasks: TPromise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry): TPromise { let _createEntries = (): TPromise => { if (Array.isArray(tasks)) { - return TPromise.as(this.createTaskQuickPickEntries(tasks, group, sort)); + return TPromise.as(this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry)); } else { - return tasks.then((tasks) => this.createTaskQuickPickEntries(tasks, group, sort)); + return tasks.then((tasks) => this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry)); } }; return this.quickInputService.pick(_createEntries().then((entries) => { - if (entries.length === 0 && defaultEntry) { + if ((entries.length === 0) && defaultEntry) { entries.push(defaultEntry); } return entries; @@ -2338,33 +2348,36 @@ class TaskService extends Disposable implements ITaskService { this.runConfigureTasks(); return; } - let defaultTask: Task; - let defaultEntry: TaskQuickPickEntry; + let selectedTask: Task; + let selectedEntry: TaskQuickPickEntry; for (let task of tasks) { if (task.group === TaskGroup.Build && task.groupType === GroupType.default) { - defaultTask = task; + selectedTask = task; break; } } - if (defaultTask) { - tasks = []; - defaultEntry = { - label: nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task', Task.getQualifiedLabel(defaultTask)), - task: defaultTask + if (selectedTask) { + selectedEntry = { + label: nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task', Task.getQualifiedLabel(selectedTask)), + task: selectedTask }; } this.showIgnoredFoldersMessage().then(() => { this.showQuickPick(tasks, - nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), defaultEntry, true). + nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), undefined, true, false, selectedEntry). then((task) => { if (task === void 0) { return; } - if (task === defaultTask && CustomTask.is(task)) { + if (task === selectedTask && CustomTask.is(task)) { this.openConfig(task); } if (!InMemoryTask.is(task)) { - this.customize(task, { group: { kind: 'build', isDefault: true } }, true); + this.customize(task, { group: { kind: 'build', isDefault: true } }, true).then(() => { + if (selectedTask && (task !== selectedTask) && !InMemoryTask.is(selectedTask)) { + this.customize(selectedTask, { group: 'build' }, true); + } + }); } }); }); @@ -2382,27 +2395,41 @@ class TaskService extends Disposable implements ITaskService { this.tasks().then((tasks => { if (tasks.length === 0) { this.runConfigureTasks(); + return; } - let defaultTask: Task; + let selectedTask: Task; + let selectedEntry: TaskQuickPickEntry; + for (let task of tasks) { if (task.group === TaskGroup.Test && task.groupType === GroupType.default) { - defaultTask = task; + selectedTask = task; break; } } - if (defaultTask) { - this.notificationService.info(nls.localize('TaskService.defaultTestTaskExists', '{0} is already marked as the default test task.', Task.getQualifiedLabel(defaultTask))); - return; + if (selectedTask) { + selectedEntry = { + label: nls.localize('TaskService.defaultTestTaskExists', '{0} is already marked as the default test task.', Task.getQualifiedLabel(selectedTask)), + task: selectedTask + }; } + this.showIgnoredFoldersMessage().then(() => { - this.showQuickPick(tasks, nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), undefined, true).then((task) => { - if (!task) { - return; - } - if (!InMemoryTask.is(task)) { - this.customize(task, { group: { kind: 'test', isDefault: true } }, true); - } - }); + this.showQuickPick(tasks, + nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), undefined, true, false, selectedEntry).then((task) => { + if (!task) { + return; + } + if (task === selectedTask && CustomTask.is(task)) { + this.openConfig(task); + } + if (!InMemoryTask.is(task)) { + this.customize(task, { group: { kind: 'test', isDefault: true } }, true).then(() => { + if (selectedTask && (task !== selectedTask) && !InMemoryTask.is(selectedTask)) { + this.customize(selectedTask, { group: 'test' }, true); + } + }); + } + }); }); })); } else { @@ -2517,7 +2544,7 @@ let outputChannelRegistry = Registry.as(OutputExt.Output outputChannelRegistry.registerChannel({ id: TaskService.OutputChannelId, label: TaskService.OutputChannelLabel, log: false }); // Task Service -registerSingleton(ITaskService, TaskService); +registerSingleton(ITaskService, TaskService, true); // Register Quick Open const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index e936a5ca7cc..34868ee480f 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import * as path from 'path'; import * as nls from 'vs/nls'; @@ -19,6 +18,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as TPath from 'vs/base/common/paths'; +import { win32 } from 'vs/base/node/processes'; + import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -36,7 +37,7 @@ import { } from 'vs/workbench/parts/tasks/common/tasks'; import { ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver, - TelemetryEvent, Triggers, TaskTerminateResponse, TaskSystemInfoResovler, TaskSystemInfo + TelemetryEvent, Triggers, TaskTerminateResponse, TaskSystemInfoResovler, TaskSystemInfo, ResolveSet } from 'vs/workbench/parts/tasks/common/taskSystem'; interface TerminalData { @@ -72,6 +73,8 @@ export class TerminalTaskSystem implements ITaskSystem { public static TelemetryEventName: string = 'taskService'; + private static ProcessVarName = '${__process__}'; + private static shellQuotes: IStringDictionary = { 'cmd': { strong: '"' @@ -304,18 +307,60 @@ export class TerminalTaskSystem implements ITaskSystem { if (workspaceFolder) { taskSystemInfo = this.taskSystemInfoResolver(workspaceFolder); } - let resolvedVariables: TPromise>; + let variableResolver: TPromise; + let isProcess = task.command && task.command.runtime === RuntimeType.Process; + let options = task.command && task.command.options ? task.command.options : undefined; + let cwd = options ? options.cwd : undefined; + let envPath: string | undefined = undefined; + if (options && options.env) { + for (let key of Object.keys(options.env)) { + if (key.toLowerCase() === 'path') { + if (Types.isString(options.env[key])) { + envPath = options.env[key]; + } + break; + } + } + } if (taskSystemInfo) { - resolvedVariables = taskSystemInfo.resolveVariables(workspaceFolder, variables); + let resolveSet: ResolveSet = { + variables + }; + if (taskSystemInfo.platform === Platform.Platform.Windows && isProcess) { + resolveSet.process = { name: CommandString.value(task.command.name) }; + if (cwd) { + resolveSet.process.cwd = cwd; + } + if (envPath) { + resolveSet.process.path = envPath; + } + } + variableResolver = taskSystemInfo.resolveVariables(workspaceFolder, resolveSet).then((resolved) => { + let result = new Map(); + resolved.variables.forEach(variable => { + result.set(variable, this.configurationResolverService.resolve(workspaceFolder, variable)); + }); + if (resolved.process !== void 0) { + result.set(TerminalTaskSystem.ProcessVarName, resolved.process); + } + return new VariableResolver(workspaceFolder, taskSystemInfo, result, undefined); + }); } else { let result = new Map(); variables.forEach(variable => { result.set(variable, this.configurationResolverService.resolve(workspaceFolder, variable)); }); - resolvedVariables = TPromise.as(result); + if (Platform.isWindows && isProcess) { + result.set(TerminalTaskSystem.ProcessVarName, win32.findExecutable( + this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name)), + cwd ? this.configurationResolverService.resolve(workspaceFolder, cwd) : undefined, + envPath ? envPath.split(path.delimiter).map(p => this.configurationResolverService.resolve(workspaceFolder, p)) : undefined + )); + } + variableResolver = TPromise.as(new VariableResolver(workspaceFolder, taskSystemInfo, result, this.configurationResolverService)); } - return resolvedVariables.then((variables) => { - return this.executeInTerminal(task, trigger, new VariableResolver(workspaceFolder, taskSystemInfo, variables, this.configurationResolverService)); + return variableResolver.then((resolver) => { + return this.executeInTerminal(task, trigger, resolver); }); } @@ -339,7 +384,7 @@ export class TerminalTaskSystem implements ITaskSystem { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task)); if (eventCounter === 0) { let reveal = task.command.presentation.reveal; - if (reveal === RevealKind.Silent && watchingProblemMatcher.numberOfMatches > 0 && watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error) { + if ((reveal === RevealKind.Silent) && (watchingProblemMatcher.numberOfMatches > 0) && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(false); } @@ -352,10 +397,12 @@ export class TerminalTaskSystem implements ITaskSystem { if (error || !terminal) { return; } - let processStartedSignaled: boolean = false; + let processStartedSignaled = false; terminal.processReady.then(() => { - processStartedSignaled = true; - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + if (!processStartedSignaled) { + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + processStartedSignaled = true; + } }, (_error) => { // The process never got ready. Need to think how to handle this. }); @@ -386,24 +433,26 @@ export class TerminalTaskSystem implements ITaskSystem { break; } let reveal = task.command.presentation.reveal; - if (reveal === RevealKind.Silent && (exitCode !== 0 || watchingProblemMatcher.numberOfMatches > 0 && watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { + if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (watchingProblemMatcher.numberOfMatches > 0) && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(false); } watchingProblemMatcher.done(); watchingProblemMatcher.dispose(); registeredLinkMatchers.forEach(handle => terminal.deregisterLinkMatcher(handle)); - if (processStartedSignaled) { - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); + if (!processStartedSignaled) { + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + processStartedSignaled = true; } - toDispose = dispose(toDispose); - toDispose = null; + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); for (let i = 0; i < eventCounter; i++) { let event = TaskEvent.create(TaskEventKind.Inactive, task); this._onDidStateChange.fire(event); } eventCounter = 0; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task)); + toDispose = dispose(toDispose); + toDispose = null; resolve({ exitCode }); }); }); @@ -413,10 +462,13 @@ export class TerminalTaskSystem implements ITaskSystem { if (error || !terminal) { return; } - let processStartedSignaled: boolean = false; + + let processStartedSignaled = false; terminal.processReady.then(() => { - processStartedSignaled = true; - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + if (!processStartedSignaled) { + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + processStartedSignaled = true; + } }, (_error) => { // The process never got ready. Need to think how to handle this. }); @@ -443,16 +495,18 @@ export class TerminalTaskSystem implements ITaskSystem { break; } let reveal = task.command.presentation.reveal; - if (reveal === RevealKind.Silent && (exitCode !== 0 || startStopProblemMatcher.numberOfMatches > 0 && startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { + if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && (startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(false); } startStopProblemMatcher.done(); startStopProblemMatcher.dispose(); registeredLinkMatchers.forEach(handle => terminal.deregisterLinkMatcher(handle)); - if (processStartedSignaled) { - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); + if (!processStartedSignaled) { + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + processStartedSignaled = true; } + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task)); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task)); resolve({ exitCode }); @@ -465,7 +519,7 @@ export class TerminalTaskSystem implements ITaskSystem { if (!terminal) { return TPromise.wrapError(new Error(`Failed to create terminal for task ${task._label}`)); } - if (task.command.presentation.reveal === RevealKind.Always || (task.command.presentation.reveal === RevealKind.Silent && task.problemMatchers.length === 0)) { + if (task.command.presentation.reveal === RevealKind.Always) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(task.command.presentation.focus); } @@ -531,7 +585,7 @@ export class TerminalTaskSystem implements ITaskSystem { } else if (task.command.presentation.showReuseMessage) { waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'); } else { - waitOnExit = '\u200B'; + waitOnExit = true; } } let shellLaunchConfig: IShellLaunchConfig | undefined = undefined; @@ -554,7 +608,7 @@ export class TerminalTaskSystem implements ITaskSystem { } let shellArgs = shellLaunchConfig.args.slice(0); let toAdd: string[] = []; - let commandLine = this.buildShellCommandLine(shellLaunchConfig.executable, shellOptions, command, originalCommand, args); + let commandLine = this.buildShellCommandLine(platform, shellLaunchConfig.executable, shellOptions, command, originalCommand, args); let windowsShellArgs: boolean = false; if (platform === Platform.Platform.Windows) { // Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's @@ -588,7 +642,7 @@ export class TerminalTaskSystem implements ITaskSystem { } else { if (!shellSpecified) { // Under Mac remove -l to not start it as a login shell. - if (Platform.isMacintosh) { + if (platform === Platform.Platform.Mac) { let index = shellArgs.indexOf('-l'); if (index !== -1) { shellArgs.splice(index, 1); @@ -612,10 +666,9 @@ export class TerminalTaskSystem implements ITaskSystem { } } } else { - let cwd = options && options.cwd ? options.cwd : process.cwd(); - // On Windows executed process must be described absolute. Since we allowed command without an - // absolute path (e.g. "command": "node") we need to find the executable in the CWD or PATH. - let executable = Platform.isWindows && !isShellCommand ? this.findExecutable(commandExecutable, cwd, options) : commandExecutable; + let executable = !isShellCommand + ? this.resolveVariable(resolver, TerminalTaskSystem.ProcessVarName) + : commandExecutable; // When we have a process task there is no need to quote arguments. So we go ahead and take the string value. shellLaunchConfig = { @@ -683,6 +736,9 @@ export class TerminalTaskSystem implements ITaskSystem { } if (terminalToReuse) { terminalToReuse.terminal.reuseTerminal(shellLaunchConfig); + if (task.command.presentation.clear) { + terminalToReuse.terminal.clear(); + } return [terminalToReuse.terminal, commandExecutable, undefined]; } @@ -694,13 +750,18 @@ export class TerminalTaskSystem implements ITaskSystem { delete this.terminals[terminalKey]; delete this.sameTaskTerminals[terminalData.lastTask]; this.idleTaskTerminals.delete(terminalData.lastTask); + // Delete the task now as a work around for cases when the onExit isn't fired. + // This can happen if the terminal wasn't shutdown with an "immediate" flag and is expected. + // For correct terminal re-use, the task needs to be deleted immediately. + // Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate. + delete this.activeTasks[Task.getMapKey(task)]; } }); this.terminals[terminalKey] = { terminal: result, lastTask: taskKey }; return [result, commandExecutable, undefined]; } - private buildShellCommandLine(shellExecutable: string, shellOptions: ShellConfiguration, command: CommandString, originalCommand: CommandString, args: CommandString[]): string { + private buildShellCommandLine(platform: Platform.Platform, shellExecutable: string, shellOptions: ShellConfiguration, command: CommandString, originalCommand: CommandString, args: CommandString[]): string { let basename = path.parse(shellExecutable).name.toLowerCase(); let shellQuoteOptions = this.getQuotingOptions(basename, shellOptions); @@ -788,7 +849,7 @@ export class TerminalTaskSystem implements ITaskSystem { let commandLine = result.join(' '); // There are special rules quoted command line in cmd.exe - if (Platform.isWindows) { + if (platform === Platform.Platform.Windows) { if (basename === 'cmd' && commandQuoted && argQuoted) { commandLine = '"' + commandLine + '"'; } else if (basename === 'powershell' && commandQuoted) { @@ -796,7 +857,7 @@ export class TerminalTaskSystem implements ITaskSystem { } } - if (basename === 'cmd' && Platform.isWindows && commandQuoted && argQuoted) { + if (basename === 'cmd' && platform === Platform.Platform.Windows && commandQuoted && argQuoted) { commandLine = '"' + commandLine + '"'; } return commandLine; @@ -887,62 +948,6 @@ export class TerminalTaskSystem implements ITaskSystem { return { command, args }; } - private findExecutable(command: string, cwd: string, options: CommandOptions): string { - // If we have an absolute path then we take it. - if (path.isAbsolute(command)) { - return command; - } - let dir = path.dirname(command); - if (dir !== '.') { - // We have a directory and the directory is relative (see above). Make the path absolute - // to the current working directory. - return path.join(cwd, command); - } - let paths: string[] | undefined = undefined; - // The options can override the PATH. So consider that PATH if present. - if (options && options.env) { - // Path can be named in many different ways and for the execution it doesn't matter - for (let key of Object.keys(options.env)) { - if (key.toLowerCase() === 'path') { - if (Types.isString(options.env[key])) { - paths = options.env[key].split(path.delimiter); - } - break; - } - } - } - if (paths === void 0 && Types.isString(process.env.PATH)) { - paths = process.env.PATH.split(path.delimiter); - } - // No PATH environment. Make path absolute to the cwd. - if (paths === void 0 || paths.length === 0) { - return path.join(cwd, command); - } - // We have a simple file name. We get the path variable from the env - // and try to find the executable on the path. - for (let pathEntry of paths) { - // The path entry is absolute. - let fullPath: string; - if (path.isAbsolute(pathEntry)) { - fullPath = path.join(pathEntry, command); - } else { - fullPath = path.join(cwd, pathEntry, command); - } - if (fs.existsSync(fullPath)) { - return fullPath; - } - let withExtension = fullPath + '.com'; - if (fs.existsSync(withExtension)) { - return withExtension; - } - withExtension = fullPath + '.exe'; - if (fs.existsSync(withExtension)) { - return withExtension; - } - } - return path.join(cwd, command); - } - private resolveVariables(resolver: VariableResolver, value: string[]): string[]; private resolveVariables(resolver: VariableResolver, value: CommandString[]): CommandString[]; private resolveVariables(resolver: VariableResolver, value: CommandString[]): CommandString[] { diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts index 7ef52c614c2..717f148e5e4 100644 --- a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import * as Objects from 'vs/base/common/objects'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; -import { TPromise, Promise } from 'vs/base/common/winjs.base'; import * as Async from 'vs/base/common/async'; import Severity from 'vs/base/common/severity'; import * as Strings from 'vs/base/common/strings'; @@ -51,7 +50,7 @@ export class ProcessTaskSystem implements ITaskSystem { private errorsShown: boolean; private childProcess: LineProcess; private activeTask: CustomTask; - private activeTaskPromise: TPromise; + private activeTaskPromise: Promise; private readonly _onDidStateChange: Emitter; @@ -75,8 +74,8 @@ export class ProcessTaskSystem implements ITaskSystem { return this._onDidStateChange.event; } - public isActive(): TPromise { - return TPromise.as(!!this.childProcess); + public isActive(): Promise { + return Promise.resolve(!!this.childProcess); } public isActiveSync(): boolean { @@ -117,14 +116,14 @@ export class ProcessTaskSystem implements ITaskSystem { return true; } - public terminate(task: Task): TPromise { + public terminate(task: Task): Promise { if (!this.activeTask || Task.getMapKey(this.activeTask) !== Task.getMapKey(task)) { - return TPromise.as({ success: false, task: undefined }); + return Promise.resolve({ success: false, task: undefined }); } return this.terminateAll().then(values => values[0]); } - public terminateAll(): TPromise { + public terminateAll(): Promise { if (this.childProcess) { let task = this.activeTask; return this.childProcess.terminate().then((response) => { @@ -133,7 +132,7 @@ export class ProcessTaskSystem implements ITaskSystem { return [result]; }); } - return TPromise.as([{ success: true, task: undefined }]); + return Promise.resolve([{ success: true, task: undefined }]); } private executeTask(task: Task, trigger: string = Triggers.command): ITaskExecuteResult { @@ -169,7 +168,7 @@ export class ProcessTaskSystem implements ITaskSystem { } */ this.telemetryService.publicLog(ProcessTaskSystem.TelemetryEventName, telemetryEvent); - return TPromise.wrapError(err); + return Promise.reject(err); }); return result; } catch (err) { @@ -356,7 +355,7 @@ export class ProcessTaskSystem implements ITaskSystem { this.activeTaskPromise = null; } - private handleError(task: CustomTask, errorData: ErrorData): Promise { + private handleError(task: CustomTask, errorData: ErrorData): Promise { let makeVisible = false; if (errorData.error && !errorData.terminated) { let args: string = task.command.args ? task.command.args.join(' ') : ''; @@ -382,7 +381,7 @@ export class ProcessTaskSystem implements ITaskSystem { error.stderr = errorData.stderr; error.stdout = errorData.stdout; error.terminated = errorData.terminated; - return TPromise.wrapError(error); + return Promise.reject(error); } private checkTerminated(task: Task, data: SuccessData | ErrorData): boolean { diff --git a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts index 324da8a9616..dc2578d5fd3 100644 --- a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts +++ b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts @@ -112,6 +112,11 @@ export interface PresentationOptions { * Controls whether to show the "Terminal will be reused by tasks, press any key to close it" message. */ showReuseMessage?: boolean; + + /** + * Controls whether the terminal should be cleared before running the task. + */ + clear?: boolean; } export interface TaskIdentifier { @@ -312,7 +317,7 @@ export interface ConfigurationProperties { export interface CustomTask extends CommandProperties, ConfigurationProperties { /** - * Custom tasks have the type 'custom' + * Custom tasks have the type CUSTOMIZED_TASK_TYPE */ type?: string; @@ -742,7 +747,7 @@ namespace CommandOptions { namespace CommandConfiguration { export namespace PresentationOptions { - const properties: MetaData[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'panel' }, { property: 'showReuseMessage' }]; + const properties: MetaData[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'panel' }, { property: 'showReuseMessage' }, { property: 'clear' }]; interface PresentationOptionsShape extends LegacyCommandProperties { presentation?: PresentationOptions; @@ -754,6 +759,7 @@ namespace CommandConfiguration { let focus: boolean; let panel: Tasks.PanelKind; let showReuseMessage: boolean; + let clear: boolean; if (Types.isBoolean(config.echoCommand)) { echo = config.echoCommand; } @@ -777,11 +783,14 @@ namespace CommandConfiguration { if (Types.isBoolean(presentation.showReuseMessage)) { showReuseMessage = presentation.showReuseMessage; } + if (Types.isBoolean(presentation.clear)) { + clear = presentation.clear; + } } if (echo === void 0 && reveal === void 0 && focus === void 0 && panel === void 0 && showReuseMessage === void 0) { return undefined; } - return { echo, reveal, focus, panel, showReuseMessage }; + return { echo, reveal, focus, panel, showReuseMessage, clear }; } export function assignProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions): Tasks.PresentationOptions { @@ -794,7 +803,7 @@ namespace CommandConfiguration { export function fillDefaults(value: Tasks.PresentationOptions, context: ParseContext): Tasks.PresentationOptions { let defaultEcho = context.engine === Tasks.ExecutionEngine.Terminal ? true : false; - return _fillDefaults(value, { echo: defaultEcho, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared, showReuseMessage: true }, properties, context); + return _fillDefaults(value, { echo: defaultEcho, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared, showReuseMessage: true, clear: false }, properties, context); } export function freeze(value: Tasks.PresentationOptions): Readonly { @@ -1312,9 +1321,9 @@ namespace CustomTask { } let type = external.type; if (type === void 0 || type === null) { - type = 'custom'; + type = Tasks.CUSTOMIZED_TASK_TYPE; } - if (type !== 'custom' && type !== 'shell' && type !== 'process') { + if (type !== Tasks.CUSTOMIZED_TASK_TYPE && type !== 'shell' && type !== 'process') { context.problemReporter.error(nls.localize('ConfigurationParser.notCustom', 'Error: tasks is not declared as a custom task. The configuration will be ignored.\n{0}\n', JSON.stringify(external, null, 4))); return undefined; } @@ -1328,7 +1337,7 @@ namespace CustomTask { } let result: Tasks.CustomTask = { - type: 'custom', + type: Tasks.CUSTOMIZED_TASK_TYPE, _id: context.uuidMap.getUUID(taskName), _source: Objects.assign({}, source, { config: { index, element: external, file: '.vscode\\tasks.json', workspaceFolder: context.workspaceFolder } }), _label: taskName, @@ -1404,7 +1413,7 @@ namespace CustomTask { _id: configuredProps._id, _source: Objects.assign({}, configuredProps._source, { customizes: contributedTask.defines }), _label: configuredProps.name || contributedTask._label, - type: 'custom', + type: Tasks.CUSTOMIZED_TASK_TYPE, command: contributedTask.command, name: configuredProps.name || contributedTask.name, identifier: configuredProps.identifier || contributedTask.identifier, @@ -1451,7 +1460,7 @@ namespace TaskParser { function isCustomTask(value: CustomTask | ConfiguringTask): value is CustomTask { let type = value.type; let customize = (value as any).customize; - return customize === void 0 && (type === void 0 || type === null || type === 'custom' || type === 'shell' || type === 'process'); + return customize === void 0 && (type === void 0 || type === null || type === Tasks.CUSTOMIZED_TASK_TYPE || type === 'shell' || type === 'process'); } export function from(this: void, externals: (CustomTask | ConfiguringTask)[], globals: Globals, context: ParseContext): TaskParseResult { @@ -1830,7 +1839,7 @@ class ConfigurationParser { _id: context.uuidMap.getUUID(name), _source: Objects.assign({}, source, { config: { index: -1, element: fileConfig, workspaceFolder: context.workspaceFolder } }), _label: name, - type: 'custom', + type: Tasks.CUSTOMIZED_TASK_TYPE, name: name, identifier: name, group: Tasks.TaskGroup.Build, diff --git a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts index 46fe439f55d..6adf850b5cc 100644 --- a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts +++ b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts @@ -83,7 +83,7 @@ class PresentationBuilder { public result: Tasks.PresentationOptions; constructor(public parent: CommandConfigurationBuilder) { - this.result = { echo: false, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared, showReuseMessage: true }; + this.result = { echo: false, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared, showReuseMessage: true, clear: false }; } public echo(value: boolean): PresentationBuilder { @@ -185,7 +185,7 @@ class CustomTaskBuilder { _id: name, _source: { kind: Tasks.TaskSourceKind.Workspace, label: 'workspace', config: { workspaceFolder: workspaceFolder, element: undefined, index: -1, file: '.vscode/tasks.json' } }, _label: name, - type: 'custom', + type: Tasks.CUSTOMIZED_TASK_TYPE, identifier: name, name: name, command: this.commandBuilder.result, diff --git a/src/vs/workbench/parts/terminal/browser/terminalTab.ts b/src/vs/workbench/parts/terminal/browser/terminalTab.ts index f3a0ebc8e67..38b5fbe58df 100644 --- a/src/vs/workbench/parts/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/parts/terminal/browser/terminalTab.ts @@ -61,7 +61,7 @@ class SplitPaneContainer { } // Get sizes - const sizes = []; + const sizes: number[] = []; for (let i = 0; i < this._splitView.length; i++) { sizes.push(this._splitView.getViewSize(i)); } @@ -109,7 +109,7 @@ class SplitPaneContainer { } public remove(instance: ITerminalInstance): void { - let index = null; + let index: number | null = null; for (let i = 0; i < this._children.length; i++) { if (this._children[i].instance === instance) { index = i; @@ -209,7 +209,7 @@ class SplitPane implements IView { export class TerminalTab extends Disposable implements ITerminalTab { private _terminalInstances: ITerminalInstance[] = []; private _splitPaneContainer: SplitPaneContainer | undefined; - private _tabElement: HTMLElement; + private _tabElement: HTMLElement | null; private _panelPosition: Position = Position.BOTTOM; private _activeInstanceIndex: number; @@ -258,7 +258,7 @@ export class TerminalTab extends Disposable implements ITerminalTab { this._onInstancesChanged.fire(); } - public get activeInstance(): ITerminalInstance { + public get activeInstance(): ITerminalInstance | null { if (this._terminalInstances.length === 0) { return null; } @@ -286,7 +286,9 @@ export class TerminalTab extends Disposable implements ITerminalTab { const newIndex = index < this._terminalInstances.length ? index : this._terminalInstances.length - 1; this.setActiveInstanceByIndex(newIndex); // TODO: Only focus the new instance if the tab had focus? - this.activeInstance.focus(true); + if (this.activeInstance) { + this.activeInstance.focus(true); + } } // Remove the instance from the split pane if it has been created @@ -341,8 +343,9 @@ export class TerminalTab extends Disposable implements ITerminalTab { if (!this._splitPaneContainer) { this._panelPosition = this._partService.getPanelPosition(); const orientation = this._panelPosition === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL; - this._splitPaneContainer = new SplitPaneContainer(this._tabElement, orientation); - this.terminalInstances.forEach(instance => this._splitPaneContainer.split(instance)); + const newLocal = new SplitPaneContainer(this._tabElement, orientation); + this._splitPaneContainer = newLocal; + this.terminalInstances.forEach(instance => this._splitPaneContainer!.split(instance)); } } @@ -365,7 +368,7 @@ export class TerminalTab extends Disposable implements ITerminalTab { terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, shellLaunchConfig: IShellLaunchConfig - ): ITerminalInstance { + ): ITerminalInstance | undefined { const newTerminalSize = ((this._panelPosition === Position.BOTTOM ? this._container.clientWidth : this._container.clientHeight) / (this._terminalInstances.length + 1)); if (newTerminalSize < TERMINAL_MIN_USEFUL_SIZE) { return undefined; diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 8ecfea8987f..3b5e23acbe5 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -100,7 +100,7 @@ export interface ITerminalConfiguration { }; showExitAlert: boolean; experimentalBufferImpl: 'JsArray' | 'TypedArray'; - splitCwd: 'workspaceRoot' | 'sourceInitialCwd' | 'sourceCwd'; + splitCwd: 'workspaceRoot' | 'initial' | 'inherited'; } export interface ITerminalConfigHelper { @@ -211,17 +211,17 @@ export interface ITerminalService { /** * Creates a raw terminal instance, this should not be used outside of the terminal part. */ - createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance; + createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance; getInstanceFromId(terminalId: number): ITerminalInstance; getInstanceFromIndex(terminalIndex: number): ITerminalInstance; getTabLabels(): string[]; - getActiveInstance(): ITerminalInstance; + getActiveInstance(): ITerminalInstance | null; setActiveInstance(terminalInstance: ITerminalInstance): void; setActiveInstanceByIndex(terminalIndex: number): void; getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance; splitInstance(instance: ITerminalInstance, shell?: IShellLaunchConfig): void; - getActiveTab(): ITerminalTab; + getActiveTab(): ITerminalTab | null; setActiveTabToNext(): void; setActiveTabToPrevious(): void; setActiveTabByIndex(tabIndex: number): void; @@ -249,7 +249,7 @@ export const enum Direction { } export interface ITerminalTab { - activeInstance: ITerminalInstance; + activeInstance: ITerminalInstance | null; terminalInstances: ITerminalInstance[]; title: string; onDisposed: Event; @@ -263,7 +263,7 @@ export interface ITerminalTab { setVisible(visible: boolean): void; layout(width: number, height: number): void; addDisposable(disposable: IDisposable): void; - split(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; + split(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance | undefined; } export interface ITerminalDimensions { @@ -410,10 +410,12 @@ export interface ITerminalInstance { /** * Dispose the terminal instance, removing it from the panel/service and freeing up resources. * - * @param isShuttingDown Whether VS Code is shutting down, if so kill any terminal processes - * immediately. + * @param immediate Whether the kill should be immediate or not. Immediate should only be used + * when VS Code is shutting down or in cases where the terminal dispose was user initiated. + * The immediate===false exists to cover an edge case where the final output of the terminal can + * get cut off. If immediate kill any terminal processes immediately. */ - dispose(isShuttingDown?: boolean): void; + dispose(immediate?: boolean): void; /** * Registers a link matcher, allowing custom link patterns to be matched and handled. diff --git a/src/vs/workbench/parts/terminal/common/terminalCommands.ts b/src/vs/workbench/parts/terminal/common/terminalCommands.ts index 2eff4e1f6b0..5ddb88fa3cb 100644 --- a/src/vs/workbench/parts/terminal/common/terminalCommands.ts +++ b/src/vs/workbench/parts/terminal/common/terminalCommands.ts @@ -18,6 +18,7 @@ export const enum TERMINAL_COMMAND_ID { SELECT_ALL = 'workbench.action.terminal.selectAll', DELETE_WORD_LEFT = 'workbench.action.terminal.deleteWordLeft', DELETE_WORD_RIGHT = 'workbench.action.terminal.deleteWordRight', + DELETE_TO_LINE_START = 'workbench.action.terminal.deleteToLineStart', MOVE_TO_LINE_START = 'workbench.action.terminal.moveToLineStart', MOVE_TO_LINE_END = 'workbench.action.terminal.moveToLineEnd', NEW = 'workbench.action.terminal.new', diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts index 7108b21f38f..c4230b231f3 100644 --- a/src/vs/workbench/parts/terminal/common/terminalService.ts +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -139,7 +139,10 @@ export abstract class TerminalService implements ITerminalService { // const hasFocusOnExit = tab.activeInstance.hadFocusOnExit; const newIndex = index < this._terminalTabs.length ? index : this._terminalTabs.length - 1; this.setActiveTabByIndex(newIndex); - this.getActiveInstance().focus(true); + const activeInstance = this.getActiveInstance(); + if (activeInstance) { + activeInstance.focus(true); + } } // Hide the panel if there are no more instances, provided that VS Code is not shutting @@ -157,14 +160,14 @@ export abstract class TerminalService implements ITerminalService { } } - public getActiveTab(): ITerminalTab { + public getActiveTab(): ITerminalTab | null { if (this._activeTabIndex < 0 || this._activeTabIndex >= this._terminalTabs.length) { return null; } return this._terminalTabs[this._activeTabIndex]; } - public getActiveInstance(): ITerminalInstance { + public getActiveInstance(): ITerminalInstance | null { const tab = this.getActiveTab(); if (!tab) { return null; @@ -198,7 +201,7 @@ export abstract class TerminalService implements ITerminalService { } } - private _getInstanceFromGlobalInstanceIndex(index: number): { tab: ITerminalTab, tabIndex: number, instance: ITerminalInstance, localInstanceIndex: number } { + private _getInstanceFromGlobalInstanceIndex(index: number): { tab: ITerminalTab, tabIndex: number, instance: ITerminalInstance, localInstanceIndex: number } | null { let currentTabIndex = 0; while (index >= 0 && currentTabIndex < this._terminalTabs.length) { const tab = this._terminalTabs[currentTabIndex]; @@ -281,7 +284,7 @@ export abstract class TerminalService implements ITerminalService { instance.addDisposable(instance.onFocus(this._onActiveInstanceChanged.fire, this._onActiveInstanceChanged)); } - private _getTabForInstance(instance: ITerminalInstance): ITerminalTab { + private _getTabForInstance(instance: ITerminalInstance): ITerminalTab | null { for (let i = 0; i < this._terminalTabs.length; i++) { const tab = this._terminalTabs[i]; if (tab.terminalInstances.indexOf(instance) !== -1) { @@ -295,22 +298,21 @@ export abstract class TerminalService implements ITerminalService { return new Promise((complete) => { const panel = this._panelService.getActivePanel(); if (!panel || panel.getId() !== TERMINAL_PANEL_ID) { - return this._panelService.openPanel(TERMINAL_PANEL_ID, focus).then(() => { - if (focus) { - // Do the focus call asynchronously as going through the - // command palette will force editor focus - setTimeout(() => { - const instance = this.getActiveInstance(); - if (instance) { - instance.focusWhenReady(true).then(() => complete(void 0)); - } else { - complete(void 0); - } - }, 0); - } else { - complete(void 0); - } - }); + this._panelService.openPanel(TERMINAL_PANEL_ID, focus); + if (focus) { + // Do the focus call asynchronously as going through the + // command palette will force editor focus + setTimeout(() => { + const instance = this.getActiveInstance(); + if (instance) { + instance.focusWhenReady(true).then(() => complete(void 0)); + } else { + complete(void 0); + } + }, 0); + } else { + complete(void 0); + } } else { if (focus) { // Do the focus call asynchronously as going through the 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 18c85aac5e6..2cf59b28164 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -17,7 +17,7 @@ import { getDefaultShell } from 'vs/workbench/parts/terminal/node/terminal'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KillTerminalAction, ClearSelectionTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction, ToggleEscapeSequenceLoggingAction, SendSequenceTerminalCommand, ToggleRegexCommand, ToggleWholeWordCommand, ToggleCaseSensitiveCommand, FindNext, FindPrevious } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; +import { KillTerminalAction, ClearSelectionTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction, ToggleEscapeSequenceLoggingAction, SendSequenceTerminalCommand, ToggleRegexCommand, ToggleWholeWordCommand, ToggleCaseSensitiveCommand, FindNext, FindPrevious, DeleteToLineStartTerminalAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; @@ -249,6 +249,7 @@ configurationRegistry.registerConfiguration({ TERMINAL_COMMAND_ID.CLEAR_SELECTION, TERMINAL_COMMAND_ID.CLEAR, TERMINAL_COMMAND_ID.COPY_SELECTION, + TERMINAL_COMMAND_ID.DELETE_TO_LINE_START, TERMINAL_COMMAND_ID.DELETE_WORD_LEFT, TERMINAL_COMMAND_ID.DELETE_WORD_RIGHT, TERMINAL_COMMAND_ID.FIND_WIDGET_FOCUS, @@ -381,15 +382,15 @@ configurationRegistry.registerConfiguration({ default: 'JsArray' }, 'terminal.integrated.splitCwd': { - description: nls.localize('terminal.integrated.splitCwd', "Controls the source of the starting cwd for terminals created by splitting."), + description: nls.localize('terminal.integrated.splitCwd', "Controls the working directory a split terminal starts with."), type: 'string', - enum: ['workspaceRoot', 'sourceInitialCwd', 'sourceCwd'], + enum: ['workspaceRoot', 'initial', 'inherited'], enumDescriptions: [ - nls.localize('terminal.integrated.splitCwd.workspaceRoot', "A new split terminal will use the workspace root as the cwd."), - nls.localize('terminal.integrated.splitCwd.sourceInitialCwd', "A new split terminal will use the cwd that the parent terminal started with."), - nls.localize('terminal.integrated.splitCwd.sourceCwd', "On macOS and Linux, a new split terminal will use the cwd of the parent terminal. On Windows, this behaves the same as sourceInitialCwd."), + nls.localize('terminal.integrated.splitCwd.workspaceRoot', "A new split terminal will use the workspace root as the working directory. In a multi-root workspace a choice for which root folder to use is offered."), + nls.localize('terminal.integrated.splitCwd.initial', "A new split terminal will use the working directory that the parent terminal started with."), + nls.localize('terminal.integrated.splitCwd.inherited', "On macOS and Linux, a new split terminal will use the working directory of the parent terminal. On Windows, this behaves the same as initial."), ], - default: 'workspaceRoot' + default: 'inherited' }, } }); @@ -498,6 +499,10 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordRightT primary: KeyMod.CtrlCmd | KeyCode.Delete, mac: { primary: KeyMod.Alt | KeyCode.Delete } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Right', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteToLineStartTerminalAction, DeleteToLineStartTerminalAction.ID, DeleteToLineStartTerminalAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete To Line Start', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineStartTerminalAction, MoveToLineStartTerminalAction.ID, MoveToLineStartTerminalAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index 2cdfd3b16a0..367833d0dd2 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -8,7 +8,7 @@ import * as os from 'os'; import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, IShellLaunchConfig, ITerminalConfigHelper } from 'vs/workbench/parts/terminal/common/terminal'; +import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper } from 'vs/workbench/parts/terminal/common/terminal'; import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IPartService } from 'vs/workbench/services/part/common/partService'; @@ -22,7 +22,7 @@ import { TerminalEntry } from 'vs/workbench/parts/terminal/browser/terminalQuick import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; @@ -32,20 +32,33 @@ import { FindReplaceState } from 'vs/editor/contrib/find/findState'; export const TERMINAL_PICKER_PREFIX = 'term '; -function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance): Promise { +function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { switch (configHelper.config.splitCwd) { case 'workspaceRoot': { - // allow default behavior - return new Promise(resolve => { - resolve(''); - }); + // allow original behavior + let pathPromise: Promise = Promise.resolve(''); + if (folders.length > 1) { + // Only choose a path when there's more than 1 folder + const options: IPickOptions = { + placeHolder: nls.localize('workbench.action.terminal.newWorkspacePlaceholder', "Select current working directory for new terminal") + }; + pathPromise = commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, [options]).then(workspace => { + if (!workspace) { + // Don't split the instance if the workspace picker was canceled + return undefined; + } + return Promise.resolve(workspace.uri.fsPath); + }); + } + + return pathPromise; } - case 'sourceInitialCwd': { + case 'initial': { return new Promise(resolve => { resolve(instance.initialCwd); }); } - case 'sourceCwd': { + case 'inherited': { return instance.getCwd(); } } @@ -95,7 +108,7 @@ export class KillTerminalAction extends Action { public run(event?: any): PromiseLike { const instance = this.terminalService.getActiveInstance(); if (instance) { - instance.dispose(); + instance.dispose(true); if (this.terminalService.terminalInstances.length > 0) { this.terminalService.showPanel(true); } @@ -120,7 +133,7 @@ export class QuickKillTerminalAction extends Action { public run(event?: any): Promise { const instance = this.terminalEntry.instance; if (instance) { - instance.dispose(); + instance.dispose(true); } return Promise.resolve(timeout(50)).then(result => this.quickOpenService.show(TERMINAL_PICKER_PREFIX, null)); } @@ -220,6 +233,20 @@ export class DeleteWordRightTerminalAction extends BaseSendTextTerminalAction { } } +export class DeleteToLineStartTerminalAction extends BaseSendTextTerminalAction { + public static readonly ID = TERMINAL_COMMAND_ID.DELETE_TO_LINE_START; + public static readonly LABEL = nls.localize('workbench.action.terminal.deleteToLineStart', "Delete to Line Start"); + + constructor( + id: string, + label: string, + @ITerminalService terminalService: ITerminalService + ) { + // Send ctrl+u + super(id, label, '\u0015', terminalService); + } +} + export class MoveToLineStartTerminalAction extends BaseSendTextTerminalAction { public static readonly ID = TERMINAL_COMMAND_ID.MOVE_TO_LINE_START; public static readonly LABEL = nls.localize('workbench.action.terminal.moveToLineStart', "Move To Line Start"); @@ -358,32 +385,13 @@ export class SplitTerminalAction extends Action { return Promise.resolve(void 0); } - const folders = this.workspaceContextService.getWorkspace().folders; - - let pathPromise: Promise = Promise.resolve({}); - if (folders.length > 1) { - // Only choose a path when there's more than 1 folder - const options: IPickOptions = { - placeHolder: nls.localize('workbench.action.terminal.newWorkspacePlaceholder', "Select current working directory for new terminal") - }; - pathPromise = this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, [options]).then(workspace => { - if (!workspace) { - // Don't split the instance if the workspace picker was canceled - return null; - } - return Promise.resolve({ cwd: workspace.uri.fsPath }); - }); - } - - return pathPromise.then(path => { - if (!path) { - return Promise.resolve(void 0); - } - return getCwdForSplit(this._terminalService.configHelper, instance).then(cwd => { - path.cwd = cwd; - this._terminalService.splitInstance(instance, path); + return getCwdForSplit(this._terminalService.configHelper, instance, this.workspaceContextService.getWorkspace().folders, this.commandService).then(cwd => { + if (cwd || (cwd === '')) { + this._terminalService.splitInstance(instance, { cwd }); return this._terminalService.showPanel(true); - }); + } else { + return undefined; + } }); } } @@ -717,7 +725,7 @@ export class SwitchTerminalActionItem extends SelectActionItem { @IThemeService themeService: IThemeService, @IContextViewService contextViewService: IContextViewService ) { - super(null, action, terminalService.getTabLabels(), terminalService.activeTabIndex, contextViewService, { ariaLabel: nls.localize('terminals', 'Terminals') }); + super(null, action, terminalService.getTabLabels(), terminalService.activeTabIndex, contextViewService, { ariaLabel: nls.localize('terminals', 'Open Terminals.') }); this.toDispose.push(terminalService.onInstancesChanged(this._updateItems, this)); this.toDispose.push(terminalService.onActiveTabChanged(this._updateItems, this)); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 5d9dcdcef99..fedf68dbadd 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -487,8 +487,8 @@ export class TerminalInstance implements ITerminalInstance { // Discard first frame time as it's normal to take longer frameTimes.shift(); - const averageTime = frameTimes.reduce((p, c) => p + c) / frameTimes.length; - if (averageTime > SLOW_CANVAS_RENDER_THRESHOLD) { + const medianTime = frameTimes.sort()[Math.floor(frameTimes.length / 2)]; + if (medianTime > SLOW_CANVAS_RENDER_THRESHOLD) { const promptChoices: IPromptChoice[] = [ { label: nls.localize('yes', "Yes"), @@ -513,7 +513,6 @@ export class TerminalInstance implements ITerminalInstance { nls.localize('terminal.slowRendering', 'The standard renderer for the integrated terminal appears to be slow on your computer. Would you like to switch to the alternative DOM-based renderer which may improve performance? [Read more about terminal settings](https://code.visualstudio.com/docs/editor/integrated-terminal#_changing-how-the-terminal-is-rendered).'), promptChoices ); - console.warn('The standard renderer for the integrated terminal appears to be slow, frame times follow:', frameTimes); } }; @@ -576,7 +575,7 @@ export class TerminalInstance implements ITerminalInstance { this._terminalFocusContextKey.set(terminalFocused); } - public dispose(isShuttingDown?: boolean): void { + public dispose(immediate?: boolean): void { this._logService.trace(`terminalInstance#dispose (id: ${this.id})`); this._windowsShellHelper = lifecycle.dispose(this._windowsShellHelper); @@ -602,7 +601,7 @@ export class TerminalInstance implements ITerminalInstance { this._xterm = null; } if (this._processManager) { - this._processManager.dispose(isShuttingDown); + this._processManager.dispose(immediate); } if (!this._isDisposed) { this._isDisposed = true; @@ -834,12 +833,12 @@ export class TerminalInstance implements ITerminalInstance { if (exitCode) { this._xterm.writeln(exitCodeMessage); } - let message = typeof this._shellLaunchConfig.waitOnExit === 'string' - ? this._shellLaunchConfig.waitOnExit - : nls.localize('terminal.integrated.waitOnExit', 'Press any key to close the terminal'); - // Bold the message and add an extra new line to make it stand out from the rest of the output - message = `\n\x1b[1m${message}\x1b[0m`; - this._xterm.writeln(message); + if (typeof this._shellLaunchConfig.waitOnExit === 'string') { + let message = this._shellLaunchConfig.waitOnExit; + // Bold the message and add an extra new line to make it stand out from the rest of the output + message = `\n\x1b[1m${message}\x1b[0m`; + this._xterm.writeln(message); + } // Disable all input if the terminal is exiting and listen for next keypress this._xterm.setOption('disableStdin', true); if (this._xterm.textarea) { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index 4fb2beb4e61..89eca9e2f91 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -53,7 +53,7 @@ export class TerminalPanel extends Panel { super(TERMINAL_PANEL_ID, telemetryService, _themeService, storageService); } - public create(parent: HTMLElement): Promise { + public create(parent: HTMLElement): void { super.create(parent); this._parentDomElement = parent; dom.addClass(this._parentDomElement, 'integrated-terminal'); @@ -98,7 +98,6 @@ export class TerminalPanel extends Panel { // Force another layout (first is setContainers) since config has changed this.layout(new dom.Dimension(this._terminalContainer.offsetWidth, this._terminalContainer.offsetHeight)); - return Promise.resolve(void 0); } public layout(dimension?: dom.Dimension): void { @@ -108,26 +107,26 @@ export class TerminalPanel extends Panel { this._terminalService.terminalTabs.forEach(t => t.layout(dimension.width, dimension.height)); } - public setVisible(visible: boolean): Promise { + public setVisible(visible: boolean): void { if (visible) { if (this._terminalService.terminalInstances.length > 0) { this._updateFont(); this._updateTheme(); } else { - return super.setVisible(visible).then(() => { - // Check if instances were already restored as part of workbench restore - if (this._terminalService.terminalInstances.length === 0) { - this._terminalService.createTerminal(); - } - if (this._terminalService.terminalInstances.length > 0) { - this._updateFont(); - this._updateTheme(); - } - return Promise.resolve(void 0); - }); + super.setVisible(visible); + // Check if instances were already restored as part of workbench restore + if (this._terminalService.terminalInstances.length === 0) { + this._terminalService.createTerminal(); + } + if (this._terminalService.terminalInstances.length > 0) { + this._updateFont(); + this._updateTheme(); + } + return; } } - return super.setVisible(visible); + super.setVisible(visible); + } public getActions(): IAction[] { @@ -260,7 +259,7 @@ export class TerminalPanel extends Panel { const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy }; this._contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(this._getContextMenuActions()), + getActions: () => this._getContextMenuActions(), getActionsContext: () => this._parentDomElement }); } @@ -344,4 +343,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { if (hoverForeground) { collector.addRule(`.monaco-workbench .panel.integrated-terminal .terminal-message-widget { color: ${hoverForeground}; }`); } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts index 75f1d8fc1df..8b455630977 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts @@ -92,7 +92,7 @@ export class TerminalService extends AbstractTerminalService implements ITermina return this.createTerminal({ name, isRendererOnly: true }); } - public createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance { + public createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance { const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig); this._onInstanceCreated.fire(instance); return instance; diff --git a/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts b/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts index 3ecc8d5cb0a..7074ca39d1e 100644 --- a/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts +++ b/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts @@ -8,7 +8,7 @@ import { ITerminalCommandTracker } from 'vs/workbench/parts/terminal/common/term import { IDisposable } from 'vs/base/common/lifecycle'; /** - * The minimize size of the prompt in which to assume the line is a command. + * The minimum size of the prompt in which to assume the line is a command. */ const MINIMUM_PROMPT_LENGTH = 2; diff --git a/src/vs/workbench/parts/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts index cb23452fdb6..ea9573246b0 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts @@ -17,6 +17,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { private _closeTimeout: any; private _ptyProcess: pty.IPty; private _currentTitle: string = ''; + private _processStartupComplete: Promise; private readonly _onProcessData: Emitter = new Emitter(); public get onProcessData(): Event { return this._onProcessData.event; } @@ -36,7 +37,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { ) { let shellName: string; if (os.platform() === 'win32') { - shellName = path.basename(shellLaunchConfig.executable); + shellName = path.basename(shellLaunchConfig.executable || ''); } else { // Using 'xterm-256color' here helps ensure that the majority of Linux distributions will use a // color prompt as defined in the default ~/.bashrc file. @@ -52,11 +53,17 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { }; try { - this._ptyProcess = pty.spawn(shellLaunchConfig.executable, shellLaunchConfig.args, options); + this._ptyProcess = pty.spawn(shellLaunchConfig.executable!, shellLaunchConfig.args || [], options); + this._processStartupComplete = new Promise(c => { + this.onProcessIdReady((pid) => { + c(); + }); + }); } catch (error) { // The only time this is expected to happen is when the file specified to launch with does not exist. this._exitCode = 2; this._queueProcessExit(); + this._processStartupComplete = Promise.resolve(void 0); return; } this._ptyProcess.on('data', (data) => { @@ -108,15 +115,19 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { } private _kill(): void { - // Attempt to kill the pty, it may have already been killed at this - // point but we want to make sure - try { - this._ptyProcess.kill(); - } catch (ex) { - // Swallow, the pty has already been killed - } - this._onProcessExit.fire(this._exitCode); - this.dispose(); + // Wait to kill to process until the start up code has run. This prevents us from firing a process exit before a + // process start. + this._processStartupComplete.then(() => { + // Attempt to kill the pty, it may have already been killed at this + // point but we want to make sure + try { + this._ptyProcess.kill(); + } catch (ex) { + // Swallow, the pty has already been killed + } + this._onProcessExit.fire(this._exitCode); + this.dispose(); + }); } private _sendProcessId() { diff --git a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts index fe2d2f4ba9e..a1235fcd8b3 100644 --- a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts +++ b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts @@ -198,7 +198,7 @@ class GenerateColorThemeAction extends Action { let colors = Registry.as(ColorRegistryExtensions.ColorContribution).getColors(); let colorIds = colors.map(c => c.id).sort(); let resultingColors = {}; - let inherited = []; + let inherited: string[] = []; for (let colorId of colorIds) { const color = theme.getColor(colorId, false); if (color) { diff --git a/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts b/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts index 8ac211ef77d..f2696c752d3 100644 --- a/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts +++ b/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts @@ -215,15 +215,14 @@ class Snapper { } public captureSyntaxTokens(fileName: string, content: string): Thenable { - return this.modeService.getOrCreateModeByFilepathOrFirstLine(fileName).then(mode => { - return this.textMateService.createGrammar(mode.getId()).then((grammar) => { - let lines = content.split(/\r\n|\r|\n/); + const modeId = this.modeService.getModeIdByFilepathOrFirstLine(fileName); + return this.textMateService.createGrammar(modeId).then((grammar) => { + let lines = content.split(/\r\n|\r|\n/); - let result = this._tokenize(grammar, lines); - return this._getThemesResult(grammar, lines).then((themesResult) => { - this._enrichResult(result, themesResult); - return result.filter(t => t.c.length > 0); - }); + let result = this._tokenize(grammar, lines); + return this._getThemesResult(grammar, lines).then((themesResult) => { + this._enrichResult(result, themesResult); + return result.filter(t => t.c.length > 0); }); }); } diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index f12c4ed45df..cbabfd24a9c 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -26,6 +26,7 @@ import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/commo import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInput'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; function renderBody( body: string, @@ -60,7 +61,8 @@ export class ReleaseNotesManager { @IRequestService private readonly _requestService: IRequestService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IEditorService private readonly _editorService: IEditorService, - @IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService + @IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService, + @IExtensionService private readonly _extensionService: IExtensionService ) { TokenizationRegistry.onDidChange(async () => { if (!this._currentReleaseNotes || !this._lastText) { @@ -194,7 +196,10 @@ export class ReleaseNotesManager { const renderer = new marked.Renderer(); renderer.code = (code, lang) => { const modeId = this._modeService.getModeIdForLanguageName(lang); - result.push(this._modeService.getOrCreateMode(modeId).then(_ => TokenizationRegistry.getPromise(modeId))); + result.push(this._extensionService.whenInstalledExtensionsRegistered().then(_ => { + this._modeService.triggerMode(modeId); + return TokenizationRegistry.getPromise(modeId); + })); return ''; }; diff --git a/src/vs/workbench/parts/update/electron-browser/update.ts b/src/vs/workbench/parts/update/electron-browser/update.ts index 68e4617fc97..530ddf9892d 100644 --- a/src/vs/workbench/parts/update/electron-browser/update.ts +++ b/src/vs/workbench/parts/update/electron-browser/update.ts @@ -424,24 +424,31 @@ export class UpdateContribution implements IGlobalActivity { return; } - // windows user fast updates and mac - this.notificationService.prompt( - severity.Info, - nls.localize('updateAvailableAfterRestart', "Restart {0} to apply the latest update.", product.nameLong), - [{ - label: nls.localize('updateNow', "Update Now"), - run: () => this.updateService.quitAndInstall() - }, { - label: nls.localize('later', "Later"), - run: () => { } - }, { + const actions = [{ + label: nls.localize('updateNow', "Update Now"), + run: () => this.updateService.quitAndInstall() + }, { + label: nls.localize('later', "Later"), + run: () => { } + }]; + + // TODO@joao check why snap updates send `update` as falsy + if (update.productVersion) { + actions.push({ label: nls.localize('releaseNotes', "Release Notes"), run: () => { const action = this.instantiationService.createInstance(ShowReleaseNotesAction, update.productVersion); action.run(); action.dispose(); } - }], + }); + } + + // windows user fast updates and mac + this.notificationService.prompt( + severity.Info, + nls.localize('updateAvailableAfterRestart', "Restart {0} to apply the latest update.", product.nameLong), + actions, { sticky: true } ); } diff --git a/src/vs/workbench/parts/webview/electron-browser/webview-pre.js b/src/vs/workbench/parts/webview/electron-browser/webview-pre.js index 6cfbf3f08f6..7ca8427445e 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webview-pre.js +++ b/src/vs/workbench/parts/webview/electron-browser/webview-pre.js @@ -269,33 +269,13 @@ } ::-webkit-scrollbar-thumb { - background-color: rgba(121, 121, 121, 0.4); + background-color: var(--vscode-scrollbarSlider-background); } - body.vscode-light::-webkit-scrollbar-thumb { - background-color: rgba(100, 100, 100, 0.4); - } - body.vscode-high-contrast::-webkit-scrollbar-thumb { - background-color: rgba(111, 195, 223, 0.3); - } - ::-webkit-scrollbar-thumb:hover { - background-color: rgba(100, 100, 100, 0.7); + background-color: var(--vscode-scrollbarSlider-hoverBackground); } - body.vscode-light::-webkit-scrollbar-thumb:hover { - background-color: rgba(100, 100, 100, 0.7); - } - body.vscode-high-contrast::-webkit-scrollbar-thumb:hover { - background-color: rgba(111, 195, 223, 0.8); - } - ::-webkit-scrollbar-thumb:active { - background-color: rgba(85, 85, 85, 0.8); - } - body.vscode-light::-webkit-scrollbar-thumb:active { - background-color: rgba(0, 0, 0, 0.6); - } - body.vscode-high-contrast::-webkit-scrollbar-thumb:active { - background-color: rgba(111, 195, 223, 0.8); + background-color: var(--vscode-scrollbarSlider-activeBackground); } `; if (newDocument.head.hasChildNodes()) { diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts index 8cc701c66d8..707ddc031cf 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts @@ -7,16 +7,21 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IEditorInputFactory } from 'vs/workbench/common/editor'; import { WebviewEditorInput } from './webviewEditorInput'; import { IWebviewEditorService, WebviewInputOptions } from './webviewEditorService'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; + +interface SerializedIconPath { + light: string | UriComponents; + dark: string | UriComponents; +} interface SerializedWebview { readonly viewType: string; readonly id: number; readonly title: string; readonly options: WebviewInputOptions; - readonly extensionLocation: string; + readonly extensionLocation: string | UriComponents; readonly state: any; - readonly iconPath: { light: string, dark: string } | undefined; + readonly iconPath: SerializedIconPath | undefined; } export class WebviewEditorInputFactory implements IEditorInputFactory { @@ -45,11 +50,16 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { id: input.getId(), title: input.getName(), options: input.options, - extensionLocation: input.extensionLocation.toString(), + extensionLocation: input.extensionLocation, state: input.state, - iconPath: input.iconPath ? { light: input.iconPath.light.toString(), dark: input.iconPath.dark.toString(), } : undefined, + iconPath: input.iconPath ? { light: input.iconPath.light, dark: input.iconPath.dark, } : undefined, }; - return JSON.stringify(data); + + try { + return JSON.stringify(data); + } catch { + return null; + } } public deserialize( @@ -57,8 +67,32 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { serializedEditorInput: string ): WebviewEditorInput { const data: SerializedWebview = JSON.parse(serializedEditorInput); - const extensionLocation = URI.parse(data.extensionLocation); - const iconPath = data.iconPath ? { light: URI.parse(data.iconPath.light), dark: URI.parse(data.iconPath.dark) } : undefined; + const extensionLocation = reviveUri(data.extensionLocation); + const iconPath = reviveIconPath(data.iconPath); return this._webviewService.reviveWebview(data.viewType, data.id, data.title, iconPath, data.state, data.options, extensionLocation); } } +function reviveIconPath(data: SerializedIconPath | undefined) { + if (!data) { + return undefined; + } + + const light = reviveUri(data.light); + const dark = reviveUri(data.dark); + return light && dark ? { light, dark } : undefined; +} + +function reviveUri(data: string | UriComponents | undefined): URI | undefined { + if (!data) { + return undefined; + } + + try { + if (typeof data === 'string') { + return URI.parse(data); + } + return URI.from(data); + } catch { + return undefined; + } +} diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts index 5824d631de6..624aa9affcc 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts @@ -5,7 +5,6 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService, ACTIVE_GROUP_TYPE, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; @@ -66,7 +65,7 @@ export interface WebviewReviver { reviveWebview( webview: WebviewEditorInput - ): TPromise; + ): Promise; } export interface WebviewEvents { @@ -138,15 +137,15 @@ export class WebviewEditorService implements IWebviewEditorService { canRevive: (_webview) => { return true; }, - reviveWebview: (webview: WebviewEditorInput): TPromise => { - return TPromise.wrap(this.tryRevive(webview)).then(didRevive => { + reviveWebview: (webview: WebviewEditorInput): Promise => { + return this.tryRevive(webview).then(didRevive => { if (didRevive) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } // A reviver may not be registered yet. Put into queue and resolve promise when we can revive let resolve: (value: void) => void; - const promise = new TPromise(r => { resolve = r; }); + const promise = new Promise(r => { resolve = r; }); this._awaitingRevival.push({ input: webview, resolve }); return promise; }); diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts index e85679a2614..31eda153ac6 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts @@ -21,10 +21,11 @@ Registry.as(ConfigurationExtensions.Configuration) 'properties': { 'workbench.startupEditor': { 'type': 'string', - 'enum': ['none', 'welcomePage', 'newUntitledFile', 'welcomePageInEmptyWorkbench'], + 'enum': ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench'], 'enumDescriptions': [ localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the Welcome page (default)."), + localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file (only applies when opening an empty workspace)."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."), ], 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 a19d39a6ddf..3cf2f7cc38c 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -6,6 +6,8 @@ import 'vs/css!./welcomePage'; import { URI } from 'vs/base/common/uri'; import * as path from 'path'; +import { readdir } from 'vs/base/node/extfs'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import * as arrays from 'vs/base/common/arrays'; import { WalkThroughInput } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughInput'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -55,14 +57,45 @@ export class WelcomePageContribution implements IWorkbenchContribution { @IBackupFileService backupFileService: IBackupFileService, @IWorkspaceContextService contextService: IWorkspaceContextService, @ILifecycleService lifecycleService: ILifecycleService, + @ICommandService private commandService: ICommandService, ) { const enabled = isWelcomePageEnabled(configurationService, contextService); if (enabled && lifecycleService.startupKind !== StartupKind.ReloadedWindow) { backupFileService.hasBackups().then(hasBackups => { const activeEditor = editorService.activeEditor; if (!activeEditor && !hasBackups) { - return instantiationService.createInstance(WelcomePage) - .openEditor(); + const openWithReadme = configurationService.getValue(configurationKey) === 'readme'; + if (openWithReadme) { + const workSpaceFolders = contextService.getWorkspace().folders.map(el => el.uri); + for (let i = 0; i < workSpaceFolders.length; i += 1) { + const workSpaceFolder = workSpaceFolders[i]; + let foldersChecked = 0; + readdir(workSpaceFolder.path, (err, files) => { + foldersChecked += 1; + if (err) { + onUnexpectedError(err); + } else { + for (const content of files) { + if (content.toLowerCase().lastIndexOf('readme', 0) === 0) { + const readmeLocation = path.join(workSpaceFolder.path, content); + if (readmeLocation.toLowerCase().slice(readmeLocation.length - 3) === '.md') { + return !editorService.activeEditor && this.commandService + .executeCommand('markdown.showPreview', URI.file(readmeLocation)); + } + return !editorService.activeEditor && editorService + .openEditor({ resource: URI.file(readmeLocation) }); + } + } + } + if (foldersChecked === workSpaceFolders.length - 1) { + return instantiationService.createInstance(WelcomePage).openEditor(); + } + return undefined; + }); + } + } else { + return instantiationService.createInstance(WelcomePage).openEditor(); + } } return undefined; }).then(null, onUnexpectedError); @@ -78,7 +111,7 @@ function isWelcomePageEnabled(configurationService: IConfigurationService, conte return welcomeEnabled.value; } } - return startupEditor.value === 'welcomePage' || startupEditor.value === 'welcomePageInEmptyWorkbench' && contextService.getWorkbenchState() === WorkbenchState.EMPTY; + return startupEditor.value === 'welcomePage' || startupEditor.value === 'readme' || startupEditor.value === 'welcomePageInEmptyWorkbench' && contextService.getWorkbenchState() === WorkbenchState.EMPTY; } export class WelcomePageAction extends Action { @@ -590,8 +623,13 @@ export class WelcomeInputFactory implements IEditorInputFactory { export const buttonBackground = registerColor('welcomePage.buttonBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonBackground', 'Background color for the buttons on the Welcome page.')); export const buttonHoverBackground = registerColor('welcomePage.buttonHoverBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonHoverBackground', 'Hover background color for the buttons on the Welcome page.')); +export const welcomePageBackground = registerColor('welcomePage.background', { light: null, dark: null, hc: null }, localize('welcomePage.background', 'Background color for the Welcome page.')); registerThemingParticipant((theme, collector) => { + const backgroundColor = theme.getColor(welcomePageBackground); + if (backgroundColor) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePageContainer { background-color: ${backgroundColor}; }`); + } const foregroundColor = theme.getColor(foreground); if (foregroundColor) { collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .caption { color: ${foregroundColor}; }`); diff --git a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts index 7895c98f556..7b4e8721ac6 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -26,9 +25,9 @@ export class WalkThroughContentProvider implements ITextModelContentProvider, IW this.textModelResolverService.registerTextModelContentProvider(Schemas.walkThrough, this); } - public provideTextContent(resource: URI): TPromise { + public provideTextContent(resource: URI): Thenable { const query = resource.query ? JSON.parse(resource.query) : {}; - const content: TPromise = (query.moduleId ? new TPromise((resolve, reject) => { + const content: Thenable = (query.moduleId ? new Promise((resolve, reject) => { require([query.moduleId], content => { try { resolve(content.default()); @@ -40,7 +39,7 @@ export class WalkThroughContentProvider implements ITextModelContentProvider, IW return content.then(content => { let codeEditorModel = this.modelService.getModel(resource); if (!codeEditorModel) { - codeEditorModel = this.modelService.createModel(content, this.modeService.getOrCreateModeByFilepathOrFirstLine(resource.fsPath), resource); + codeEditorModel = this.modelService.createModel(content, this.modeService.createByFilepathOrFirstLine(resource.fsPath), resource); } else { this.modelService.updateModel(codeEditorModel, content); } @@ -61,7 +60,7 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi this.textModelResolverService.registerTextModelContentProvider(Schemas.walkThroughSnippet, this); } - public provideTextContent(resource: URI): TPromise { + public provideTextContent(resource: URI): Thenable { return this.textFileService.resolveTextContent(URI.file(resource.fsPath)).then(content => { let codeEditorModel = this.modelService.getModel(resource); if (!codeEditorModel) { @@ -85,9 +84,9 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi const markdown = textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined); marked(markdown, { renderer }); - const modeId = this.modeService.getModeIdForLanguageName(languageName); - const mode = this.modeService.getOrCreateMode(modeId); - codeEditorModel = this.modelService.createModel(codeSnippet, mode, resource); + const languageId = this.modeService.getModeIdForLanguageName(languageName); + const languageSelection = this.modeService.create(languageId); + codeEditorModel = this.modelService.createModel(codeSnippet, languageSelection, resource); } else { this.modelService.updateModel(codeEditorModel, content.value); } diff --git a/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts b/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts index f351878e50d..dbebd087bf0 100644 --- a/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts @@ -24,7 +24,7 @@ namespace schema { group?: string; } - export function parseMenuId(value: string): MenuId { + export function parseMenuId(value: string): MenuId | undefined { switch (value) { case 'commandPalette': return MenuId.CommandPalette; case 'touchBar': return MenuId.TouchBarContext; @@ -326,7 +326,7 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyM } const menu = schema.parseMenuId(entry.key); - if (!menu) { + if (typeof menu !== 'number') { collector.warn(localize('menuId.invalid', "`{0}` is not a valid menu identifier", entry.key)); return; } diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index 6555f087bb2..212ccb5194f 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -415,4 +415,4 @@ export class BulkEditService implements IBulkEditService { } -registerSingleton(IBulkEditService, BulkEditService); +registerSingleton(IBulkEditService, BulkEditService, true); diff --git a/src/vs/workbench/services/codeEditor/browser/codeEditorService.ts b/src/vs/workbench/services/codeEditor/browser/codeEditorService.ts index 4453c6a8ad0..2825a04ea6a 100644 --- a/src/vs/workbench/services/codeEditor/browser/codeEditorService.ts +++ b/src/vs/workbench/services/codeEditor/browser/codeEditorService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; -import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { IResourceInput } from 'vs/platform/editor/common/editor'; -import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TPromise } from 'vs/base/common/winjs.base'; -import { TextEditorOptions } from 'vs/workbench/common/editor'; +import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; import { ScrollType } from 'vs/editor/common/editorCommon'; +import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { TextEditorOptions } from 'vs/workbench/common/editor'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; export class CodeEditorService extends CodeEditorServiceImpl { diff --git a/src/vs/workbench/services/commands/common/commandService.ts b/src/vs/workbench/services/commands/common/commandService.ts index abbbcb05078..c1b71485ed6 100644 --- a/src/vs/workbench/services/commands/common/commandService.ts +++ b/src/vs/workbench/services/commands/common/commandService.ts @@ -6,7 +6,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICommandService, ICommandEvent, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Event, Emitter, filterEvent, toPromise } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; @@ -35,17 +35,21 @@ export class CommandService extends Disposable implements ICommandService { // we don't wait for it when the extension // host didn't yet start and the command is already registered - const activation = Promise.resolve(this._extensionService.activateByEvent(`onCommand:${id}`)); + const activation: Thenable = this._extensionService.activateByEvent(`onCommand:${id}`); const commandIsRegistered = !!CommandsRegistry.getCommand(id); if (!this._extensionHostIsReady && commandIsRegistered) { return this._tryExecuteCommand(id, args); } else { - let waitFor: Promise = activation; + let waitFor = activation; if (!commandIsRegistered) { - waitFor = Promise.all([activation, this._extensionService.activateByEvent(`*`)]); + waitFor = Promise.race([ + // race activation events against command registration + Promise.all([activation, this._extensionService.activateByEvent(`*`)]), + toPromise(filterEvent(CommandsRegistry.onDidRegisterCommand, e => e === id)), + ]); } - return waitFor.then(_ => this._tryExecuteCommand(id, args)); + return (waitFor as Promise).then(_ => this._tryExecuteCommand(id, args)); } } diff --git a/src/vs/workbench/services/commands/test/common/commandService.test.ts b/src/vs/workbench/services/commands/test/common/commandService.test.ts index 3966c2b8513..6be443f3cd1 100644 --- a/src/vs/workbench/services/commands/test/common/commandService.test.ts +++ b/src/vs/workbench/services/commands/test/common/commandService.test.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; import { IExtensionService, ExtensionPointContribution, IExtensionDescription, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; @@ -20,25 +19,26 @@ class SimpleExtensionService implements IExtensionService { return this._onDidRegisterExtensions.event; } onDidChangeExtensionsStatus = null; - activateByEvent(activationEvent: string): TPromise { + onWillActivateByEvent = null; + activateByEvent(activationEvent: string): Promise { return this.whenInstalledExtensionsRegistered().then(() => { }); } - whenInstalledExtensionsRegistered(): TPromise { - return TPromise.as(true); + whenInstalledExtensionsRegistered(): Promise { + return Promise.resolve(true); } - readExtensionPointContributions(extPoint: IExtensionPoint): TPromise[]> { - return TPromise.as([]); + readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { + return Promise.resolve([]); } getExtensionsStatus() { return undefined; } - getExtensions(): TPromise { - return TPromise.wrap([]); + getExtensions(): Promise { + return Promise.resolve([]); } canProfileExtensionHost() { return false; } - startExtensionHostProfile(): TPromise { + startExtensionHostProfile(): Promise { throw new Error('Not implemented'); } getInspectPort(): number { @@ -69,7 +69,7 @@ suite('CommandService', function () { let lastEvent: string; let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService { - activateByEvent(activationEvent: string): TPromise { + activateByEvent(activationEvent: string): Promise { lastEvent = activationEvent; return super.activateByEvent(activationEvent); } @@ -85,13 +85,17 @@ suite('CommandService', function () { }); }); - test('fwd activation error', function () { + test('fwd activation error', async function () { - let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService { - activateByEvent(activationEvent: string): TPromise { - return TPromise.wrapError(new Error('bad_activate')); + const extensionService = new class extends SimpleExtensionService { + activateByEvent(activationEvent: string): Promise { + return Promise.reject(new Error('bad_activate')); } - }, new NullLogService()); + }; + + let service = new CommandService(new InstantiationService(), extensionService, new NullLogService()); + + await extensionService.whenInstalledExtensionsRegistered(); return service.executeCommand('foo').then(() => assert.ok(false), err => { assert.equal(err.message, 'bad_activate'); @@ -105,7 +109,7 @@ suite('CommandService', function () { let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService { whenInstalledExtensionsRegistered() { - return new TPromise(_resolve => { /*ignore*/ }); + return new Promise(_resolve => { /*ignore*/ }); } }, new NullLogService()); @@ -118,7 +122,7 @@ suite('CommandService', function () { let callCounter = 0; let resolveFunc: Function; - const whenInstalledExtensionsRegistered = new TPromise(_resolve => { resolveFunc = _resolve; }); + const whenInstalledExtensionsRegistered = new Promise(_resolve => { resolveFunc = _resolve; }); let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService { whenInstalledExtensionsRegistered() { @@ -137,4 +141,39 @@ suite('CommandService', function () { assert.equal(callCounter, 1); }); }); + + test('Stop waiting for * extensions to activate when trigger is satisfied #62457', function () { + + let callCounter = 0; + let dispoables: IDisposable[] = []; + let events: string[] = []; + let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService { + + activateByEvent(event: string): Promise { + events.push(event); + if (event === '*') { + return new Promise(() => { }); //forever promise... + } + if (event.indexOf('onCommand:') === 0) { + return new Promise(resolve => { + setTimeout(() => { + let reg = CommandsRegistry.registerCommand(event.substr('onCommand:'.length), () => { + callCounter += 1; + }); + dispoables.push(reg); + resolve(); + }, 0); + }); + } + return Promise.resolve(); + } + + }, new NullLogService()); + + return service.executeCommand('farboo').then(() => { + assert.equal(callCounter, 1); + assert.deepEqual(events.sort(), ['*', 'onCommand:farboo'].sort()); + dispose(dispoables); + }); + }); }); diff --git a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts index 33e539305f4..0f83fe3d726 100644 --- a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts @@ -67,7 +67,7 @@ const configurationEntrySchema: IJSONSchema = { }, deprecationMessage: { type: 'string', - description: nls.localize('scope.deprecationMessage', 'If set, the property is marked as deprecated and the given message is shown as as explanation.') + description: nls.localize('scope.deprecationMessage', 'If set, the property is marked as deprecated and the given message is shown as an explanation.') } } } @@ -160,7 +160,7 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten extension.collector.warn(message); continue; } - const propertyConfiguration = configuration.properties[key]; + const propertyConfiguration = properties[key]; if (!isObject(propertyConfiguration)) { delete properties[key]; extension.collector.error(nls.localize('invalid.property', "'configuration.property' must be an object")); diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts index f5fbcc43d26..98e9f715d79 100644 --- a/src/vs/workbench/services/configuration/common/configurationModels.ts +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -123,7 +123,7 @@ export class FolderSettingsModelParser extends ConfigurationModelParser { private getScope(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): ConfigurationScope { const propertySchema = configurationProperties[key]; - return propertySchema ? propertySchema.scope : ConfigurationScope.WINDOW; + return propertySchema && typeof propertySchema.scope !== 'undefined' ? propertySchema.scope : ConfigurationScope.WINDOW; } } @@ -147,8 +147,8 @@ export class Configuration extends BaseConfiguration { inspect(key: string, overrides: IConfigurationOverrides = {}): { default: C, user: C, - workspace: C, - workspaceFolder: C + workspace?: C, + workspaceFolder?: C memory?: C value: C, } { @@ -208,7 +208,7 @@ export class Configuration extends BaseConfiguration { } compare(other: Configuration): string[] { - const result = []; + const result: string[] = []; for (const key of this.allKeys()) { if (!equals(this.getValue(key), other.getValue(key)) || (this._workspace && this._workspace.folders.some(folder => !equals(this.getValue(key, { resource: folder.uri }), other.getValue(key, { resource: folder.uri }))))) { diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index ea48e1b4e4a..039dac7f9cd 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { dirname, basename } from 'path'; +import { dirname } from 'path'; import * as assert from 'vs/base/common/assert'; import { Event, Emitter } from 'vs/base/common/event'; import { ResourceMap } from 'vs/base/common/map'; import { equals, deepClone } from 'vs/base/common/objects'; import { Disposable } from 'vs/base/common/lifecycle'; import { Queue } from 'vs/base/common/async'; -import { stat, writeFile } from 'vs/base/node/pfs'; +import { writeFile } from 'vs/base/node/pfs'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform'; +import { isLinux } from 'vs/base/common/platform'; import { IFileService } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; @@ -23,9 +23,7 @@ import { Configuration, WorkspaceConfigurationChangeEvent, AllKeysConfigurationC import { IWorkspaceConfigurationService, FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema, allSettings, windowSettings, resourceSettings, applicationSettings } from 'vs/platform/configuration/common/configurationRegistry'; -import { createHash } from 'crypto'; import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import product from 'vs/platform/node/product'; @@ -40,6 +38,15 @@ import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { localize } from 'vs/nls'; import { isEqual } from 'vs/base/common/resources'; +export type IMultiFolderWorkspaceInitializationPayload = IWorkspaceIdentifier; +export interface ISingleFolderWorkspaceInitializationPayload { id: string; folder: ISingleFolderWorkspaceIdentifier; } +export interface IEmptyWorkspaceInitializationPayload { id: string; } +export type IWorkspaceInitializationPayload = IMultiFolderWorkspaceInitializationPayload | ISingleFolderWorkspaceInitializationPayload | IEmptyWorkspaceInitializationPayload; + +export function isSingleFolderWorkspaceInitializationPayload(obj: any): obj is ISingleFolderWorkspaceInitializationPayload { + return isSingleFolderWorkspaceIdentifier((obj.folder as ISingleFolderWorkspaceIdentifier)); +} + export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService { public _serviceBrand: any; @@ -276,8 +283,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat inspect(key: string, overrides?: IConfigurationOverrides): { default: T, user: T, - workspace: T, - workspaceFolder: T, + workspace?: T, + workspaceFolder?: T, memory?: T, value: T } { @@ -293,7 +300,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat return this._configuration.keys(); } - initialize(arg: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWindowConfiguration, postInitialisationTask: () => void = () => null): Promise { + initialize(arg: IWorkspaceInitializationPayload, postInitialisationTask: () => void = () => null): Promise { return this.createWorkspace(arg) .then(workspace => this.updateWorkspaceAndInitializeConfiguration(workspace, postInitialisationTask)); } @@ -320,12 +327,12 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat this.jsonEditingService = instantiationService.createInstance(JSONEditingService); } - private createWorkspace(arg: IWorkspaceIdentifier | URI | IWindowConfiguration): Promise { + private createWorkspace(arg: IWorkspaceInitializationPayload): Promise { if (isWorkspaceIdentifier(arg)) { return this.createMultiFolderWorkspace(arg); } - if (isSingleFolderWorkspaceIdentifier(arg)) { + if (isSingleFolderWorkspaceInitializationPayload(arg)) { return this.createSingleFolderWorkspace(arg); } @@ -342,35 +349,21 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat }); } - private createSingleFolderWorkspace(folder: URI): Promise { - if (folder.scheme === Schemas.file) { - return stat(folder.fsPath) - .then(workspaceStat => { - let ctime: number; - if (isLinux) { - ctime = workspaceStat.ino; // Linux: birthtime is ctime, so we cannot use it! We use the ino instead! - } else if (isMacintosh) { - ctime = workspaceStat.birthtime.getTime(); // macOS: birthtime is fine to use as is - } else if (isWindows) { - if (typeof workspaceStat.birthtimeMs === 'number') { - ctime = Math.floor(workspaceStat.birthtimeMs); // Windows: fix precision issue in node.js 8.x to get 7.x results (see https://github.com/nodejs/node/issues/19897) - } else { - ctime = workspaceStat.birthtime.getTime(); - } - } + private createSingleFolderWorkspace(singleFolder: ISingleFolderWorkspaceInitializationPayload): Promise { + const folder = singleFolder.folder; - const id = createHash('md5').update(folder.fsPath).update(ctime ? String(ctime) : '').digest('hex'); - return new Workspace(id, toWorkspaceFolders([{ path: folder.fsPath }]), null, ctime); - }); + let configuredFolders: IStoredWorkspaceFolder[]; + if (folder.scheme === 'file') { + configuredFolders = [{ path: folder.fsPath }]; } else { - const id = createHash('md5').update(folder.toString()).digest('hex'); - return Promise.resolve(new Workspace(id, toWorkspaceFolders([{ uri: folder.toString() }]), null)); + configuredFolders = [{ uri: folder.toString() }]; } + + return Promise.resolve(new Workspace(singleFolder.id, toWorkspaceFolders(configuredFolders))); } - private createEmptyWorkspace(configuration: IWindowConfiguration): Promise { - let id = configuration.backupPath ? URI.from({ path: basename(configuration.backupPath), scheme: 'empty' }).toString() : ''; - return Promise.resolve(new Workspace(id)); + private createEmptyWorkspace(emptyWorkspace: IEmptyWorkspaceInitializationPayload): Promise { + return Promise.resolve(new Workspace(emptyWorkspace.id)); } private updateWorkspaceAndInitializeConfiguration(workspace: Workspace, postInitialisationTask: () => void): Promise { diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts index 15014128efb..629b122882d 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts @@ -30,13 +30,13 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { mkdirp } from 'vs/base/node/pfs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; import { URI } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; +import { createHash } from 'crypto'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -102,7 +102,7 @@ suite('ConfigurationEditingService', () => { instantiationService.stub(IEnvironmentService, environmentService); const workspaceService = new WorkspaceService(environmentService); instantiationService.stub(IWorkspaceContextService, workspaceService); - return workspaceService.initialize(noWorkspace ? {} as IWindowConfiguration : URI.file(workspaceDir)).then(() => { + return workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }).then(() => { instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IFileService, new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true })); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index 5b5e16396eb..0b7f22065a5 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -16,7 +16,7 @@ import { parseArgs } from 'vs/platform/environment/node/argv'; import * as pfs from 'vs/base/node/pfs'; import * as uuid from 'vs/base/common/uuid'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; +import { WorkspaceService, ISingleFolderWorkspaceInitializationPayload } from 'vs/workbench/services/configuration/node/configurationService'; import { ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/node/configurationEditingService'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; @@ -31,7 +31,8 @@ import { TextModelResolverService } from 'vs/workbench/services/textmodelResolve import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { Uri } from 'vscode'; +import { createHash } from 'crypto'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -54,6 +55,13 @@ function setUpFolder(folderName: string, parentDir: string): Promise { return Promise.resolve(pfs.mkdirp(workspaceSettingsDir, 493).then(() => folderDir)); } +function convertToWorkspacePayload(folder: Uri): ISingleFolderWorkspaceInitializationPayload { + return { + id: createHash('md5').update(folder.fsPath).digest('hex'), + folder + } as ISingleFolderWorkspaceInitializationPayload; +} + function setUpWorkspace(folders: string[]): Promise<{ parentDir: string, configPath: string }> { const id = uuid.generateUuid(); @@ -84,7 +92,7 @@ suite('WorkspaceContextService - Folder', () => { const globalSettingsFile = path.join(parentDir, 'settings.json'); const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); workspaceContextService = new WorkspaceService(environmentService); - return (workspaceContextService).initialize(URI.file(folderDir)); + return (workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir))); }); }); @@ -408,7 +416,7 @@ suite('WorkspaceService - Initialization', () => { instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); - return workspaceService.initialize({}).then(() => { + return workspaceService.initialize({ id: '' }).then(() => { const fileService = new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), workspaceService, new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }); instantiationService.stub(IFileService, fileService); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); @@ -442,7 +450,7 @@ suite('WorkspaceService - Initialization', () => { testObject.onDidChangeWorkspaceFolders(target); testObject.onDidChangeConfiguration(target); - return testObject.initialize(URI.file(path.join(parentResource, '1'))) + return testObject.initialize(convertToWorkspacePayload(URI.file(path.join(parentResource, '1')))) .then(() => { assert.equal(testObject.getValue('initialization.testSetting1'), 'userValue'); assert.equal(target.callCount, 3); @@ -471,7 +479,7 @@ suite('WorkspaceService - Initialization', () => { fs.writeFileSync(path.join(parentResource, '1', '.vscode', 'settings.json'), '{ "initialization.testSetting1": "workspaceValue" }'); - return testObject.initialize(URI.file(path.join(parentResource, '1'))) + return testObject.initialize(convertToWorkspacePayload(URI.file(path.join(parentResource, '1')))) .then(() => { assert.equal(testObject.getValue('initialization.testSetting1'), 'workspaceValue'); assert.equal(target.callCount, 4); @@ -545,7 +553,7 @@ suite('WorkspaceService - Initialization', () => { test('initialize a folder workspace from a folder workspace with no configuration changes', () => { - return testObject.initialize(URI.file(path.join(parentResource, '1'))) + return testObject.initialize(convertToWorkspacePayload(URI.file(path.join(parentResource, '1')))) .then(() => { fs.writeFileSync(globalSettingsFile, '{ "initialization.testSetting1": "userValue" }'); @@ -557,7 +565,7 @@ suite('WorkspaceService - Initialization', () => { testObject.onDidChangeWorkspaceFolders(target); testObject.onDidChangeConfiguration(target); - return testObject.initialize(URI.file(path.join(parentResource, '2'))) + return testObject.initialize(convertToWorkspacePayload(URI.file(path.join(parentResource, '2')))) .then(() => { assert.equal(testObject.getValue('initialization.testSetting1'), 'userValue'); assert.equal(target.callCount, 1); @@ -573,7 +581,7 @@ suite('WorkspaceService - Initialization', () => { test('initialize a folder workspace from a folder workspace with configuration changes', () => { - return testObject.initialize(URI.file(path.join(parentResource, '1'))) + return testObject.initialize(convertToWorkspacePayload(URI.file(path.join(parentResource, '1')))) .then(() => { const target = sinon.spy(); @@ -583,7 +591,7 @@ suite('WorkspaceService - Initialization', () => { testObject.onDidChangeConfiguration(target); fs.writeFileSync(path.join(parentResource, '2', '.vscode', 'settings.json'), '{ "initialization.testSetting1": "workspaceValue2" }'); - return testObject.initialize(URI.file(path.join(parentResource, '2'))) + return testObject.initialize(convertToWorkspacePayload(URI.file(path.join(parentResource, '2')))) .then(() => { assert.equal(testObject.getValue('initialization.testSetting1'), 'workspaceValue2'); assert.equal(target.callCount, 2); @@ -598,7 +606,7 @@ suite('WorkspaceService - Initialization', () => { test('initialize a multi folder workspace from a folder workspacce triggers change events in the right order', () => { const folderDir = path.join(parentResource, '1'); - return testObject.initialize(URI.file(folderDir)) + return testObject.initialize(convertToWorkspacePayload(URI.file(folderDir))) .then(() => { const target = sinon.spy(); @@ -663,7 +671,7 @@ suite('WorkspaceConfigurationService - Folder', () => { instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); - return workspaceService.initialize(URI.file(folderDir)).then(() => { + return workspaceService.initialize(convertToWorkspacePayload(URI.file(folderDir))).then(() => { const fileService = new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), workspaceService, new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }); instantiationService.stub(IFileService, fileService); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); diff --git a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts index cc219f85a0c..572dcf83685 100644 --- a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts @@ -35,43 +35,42 @@ export class ContextMenuService extends Disposable implements IContextMenuServic } showContextMenu(delegate: IContextMenuDelegate): void { - delegate.getActions().then(actions => { - if (actions.length) { - const onHide = once(() => { - if (delegate.onHide) { - delegate.onHide(undefined); - } - - this._onDidContextMenu.fire(); - }); - - const menu = this.createMenu(delegate, actions, onHide); - const anchor = delegate.getAnchor(); - let x: number, y: number; - - if (dom.isHTMLElement(anchor)) { - let elementPosition = dom.getDomNodePagePosition(anchor); - - x = elementPosition.left; - y = elementPosition.top + elementPosition.height; - } else { - const pos = <{ x: number; y: number; }>anchor; - x = pos.x + 1; /* prevent first item from being selected automatically under mouse */ - y = pos.y; + const actions = delegate.getActions(); + if (actions.length) { + const onHide = once(() => { + if (delegate.onHide) { + delegate.onHide(undefined); } - let zoom = webFrame.getZoomFactor(); - x *= zoom; - y *= zoom; + this._onDidContextMenu.fire(); + }); - popup(menu, { - x: Math.floor(x), - y: Math.floor(y), - positioningItem: delegate.autoSelectFirstItem ? 0 : void 0, - onHide: () => onHide() - }); + const menu = this.createMenu(delegate, actions, onHide); + const anchor = delegate.getAnchor(); + let x: number, y: number; + + if (dom.isHTMLElement(anchor)) { + let elementPosition = dom.getDomNodePagePosition(anchor); + + x = elementPosition.left; + y = elementPosition.top + elementPosition.height; + } else { + const pos = <{ x: number; y: number; }>anchor; + x = pos.x + 1; /* prevent first item from being selected automatically under mouse */ + y = pos.y; } - }); + + let zoom = webFrame.getZoomFactor(); + x *= zoom; + y *= zoom; + + popup(menu, { + x: Math.floor(x), + y: Math.floor(y), + positioningItem: delegate.autoSelectFirstItem ? 0 : void 0, + onHide: () => onHide() + }); + } } private createMenu(delegate: IContextMenuDelegate, entries: (IAction | ContextSubMenu)[], onHide: () => void): IContextMenuItem[] { diff --git a/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts b/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts index 40cf8c7a75d..0930fed9d45 100644 --- a/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts +++ b/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts @@ -43,7 +43,7 @@ configurationRegistry.registerConfiguration({ export interface ICrashReporterService { _serviceBrand: any; - getChildProcessStartOptions(processName: string): Electron.CrashReporterStartOptions; // TODO + getChildProcessStartOptions(processName: string): Electron.CrashReporterStartOptions | undefined; // TODO } export const NullCrashReporterService: ICrashReporterService = { @@ -100,19 +100,17 @@ export class CrashReporterService implements ICrashReporterService { } private getSubmitURL(): string { - let submitURL: string; if (isWindows) { - submitURL = product.hockeyApp[`win32-${process.arch}`]; + return product.hockeyApp[`win32-${process.arch}`]; } else if (isMacintosh) { - submitURL = product.hockeyApp.darwin; + return product.hockeyApp.darwin; } else if (isLinux) { - submitURL = product.hockeyApp[`linux-${process.arch}`]; + return product.hockeyApp[`linux-${process.arch}`]; } - - return submitURL; + throw new Error('Unknown platform'); } - getChildProcessStartOptions(name: string): Electron.CrashReporterStartOptions { + getChildProcessStartOptions(name: string): Electron.CrashReporterStartOptions | undefined { // Experimental crash reporting support for child processes on Mac only for now if (this.isEnabled && isMacintosh) { diff --git a/src/vs/workbench/services/decorations/browser/decorations.ts b/src/vs/workbench/services/decorations/browser/decorations.ts index 5a24b725c08..ff98b24c992 100644 --- a/src/vs/workbench/services/decorations/browser/decorations.ts +++ b/src/vs/workbench/services/decorations/browser/decorations.ts @@ -31,7 +31,7 @@ export interface IDecoration { export interface IDecorationsProvider { readonly label: string; readonly onDidChange: Event; - provideDecorations(uri: URI, token: CancellationToken): IDecorationData | Thenable; + provideDecorations(uri: URI, token: CancellationToken): IDecorationData | Thenable | undefined; } export interface IResourceDecorationChangeEvent { diff --git a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts index f796cc491fc..c183860eab6 100644 --- a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts +++ b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts @@ -93,13 +93,16 @@ suite('DecorationsService', function () { // un-register -> ensure good event let didSeeEvent = false; - let p = toPromise(service.onDidChangeDecorations).then(e => { - assert.equal(e.affectsResource(uri), true); - assert.deepEqual(service.getDecoration(uri, false), undefined); - assert.equal(callCounter, 1); - didSeeEvent = true; + let p = new Promise(resolve => { + service.onDidChangeDecorations(e => { + assert.equal(e.affectsResource(uri), true); + assert.deepEqual(service.getDecoration(uri, false), undefined); + assert.equal(callCounter, 1); + didSeeEvent = true; + resolve(); + }); }); - reg.dispose(); + reg.dispose(); // will clear all data await p; assert.equal(didSeeEvent, true); }); @@ -238,4 +241,80 @@ suite('DecorationsService', function () { reg.dispose(); }); + + test('Folder decorations don\'t go away when file with problems is deleted #61919 (part1)', function () { + + let emitter = new Emitter(); + let gone = false; + let reg = service.registerDecorationsProvider({ + label: 'Test', + onDidChange: emitter.event, + provideDecorations(uri: URI) { + if (!gone && uri.path.match(/file.ts$/)) { + return { tooltip: 'FOO', weight: 17, bubble: true }; + } + return undefined; + } + }); + + let uri = URI.parse('foo:/folder/file.ts'); + let uri2 = URI.parse('foo:/folder/'); + let data = service.getDecoration(uri, true); + assert.equal(data.tooltip, 'FOO'); + + data = service.getDecoration(uri2, true); + assert.ok(data.tooltip); // emphazied items... + + gone = true; + emitter.fire([uri]); + + data = service.getDecoration(uri, true); + assert.equal(data, undefined); + + data = service.getDecoration(uri2, true); + assert.equal(data, undefined); + + reg.dispose(); + }); + + test('Folder decorations don\'t go away when file with problems is deleted #61919 (part2)', function () { + + let emitter = new Emitter(); + let gone = false; + let reg = service.registerDecorationsProvider({ + label: 'Test', + onDidChange: emitter.event, + provideDecorations(uri: URI) { + if (!gone && uri.path.match(/file.ts$/)) { + return { tooltip: 'FOO', weight: 17, bubble: true }; + } + return undefined; + } + }); + + let uri = URI.parse('foo:/folder/file.ts'); + let uri2 = URI.parse('foo:/folder/'); + let data = service.getDecoration(uri, true); + assert.equal(data.tooltip, 'FOO'); + + data = service.getDecoration(uri2, true); + assert.ok(data.tooltip); // emphazied items... + + return new Promise((resolve, reject) => { + let l = service.onDidChangeDecorations(e => { + l.dispose(); + try { + assert.ok(e.affectsResource(uri)); + assert.ok(e.affectsResource(uri2)); + resolve(); + reg.dispose(); + } catch (err) { + reject(err); + reg.dispose(); + } + }); + gone = true; + emitter.fire([uri]); + }); + }); }); diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index bdd20aa5e3c..f6ab8c3654e 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -272,17 +272,12 @@ export class FileDialogService implements IFileDialogService { if (defaultUri && defaultUri.scheme !== Schemas.file) { return Promise.reject(new Error('Not supported - Open-dialogs can only be opened on `file`-uris.')); } - const filters = []; - if (options.filters) { - for (let name in options.filters) { - filters.push({ name, extensions: options.filters[name] }); - } - } + const newOptions: OpenDialogOptions = { title: options.title, defaultPath: defaultUri && defaultUri.fsPath, buttonLabel: options.openLabel, - filters, + filters: options.filters, properties: [] }; newOptions.properties.push('createDirectory'); diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 3fe406c1d72..ee3572077c6 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from 'vs/base/common/event'; import Severity from 'vs/base/common/severity'; +import { URI } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { Event } from 'vs/base/common/event'; -import { URI } from 'vs/base/common/uri'; export interface IExtensionDescription { readonly id: string; @@ -34,6 +34,17 @@ export interface IExtensionDescription { enableProposedApi?: boolean; } +export const nullExtensionDescription = Object.freeze({ + id: 'nullExtensionDescription', + name: 'Null Extension Description', + version: '0.0.0', + publisher: 'vscode', + enableProposedApi: false, + engines: { vscode: '' }, + extensionLocation: URI.parse('void:location'), + isBuiltin: false, +}); + export const IExtensionService = createDecorator('extensionService'); export interface IMessage { @@ -117,6 +128,11 @@ export class ExtensionPointContribution { export const ExtensionHostLogFileName = 'exthost'; +export interface IWillActivateEvent { + readonly event: string; + readonly activation: Thenable; +} + export interface IExtensionService { _serviceBrand: any; @@ -136,6 +152,11 @@ export interface IExtensionService { */ onDidChangeExtensionsStatus: Event; + /** + * An event that is fired when activation happens. + */ + onWillActivateByEvent: Event; + /** * Send an activation event and activate interested extensions. */ @@ -194,5 +215,15 @@ export interface IExtensionService { } export interface ProfileSession { - stop(): TPromise; + stop(): Promise; +} + +export function checkProposedApiEnabled(extension: IExtensionDescription): void { + if (!extension.enableProposedApi) { + throwProposedApiError(extension); + } +} + +export function throwProposedApiError(extension: IExtensionDescription): never { + throw new Error(`[${extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.id}`); } diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 23fa3db943b..539c21f2d3d 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -7,10 +7,10 @@ import * as nls from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import Severity from 'vs/base/common/severity'; -import { IMessage, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionDescription, IMessage } from 'vs/workbench/services/extensions/common/extensions'; const hasOwnProperty = Object.hasOwnProperty; const schemaRegistry = Registry.as(Extensions.JSONContribution); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 1fa003f77ca..450783e0cec 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -4,42 +4,42 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { toErrorMessage } from 'vs/base/common/errorMessage'; -import * as objects from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { isWindows } from 'vs/base/common/platform'; -import { findFreePort } from 'vs/base/node/ports'; -import { ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; 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 { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; -import { generateRandomPipeName, Protocol } from 'vs/base/parts/ipc/node/ipc.net'; -import { createServer, Server, Socket } from 'net'; -import { Event, Emitter, debounceEvent, mapEvent, anyEvent, fromNodeEventEmitter } from 'vs/base/common/event'; -import { IInitData, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { ICrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService'; -import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService'; -import { isEqual } from 'vs/base/common/resources'; -import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; -import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; -import { getScopes } from 'vs/platform/configuration/common/configurationRegistry'; -import { ILogService } from 'vs/platform/log/common/log'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { Server, Socket, createServer } from 'net'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { timeout } from 'vs/base/common/async'; -import { isMessageOfType, MessageType, createMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event, anyEvent, debounceEvent, fromNodeEventEmitter, mapEvent } from 'vs/base/common/event'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import * as objects from 'vs/base/common/objects'; +import { isWindows } from 'vs/base/common/platform'; +import { isEqual } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; +import { findFreePort } from 'vs/base/node/ports'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; +import { Protocol, generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net'; +import { IBroadcast, IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService'; +import { getScopes } from 'vs/platform/configuration/common/configurationRegistry'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; +import product from 'vs/platform/node/product'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationInitData, IInitData } from 'vs/workbench/api/node/extHost.protocol'; +import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { ICrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; export interface IExtensionHostStarter { readonly onCrashed: Event<[number, string]>; @@ -263,7 +263,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { // Help in case we fail to start it let startupTimeoutHandle: any; - if (!this._environmentService.isBuilt || this._isExtensionDevHost) { + if (!this._environmentService.isBuilt && !this._windowService.getConfiguration().remoteAuthority || 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.") @@ -362,22 +362,38 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { // 2) wait for the incoming `initialized` event. return new Promise((resolve, reject) => { - let handle = setTimeout(() => { - reject('timeout'); - }, 60 * 1000); + let timeoutHandle: NodeJS.Timer; + const installTimeoutCheck = () => { + timeoutHandle = setTimeout(() => { + reject('timeout'); + }, 60 * 1000); + }; + const uninstallTimeoutCheck = () => { + clearTimeout(timeoutHandle); + }; + + // Wait 60s for the ready message + installTimeoutCheck(); const disposable = protocol.onMessage(msg => { if (isMessageOfType(msg, MessageType.Ready)) { // 1) Extension Host is ready to receive messages, initialize it - this._createExtHostInitData().then(data => protocol.send(Buffer.from(JSON.stringify(data)))); + uninstallTimeoutCheck(); + + this._createExtHostInitData().then(data => { + + // Wait 60s for the initialized message + installTimeoutCheck(); + + protocol.send(Buffer.from(JSON.stringify(data))); + }); return; } if (isMessageOfType(msg, MessageType.Initialized)) { // 2) Extension Host is initialized - - clearTimeout(handle); + uninstallTimeoutCheck(); // stop listening for messages here disposable.dispose(); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index 3cc6e830d53..d7bdc83c649 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -3,31 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IExtensionService, IExtensionDescription, ProfileSession, IExtensionHostProfile, ProfileSegmentId } from 'vs/workbench/services/extensions/common/extensions'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { Profile, ProfileNode } from 'v8-inspect-profiler'; import { TernarySearchTree } from 'vs/base/common/map'; import { realpathSync } from 'vs/base/node/extfs'; -import { Profile, ProfileNode } from 'v8-inspect-profiler'; +import { IExtensionDescription, IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; export class ExtensionHostProfiler { constructor(private readonly _port: number, @IExtensionService private readonly _extensionService: IExtensionService) { } - public start(): TPromise { - return TPromise.wrap(import('v8-inspect-profiler')).then(profiler => { - return profiler.startProfiling({ port: this._port }).then(session => { - return { - stop: () => { - return TPromise.wrap(session.stop()).then(profile => { - return this._extensionService.getExtensions().then(extensions => { - return this.distill(profile.profile, extensions); - }); - }); - } - }; - }); - }); + public async start(): Promise { + const profiler = await import('v8-inspect-profiler'); + const session = await profiler.startProfiling({ port: this._port }); + return { + stop: async () => { + const profile = await session.stop(); + const extensions = await this._extensionService.getExtensions(); + return this.distill((profile as any).profile, extensions); + } + }; } private distill(profile: Profile, extensions: IExtensionDescription[]): IExtensionHostProfile { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 76170daf43c..9eaf262d9bf 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -4,48 +4,48 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as errors from 'vs/base/common/errors'; -import * as objects from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; -import pkg from 'vs/platform/node/package'; -import * as path from 'path'; import * as os from 'os'; -import * as pfs from 'vs/base/node/pfs'; -import { URI } from 'vs/base/common/uri'; -import * as platform from 'vs/base/common/platform'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; -import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService, ExtensionPointContribution, ActivationTimes, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; -import { USER_MANIFEST_CACHE_FILE, BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER } from 'vs/platform/extensions/common/extensions'; -import { IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionManagementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint, schema } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ExtensionScanner, ILog, ExtensionScannerInput, IExtensionResolver, IExtensionReference, Translations, IRelaxedExtensionDescription } from 'vs/workbench/services/extensions/node/extensionPoints'; -import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; -import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ExtensionHostProcessWorker, IExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; -import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers'; -import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; -import * as perf from 'vs/base/common/performance'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { Barrier, runWhenIdle } from 'vs/base/common/async'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; -import product from 'vs/platform/node/product'; -import * as strings from 'vs/base/common/strings'; -import { RPCProtocol, IRPCProtocolLogger, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; -import { INotificationService, Severity, INotificationHandle } from 'vs/platform/notification/common/notification'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; -import { Schemas } from 'vs/base/common/network'; +import * as path from 'path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; -import { isEqualOrParent, fsPath } from 'vs/base/common/resources'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { Barrier, runWhenIdle } from 'vs/base/common/async'; +import * as errors from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import * as objects from 'vs/base/common/objects'; +import * as perf from 'vs/base/common/performance'; +import * as platform from 'vs/base/common/platform'; +import { fsPath, isEqualOrParent } from 'vs/base/common/resources'; +import * as strings from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as pfs from 'vs/base/node/pfs'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { EnablementState, IExtensionEnablementService, IExtensionIdentifier, IExtensionManagementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { BetterMergeDisabledNowKey, BetterMergeId, areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import pkg from 'vs/platform/node/package'; +import product from 'vs/platform/node/product'; +import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ActivationTimes, ExtensionPointContribution, IExtensionDescription, IExtensionService, IExtensionsStatus, IMessage, ProfileSession, IWillActivateEvent } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser, schema } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { ExtensionHostProcessWorker, IExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; +import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; import { RuntimeExtensionsInput } from 'vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; +import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, ILog, IRelaxedExtensionDescription, Translations } from 'vs/workbench/services/extensions/node/extensionPoints'; +import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; +import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; // Enable to see detailed message communication between window and extension host const LOG_EXTENSION_HOST_COMMUNICATION = false; @@ -133,6 +133,7 @@ export class ExtensionHostProcessManager extends Disposable { constructor( extensionHostProcessWorker: IExtensionHostStarter, + private readonly _remoteAuthority: string, initialActivationEvents: string[], @IInstantiationService private readonly _instantiationService: IInstantiationService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, @@ -193,6 +194,7 @@ export class ExtensionHostProcessManager extends Disposable { this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol, logger); this._register(this._extensionHostProcessRPCProtocol.onDidChangeResponsiveState((responsiveState: ResponsiveState) => this._onDidChangeResponsiveState.fire(responsiveState))); const extHostContext: IExtHostContext = { + remoteAuthority: this._remoteAuthority, getProxy: (identifier: ProxyIdentifier): T => this._extensionHostProcessRPCProtocol.getProxy(identifier), set: (identifier: ProxyIdentifier, instance: R): R => this._extensionHostProcessRPCProtocol.set(identifier, instance), assertRegistered: (identifiers: ProxyIdentifier[]): void => this._extensionHostProcessRPCProtocol.assertRegistered(identifiers), @@ -238,7 +240,7 @@ export class ExtensionHostProcessManager extends Disposable { }); } - public startExtensionHostProfile(): TPromise { + public startExtensionHostProfile(): Promise { if (this._extensionHostProcessWorker) { let port = this._extensionHostProcessWorker.getInspectPort(); if (port) { @@ -277,6 +279,9 @@ export class ExtensionService extends Disposable implements IExtensionService { private readonly _onDidChangeExtensionsStatus: Emitter = this._register(new Emitter()); public readonly onDidChangeExtensionsStatus: Event = this._onDidChangeExtensionsStatus.event; + private _onWillActivateByEvent = new Emitter(); + readonly onWillActivateByEvent: Event = this._onWillActivateByEvent.event; + private _unresponsiveNotificationHandle: INotificationHandle; // --- Members used per extension host process @@ -381,7 +386,7 @@ export class ExtensionService extends Disposable implements IExtensionService { this._stopExtensionHostProcess(); const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this.getExtensions(), this._extensionHostLogsLocation); - const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, initialActivationEvents); + const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); extHostProcessManager.onDidChangeResponsiveState((responsiveState) => this._onResponsiveStateChanged(responsiveState)); this._extensionHostProcessManagers.push(extHostProcessManager); @@ -497,9 +502,14 @@ export class ExtensionService extends Disposable implements IExtensionService { } private _activateByEvent(activationEvent: string): TPromise { - return TPromise.join( + const result = TPromise.join( this._extensionHostProcessManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) ).then(() => { }); + this._onWillActivateByEvent.fire({ + event: activationEvent, + activation: result + }); + return result; } public whenInstalledExtensionsRegistered(): TPromise { @@ -556,7 +566,7 @@ export class ExtensionService extends Disposable implements IExtensionService { return false; } - public startExtensionHostProfile(): TPromise { + public startExtensionHostProfile(): Promise { for (let i = 0, len = this._extensionHostProcessManagers.length; i < len; i++) { const extHostProcessManager = this._extensionHostProcessManagers[i]; if (extHostProcessManager.canProfileExtensionHost()) { diff --git a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts index d667af60c1b..c3f0deffca5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; +import { localize } from 'vs/nls'; +import { Action } from 'vs/base/common/actions'; +import { IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { localize } from 'vs/nls'; -import { IExtensionManagementService, IExtensionIdentifier, IExtensionEnablementService, EnablementState, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { EnablementState, IExtensionEnablementService, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IWindowService } from 'vs/platform/windows/common/windows'; -import { Action } from 'vs/base/common/actions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; const FIVE_MINUTES = 5 * 60 * 1000; const THIRTY_SECONDS = 30 * 1000; @@ -167,7 +167,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { this.dialogService.confirm({ message: localize('reloadAndHandle', "Extension '{0}' is not loaded. Would you like to reload the window to load the extension and open the URL?", extension.manifest.displayName || extension.manifest.name), detail: `${extension.manifest.displayName || extension.manifest.name} (${extensionIdentifier.id}) wants to open a URL:\n\n${uri.toString()}`, - primaryButton: localize('reload&Open', "&&Reload Window and Open"), + primaryButton: localize('reloadAndOpen', "&&Reload Window and Open"), type: 'question' }).then(result => { if (result.confirmed) { @@ -184,7 +184,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { detail: `${extension.manifest.displayName || extension.manifest.name} (${extensionIdentifier.id}) wants to open a URL:\n\n${uri.toString()}`, primaryButton: localize('enableAndReload', "&&Enable and Open"), type: 'question' - }).then(result => { + }).then((result): TPromise | null => { if (result.confirmed) { return this.extensionEnablementService.setEnablement(extension, EnablementState.Enabled) .then(() => this.reloadAndHandle(uri)); @@ -206,7 +206,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { type: 'question' }).then(async result => { if (result.confirmed) { - let notificationHandle = this.notificationService.notify({ severity: Severity.Info, message: localize('Installing', "Installing Extension '{0}'...", galleryExtension.displayName || galleryExtension.name) }); + let notificationHandle: INotificationHandle | null = this.notificationService.notify({ severity: Severity.Info, message: localize('Installing', "Installing Extension '{0}'...", galleryExtension.displayName || galleryExtension.name) }); notificationHandle.progress.infinite(); notificationHandle.onDidClose(() => notificationHandle = null); try { @@ -217,7 +217,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { notificationHandle.progress.done(); notificationHandle.updateMessage(reloadMessage); notificationHandle.updateActions({ - primary: [new Action('reloadWindow', reloadActionLabel, null, true, () => this.reloadAndHandle(uri))] + primary: [new Action('reloadWindow', reloadActionLabel, undefined, true, () => this.reloadAndHandle(uri))] }); } else { this.notificationService.prompt(Severity.Info, reloadMessage, diff --git a/src/vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput.ts b/src/vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput.ts index 29755fb27dd..8c88de504c6 100644 --- a/src/vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput.ts +++ b/src/vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput.ts @@ -5,8 +5,8 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; -import { EditorInput } from 'vs/workbench/common/editor'; import { TPromise } from 'vs/base/common/winjs.base'; +import { EditorInput } from 'vs/workbench/common/editor'; export class RuntimeExtensionsInput extends EditorInput { diff --git a/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts b/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts index 87bfffa3579..bc6deeeaab6 100644 --- a/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IExtensionManagementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; import { localize } from 'vs/nls'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { IExtensionManagementServer, IExtensionManagementServerService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; const localExtensionManagementServerAuthority: string = 'vscode-local'; @@ -14,20 +14,20 @@ export class ExtensionManagementServerService implements IExtensionManagementSer _serviceBrand: any; - readonly extensionManagementServers: IExtensionManagementServer[]; + readonly localExtensionManagementServer; constructor( localExtensionManagementService: IExtensionManagementService ) { - this.extensionManagementServers = [{ extensionManagementService: localExtensionManagementService, authority: localExtensionManagementServerAuthority, label: localize('local', "Local") }]; + this.localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, authority: localExtensionManagementServerAuthority, label: localize('local', "Local") }; } - getExtensionManagementServer(location: URI): IExtensionManagementServer { - return this.extensionManagementServers[0]; + getExtensionManagementServer(location: URI): IExtensionManagementServer | null { + return this.localExtensionManagementServer; } - getLocalExtensionManagementServer(): IExtensionManagementServer { - return this.extensionManagementServers[0]; + get otherExtensionManagementServer(): IExtensionManagementServer | null { + return null; } } @@ -35,20 +35,22 @@ export class SingleServerExtensionManagementServerService implements IExtensionM _serviceBrand: any; - readonly extensionManagementServers: IExtensionManagementServer[]; constructor( - extensionManagementServer: IExtensionManagementServer + private readonly extensionManagementServer: IExtensionManagementServer ) { - this.extensionManagementServers = [extensionManagementServer]; } - getExtensionManagementServer(location: URI): IExtensionManagementServer { + getExtensionManagementServer(location: URI): IExtensionManagementServer | null { const authority = location.scheme === Schemas.file ? localExtensionManagementServerAuthority : location.authority; - return this.extensionManagementServers.filter(server => authority === server.authority)[0]; + return this.extensionManagementServer.authority === authority ? this.extensionManagementServer : null; } - getLocalExtensionManagementServer(): IExtensionManagementServer { - return this.extensionManagementServers[0]; + get localExtensionManagementServer(): IExtensionManagementServer | null { + return this.extensionManagementServer.authority === localExtensionManagementServerAuthority ? this.extensionManagementServer : null; + } + + get otherExtensionManagementServer(): IExtensionManagementServer | null { + return this.extensionManagementServer.authority !== localExtensionManagementServerAuthority ? this.extensionManagementServer : null; } } \ No newline at end of file diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index dba83c817aa..fa805336b37 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -4,18 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as pfs from 'vs/base/node/pfs'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { join, normalize, extname } from 'path'; -import * as json from 'vs/base/common/json'; -import * as types from 'vs/base/common/types'; -import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator'; +import { extname, join, normalize } from 'path'; import * as semver from 'semver'; -import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; +import * as json from 'vs/base/common/json'; import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; -import { groupByExtension, getGalleryExtensionId, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as pfs from 'vs/base/node/pfs'; +import { getGalleryExtensionId, getLocalExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; +import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; const MANIFEST_FILE = 'package.json'; diff --git a/src/vs/workbench/services/extensions/node/lazyPromise.ts b/src/vs/workbench/services/extensions/node/lazyPromise.ts index a620e3aaf86..e6b59dc3a38 100644 --- a/src/vs/workbench/services/extensions/node/lazyPromise.ts +++ b/src/vs/workbench/services/extensions/node/lazyPromise.ts @@ -3,14 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise, ValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; import { onUnexpectedError } from 'vs/base/common/errors'; export class LazyPromise implements Thenable { - private _actual: TPromise | null; - private _actualOk: ValueCallback | null; - private _actualErr: ErrorCallback | null; + private _actual: Promise | null; + private _actualOk: ((value?: any) => any) | null; + private _actualErr: ((err?: any) => any) | null; private _hasValue: boolean; private _value: any; @@ -28,9 +27,9 @@ export class LazyPromise implements Thenable { this._err = null; } - private _ensureActual(): TPromise { + private _ensureActual(): Promise { if (!this._actual) { - this._actual = new TPromise((c, e) => { + this._actual = new Promise((c, e) => { this._actualOk = c; this._actualErr = e; diff --git a/src/vs/workbench/services/extensions/node/rpcProtocol.ts b/src/vs/workbench/services/extensions/node/rpcProtocol.ts index 708d5da9abd..80840958df3 100644 --- a/src/vs/workbench/services/extensions/node/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/node/rpcProtocol.ts @@ -3,19 +3,43 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CharCode } from 'vs/base/common/charCode'; import * as errors from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { MarshalledObject } from 'vs/base/common/marshalling'; +import { URI } from 'vs/base/common/uri'; +import { IURITransformer } from 'vs/base/common/uriIpc'; +import { TPromise } from 'vs/base/common/winjs.base'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { LazyPromise } from 'vs/workbench/services/extensions/node/lazyPromise'; -import { ProxyIdentifier, IRPCProtocol, getStringIdentifierForProxy } from 'vs/workbench/services/extensions/node/proxyIdentifier'; -import { CharCode } from 'vs/base/common/charCode'; -import { URI } from 'vs/base/common/uri'; -import { MarshalledObject } from 'vs/base/common/marshalling'; -import { IURITransformer } from 'vs/base/common/uriIpc'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; +import { IRPCProtocol, ProxyIdentifier, getStringIdentifierForProxy } from 'vs/workbench/services/extensions/node/proxyIdentifier'; + +export interface JSONStringifyReplacer { + (key: string, value: any): any; +} + +function safeStringify(obj: any, replacer: JSONStringifyReplacer | null): string { + try { + return JSON.stringify(obj, <(key: string, value: any) => any>replacer); + } catch (err) { + return 'null'; + } +} + +function createURIReplacer(transformer: IURITransformer | null): JSONStringifyReplacer | null { + if (!transformer) { + return null; + } + return (key: string, value: any): any => { + if (value && value.$mid === 1) { + return transformer.transformOutgoing(value); + } + return value; + }; +} function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: number): any { @@ -42,7 +66,7 @@ function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: n return null; } -export function transformOutgoingURIs(obj: any, transformer: IURITransformer): any { +export function transformOutgoingURIs(obj: T, transformer: IURITransformer): T { const result = _transformOutgoingURIs(obj, transformer, 0); if (result === null) { // no change @@ -59,7 +83,7 @@ function _transformIncomingURIs(obj: any, transformer: IURITransformer, depth: n if (typeof obj === 'object') { - if ((obj).$mid === 100) { + if ((obj).$mid === 1) { return transformer.transformIncoming(obj); } @@ -77,7 +101,7 @@ function _transformIncomingURIs(obj: any, transformer: IURITransformer, depth: n return null; } -function transformIncomingURIs(obj: any, transformer: IURITransformer): any { +function transformIncomingURIs(obj: T, transformer: IURITransformer): T { const result = _transformIncomingURIs(obj, transformer, 0); if (result === null) { // no change @@ -113,6 +137,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { private readonly _protocol: IMessagePassingProtocol; private readonly _logger: IRPCProtocolLogger | null; private readonly _uriTransformer: IURITransformer | null; + private readonly _uriReplacer: JSONStringifyReplacer | null; private _isDisposed: boolean; private readonly _locals: any[]; private readonly _proxies: any[]; @@ -129,6 +154,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { this._protocol = protocol; this._logger = logger; this._uriTransformer = transformer; + this._uriReplacer = createURIReplacer(this._uriTransformer); this._isDisposed = false; this._locals = []; this._proxies = []; @@ -353,10 +379,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { promise.then((r) => { delete this._cancelInvokedHandlers[callId]; - if (this._uriTransformer) { - r = transformOutgoingURIs(r, this._uriTransformer); - } - const msg = MessageIO.serializeReplyOK(req, r); + const msg = MessageIO.serializeReplyOK(req, r, this._uriReplacer); if (this._logger) { this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `reply:`, r); } @@ -469,10 +492,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { this._pendingRPCReplies[callId] = result; this._onWillSendRequest(req); - if (this._uriTransformer) { - args = transformOutgoingURIs(args, this._uriTransformer); - } - const msg = MessageIO.serializeRequest(req, rpcId, methodName, args, !!cancellationToken); + const msg = MessageIO.serializeRequest(req, rpcId, methodName, args, !!cancellationToken, this._uriReplacer); if (this._logger) { this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `request: ${getStringIdentifierForProxy(rpcId)}.${methodName}(`, args); } @@ -630,7 +650,7 @@ class MessageIO { return false; } - public static serializeRequest(req: number, rpcId: number, method: string, args: any[], usesCancellationToken: boolean): Buffer { + public static serializeRequest(req: number, rpcId: number, method: string, args: any[], usesCancellationToken: boolean, replacer: JSONStringifyReplacer | null): Buffer { if (this._arrayContainsBuffer(args)) { let massagedArgs: (string | Buffer)[] = new Array(args.length); let argsLengths: number[] = new Array(args.length); @@ -640,13 +660,13 @@ class MessageIO { massagedArgs[i] = arg; argsLengths[i] = arg.byteLength; } else { - massagedArgs[i] = safeStringify(arg); + massagedArgs[i] = safeStringify(arg, replacer); argsLengths[i] = Buffer.byteLength(massagedArgs[i], 'utf8'); } } return this._requestMixedArgs(req, rpcId, method, massagedArgs, argsLengths, usesCancellationToken); } - return this._requestJSONArgs(req, rpcId, method, safeStringify(args), usesCancellationToken); + return this._requestJSONArgs(req, rpcId, method, safeStringify(args, replacer), usesCancellationToken); } private static _requestJSONArgs(req: number, rpcId: number, method: string, args: string, usesCancellationToken: boolean): Buffer { @@ -719,14 +739,14 @@ class MessageIO { return MessageBuffer.alloc(MessageType.Cancel, req, 0).buffer; } - public static serializeReplyOK(req: number, res: any): Buffer { + public static serializeReplyOK(req: number, res: any, replacer: JSONStringifyReplacer | null): Buffer { if (typeof res === 'undefined') { return this._serializeReplyOKEmpty(req); } if (Buffer.isBuffer(res)) { return this._serializeReplyOKBuffer(req, res); } - return this._serializeReplyOKJSON(req, safeStringify(res)); + return this._serializeReplyOKJSON(req, safeStringify(res, replacer)); } private static _serializeReplyOKEmpty(req: number): Buffer { @@ -772,7 +792,7 @@ class MessageIO { } private static _serializeReplyErrEror(req: number, _err: Error): Buffer { - const err = safeStringify(errors.transformErrorForSerialization(_err)); + const err = safeStringify(errors.transformErrorForSerialization(_err), null); const errByteLength = Buffer.byteLength(err, 'utf8'); let len = 0; @@ -793,14 +813,6 @@ class MessageIO { } } -function safeStringify(obj: any): string { - try { - return JSON.stringify(obj); - } catch (err) { - return 'null'; - } -} - const enum MessageType { RequestJSONArgs = 1, RequestJSONArgsWithCancellation = 2, diff --git a/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts b/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts index 757bb0e6224..68fb156e1eb 100644 --- a/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts +++ b/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Emitter, Event } from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; +import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; +import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; suite('RPCProtocol', () => { diff --git a/src/vs/workbench/services/files/electron-browser/encoding.ts b/src/vs/workbench/services/files/electron-browser/encoding.ts index ac72e28d5bb..0f9b92e4f10 100644 --- a/src/vs/workbench/services/files/electron-browser/encoding.ts +++ b/src/vs/workbench/services/files/electron-browser/encoding.ts @@ -8,11 +8,12 @@ import * as encoding from 'vs/base/node/encoding'; import { URI as uri } from 'vs/base/common/uri'; import { IResolveContentOptions, isParent, IResourceEncodings } from 'vs/platform/files/common/files'; import { isLinux } from 'vs/base/common/platform'; -import { join, extname } from 'path'; +import { extname } from 'path'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Disposable } from 'vs/base/common/lifecycle'; +import { joinPath } from 'vs/base/common/resources'; export interface IEncodingOverride { parent?: uri; @@ -49,7 +50,7 @@ export class ResourceEncodings extends Disposable implements IResourceEncodings } getReadEncoding(resource: uri, options: IResolveContentOptions, detected: encoding.IDetectedEncodingResult): string { - let preferredEncoding: string; + let preferredEncoding: string | undefined; // Encoding passed in as option if (options && options.encoding) { @@ -111,13 +112,13 @@ export class ResourceEncodings extends Disposable implements IResourceEncodings // Folder Settings this.contextService.getWorkspace().folders.forEach(folder => { - encodingOverride.push({ parent: uri.file(join(folder.uri.fsPath, '.vscode')), encoding: encoding.UTF8 }); + encodingOverride.push({ parent: joinPath(folder.uri, '.vscode'), encoding: encoding.UTF8 }); }); return encodingOverride; } - private getEncodingOverride(resource: uri): string { + private getEncodingOverride(resource: uri): string | null { if (resource && this.encodingOverride && this.encodingOverride.length) { for (let i = 0; i < this.encodingOverride.length; i++) { const override = this.encodingOverride[i]; diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index ad5f298ac51..3b916c955b9 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -16,7 +16,7 @@ import * as arrays from 'vs/base/common/arrays'; import { TPromise } from 'vs/base/common/winjs.base'; import * as objects from 'vs/base/common/objects'; import * as extfs from 'vs/base/node/extfs'; -import { nfcall, ThrottledDelayer } from 'vs/base/common/async'; +import { nfcall, ThrottledDelayer, timeout } from 'vs/base/common/async'; import { URI as uri } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; @@ -208,6 +208,10 @@ export class FileService extends Disposable implements IFileService { throw new Error('not implemented'); } + activateProvider(scheme: string): Thenable { + return Promise.reject(new Error('not implemented')); + } + canHandleResource(resource: uri): boolean { return resource.scheme === Schemas.file; } @@ -589,24 +593,28 @@ export class FileService extends Disposable implements IFileService { else { // 4.) truncate - let retryFromFailingTruncate = true; return pfs.truncate(absolutePath, 0).then(() => { - retryFromFailingTruncate = false; // 5.) set contents (with r+ mode) and resolve - return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { flag: 'r+' }); - }, error => { - if (retryFromFailingTruncate) { + return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { flag: 'r+' }).then(null, error => { if (this.environmentService.verbose) { - console.error(`Truncate failed (${error}), falling back to normal save`); + console.error(`Truncate succeeded, but save failed (${error}), retrying after 100ms`); } - // we heard from users that fs.truncate() fails (https://github.com/Microsoft/vscode/issues/59561) - // in that case we simply save the file without truncating first (same as macOS and Linux) - return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite); + // We heard from one user that fs.truncate() succeeds, but the save fails (https://github.com/Microsoft/vscode/issues/61310) + // In that case, the file is now entirely empty and the contents are gone. This can happen if an external file watcher is + // installed that reacts on the truncate and keeps the file busy right after. Our workaround is to retry to save after a + // short timeout, assuming that the file is free to write then. + return timeout(100).then(() => this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { flag: 'r+' })); + }); + }, error => { + if (this.environmentService.verbose) { + console.error(`Truncate failed (${error}), falling back to normal save`); } - return TPromise.wrapError(error); + // we heard from users that fs.truncate() fails (https://github.com/Microsoft/vscode/issues/59561) + // in that case we simply save the file without truncating first (same as macOS and Linux) + return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite); }); } }); diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index f08e96ed830..3738495c314 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -206,6 +206,10 @@ export class RemoteFileService extends FileService { }; } + activateProvider(scheme: string): Thenable { + return this._extensionService.activateByEvent('onFileSystem:' + scheme); + } + canHandleResource(resource: URI): boolean { return resource.scheme === Schemas.file || this._provider.has(resource.scheme); } @@ -253,7 +257,7 @@ export class RemoteFileService extends FileService { } return Promise.all([ - this._extensionService.activateByEvent('onFileSystem:' + resource.scheme) + this.activateProvider(resource.scheme) ]).then(() => { const provider = this._provider.get(resource.scheme); if (!provider) { diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts index 83ea7009d40..2ed86714ba9 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts @@ -27,7 +27,8 @@ export class WatcherChannel implements IWatcherChannel { switch (event) { case 'watch': return this.service.watch(arg); } - throw new Error('No events'); + + throw new Error(`Event not found: ${event}`); } call(command: string, arg?: any): TPromise { @@ -36,7 +37,8 @@ export class WatcherChannel implements IWatcherChannel { case 'setVerboseLogging': return this.service.setVerboseLogging(arg); case 'stop': return this.service.stop(); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } diff --git a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts index 4e907ad9d5d..3fdca226618 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts @@ -68,7 +68,7 @@ export class ChokidarWatcherService implements IWatcherService { public setRoots(requests: IWatcherRequest[]): TPromise { const watchers = Object.create(null); - const newRequests = []; + const newRequests: string[] = []; const requestsByBasePath = normalizeRoots(requests); diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts index 83ea7009d40..2ed86714ba9 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts @@ -27,7 +27,8 @@ export class WatcherChannel implements IWatcherChannel { switch (event) { case 'watch': return this.service.watch(arg); } - throw new Error('No events'); + + throw new Error(`Event not found: ${event}`); } call(command: string, arg?: any): TPromise { @@ -36,7 +37,8 @@ export class WatcherChannel implements IWatcherChannel { case 'setVerboseLogging': return this.service.setVerboseLogging(arg); case 'stop': return this.service.stop(); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } diff --git a/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts b/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts index b71a461fd92..74dad643771 100644 --- a/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts @@ -123,7 +123,7 @@ export class OutOfProcessWin32FolderWatcher { public dispose(): void { if (this.handle) { this.handle.kill(); - this.handle = null; + this.handle = null!; // StrictNullOverride: nulling out ok in dispose } } } diff --git a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts index a4105f15307..6fc02a2fe80 100644 --- a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts +++ b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IssueReporterStyles, IIssueService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/common/issue'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { IssueReporterStyles, IIssueService, IssueReporterData, ProcessExplorerData, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { textLinkForeground, inputBackground, inputBorder, inputForeground, buttonBackground, buttonHoverBackground, buttonForeground, inputValidationErrorBorder, foreground, inputActiveOptionBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, editorBackground, editorForeground, listHoverBackground, listHoverForeground, listHighlightForeground, textLinkActiveForeground } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; @@ -26,15 +25,31 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { ) { } - openReporter(dataOverrides: Partial = {}): TPromise { + openReporter(dataOverrides: Partial = {}): Promise { return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => { const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension)); + const extensionData: IssueReporterExtensionData[] = enabledExtensions.map(extension => { + const { manifest } = extension; + const manifestKeys = manifest.contributes ? Object.keys(manifest.contributes) : []; + const isTheme = !manifest.activationEvents && manifestKeys.length === 1 && manifestKeys[0] === 'themes'; + + return { + name: manifest.name, + publisher: manifest.publisher, + version: manifest.version, + repositoryUrl: manifest.repository && manifest.repository.url, + bugsUrl: manifest.bugs && manifest.bugs.url, + displayName: manifest.displayName, + id: extension.identifier.id, + isTheme: isTheme + }; + }); const theme = this.themeService.getTheme(); const issueReporterData: IssueReporterData = assign( { styles: getIssueReporterStyles(theme), zoomLevel: webFrame.getZoomLevel(), - enabledExtensions + enabledExtensions: extensionData }, dataOverrides); @@ -42,7 +57,7 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { }); } - openProcessExplorer(): TPromise { + openProcessExplorer(): Promise { const theme = this.themeService.getTheme(); const data: ProcessExplorerData = { pid: this.windowService.getConfiguration().mainPid, diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index 374c8b468f5..d7276583746 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -4,27 +4,27 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; +import { Queue } from 'vs/base/common/async'; +import * as json from 'vs/base/common/json'; +import { setProperty } from 'vs/base/common/jsonEdit'; +import { Edit } from 'vs/base/common/jsonFormatter'; +import { Disposable, IReference } from 'vs/base/common/lifecycle'; +import { isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { isArray } from 'vs/base/common/types'; -import { Queue } from 'vs/base/common/async'; -import { IReference, Disposable } from 'vs/base/common/lifecycle'; -import * as json from 'vs/base/common/json'; -import { Edit } from 'vs/base/common/jsonFormatter'; -import { setProperty } from 'vs/base/common/jsonEdit'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IFileService } from 'vs/platform/files/common/files'; -import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { ITextModel } from 'vs/editor/common/model'; +import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ServiceIdentifier, createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; export const IKeybindingEditingService = createDecorator('keybindingEditingService'); @@ -169,7 +169,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding } private findUnassignedDefaultKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number[] { - const indices = []; + const indices: number[] = []; for (let index = 0; index < userKeybindingEntries.length; index++) { if (userKeybindingEntries[index].command === `-${keybindingItem.command}`) { indices.push(index); diff --git a/src/vs/workbench/services/keybinding/common/keybindingIO.ts b/src/vs/workbench/services/keybinding/common/keybindingIO.ts index 037498baddf..ce6b029f4df 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingIO.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingIO.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { SimpleKeybinding } from 'vs/base/common/keyCodes'; -import { OperatingSystem } from 'vs/base/common/platform'; -import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; -import { ScanCodeBinding } from 'vs/base/common/scanCode'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; +import { OperatingSystem } from 'vs/base/common/platform'; +import { ScanCodeBinding } from 'vs/base/common/scanCode'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; export interface IUserKeybindingItem { firstPart: SimpleKeybinding | ScanCodeBinding | null; diff --git a/src/vs/workbench/services/keybinding/common/keyboardMapper.ts b/src/vs/workbench/services/keybinding/common/keyboardMapper.ts index 5c646d1f83e..fc486810516 100644 --- a/src/vs/workbench/services/keybinding/common/keyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/keyboardMapper.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; -import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { ScanCodeBinding } from 'vs/base/common/scanCode'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; export interface IKeyboardMapper { dumpDebugInfo(): string; diff --git a/src/vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper.ts index e6a9e7d5fc1..123719291a7 100644 --- a/src/vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ChordKeybinding, KeyCode, Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; import { OperatingSystem } from 'vs/base/common/platform'; -import { ResolvedKeybinding, SimpleKeybinding, Keybinding, KeyCode, ChordKeybinding } from 'vs/base/common/keyCodes'; -import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { IMMUTABLE_CODE_TO_KEY_CODE, ScanCode, ScanCodeBinding } from 'vs/base/common/scanCode'; import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { ScanCodeBinding, ScanCode, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/base/common/scanCode'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; /** * A keyboard mapper to be used when reading the keymap from the OS fails. diff --git a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts index fa5b4173af0..afa8f687aca 100644 --- a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OperatingSystem } from 'vs/base/common/platform'; -import { KeyCode, ResolvedKeybinding, KeyCodeUtils, SimpleKeybinding, Keybinding, KeybindingType, ResolvedKeybindingPart } from 'vs/base/common/keyCodes'; -import { ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE, IMMUTABLE_KEY_CODE_TO_CODE, ScanCodeBinding } from 'vs/base/common/scanCode'; import { CharCode } from 'vs/base/common/charCode'; -import { UILabelProvider, AriaLabelProvider, UserSettingsLabelProvider, ElectronAcceleratorLabelProvider } from 'vs/base/common/keybindingLabels'; -import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { KeyCode, KeyCodeUtils, Keybinding, KeybindingType, ResolvedKeybinding, ResolvedKeybindingPart, SimpleKeybinding } from 'vs/base/common/keyCodes'; +import { AriaLabelProvider, ElectronAcceleratorLabelProvider, UILabelProvider, UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; +import { OperatingSystem } from 'vs/base/common/platform'; +import { IMMUTABLE_CODE_TO_KEY_CODE, IMMUTABLE_KEY_CODE_TO_CODE, ScanCode, ScanCodeBinding, ScanCodeUtils } from 'vs/base/common/scanCode'; import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; export interface IMacLinuxKeyMapping { value: string; diff --git a/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts index f48ff7a083b..5da745086ef 100644 --- a/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { KeyCode, KeyCodeUtils, ResolvedKeybinding, Keybinding, SimpleKeybinding, KeybindingType, ResolvedKeybindingPart } from 'vs/base/common/keyCodes'; -import { ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE, ScanCodeBinding } from 'vs/base/common/scanCode'; import { CharCode } from 'vs/base/common/charCode'; -import { UILabelProvider, AriaLabelProvider, ElectronAcceleratorLabelProvider, UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; +import { KeyCode, KeyCodeUtils, Keybinding, KeybindingType, ResolvedKeybinding, ResolvedKeybindingPart, SimpleKeybinding } from 'vs/base/common/keyCodes'; +import { AriaLabelProvider, ElectronAcceleratorLabelProvider, UILabelProvider, UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; import { OperatingSystem } from 'vs/base/common/platform'; -import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { IMMUTABLE_CODE_TO_KEY_CODE, ScanCode, ScanCodeBinding, ScanCodeUtils } from 'vs/base/common/scanCode'; import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; export interface IWindowsKeyMapping { vkey: string; diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index 6641a10abd5..95afbc43b9c 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -4,39 +4,39 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { ResolvedKeybinding, Keybinding } from 'vs/base/common/keyCodes'; -import { OS, OperatingSystem } from 'vs/base/common/platform'; -import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; -import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; -import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IKeybindingEvent, IUserFriendlyKeybinding, KeybindingSource, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; -import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IKeybindingItem, KeybindingsRegistry, IKeybindingRule2, KeybindingRuleSource, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { keybindingsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; -import { ConfigWatcher } from 'vs/base/node/config'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import * as nativeKeymap from 'native-keymap'; +import { release } from 'os'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; -import { KeybindingIO, OutputBuilder, IUserKeybindingItem } from 'vs/workbench/services/keybinding/common/keybindingIO'; -import * as nativeKeymap from 'native-keymap'; -import { IKeyboardMapper, CachedKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; -import { WindowsKeyboardMapper, IWindowsKeyboardMapping, windowsKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; -import { IMacLinuxKeyboardMapping, MacLinuxKeyboardMapper, macLinuxKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; -import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper'; -import { Event, Emitter } from 'vs/base/common/event'; -import { Extensions as ConfigExtensions, IConfigurationRegistry, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { release } from 'os'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { Keybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; +import { OS, OperatingSystem } from 'vs/base/common/platform'; +import { ConfigWatcher } from 'vs/base/node/config'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Extensions as ConfigExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; +import { IKeybindingEvent, IKeyboardEvent, IUserFriendlyKeybinding, KeybindingSource } from 'vs/platform/keybinding/common/keybinding'; +import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; +import { IKeybindingItem, IKeybindingRule2, KeybindingRuleSource, KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { keybindingsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; +import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { IUserKeybindingItem, KeybindingIO, OutputBuilder } from 'vs/workbench/services/keybinding/common/keybindingIO'; +import { CachedKeyboardMapper, IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper'; +import { IMacLinuxKeyboardMapping, MacLinuxKeyboardMapper, macLinuxKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; +import { IWindowsKeyboardMapping, WindowsKeyboardMapper, windowsKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; export class KeyboardMapperFactory { public static readonly INSTANCE = new KeyboardMapperFactory(); diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts index 6d3630d0a13..534d2387698 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts @@ -4,49 +4,49 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import * as fs from 'fs'; import * as json from 'vs/base/common/json'; +import { ChordKeybinding, KeyCode, SimpleKeybinding } from 'vs/base/common/keyCodes'; import { OS } from 'vs/base/common/platform'; -import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { KeyCode, SimpleKeybinding, ChordKeybinding } from 'vs/base/common/keyCodes'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import * as extfs from 'vs/base/node/extfs'; -import { TestTextFileService, TestLifecycleService, TestBackupFileService, TestContextService, TestTextResourceConfigurationService, TestHashService, TestEnvironmentService, TestEditorGroupsService, TestEditorService, TestLogService, TestStorageService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; -import { ILogService } from 'vs/platform/log/common/log'; -import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; -import { IWorkspaceContextService, Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import * as uuid from 'vs/base/common/uuid'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; -import { FileService } from 'vs/workbench/services/files/electron-browser/fileService'; -import { IFileService } from 'vs/platform/files/common/files'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as extfs from 'vs/base/node/extfs'; +import { mkdirp } from 'vs/base/node/pfs'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; -import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { IHashService } from 'vs/workbench/services/hash/common/hashService'; -import { mkdirp } from 'vs/base/node/pfs'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; -import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { IWorkspaceContextService, Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { FileService } from 'vs/workbench/services/files/electron-browser/fileService'; +import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IHashService } from 'vs/workbench/services/hash/common/hashService'; +import { KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; +import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { TestBackupFileService, TestContextService, TestEditorGroupsService, TestEditorService, TestEnvironmentService, TestHashService, TestLifecycleService, TestLogService, TestStorageService, TestTextFileService, TestTextResourceConfigurationService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; interface Modifiers { metaKey?: boolean; @@ -110,7 +110,7 @@ suite('KeybindingsEditing', () => { } teardown(() => { - return new TPromise((c, e) => { + return new Promise((c, e) => { if (testDir) { extfs.del(testDir, os.tmpdir(), () => c(null), () => c(null)); } else { @@ -222,7 +222,7 @@ suite('KeybindingsEditing', () => { .then(() => assert.deepEqual(getUserKeybindings(), [])); }); - test('reset mulitple removed keybindings', () => { + test('reset multiple removed keybindings', () => { writeToKeybindingsFile({ key: 'alt+c', command: '-b' }); writeToKeybindingsFile({ key: 'alt+shift+c', command: '-b' }); writeToKeybindingsFile({ key: 'escape', command: '-b' }); diff --git a/src/vs/workbench/services/keybinding/test/keybindingIO.test.ts b/src/vs/workbench/services/keybinding/test/keybindingIO.test.ts index 8d017ee93e8..4f3366267c9 100644 --- a/src/vs/workbench/services/keybinding/test/keybindingIO.test.ts +++ b/src/vs/workbench/services/keybinding/test/keybindingIO.test.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { KeyCode, KeyMod, KeyChord, createKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; -import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO'; +import { KeyChord, KeyCode, KeyMod, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; +import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { OS, OperatingSystem } from 'vs/base/common/platform'; +import { ScanCode, ScanCodeBinding } from 'vs/base/common/scanCode'; import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { ScanCodeBinding, ScanCode } from 'vs/base/common/scanCode'; -import { KeybindingParser } from 'vs/base/common/keybindingParser'; +import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO'; suite('keybindingIO', () => { diff --git a/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts b/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts index ad556baed02..b815647977f 100644 --- a/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts +++ b/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts @@ -5,13 +5,13 @@ import * as assert from 'assert'; import * as path from 'path'; -import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; import { Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; +import { ScanCodeBinding } from 'vs/base/common/scanCode'; import { TPromise } from 'vs/base/common/winjs.base'; import { readFile, writeFile } from 'vs/base/node/pfs'; import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; -import { ScanCodeBinding } from 'vs/base/common/scanCode'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; export interface IResolvedKeybinding { label: string; diff --git a/src/vs/workbench/services/keybinding/test/macLinuxFallbackKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/macLinuxFallbackKeyboardMapper.test.ts index 801fb995d83..36ad8ef22fa 100644 --- a/src/vs/workbench/services/keybinding/test/macLinuxFallbackKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/macLinuxFallbackKeyboardMapper.test.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { KeyMod, KeyCode, createKeybinding, KeyChord, SimpleKeybinding } from 'vs/base/common/keyCodes'; +import { KeyChord, KeyCode, KeyMod, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; import { OperatingSystem } from 'vs/base/common/platform'; -import { IResolvedKeybinding, assertResolveKeybinding, assertResolveKeyboardEvent, assertResolveUserBinding } from 'vs/workbench/services/keybinding/test/keyboardMapperTestUtils'; +import { ScanCode, ScanCodeBinding } from 'vs/base/common/scanCode'; import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper'; -import { ScanCodeBinding, ScanCode } from 'vs/base/common/scanCode'; +import { IResolvedKeybinding, assertResolveKeybinding, assertResolveKeyboardEvent, assertResolveUserBinding } from 'vs/workbench/services/keybinding/test/keyboardMapperTestUtils'; suite('keyboardMapper - MAC fallback', () => { diff --git a/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts index e80c82da7e5..fb6a602a45a 100644 --- a/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { KeyMod, KeyCode, createKeybinding, SimpleKeybinding, KeyChord } from 'vs/base/common/keyCodes'; -import { MacLinuxKeyboardMapper, IMacLinuxKeyboardMapping } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; -import { OperatingSystem } from 'vs/base/common/platform'; +import { KeyChord, KeyCode, KeyMod, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; import { UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; -import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { ScanCodeUtils, ScanCodeBinding, ScanCode } from 'vs/base/common/scanCode'; +import { OperatingSystem } from 'vs/base/common/platform'; +import { ScanCode, ScanCodeBinding, ScanCodeUtils } from 'vs/base/common/scanCode'; import { TPromise } from 'vs/base/common/winjs.base'; -import { readRawMapping, assertMapping, IResolvedKeybinding, assertResolveKeybinding, assertResolveKeyboardEvent, assertResolveUserBinding } from 'vs/workbench/services/keybinding/test/keyboardMapperTestUtils'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; +import { IMacLinuxKeyboardMapping, MacLinuxKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; +import { IResolvedKeybinding, assertMapping, assertResolveKeybinding, assertResolveKeyboardEvent, assertResolveUserBinding, readRawMapping } from 'vs/workbench/services/keybinding/test/keyboardMapperTestUtils'; const WRITE_FILE_IF_DIFFERENT = false; diff --git a/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts index 2f4e79f208a..42a2626b1db 100644 --- a/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { KeyChord, KeyCode, KeyMod, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; import { OperatingSystem } from 'vs/base/common/platform'; +import { ScanCode, ScanCodeBinding } from 'vs/base/common/scanCode'; import { TPromise } from 'vs/base/common/winjs.base'; -import { WindowsKeyboardMapper, IWindowsKeyboardMapping } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; -import { createKeybinding, KeyMod, KeyCode, KeyChord, SimpleKeybinding } from 'vs/base/common/keyCodes'; -import { IResolvedKeybinding, assertResolveKeybinding, readRawMapping, assertMapping, assertResolveKeyboardEvent, assertResolveUserBinding } from 'vs/workbench/services/keybinding/test/keyboardMapperTestUtils'; -import { ScanCodeBinding, ScanCode } from 'vs/base/common/scanCode'; +import { IWindowsKeyboardMapping, WindowsKeyboardMapper } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; +import { IResolvedKeybinding, assertMapping, assertResolveKeybinding, assertResolveKeyboardEvent, assertResolveUserBinding, readRawMapping } from 'vs/workbench/services/keybinding/test/keyboardMapperTestUtils'; const WRITE_FILE_IF_DIFFERENT = false; diff --git a/src/vs/workbench/services/mode/common/workbenchModeService.ts b/src/vs/workbench/services/mode/common/workbenchModeService.ts index 7eb32726bad..7e9140e69c9 100644 --- a/src/vs/workbench/services/mode/common/workbenchModeService.ts +++ b/src/vs/workbench/services/mode/common/workbenchModeService.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as resources from 'vs/base/common/resources'; import * as mime from 'vs/base/common/mime'; -import { IFilesConfiguration, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import * as resources from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { URI } from 'vs/base/common/uri'; +import { FILES_ASSOCIATIONS_CONFIG, IFilesConfiguration } from 'vs/platform/files/common/files'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; export interface IRawLanguageExtensionPoint { id: string; @@ -137,11 +137,15 @@ export class WorkbenchModeServiceImpl extends ModeServiceImpl { }); + this.updateMime(); this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(FILES_ASSOCIATIONS_CONFIG)) { this.updateMime(); } }); + this._extensionService.whenInstalledExtensionsRegistered().then(() => { + this.updateMime(); + }); this.onDidCreateMode((mode) => { this._extensionService.activateByEvent(`onLanguage:${mode.getId()}`); @@ -151,10 +155,7 @@ export class WorkbenchModeServiceImpl extends ModeServiceImpl { protected _onReady(): Promise { if (!this._onReadyPromise) { this._onReadyPromise = Promise.resolve( - this._extensionService.whenInstalledExtensionsRegistered().then(() => { - this.updateMime(); - return true; - }) + this._extensionService.whenInstalledExtensionsRegistered().then(() => true) ); } @@ -176,6 +177,8 @@ export class WorkbenchModeServiceImpl extends ModeServiceImpl { mime.registerTextMime({ id: langId, mime: mimetype, filepattern: pattern, userConfigured: true }); }); } + + this._onLanguagesMaybeChanged.fire(); } } diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index b8eadaddd49..48e02e5db8f 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -63,8 +63,14 @@ export class NotificationService extends Disposable implements INotificationServ choices.forEach((choice, index) => { const action = new ChoiceAction(`workbench.dialog.choice.${index}`, choice); if (!choice.isSecondary) { + if (!actions.primary) { + actions.primary = []; + } actions.primary.push(action); } else { + if (!actions.secondary) { + actions.secondary = []; + } actions.secondary.push(action); } diff --git a/src/vs/workbench/services/panel/common/panelService.ts b/src/vs/workbench/services/panel/common/panelService.ts index 6d73423c591..204cf1f81ca 100644 --- a/src/vs/workbench/services/panel/common/panelService.ts +++ b/src/vs/workbench/services/panel/common/panelService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IPanel } from 'vs/workbench/common/panel'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; @@ -19,14 +18,14 @@ export interface IPanelIdentifier { export interface IPanelService { _serviceBrand: ServiceIdentifier; - onDidPanelOpen: Event; + onDidPanelOpen: Event<{ panel: IPanel, focus: boolean }>; onDidPanelClose: Event; /** * Opens a panel with the given identifier and pass keyboard focus to it if specified. */ - openPanel(id: string, focus?: boolean): TPromise; + openPanel(id: string, focus?: boolean): IPanel; /** * Returns the current active panel or null if none diff --git a/src/vs/workbench/services/part/common/partService.ts b/src/vs/workbench/services/part/common/partService.ts index 94cc9a6efa1..c5a5ac378d6 100644 --- a/src/vs/workbench/services/part/common/partService.ts +++ b/src/vs/workbench/services/part/common/partService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { MenuBarVisibility } from 'vs/platform/windows/common/windows'; @@ -89,12 +88,12 @@ export interface IPartService { /** * Set sidebar hidden or not */ - setSideBarHidden(hidden: boolean): TPromise; + setSideBarHidden(hidden: boolean): void; /** * Set panel part hidden or not */ - setPanelHidden(hidden: boolean): TPromise; + setPanelHidden(hidden: boolean): void; /** * Maximizes the panel height if the panel is not already maximized. @@ -125,7 +124,7 @@ export interface IPartService { /** * Sets the panel position. */ - setPanelPosition(position: Position): TPromise; + setPanelPosition(position: Position): void; /** * Returns the element that contains the workbench. diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 820b9377bec..f79621db7f8 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -10,7 +10,6 @@ import * as network from 'vs/base/common/network'; import { assign } from 'vs/base/common/objects'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition, Position } from 'vs/editor/common/core/position'; @@ -104,12 +103,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, resource); } - resolveModel(uri: URI): TPromise { + resolveModel(uri: URI): Thenable { if (this.isDefaultSettingsResource(uri)) { const target = this.getConfigurationTargetFromDefaultSettingsResource(uri); - const mode = this.modeService.getOrCreateMode('jsonc'); - const model = this._register(this.modelService.createModel('', mode, uri)); + const languageSelection = this.modeService.create('jsonc'); + const model = this._register(this.modelService.createModel('', languageSelection, uri)); let defaultSettings: DefaultSettings; this.configurationService.onDidChangeConfiguration(e => { @@ -131,27 +130,27 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.modelService.updateModel(model, defaultSettings.getContent(true)); } - return TPromise.as(model); + return Promise.resolve(model); } if (this.defaultSettingsRawResource.toString() === uri.toString()) { let defaultSettings: DefaultSettings = this.getDefaultSettings(ConfigurationTarget.USER); - const mode = this.modeService.getOrCreateMode('jsonc'); - const model = this._register(this.modelService.createModel(defaultSettings.raw, mode, uri)); - return TPromise.as(model); + const languageSelection = this.modeService.create('jsonc'); + const model = this._register(this.modelService.createModel(defaultSettings.raw, languageSelection, uri)); + return Promise.resolve(model); } if (this.defaultKeybindingsResource.toString() === uri.toString()) { const defaultKeybindingsEditorModel = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri); - const mode = this.modeService.getOrCreateMode('jsonc'); - const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, mode, uri)); - return TPromise.as(model); + const languageSelection = this.modeService.create('jsonc'); + const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, languageSelection, uri)); + return Promise.resolve(model); } - return TPromise.as(null); + return Promise.resolve(null); } - createPreferencesEditorModel(uri: URI): TPromise> { + createPreferencesEditorModel(uri: URI): Thenable> { if (this.isDefaultSettingsResource(uri)) { return this.createDefaultSettingsEditorModel(uri); } @@ -169,18 +168,18 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE_FOLDER, uri); } - return TPromise.wrap>(null); + return Promise.resolve>(null); } - openRawDefaultSettings(): TPromise { + openRawDefaultSettings(): Thenable { return this.editorService.openEditor({ resource: this.defaultSettingsRawResource }); } - openRawUserSettings(): TPromise { + openRawUserSettings(): Thenable { return this.editorService.openEditor({ resource: this.userSettingsResource }); } - openSettings(jsonEditor?: boolean): TPromise { + openSettings(jsonEditor?: boolean): Thenable { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -195,13 +194,13 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.openOrSwitchSettings(target, resource); } - private openSettings2(): TPromise { + private openSettings2(): Thenable { const input = this.settingsEditor2Input; return this.editorGroupService.activeGroup.openEditor(input) .then(() => this.editorGroupService.activeGroup.activeControl); } - openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise { + openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -211,14 +210,14 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.USER, undefined, options, group); } - openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise { + openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.notificationService.info(nls.localize('openFolderFirst', "Open a folder first to create workspace settings")); - return TPromise.as(null); + return Promise.resolve(null); } return jsonEditor ? @@ -226,7 +225,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE, undefined, options, group); } - openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise { + openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -236,7 +235,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE_FOLDER, folder, options, group); } - switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): TPromise { + switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Thenable { if (!jsonEditor) { return this.doOpenSettings2(target, resource).then(() => null); } @@ -249,7 +248,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic } } - openGlobalKeybindingSettings(textual: boolean): TPromise { + openGlobalKeybindingSettings(textual: boolean): Thenable { /* __GDPR__ "openKeybindings" : { "textual" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } @@ -266,7 +265,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic if (openDefaultKeybindings) { const activeEditorGroup = this.editorGroupService.activeGroup; const sideEditorGroup = this.editorGroupService.addGroup(activeEditorGroup.id, GroupDirection.RIGHT); - return TPromise.join([ + return Promise.all([ this.editorService.openEditor({ resource: this.defaultKeybindingsResource, options: { pinned: true, preserveFocus: true }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }), this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true } }, sideEditorGroup.id) ]).then(editors => void 0); @@ -279,7 +278,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true }).then(() => null); } - openDefaultKeybindingsFile(): TPromise { + openDefaultKeybindingsFile(): Thenable { return this.editorService.openEditor({ resource: this.defaultKeybindingsResource, label: nls.localize('defaultKeybindings', "Default Keybindings") }); } @@ -301,7 +300,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic })); } - private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): TPromise { + private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Thenable { const editorInput = this.getActiveSettingsEditorInput(group); if (editorInput && editorInput.master.getResource().fsPath !== resource.fsPath) { return this.doSwitchSettings(configurationTarget, resource, editorInput, group, options); @@ -309,11 +308,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.doOpenSettings(configurationTarget, resource, options, group); } - private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): TPromise { + private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Thenable { return this.doOpenSettings2(configurationTarget, folderUri, options, group); } - private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise { + private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable { const openDefaultSettings = !!this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING); return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource) .then(editableSettingsEditorInput => { @@ -337,7 +336,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.instantiationService.createInstance(Settings2EditorModel, this.getDefaultSettings(ConfigurationTarget.USER)); } - private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): TPromise { + private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Thenable { const input = this.settingsEditor2Input; const settingsOptions: ISettingsEditorOptions = { ...options, @@ -348,7 +347,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.editorService.openEditor(input, SettingsEditorOptions.create(settingsOptions), group); } - private doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup, options?: ISettingsEditorOptions): TPromise { + private doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup, options?: ISettingsEditorOptions): Thenable { return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource)) .then(toInput => { return group.openEditor(input).then(() => { @@ -423,12 +422,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic return target === ConfigurationTarget.WORKSPACE_FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name; } - private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): TPromise { + private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): Thenable { return this.createSettingsIfNotExists(target, resource) .then(() => this.editorService.createInput({ resource })); } - private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): TPromise { + private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): Thenable { const settingsUri = this.getEditableSettingsURI(configurationTarget, resource); if (settingsUri) { const workspace = this.contextService.getWorkspace(); @@ -439,10 +438,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.textModelResolverService.createModelReference(settingsUri) .then(reference => this.instantiationService.createInstance(SettingsEditorModel, reference, configurationTarget)); } - return TPromise.wrap(null); + return Promise.resolve(null); } - private createDefaultSettingsEditorModel(defaultSettingsUri: URI): TPromise { + private createDefaultSettingsEditorModel(defaultSettingsUri: URI): Thenable { return this.textModelResolverService.createModelReference(defaultSettingsUri) .then(reference => { const target = this.getConfigurationTargetFromDefaultSettingsResource(defaultSettingsUri); @@ -486,7 +485,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return null; } - private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): TPromise { + private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): Thenable { if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && target === ConfigurationTarget.WORKSPACE) { return this.fileService.resolveContent(this.contextService.getWorkspace().configuration) .then(content => { @@ -499,15 +498,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): Thenable { 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}).", this.labelService.getUriLabel(resource, { relative: true }), error))); + return Promise.reject(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", this.labelService.getUriLabel(resource, { relative: true }), error))); }); } - return TPromise.wrapError(error); + return Promise.reject(error); }); } @@ -527,7 +526,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic ]; } - private getPosition(language: string, settingsModel: IPreferencesEditorModel, codeEditor: ICodeEditor): TPromise { + private getPosition(language: string, settingsModel: IPreferencesEditorModel, codeEditor: ICodeEditor): Thenable { const languageKey = `[${language}]`; let setting = settingsModel.getPreference(languageKey); const model = codeEditor.getModel(); @@ -544,9 +543,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic } const editOperation = EditOperation.insert(new Position(lastSetting.valueRange.endLineNumber, lastSetting.valueRange.endColumn), content); model.pushEditOperations([], [editOperation], () => []); - return TPromise.as({ lineNumber: lastSetting.valueRange.endLineNumber + 1, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber + 1) }); + return Promise.resolve({ lineNumber: lastSetting.valueRange.endLineNumber + 1, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber + 1) }); } - return TPromise.as({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 }); + return Promise.resolve({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 }); } return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER) .then(() => { diff --git a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts index 11b1a71a867..06ee1fb614c 100644 --- a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts +++ b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts @@ -157,7 +157,7 @@ export class KeybindingsEditorModel extends EditorModel { } private splitKeybindingWords(wordsSeparatedBySpaces: string[]): string[] { - const result = []; + const result: string[] = []; for (const word of wordsSeparatedBySpaces) { result.push(...word.split('+').filter(w => !!w)); } @@ -314,9 +314,9 @@ class KeybindingItemMatches { let firstPartMatch: KeybindingMatch = {}; let chordPartMatch: KeybindingMatch = {}; - const matchedWords = []; - let firstPartMatchedWords = []; - let chordPartMatchedWords = []; + const matchedWords: number[] = []; + let firstPartMatchedWords: number[] = []; + let chordPartMatchedWords: number[] = []; let matchFirstPart = true; for (let index = 0; index < words.length; index++) { const word = words[index]; diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 80453e0f3cf..e816d316601 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -3,23 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorOptions } from 'vs/platform/editor/common/editor'; -import { IEditor, EditorOptions } from 'vs/workbench/common/editor'; -import { ITextModel } from 'vs/editor/common/model'; -import { IRange } from 'vs/editor/common/core/range'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { join } from 'vs/base/common/paths'; -import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { Event } from 'vs/base/common/event'; import { IStringDictionary } from 'vs/base/common/collections'; -import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { Event } from 'vs/base/common/event'; +import { join } from 'vs/base/common/paths'; +import { URI } from 'vs/base/common/uri'; +import { IRange } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { EditorOptions, IEditor } from 'vs/workbench/common/editor'; import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; -import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; export enum SettingValueType { Null = 'null', @@ -202,18 +201,18 @@ export interface IPreferencesService { workspaceSettingsResource: URI; getFolderSettingsResource(resource: URI): URI; - resolveModel(uri: URI): TPromise; - createPreferencesEditorModel(uri: URI): TPromise>; + resolveModel(uri: URI): Thenable; + createPreferencesEditorModel(uri: URI): Thenable>; createSettings2EditorModel(): Settings2EditorModel; // TODO - openRawDefaultSettings(): TPromise; - openSettings(jsonEditor?: boolean): TPromise; - openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise; - openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise; - openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise; - switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): TPromise; - openGlobalKeybindingSettings(textual: boolean): TPromise; - openDefaultKeybindingsFile(): TPromise; + openRawDefaultSettings(): Thenable; + openSettings(jsonEditor?: boolean): Thenable; + openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable; + openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable; + openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable; + switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Thenable; + openGlobalKeybindingSettings(textual: boolean): Thenable; + openDefaultKeybindingsFile(): Thenable; configureSettingsForLanguage(language: string): void; } diff --git a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts index 4bd8f3110af..4934e25e233 100644 --- a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts +++ b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts @@ -5,7 +5,6 @@ import { OS } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -70,8 +69,8 @@ export class KeybindingsEditorInput extends EditorInput { return nls.localize('keybindingsInputName', "Keyboard Shortcuts"); } - resolve(): TPromise { - return TPromise.as(this.keybindingsModel); + resolve(): Promise { + return Promise.resolve(this.keybindingsModel); } matches(otherInput: any): boolean { @@ -104,8 +103,8 @@ export class SettingsEditor2Input extends EditorInput { return nls.localize('settingsEditor2InputName', "Settings"); } - resolve(): TPromise { - return TPromise.as(this._settingsModel); + resolve(): Promise { + return Promise.resolve(this._settingsModel); } public getResource(): URI { diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 950371a1a7d..7888be41f0c 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -587,7 +587,7 @@ export class DefaultSettings extends Disposable { } private removeEmptySettingsGroups(settingsGroups: ISettingsGroup[]): ISettingsGroup[] { - const result = []; + const result: ISettingsGroup[] = []; for (const settingsGroup of settingsGroups) { settingsGroup.sections = settingsGroup.sections.filter(section => section.settings.length > 0); if (settingsGroup.sections.length) { @@ -1082,12 +1082,12 @@ export function createValidator(prop: IConfigurationPropertySchema): ((value: an { enabled: prop.maxLength !== undefined, isValid: (value => value.length <= prop.maxLength), - message: nls.localize('validations.maxLength', "Value must be fewer than {0} characters long.", prop.maxLength) + message: nls.localize('validations.maxLength', "Value must be {0} or fewer characters long.", prop.maxLength) }, { enabled: prop.minLength !== undefined, isValid: (value => value.length >= prop.minLength), - message: nls.localize('validations.minLength', "Value must be more than {0} characters long.", prop.minLength) + message: nls.localize('validations.minLength', "Value must be {0} or more characters long.", prop.minLength) }, { enabled: patternRegex !== undefined, @@ -1099,7 +1099,7 @@ export function createValidator(prop: IConfigurationPropertySchema): ((value: an if (prop.type === 'string' && stringValidations.length === 0) { return null; } if (isNullable && value === '') { return ''; } - let errors = []; + let errors: string[] = []; if (isNumeric) { if (value === '' || isNaN(+value)) { diff --git a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts index caf3501a28e..83430e62830 100644 --- a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts @@ -189,7 +189,7 @@ suite('KeybindingsEditorModel test', () => { assert.equal(actual.keybindingItem.when, ''); }); - test('convert with title and wihtout binding to entry', async () => { + test('convert with title and without binding to entry', async () => { const id = 'a' + uuid.generateUuid(); registerCommandWithTitle(id, 'some title'); prepareKeybindingService(); diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index b68cb4c8b77..f2432e817eb 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as types from 'vs/base/common/types'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -16,7 +15,7 @@ interface ProgressState { total?: number; worked?: number; done?: boolean; - whilePromise?: TPromise; + whilePromise?: Thenable; whileStart?: number; whileDelay?: number; } @@ -31,7 +30,7 @@ export abstract class ScopedService extends Disposable { registerListeners(): void { this._register(this.viewletService.onDidViewletOpen(viewlet => this.onScopeOpened(viewlet.getId()))); - this._register(this.panelService.onDidPanelOpen(panel => this.onScopeOpened(panel.getId()))); + this._register(this.panelService.onDidPanelOpen(({ panel }) => this.onScopeOpened(panel.getId()))); this._register(this.viewletService.onDidViewletClose(viewlet => this.onScopeClosed(viewlet.getId()))); this._register(this.panelService.onDidPanelClose(panel => this.onScopeClosed(panel.getId()))); @@ -206,7 +205,7 @@ export class ScopedProgressService extends ScopedService implements IProgressSer }; } - showWhile(promise: TPromise, delay?: number): TPromise { + showWhile(promise: Thenable, delay?: number): Thenable { let stack: boolean = !!this.progressState.whilePromise; // Reset State @@ -216,7 +215,7 @@ export class ScopedProgressService extends ScopedService implements IProgressSer // Otherwise join with existing running promise to ensure progress is accurate else { - promise = TPromise.join([promise, this.progressState.whilePromise]); + promise = Promise.all([promise, this.progressState.whilePromise]); } // Keep Promise in State @@ -287,7 +286,7 @@ export class ProgressService implements IProgressService { }; } - showWhile(promise: TPromise, delay?: number): TPromise { + showWhile(promise: Thenable, delay?: number): Thenable { const stop = () => { this.progressbar.stop().hide(); }; @@ -296,4 +295,4 @@ export class ProgressService implements IProgressService { return promise.then(stop, stop); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts index 2c9fce954e7..e075b3ebd2b 100644 --- a/src/vs/workbench/services/progress/browser/progressService2.ts +++ b/src/vs/workbench/services/progress/browser/progressService2.ts @@ -7,8 +7,7 @@ import 'vs/css!./media/progressService2'; import { localize } from 'vs/nls'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IProgressService2, IProgressOptions, IProgressStep, ProgressLocation } from 'vs/workbench/services/progress/common/progress'; -import { IProgress, emptyProgress, Progress } from 'vs/platform/progress/common/progress'; +import { IProgressService2, IProgressOptions, IProgressStep, ProgressLocation, IProgress, emptyProgress, Progress } from 'vs/platform/progress/common/progress'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { StatusbarAlignment, IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -17,7 +16,6 @@ import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/ import { INotificationService, Severity, INotificationHandle, INotificationActions } from 'vs/platform/notification/common/notification'; import { Action } from 'vs/base/common/actions'; import { once } from 'vs/base/common/event'; -import { ViewContainer } from 'vs/workbench/common/views'; export class ProgressService2 implements IProgressService2 { @@ -38,12 +36,12 @@ export class ProgressService2 implements IProgressService2 { withProgress

, R=any>(options: IProgressOptions, task: (progress: IProgress) => P, onDidCancel?: () => void): P { const { location } = options; - if (location instanceof ViewContainer) { - const viewlet = this._viewletService.getViewlet(location.id); + if (typeof location === 'string') { + const viewlet = this._viewletService.getViewlet(location); if (viewlet) { - return this._withViewletProgress(location.id, task); + return this._withViewletProgress(location, task); } - console.warn(`Bad progress location: ${location.id}`); + console.warn(`Bad progress location: ${location}`); return undefined; } diff --git a/src/vs/workbench/services/progress/common/progress.ts b/src/vs/workbench/services/progress/common/progress.ts deleted file mode 100644 index 3d6136158ff..00000000000 --- a/src/vs/workbench/services/progress/common/progress.ts +++ /dev/null @@ -1,38 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IProgress } from 'vs/platform/progress/common/progress'; -import { ViewContainer } from 'vs/workbench/common/views'; - -export const enum ProgressLocation { - Explorer = 1, - Scm = 3, - Extensions = 5, - Window = 10, - Notification = 15 -} - -export interface IProgressOptions { - location: ProgressLocation | ViewContainer; - title?: string; - source?: string; - total?: number; - cancellable?: boolean; -} - -export interface IProgressStep { - message?: string; - increment?: number; -} - -export const IProgressService2 = createDecorator('progressService2'); - -export interface IProgressService2 { - - _serviceBrand: any; - - withProgress

, R=any>(options: IProgressOptions, task: (progress: IProgress) => P, onDidCancel?: () => void): P; -} \ No newline at end of file diff --git a/src/vs/workbench/services/progress/test/progressService.test.ts b/src/vs/workbench/services/progress/test/progressService.test.ts index 042124c3824..f2ac81a6440 100644 --- a/src/vs/workbench/services/progress/test/progressService.test.ts +++ b/src/vs/workbench/services/progress/test/progressService.test.ts @@ -66,11 +66,11 @@ class TestViewletService implements IViewletService { class TestPanelService implements IPanelService { public _serviceBrand: any; - onDidPanelOpen = new Emitter().event; + onDidPanelOpen = new Emitter<{ panel: IPanel, focus: boolean }>().event; onDidPanelClose = new Emitter().event; - public openPanel(id: string, focus?: boolean): TPromise { - return TPromise.as(null); + public openPanel(id: string, focus?: boolean): IPanel { + return null; } public getPanels(): any[] { diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts index b359fd41ff2..88882ad8cb1 100644 --- a/src/vs/workbench/services/scm/common/scm.ts +++ b/src/vs/workbench/services/scm/common/scm.ts @@ -90,6 +90,9 @@ export interface ISCMInput { validateInput: IInputValidator; readonly onDidChangeValidateInput: Event; + + visible: boolean; + readonly onDidChangeVisibility: Event; } export interface ISCMRepository extends IDisposable { diff --git a/src/vs/workbench/services/scm/common/scmService.ts b/src/vs/workbench/services/scm/common/scmService.ts index 1137b195abf..618d669a2b6 100644 --- a/src/vs/workbench/services/scm/common/scmService.ts +++ b/src/vs/workbench/services/scm/common/scmService.ts @@ -40,6 +40,20 @@ class SCMInput implements ISCMInput { private _onDidChangePlaceholder = new Emitter(); get onDidChangePlaceholder(): Event { return this._onDidChangePlaceholder.event; } + private _visible = true; + + get visible(): boolean { + return this._visible; + } + + set visible(visible: boolean) { + this._visible = visible; + this._onDidChangeVisibility.fire(visible); + } + + private _onDidChangeVisibility = new Emitter(); + get onDidChangeVisibility(): Event { return this._onDidChangeVisibility.event; } + private _validateInput: IInputValidator = () => TPromise.as(undefined); get validateInput(): IInputValidator { diff --git a/src/vs/workbench/services/search/common/searchHelpers.ts b/src/vs/workbench/services/search/common/searchHelpers.ts new file mode 100644 index 00000000000..4e95819cab7 --- /dev/null +++ b/src/vs/workbench/services/search/common/searchHelpers.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Range } from 'vs/editor/common/core/range'; +import { FindMatch, ITextModel } from 'vs/editor/common/model'; +import { ITextSearchPreviewOptions, TextSearchMatch, ITextSearchResult, ITextSearchMatch, ITextQuery, ITextSearchContext } from 'vs/platform/search/common/search'; + +function editorMatchToTextSearchResult(matches: FindMatch[], model: ITextModel, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch { + const firstLine = matches[0].range.startLineNumber; + const lastLine = matches[matches.length - 1].range.endLineNumber; + + const lineTexts: string[] = []; + const numLines = (previewOptions && matches.length === 1) ? previewOptions.matchLines : Number.MAX_VALUE; + for (let i = firstLine; i <= lastLine && (i - firstLine) < numLines; i++) { + lineTexts.push(model.getLineContent(i)); + } + + return new TextSearchMatch( + lineTexts.join('\n') + '\n', + matches.map(m => new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endLineNumber - 1, m.range.endColumn - 1)), + previewOptions); +} + +/** + * Combine a set of FindMatches into a set of TextSearchResults. They should be grouped by matches that start on the same line that the previous match ends on. + */ +export function editorMatchesToTextSearchResults(matches: FindMatch[], model: ITextModel, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch[] { + let previousEndLine = -1; + const groupedMatches: FindMatch[][] = []; + let currentMatches: FindMatch[] = []; + matches.forEach((match) => { + if (match.range.startLineNumber !== previousEndLine) { + currentMatches = []; + groupedMatches.push(currentMatches); + } + + currentMatches.push(match); + previousEndLine = match.range.endLineNumber; + }); + + return groupedMatches.map(sameLineMatches => { + return editorMatchToTextSearchResult(sameLineMatches, model, previewOptions); + }); +} + +export function addContextToEditorMatches(matches: ITextSearchMatch[], model: ITextModel, query: ITextQuery): ITextSearchResult[] { + const results: ITextSearchResult[] = []; + + let prevLine = -1; + for (let i = 0; i < matches.length; i++) { + const { start: matchStartLine, end: matchEndLine } = getMatchStartEnd(matches[i]); + if (typeof query.beforeContext === 'number' && query.beforeContext > 0) { + const beforeContextStartLine = Math.max(prevLine + 1, matchStartLine - query.beforeContext); + for (let b = beforeContextStartLine; b < matchStartLine; b++) { + results.push({ + text: model.getLineContent(b + 1), + lineNumber: b + }); + } + } + + results.push(matches[i]); + + const nextMatch = matches[i + 1]; + let nextMatchStartLine = nextMatch ? getMatchStartEnd(nextMatch).start : Number.MAX_VALUE; + if (typeof query.afterContext === 'number' && query.afterContext > 0) { + const afterContextToLine = Math.min(nextMatchStartLine - 1, matchEndLine + query.afterContext, model.getLineCount() - 1); + for (let a = matchEndLine + 1; a <= afterContextToLine; a++) { + results.push({ + text: model.getLineContent(a + 1), + lineNumber: a + }); + } + } + + prevLine = matchEndLine; + } + + return results; +} + +function getMatchStartEnd(match: ITextSearchMatch): { start: number, end: number } { + const matchRanges = match.ranges; + const matchStartLine = Array.isArray(matchRanges) ? matchRanges[0].startLineNumber : matchRanges.startLineNumber; + const matchEndLine = Array.isArray(matchRanges) ? matchRanges[matchRanges.length - 1].endLineNumber : matchRanges.endLineNumber; + + return { + start: matchStartLine, + end: matchEndLine + }; +} \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index d3c2f1cc84b..9981f33919c 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -15,20 +15,19 @@ import * as normalization from 'vs/base/common/normalization'; import * as objects from 'vs/base/common/objects'; import { isEqualOrParent } from 'vs/base/common/paths'; import * as platform from 'vs/base/common/platform'; +import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import * as types from 'vs/base/common/types'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { URI } from 'vs/base/common/uri'; import * as extfs from 'vs/base/node/extfs'; import * as flow from 'vs/base/node/flow'; -import { IProgress, ISearchEngineStats } from 'vs/platform/search/common/search'; +import { IFileQuery, IFolderQuery, IProgress, ISearchEngineStats } from 'vs/platform/search/common/search'; +import { IRawFileMatch, ISearchEngine, ISearchEngineSuccess } from 'vs/workbench/services/search/node/search'; import { spawnRipgrepCmd } from './ripgrepFileSearch'; -import { IFolderSearch, IRawFileMatch, IRawSearch, ISearchEngine, ISearchEngineSuccess } from './search'; -import { StopWatch } from 'vs/base/common/stopwatch'; enum Traversal { Node = 1, MacFind, - WindowsDir, LinuxFind, Ripgrep } @@ -44,8 +43,13 @@ interface IDirectoryTree { pathToEntries: { [relativePath: string]: IDirectoryEntry[] }; } +const killCmds = new Set<() => void>(); +process.on('exit', () => { + killCmds.forEach(cmd => cmd()); +}); + export class FileWalker { - private config: IRawSearch; + private config: IFileQuery; private useRipgrep: boolean; private filePattern: string; private normalizedFilePatternLowercase: string; @@ -69,14 +73,14 @@ export class FileWalker { private walkedPaths: { [path: string]: boolean; }; - constructor(config: IRawSearch) { + constructor(config: IFileQuery, maxFileSize?: number) { this.config = config; this.useRipgrep = config.useRipgrep !== false; this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || null; this.exists = config.exists; - this.maxFilesize = config.maxFilesize || null; + this.maxFilesize = maxFileSize || null; this.walkedPaths = Object.create(null); this.resultCount = 0; this.isLimitHit = false; @@ -96,17 +100,18 @@ export class FileWalker { const folderExcludeExpression: glob.IExpression = objects.assign({}, folderQuery.excludePattern || {}, this.config.excludePattern || {}); // Add excludes for other root folders + const fqPath = folderQuery.folder.fsPath; config.folderQueries - .map(rootFolderQuery => rootFolderQuery.folder) - .filter(rootFolder => rootFolder !== folderQuery.folder) + .map(rootFolderQuery => rootFolderQuery.folder.fsPath) + .filter(rootFolder => rootFolder !== fqPath) .forEach(otherRootFolder => { // Exclude nested root folders - if (isEqualOrParent(otherRootFolder, folderQuery.folder)) { - folderExcludeExpression[path.relative(folderQuery.folder, otherRootFolder)] = true; + if (isEqualOrParent(otherRootFolder, fqPath)) { + folderExcludeExpression[path.relative(fqPath, otherRootFolder)] = true; } }); - this.folderExcludePatterns.set(folderQuery.folder, new AbsoluteAndRelativeParsedExpression(folderExcludeExpression, folderQuery.folder)); + this.folderExcludePatterns.set(fqPath, new AbsoluteAndRelativeParsedExpression(folderExcludeExpression, fqPath)); }); } @@ -114,7 +119,7 @@ export class FileWalker { this.isCanceled = true; } - public walk(folderQueries: IFolderSearch[], extraFiles: string[], onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, done: (error: Error, isLimitHit: boolean) => void): void { + public walk(folderQueries: IFolderQuery[], extraFiles: URI[], onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, done: (error: Error, isLimitHit: boolean) => void): void { this.fileWalkSW = StopWatch.create(false); // Support that the file pattern is a full path to a file that exists @@ -125,13 +130,13 @@ export class FileWalker { // For each extra file if (extraFiles) { extraFiles.forEach(extraFilePath => { - const basename = path.basename(extraFilePath); - if (this.globalExcludePattern && this.globalExcludePattern(extraFilePath, basename)) { + const basename = path.basename(extraFilePath.fsPath); + if (this.globalExcludePattern && this.globalExcludePattern(extraFilePath.fsPath, basename)) { return; // excluded } // File: Check for match on file pattern and include pattern - this.matchFile(onResult, { relativePath: extraFilePath /* no workspace relative path */, basename }); + this.matchFile(onResult, { relativePath: extraFilePath.fsPath /* no workspace relative path */, basename }); }); } @@ -143,11 +148,7 @@ export class FileWalker { } else if (platform.isMacintosh) { this.traversal = Traversal.MacFind; traverse = this.cmdTraversal; - // Disable 'dir' for now (#11181, #11179, #11183, #11182). - } /* else if (platform.isWindows) { - this.traversal = Traversal.WindowsDir; - traverse = this.windowsDirTraversal; - } */ else if (platform.isLinux) { + } else if (platform.isLinux) { this.traversal = Traversal.LinuxFind; traverse = this.cmdTraversal; } @@ -159,7 +160,7 @@ export class FileWalker { } // For each root folder - flow.parallel(folderQueries, (folderQuery: IFolderSearch, rootFolderDone: (err: Error, result: void) => void) => { + flow.parallel(folderQueries, (folderQuery: IFolderQuery, rootFolderDone: (err: Error, result: void) => void) => { this.call(traverse, this, folderQuery, onResult, onMessage, (err?: Error) => { if (err) { const errorMessage = toErrorMessage(err); @@ -185,14 +186,15 @@ export class FileWalker { } } - private cmdTraversal(folderQuery: IFolderSearch, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, cb: (err?: Error) => void): void { - const rootFolder = folderQuery.folder; + private cmdTraversal(folderQuery: IFolderQuery, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, cb: (err?: Error) => void): void { + const rootFolder = folderQuery.folder.fsPath; const isMac = platform.isMacintosh; let cmd: childProcess.ChildProcess; const killCmd = () => cmd && cmd.kill(); + killCmds.add(killCmd); let done = (err?: Error) => { - process.removeListener('exit', killCmd); + killCmds.delete(killCmd); done = () => { }; cb(err); }; @@ -203,7 +205,7 @@ export class FileWalker { const useRipgrep = this.useRipgrep; let noSiblingsClauses: boolean; if (useRipgrep) { - const ripgrep = spawnRipgrepCmd(this.config, folderQuery, this.config.includePattern, this.folderExcludePatterns.get(folderQuery.folder).expression); + const ripgrep = spawnRipgrepCmd(this.config, folderQuery, this.config.includePattern, this.folderExcludePatterns.get(folderQuery.folder.fsPath).expression); cmd = ripgrep.cmd; noSiblingsClauses = !Object.keys(ripgrep.siblingClauses).length; @@ -220,7 +222,6 @@ export class FileWalker { cmd = this.spawnFindCmd(folderQuery); } - process.on('exit', killCmd); this.cmdResultCount = 0; this.collectStdout(cmd, 'utf8', useRipgrep, onMessage, (err: Error, stdout?: string, last?: boolean) => { if (err) { @@ -283,38 +284,11 @@ export class FileWalker { }); } - // protected windowsDirTraversal(rootFolder: string, onResult: (result: IRawFileMatch) => void, done: (err?: Error) => void): void { - // const cmd = childProcess.spawn('cmd', ['/U', '/c', 'dir', '/s', '/b', '/a-d', rootFolder]); - // this.readStdout(cmd, 'ucs2', (err: Error, stdout?: string) => { - // if (err) { - // done(err); - // return; - // } - - // const relativeFiles = stdout.split(`\r\n${rootFolder}\\`); - // relativeFiles[0] = relativeFiles[0].trim().substr(rootFolder.length + 1); - // const n = relativeFiles.length; - // relativeFiles[n - 1] = relativeFiles[n - 1].trim(); - // if (!relativeFiles[n - 1]) { - // relativeFiles.pop(); - // } - - // if (relativeFiles.length && relativeFiles[0].indexOf('\n') !== -1) { - // done(new Error('Splitting up files failed')); - // return; - // } - - // this.matchFiles(rootFolder, relativeFiles, onResult); - - // done(); - // }); - // } - /** * Public for testing. */ - public spawnFindCmd(folderQuery: IFolderSearch) { - const excludePattern = this.folderExcludePatterns.get(folderQuery.folder); + public spawnFindCmd(folderQuery: IFolderQuery) { + const excludePattern = this.folderExcludePatterns.get(folderQuery.folder.fsPath); const basenames = excludePattern.getBasenameTerms(); const pathTerms = excludePattern.getPathTerms(); let args = ['-L', '.']; @@ -332,7 +306,7 @@ export class FileWalker { args.push(')', '-prune', ')'); } args.push('-type', 'f'); - return childProcess.spawn('find', args, { cwd: folderQuery.folder }); + return childProcess.spawn('find', args, { cwd: folderQuery.folder.fsPath }); } /** @@ -493,9 +467,9 @@ export class FileWalker { matchDirectory(rootEntries); } - private nodeJSTraversal(folderQuery: IFolderSearch, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, done: (err?: Error) => void): void { + private nodeJSTraversal(folderQuery: IFolderQuery, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, done: (err?: Error) => void): void { this.directoriesWalked++; - extfs.readdir(folderQuery.folder, (error: Error, files: string[]) => { + extfs.readdir(folderQuery.folder.fsPath, (error: Error, files: string[]) => { if (error || this.isCanceled || this.isLimitHit) { return done(); } @@ -519,7 +493,7 @@ export class FileWalker { }; } - private doWalk(folderQuery: IFolderSearch, relativeParentPath: string, files: string[], onResult: (result: IRawFileMatch) => void, done: (error: Error) => void): void { + private doWalk(folderQuery: IFolderQuery, relativeParentPath: string, files: string[], onResult: (result: IRawFileMatch) => void, done: (error: Error) => void): void { const rootFolder = folderQuery.folder; // Execute tasks on each file in parallel to optimize throughput @@ -536,12 +510,12 @@ export class FileWalker { // to ignore filtering by siblings because the user seems to know what she // is searching for and we want to include the result in that case anyway let currentRelativePath = relativeParentPath ? [relativeParentPath, file].join(path.sep) : file; - if (this.folderExcludePatterns.get(folderQuery.folder).test(currentRelativePath, file, this.config.filePattern !== file ? hasSibling : undefined)) { + if (this.folderExcludePatterns.get(folderQuery.folder.fsPath).test(currentRelativePath, file, this.config.filePattern !== file ? hasSibling : undefined)) { return clb(null, undefined); } // Use lstat to detect links - let currentAbsolutePath = [rootFolder, currentRelativePath].join(path.sep); + let currentAbsolutePath = [rootFolder.fsPath, currentRelativePath].join(path.sep); fs.lstat(currentAbsolutePath, (error, lstat) => { if (error || this.isCanceled || this.isLimitHit) { return clb(null, undefined); @@ -593,7 +567,7 @@ export class FileWalker { return clb(null, undefined); // ignore file if max file size is hit } - this.matchFile(onResult, { base: rootFolder, relativePath: currentRelativePath, basename: file, size: stat.size }); + this.matchFile(onResult, { base: rootFolder.fsPath, relativePath: currentRelativePath, basename: file, size: stat.size }); } // Unwind @@ -662,13 +636,13 @@ export class FileWalker { } export class Engine implements ISearchEngine { - private folderQueries: IFolderSearch[]; - private extraFiles: string[]; + private folderQueries: IFolderQuery[]; + private extraFiles: URI[]; private walker: FileWalker; - constructor(config: IRawSearch) { + constructor(config: IFileQuery) { this.folderQueries = config.folderQueries; - this.extraFiles = config.extraFiles; + this.extraFiles = config.extraFileResources; this.walker = new FileWalker(config); } @@ -722,13 +696,13 @@ class AbsoluteAndRelativeParsedExpression { this.relativeParsedExpr = relativeGlobExpr && glob.parse(relativeGlobExpr, { trimForExclusions: true }); } - public test(_path: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise): string | TPromise { + public test(_path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise): string | Promise { return (this.relativeParsedExpr && this.relativeParsedExpr(_path, basename, hasSibling)) || (this.absoluteParsedExpr && this.absoluteParsedExpr(path.join(this.root, _path), basename, hasSibling)); } public getBasenameTerms(): string[] { - const basenameTerms = []; + const basenameTerms: string[] = []; if (this.absoluteParsedExpr) { basenameTerms.push(...glob.getBasenameTerms(this.absoluteParsedExpr)); } @@ -741,7 +715,7 @@ class AbsoluteAndRelativeParsedExpression { } public getPathTerms(): string[] { - const pathTerms = []; + const pathTerms: string[] = []; if (this.absoluteParsedExpr) { pathTerms.push(...glob.getPathTerms(this.absoluteParsedExpr)); } diff --git a/src/vs/workbench/services/search/node/fileSearchManager.ts b/src/vs/workbench/services/search/node/fileSearchManager.ts index 8948187d9f0..2713a180fb3 100644 --- a/src/vs/workbench/services/search/node/fileSearchManager.ts +++ b/src/vs/workbench/services/search/node/fileSearchManager.ts @@ -10,8 +10,7 @@ import * as glob from 'vs/base/common/glob'; import * as resources from 'vs/base/common/resources'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search'; +import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery } from 'vs/platform/search/common/search'; import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search'; import * as vscode from 'vscode'; @@ -36,25 +35,23 @@ export interface IDirectoryTree { class FileSearchEngine { - private filePattern: string; - private includePattern: glob.ParsedExpression; - private maxResults: number; - private exists: boolean; - private isLimitHit: boolean; - private resultCount: number; - private isCanceled: boolean; + private filePattern?: string; + private includePattern?: glob.ParsedExpression; + private maxResults?: number; + private exists?: boolean; + private isLimitHit = false; + private resultCount = 0; + private isCanceled = false; private activeCancellationTokens: Set; - private globalExcludePattern: glob.ParsedExpression; + private globalExcludePattern?: glob.ParsedExpression; - constructor(private config: ISearchQuery, private provider: vscode.FileSearchProvider) { + constructor(private config: IFileQuery, private provider: vscode.FileSearchProvider) { this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); - this.maxResults = config.maxResults || null; + this.maxResults = config.maxResults || undefined; this.exists = config.exists; - this.resultCount = 0; - this.isLimitHit = false; this.activeCancellationTokens = new Set(); this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern); @@ -66,10 +63,10 @@ class FileSearchEngine { this.activeCancellationTokens = new Set(); } - public search(_onResult: (match: IInternalFileMatch) => void): TPromise { - const folderQueries = this.config.folderQueries; + public search(_onResult: (match: IInternalFileMatch) => void): Promise { + const folderQueries = this.config.folderQueries || []; - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { const onResult = (match: IInternalFileMatch) => { this.resultCount++; _onResult(match); @@ -96,26 +93,22 @@ class FileSearchEngine { } // For each root folder - TPromise.join(folderQueries.map(fq => { + Promise.all(folderQueries.map(fq => { return this.searchInFolder(fq, onResult); })).then(stats => { resolve({ limitHit: this.isLimitHit, - stats: stats[0] // Only looking at single-folder workspace stats... + stats: stats[0] || undefined // Only looking at single-folder workspace stats... }); - }, (errs: Error[]) => { - const errMsg = errs - .map(err => toErrorMessage(err)) - .filter(msg => !!msg)[0]; - - reject(new Error(errMsg)); + }, (err: Error) => { + reject(new Error(toErrorMessage(err))); }); }); } - private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): TPromise { + private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): Promise { let cancellation = new CancellationTokenSource(); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { const options = this.getSearchOptionsForFolder(fq); const tree = this.initDirectoryTree(); @@ -123,7 +116,7 @@ class FileSearchEngine { const noSiblingsClauses = !queryTester.hasSiblingExcludeClauses(); let providerSW: StopWatch; - new TPromise(_resolve => process.nextTick(_resolve)) + new Promise(_resolve => process.nextTick(_resolve)) .then(() => { this.activeCancellationTokens.add(cancellation); @@ -189,9 +182,9 @@ class FileSearchEngine { folder: fq.folder, excludes, includes, - useIgnoreFiles: !this.config.disregardIgnoreFiles, - useGlobalIgnoreFiles: !this.config.disregardGlobalIgnoreFiles, - followSymlinks: !this.config.ignoreSymlinks, + useIgnoreFiles: !fq.disregardIgnoreFiles, + useGlobalIgnoreFiles: !fq.disregardGlobalIgnoreFiles, + followSymlinks: !fq.ignoreSymlinks, maxResults: this.config.maxResults }; } @@ -267,7 +260,7 @@ class FileSearchEngine { } private matchFile(onResult: (result: IInternalFileMatch) => void, candidate: IInternalFileMatch): void { - if (!this.includePattern || this.includePattern(candidate.relativePath, candidate.basename)) { + if (!this.includePattern || (candidate.relativePath && this.includePattern(candidate.relativePath, candidate.basename))) { if (this.exists || (this.maxResults && this.resultCount >= this.maxResults)) { this.isLimitHit = true; this.cancel(); @@ -289,7 +282,7 @@ export class FileSearchManager { private static readonly BATCH_SIZE = 512; - fileSearch(config: ISearchQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { + fileSearch(config: IFileQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise { const engine = new FileSearchEngine(config, provider); let resultCount = 0; @@ -325,7 +318,7 @@ export class FileSearchManager { } } - private doSearch(engine: FileSearchEngine, batchSize: number, onResultBatch: (matches: IInternalFileMatch[]) => void, token: CancellationToken): TPromise { + private doSearch(engine: FileSearchEngine, batchSize: number, onResultBatch: (matches: IInternalFileMatch[]) => void, token: CancellationToken): Promise { token.onCancellationRequested(() => { engine.cancel(); }); @@ -352,7 +345,7 @@ export class FileSearchManager { onResultBatch(batch); } - return TPromise.wrapError(error); + return Promise.reject(error); }); } } diff --git a/src/vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts b/src/vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts new file mode 100644 index 00000000000..c1fd4d774f9 --- /dev/null +++ b/src/vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import * as gracefulFs from 'graceful-fs'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { MAX_FILE_SIZE } from 'vs/platform/files/node/files'; +import { ITextQuery, QueryType } from 'vs/platform/search/common/search'; +import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; +import { Engine } from 'vs/workbench/services/search/node/legacy/textSearch'; +import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/legacy/textSearchWorkerProvider'; +import { BatchedCollector } from 'vs/workbench/services/search/node/textSearchManager'; +import { ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from '../search'; + +gracefulFs.gracefulify(fs); + +type IProgressCallback = (p: ISerializedSearchProgressItem) => void; + +export class LegacyTextSearchService { + private static readonly BATCH_SIZE = 512; + + private textSearchWorkerProvider: TextSearchWorkerProvider; + + textSearch(config: ITextQuery, progressCallback: IProgressCallback, token?: CancellationToken): Promise { + if (!this.textSearchWorkerProvider) { + this.textSearchWorkerProvider = new TextSearchWorkerProvider(); + } + + let engine = new Engine( + config, + new FileWalker({ + type: QueryType.File, + folderQueries: config.folderQueries, + extraFileResources: config.extraFileResources, + includePattern: config.includePattern, + excludePattern: config.excludePattern, + useRipgrep: false + }, MAX_FILE_SIZE), + this.textSearchWorkerProvider); + + return this.doTextSearch(engine, progressCallback, LegacyTextSearchService.BATCH_SIZE, token); + } + + private doTextSearch(engine: Engine, progressCallback: IProgressCallback, batchSize: number, token?: CancellationToken): Promise { + if (token) { + token.onCancellationRequested(() => engine.cancel()); + } + + return new Promise((c, e) => { + // Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned + const collector = new BatchedCollector(batchSize, progressCallback); + engine.search((matches) => { + const totalMatches = matches.reduce((acc, m) => acc + m.numMatches, 0); + collector.addItems(matches, totalMatches); + }, (progress) => { + progressCallback(progress); + }, (error, stats) => { + collector.flush(); + + if (error) { + e(error); + } else { + c({ + type: 'success', + limitHit: stats.limitHit, + stats: null + }); + } + }); + }); + } +} diff --git a/src/vs/workbench/services/search/node/legacy/search.ts b/src/vs/workbench/services/search/node/legacy/search.ts new file mode 100644 index 00000000000..23d4710b031 --- /dev/null +++ b/src/vs/workbench/services/search/node/legacy/search.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as glob from 'vs/base/common/glob'; +import { IPatternInfo, ITextSearchPreviewOptions } from 'vs/platform/search/common/search'; + +export interface IFolderSearch { + folder: string; + excludePattern?: glob.IExpression; + includePattern?: glob.IExpression; + fileEncoding?: string; + disregardIgnoreFiles?: boolean; + disregardGlobalIgnoreFiles?: boolean; +} + +export interface IRawSearch { + folderQueries: IFolderSearch[]; + ignoreSymlinks?: boolean; + extraFiles?: string[]; + filePattern?: string; + excludePattern?: glob.IExpression; + includePattern?: glob.IExpression; + contentPattern?: IPatternInfo; + maxResults?: number; + exists?: boolean; + sortByScore?: boolean; + cacheKey?: string; + maxFilesize?: number; + useRipgrep?: boolean; + disregardIgnoreFiles?: boolean; + previewOptions?: ITextSearchPreviewOptions; + disregardGlobalIgnoreFiles?: boolean; +} diff --git a/src/vs/workbench/services/search/node/textSearch.ts b/src/vs/workbench/services/search/node/legacy/textSearch.ts similarity index 76% rename from src/vs/workbench/services/search/node/textSearch.ts rename to src/vs/workbench/services/search/node/legacy/textSearch.ts index c1fdc506ea1..f4fc9ba2d7b 100644 --- a/src/vs/workbench/services/search/node/textSearch.ts +++ b/src/vs/workbench/services/search/node/legacy/textSearch.ts @@ -5,17 +5,19 @@ import * as path from 'path'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IProgress } from 'vs/platform/search/common/search'; +import { IProgress, ITextQuery } from 'vs/platform/search/common/search'; import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; -import { IRawSearch, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch } from './search'; +import { ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch } from '../search'; import { ITextSearchWorkerProvider } from './textSearchWorkerProvider'; import { ISearchWorker, ISearchWorkerSearchArgs } from './worker/searchWorkerIpc'; +import { IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; export class Engine implements ISearchEngine { private static readonly PROGRESS_FLUSH_CHUNK_SIZE = 50; // optimization: number of files to process before emitting progress event private config: IRawSearch; + private config2: ITextQuery; private walker: FileWalker; private walkerError: Error; @@ -33,8 +35,9 @@ export class Engine implements ISearchEngine { private nextWorker = 0; - constructor(config: IRawSearch, walker: FileWalker, workerProvider: ITextSearchWorkerProvider) { - this.config = config; + constructor(config: ITextQuery, walker: FileWalker, workerProvider: ITextSearchWorkerProvider) { + this.config = makeRawSearch(config); + this.config2 = config; this.walker = walker; this.workerProvider = workerProvider; } @@ -122,7 +125,7 @@ export class Engine implements ISearchEngine { let nextBatch: string[] = []; let nextBatchBytes = 0; const batchFlushBytes = 2 ** 20; // 1MB - this.walker.walk(this.config.folderQueries, this.config.extraFiles, result => { + this.walker.walk(this.config2.folderQueries, this.config2.extraFileResources, result => { let bytes = result.size || 1; this.totalBytes += bytes; @@ -162,3 +165,42 @@ export class Engine implements ISearchEngine { }); } } + +/** + * Exported for tests + */ +export function makeRawSearch(query: ITextQuery): IRawSearch { + let rawSearch: IRawSearch = { + folderQueries: [], + extraFiles: [], + excludePattern: query.excludePattern, + includePattern: query.includePattern, + maxResults: query.maxResults, + useRipgrep: query.useRipgrep, + disregardIgnoreFiles: query.folderQueries.some(fq => fq.disregardIgnoreFiles), + disregardGlobalIgnoreFiles: query.folderQueries.some(fq => fq.disregardGlobalIgnoreFiles), + ignoreSymlinks: query.folderQueries.some(fq => fq.ignoreSymlinks), + previewOptions: query.previewOptions + }; + + for (const q of query.folderQueries) { + rawSearch.folderQueries.push({ + excludePattern: q.excludePattern, + includePattern: q.includePattern, + fileEncoding: q.fileEncoding, + disregardIgnoreFiles: q.disregardIgnoreFiles, + disregardGlobalIgnoreFiles: q.disregardGlobalIgnoreFiles, + folder: q.folder.fsPath + }); + } + + if (query.extraFileResources) { + for (const r of query.extraFileResources) { + rawSearch.extraFiles.push(r.fsPath); + } + } + + rawSearch.contentPattern = query.contentPattern; + + return rawSearch; +} diff --git a/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts b/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts similarity index 94% rename from src/vs/workbench/services/search/node/textSearchWorkerProvider.ts rename to src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts index af095577494..617bd4ed0c4 100644 --- a/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts +++ b/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts @@ -35,7 +35,7 @@ export class TextSearchWorkerProvider implements ITextSearchWorkerProvider { args: ['--type=searchWorker'], timeout: 30 * 1000, env: { - AMD_ENTRYPOINT: 'vs/workbench/services/search/node/worker/searchWorkerApp', + AMD_ENTRYPOINT: 'vs/workbench/services/search/node/legacy/worker/searchWorkerApp', PIPE_LOGGING: 'true', VERBOSE_LOGGING: process.env.VERBOSE_LOGGING }, diff --git a/src/vs/workbench/services/search/node/worker/searchWorker.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts similarity index 88% rename from src/vs/workbench/services/search/node/worker/searchWorker.ts rename to src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts index de39d2f6453..4ae556fff5b 100644 --- a/src/vs/workbench/services/search/node/worker/searchWorker.ts +++ b/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts @@ -7,12 +7,11 @@ import * as fs from 'fs'; import * as gracefulFs from 'graceful-fs'; import { onUnexpectedError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; -import { TPromise } from 'vs/base/common/winjs.base'; import { bomLength, decode, detectEncodingFromBuffer, encodingExists, UTF16be, UTF16le, UTF8, UTF8_with_bom } from 'vs/base/node/encoding'; import { Range } from 'vs/editor/common/core/range'; -import { ITextSearchPreviewOptions, TextSearchResult } from 'vs/platform/search/common/search'; -import { FileMatch } from '../search'; +import { ITextSearchPreviewOptions, TextSearchMatch } from 'vs/platform/search/common/search'; import { ISearchWorker, ISearchWorkerSearchArgs, ISearchWorkerSearchResult } from './searchWorkerIpc'; +import { FileMatch } from 'vs/workbench/services/search/node/search'; gracefulFs.gracefulify(fs); @@ -32,21 +31,21 @@ function onError(error: any): void { export class SearchWorker implements ISearchWorker { private currentSearchEngine: SearchWorkerEngine; - initialize(): TPromise { + initialize(): Promise { this.currentSearchEngine = new SearchWorkerEngine(); - return TPromise.wrap(undefined); + return Promise.resolve(undefined); } - cancel(): TPromise { + cancel(): Promise { // Cancel the current search. It will stop searching and close its open files. if (this.currentSearchEngine) { this.currentSearchEngine.cancel(); } - return TPromise.wrap(null); + return Promise.resolve(null); } - search(args: ISearchWorkerSearchArgs): TPromise { + search(args: ISearchWorkerSearchArgs): Promise { if (!this.currentSearchEngine) { // Worker timed out during search this.initialize(); @@ -66,13 +65,13 @@ const LF = 0x0a; const CR = 0x0d; export class SearchWorkerEngine { - private nextSearch = TPromise.wrap(null); + private nextSearch = Promise.resolve(null); private isCanceled = false; /** * Searches some number of the given paths concurrently, and starts searches in other paths when those complete. */ - searchBatch(args: ISearchWorkerSearchArgs): TPromise { + searchBatch(args: ISearchWorkerSearchArgs): Promise { const contentPattern = strings.createRegExp(args.pattern.pattern, args.pattern.isRegExp, { matchCase: args.pattern.isCaseSensitive, wholeWord: args.pattern.isWordMatch, multiline: false, global: true }); const fileEncoding = encodingExists(args.fileEncoding) ? args.fileEncoding : UTF8; return this.nextSearch = @@ -80,12 +79,12 @@ export class SearchWorkerEngine { } - private _searchBatch(args: ISearchWorkerSearchArgs, contentPattern: RegExp, fileEncoding: string): TPromise { + private _searchBatch(args: ISearchWorkerSearchArgs, contentPattern: RegExp, fileEncoding: string): Promise { if (this.isCanceled) { - return TPromise.wrap(null); + return Promise.resolve(null); } - return new TPromise(batchDone => { + return new Promise(batchDone => { const result: ISearchWorkerSearchResult = { matches: [], numMatches: 0, @@ -93,7 +92,7 @@ export class SearchWorkerEngine { }; // Search in the given path, and when it's finished, search in the next path in absolutePaths - const startSearchInFile = (absolutePath: string): TPromise => { + const startSearchInFile = (absolutePath: string): Promise => { return this.searchInFile(absolutePath, contentPattern, fileEncoding, args.maxResults && (args.maxResults - result.numMatches), args.previewOptions).then(fileResult => { // Finish early if search is canceled if (this.isCanceled) { @@ -113,7 +112,7 @@ export class SearchWorkerEngine { }, onError); }; - TPromise.join(args.absolutePaths.map(startSearchInFile)).then(() => { + Promise.all(args.absolutePaths.map(startSearchInFile)).then(() => { batchDone(result); }); }); @@ -123,7 +122,7 @@ export class SearchWorkerEngine { this.isCanceled = true; } - private searchInFile(absolutePath: string, contentPattern: RegExp, fileEncoding: string, maxResults?: number, previewOptions?: ITextSearchPreviewOptions): TPromise { + private searchInFile(absolutePath: string, contentPattern: RegExp, fileEncoding: string, maxResults?: number, previewOptions?: ITextSearchPreviewOptions): Promise { let fileMatch: FileMatch | null = null; let limitReached = false; let numMatches = 0; @@ -137,7 +136,7 @@ export class SearchWorkerEngine { fileMatch = new FileMatch(absolutePath); } - const lineMatch = new TextSearchResult(line, new Range(lineNumber, match.index, lineNumber, match.index + match[0].length), previewOptions); + const lineMatch = new TextSearchMatch(line, new Range(lineNumber, match.index, lineNumber, match.index + match[0].length), previewOptions); fileMatch.addMatch(lineMatch); numMatches++; @@ -154,8 +153,8 @@ export class SearchWorkerEngine { () => fileMatch ? { match: fileMatch, limitReached, numMatches } : null); } - private readlinesAsync(filename: string, perLineCallback: (line: string, lineNumber: number) => void, options: ReadLinesOptions): TPromise { - return new TPromise((resolve, reject) => { + private readlinesAsync(filename: string, perLineCallback: (line: string, lineNumber: number) => void, options: ReadLinesOptions): Promise { + return new Promise((resolve, reject) => { fs.open(filename, 'r', null, (error: Error, fd: number) => { if (error) { return resolve(null); diff --git a/src/vs/workbench/services/search/node/worker/searchWorkerApp.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts similarity index 100% rename from src/vs/workbench/services/search/node/worker/searchWorkerApp.ts rename to src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts diff --git a/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts similarity index 70% rename from src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts rename to src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts index 46ba13a283f..d082554ea2f 100644 --- a/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts +++ b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/node/ipc'; -import { ISerializedFileMatch } from '../search'; import { IPatternInfo, ITextSearchPreviewOptions } from 'vs/platform/search/common/search'; import { SearchWorker } from './searchWorker'; import { Event } from 'vs/base/common/event'; +import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search'; export interface ISearchWorkerSearchArgs { pattern: IPatternInfo; @@ -25,16 +24,16 @@ export interface ISearchWorkerSearchResult { } export interface ISearchWorker { - initialize(): TPromise; - search(args: ISearchWorkerSearchArgs): TPromise; - cancel(): TPromise; + initialize(): Promise; + search(args: ISearchWorkerSearchArgs): Promise; + cancel(): Promise; } export interface ISearchWorkerChannel extends IChannel { - call(command: 'initialize'): TPromise; - call(command: 'search', args: ISearchWorkerSearchArgs): TPromise; - call(command: 'cancel'): TPromise; - call(command: string, arg?: any): TPromise; + call(command: 'initialize'): Promise; + call(command: 'search', args: ISearchWorkerSearchArgs): Promise; + call(command: 'cancel'): Promise; + call(command: string, arg?: any): Promise; } export class SearchWorkerChannel implements ISearchWorkerChannel { @@ -45,28 +44,28 @@ export class SearchWorkerChannel implements ISearchWorkerChannel { throw new Error('No events'); } - call(command: string, arg?: any): TPromise { + call(command: string, arg?: any): Promise { switch (command) { case 'initialize': return this.worker.initialize(); case 'search': return this.worker.search(arg); case 'cancel': return this.worker.cancel(); } - return undefined; + throw new Error(`Call not found: ${command}`); } } export class SearchWorkerChannelClient implements ISearchWorker { constructor(private channel: ISearchWorkerChannel) { } - initialize(): TPromise { + initialize(): Promise { return this.channel.call('initialize'); } - search(args: ISearchWorkerSearchArgs): TPromise { + search(args: ISearchWorkerSearchArgs): Promise { return this.channel.call('search', args); } - cancel(): TPromise { + cancel(): Promise { return this.channel.call('cancel'); } } diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 2d3cf2bec6f..d8c2a7eacce 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -14,16 +14,14 @@ import { Emitter, Event } from 'vs/base/common/event'; import * as objects from 'vs/base/common/objects'; import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { MAX_FILE_SIZE } from 'vs/platform/files/node/files'; -import { ICachedSearchStats, IFileSearchStats, IProgress } from 'vs/platform/search/common/search'; -import { Engine as FileSearchEngine, FileWalker } from 'vs/workbench/services/search/node/fileSearch'; +import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery } from 'vs/platform/search/common/search'; +import { Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; +import { LegacyTextSearchService } from 'vs/workbench/services/search/node/legacy/rawLegacyTextSearchService'; import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; -import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch'; -import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider'; -import { IFileSearchProgressItem, IRawFileMatch, IRawSearch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from './search'; -import { BatchedCollector } from 'vs/workbench/services/search/node/textSearchManager'; +import { IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from './search'; gracefulFs.gracefulify(fs); @@ -34,17 +32,17 @@ export class SearchService implements IRawSearchService { private static readonly BATCH_SIZE = 512; + private legacyTextSearchService = new LegacyTextSearchService(); private caches: { [cacheKey: string]: Cache; } = Object.create(null); - private textSearchWorkerProvider: TextSearchWorkerProvider; - - public fileSearch(config: IRawSearch, batchSize = SearchService.BATCH_SIZE): Event { + public fileSearch(config: IRawFileQuery): Event { let promise: CancelablePromise; + const query = reviveQuery(config); const emitter = new Emitter({ onFirstListenerDidAdd: () => { promise = createCancelablePromise(token => { - return this.doFileSearch(FileSearchEngine, config, p => emitter.fire(p), token, batchSize); + return this.doFileSearchWithEngine(FileSearchEngine, query, p => emitter.fire(p), token); }); promise.then( @@ -59,13 +57,16 @@ export class SearchService implements IRawSearchService { return emitter.event; } - public textSearch(config: IRawSearch): Event { + public textSearch(rawQuery: IRawTextQuery): Event { let promise: CancelablePromise; + const query = reviveQuery(rawQuery); const emitter = new Emitter({ onFirstListenerDidAdd: () => { promise = createCancelablePromise(token => { - return (config.useRipgrep ? this.ripgrepTextSearch(config, p => emitter.fire(p), token) : this.legacyTextSearch(config, p => emitter.fire(p), token)); + return (rawQuery.useRipgrep ? + this.ripgrepTextSearch(query, p => emitter.fire(p), token) : + this.legacyTextSearchService.textSearch(query, p => emitter.fire(p), token)); }); promise.then( @@ -80,43 +81,18 @@ export class SearchService implements IRawSearchService { return emitter.event; } - private ripgrepTextSearch(config: IRawSearch, progressCallback: IProgressCallback, token: CancellationToken): Promise { - config.maxFilesize = MAX_FILE_SIZE; + private ripgrepTextSearch(config: ITextQuery, progressCallback: IProgressCallback, token: CancellationToken): Promise { + config.maxFileSize = MAX_FILE_SIZE; const engine = new TextSearchEngineAdapter(config); - return new Promise((c, e) => { - engine.search(token, progressCallback, progressCallback, (error, stats) => { - if (error) { - e(error); - } else { - c(stats); - } - }); - }); + return engine.search(token, progressCallback, progressCallback); } - private legacyTextSearch(config: IRawSearch, progressCallback: IProgressCallback, token: CancellationToken): Promise { - if (!this.textSearchWorkerProvider) { - this.textSearchWorkerProvider = new TextSearchWorkerProvider(); - } - - let engine = new TextSearchEngine( - config, - new FileWalker({ - folderQueries: config.folderQueries, - extraFiles: config.extraFiles, - includePattern: config.includePattern, - excludePattern: config.excludePattern, - filePattern: config.filePattern, - useRipgrep: false, - maxFilesize: MAX_FILE_SIZE - }), - this.textSearchWorkerProvider); - - return this.doTextSearch(engine, progressCallback, SearchService.BATCH_SIZE, token); + doFileSearch(config: IFileQuery, progressCallback: IProgressCallback, token?: CancellationToken): Promise { + return this.doFileSearchWithEngine(FileSearchEngine, config, progressCallback, token); } - doFileSearch(EngineClass: { new(config: IRawSearch): ISearchEngine; }, config: IRawSearch, progressCallback: IProgressCallback, token?: CancellationToken, batchSize?: number): TPromise { + doFileSearchWithEngine(EngineClass: { new(config: IFileQuery): ISearchEngine; }, config: IFileQuery, progressCallback: IProgressCallback, token?: CancellationToken, batchSize = SearchService.BATCH_SIZE): Promise { let resultCount = 0; const fileProgressCallback: IFileProgressCallback = progress => { if (Array.isArray(progress)) { @@ -138,7 +114,7 @@ export class SearchService implements IRawSearchService { sortedSearch = this.doSortedSearch(engine, config, progressCallback, fileProgressCallback, token); } - return new TPromise((c, e) => { + return new Promise((c, e) => { sortedSearch.then(([result, rawMatches]) => { const serializedMatches = rawMatches.map(rawMatch => this.rawMatchToSearchItem(rawMatch)); this.sendProgress(serializedMatches, progressCallback, batchSize); @@ -168,7 +144,7 @@ export class SearchService implements IRawSearchService { return { path: match.base ? join(match.base, match.relativePath) : match.relativePath }; } - private doSortedSearch(engine: ISearchEngine, config: IRawSearch, progressCallback: IProgressCallback, fileProgressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]> { + private doSortedSearch(engine: ISearchEngine, config: IFileQuery, progressCallback: IProgressCallback, fileProgressCallback: IFileProgressCallback, token?: CancellationToken): Promise<[ISerializedSearchSuccess, IRawFileMatch[]]> { const emitter = new Emitter(); let allResultsPromise = createCancelablePromise(token => { @@ -207,31 +183,29 @@ export class SearchService implements IRawSearchService { allResultsPromise = this.preventCancellation(allResultsPromise); } - return TPromise.wrap<[ISerializedSearchSuccess, IRawFileMatch[]]>( - allResultsPromise.then(([result, results]) => { - const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); - const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create(false); - return this.sortResults(config, results, scorerCache, token) - .then<[ISerializedSearchSuccess, IRawFileMatch[]]>(sortedResults => { - // sortingTime: -1 indicates a "sorted" search that was not sorted, i.e. populating the cache when quickopen is opened. - // Contrasting with findFiles which is not sorted and will have sortingTime: undefined - const sortingTime = sortSW ? sortSW.elapsed() : -1; + return allResultsPromise.then(([result, results]) => { + const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); + const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create(false); + return this.sortResults(config, results, scorerCache, token) + .then<[ISerializedSearchSuccess, IRawFileMatch[]]>(sortedResults => { + // sortingTime: -1 indicates a "sorted" search that was not sorted, i.e. populating the cache when quickopen is opened. + // Contrasting with findFiles which is not sorted and will have sortingTime: undefined + const sortingTime = sortSW ? sortSW.elapsed() : -1; - return [{ - type: 'success', - stats: { - detailStats: result.stats, - sortingTime, - fromCache: false, - type: 'searchProcess', - workspaceFolderCount: config.folderQueries.length, - resultCount: sortedResults.length - }, - limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults - } as ISerializedSearchSuccess, sortedResults]; - }); - }) - ); + return [{ + type: 'success', + stats: { + detailStats: result.stats, + sortingTime, + fromCache: false, + type: 'searchProcess', + workspaceFolderCount: config.folderQueries.length, + resultCount: sortedResults.length + }, + limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults + } as ISerializedSearchSuccess, sortedResults]; + }); + }); } private getOrCreateCache(cacheKey: string): Cache { @@ -242,7 +216,7 @@ export class SearchService implements IRawSearchService { return this.caches[cacheKey] = new Cache(); } - private trySortedSearchFromCache(config: IRawSearch, progressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]> { + private trySortedSearchFromCache(config: IFileQuery, progressCallback: IFileProgressCallback, token?: CancellationToken): Promise<[ISerializedSearchSuccess, IRawFileMatch[]]> { const cache = config.cacheKey && this.caches[config.cacheKey]; if (!cache) { return undefined; @@ -277,7 +251,7 @@ export class SearchService implements IRawSearchService { return undefined; } - private sortResults(config: IRawSearch, results: IRawFileMatch[], scorerCache: ScorerCache, token?: CancellationToken): TPromise { + private sortResults(config: IFileQuery, results: IRawFileMatch[], scorerCache: ScorerCache, token?: CancellationToken): Promise { // we use the same compare function that is used later when showing the results using fuzzy scoring // this is very important because we are also limiting the number of results by config.maxResults // and as such we want the top items to be included in this result set if the number of items @@ -298,7 +272,7 @@ export class SearchService implements IRawSearchService { } } - private getResultsFromCache(cache: Cache, searchValue: string, progressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]> { + private getResultsFromCache(cache: Cache, searchValue: string, progressCallback: IFileProgressCallback, token?: CancellationToken): Promise<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]> { const cacheLookupSW = StopWatch.create(false); // Find cache entries by prefix of search value @@ -335,7 +309,7 @@ export class SearchService implements IRawSearchService { }); } - return TPromise.wrap(cachedRow.promise.then<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]>(([complete, cachedEntries]) => { + return cachedRow.promise.then<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]>(([complete, cachedEntries]) => { if (token && token.isCancellationRequested) { throw canceled(); } @@ -360,38 +334,13 @@ export class SearchService implements IRawSearchService { cacheFilterTime: cacheFilterSW.elapsed(), cacheEntryCount: cachedEntries.length }]; - })); - } - - private doTextSearch(engine: TextSearchEngine, progressCallback: IProgressCallback, batchSize: number, token: CancellationToken): Promise { - token.onCancellationRequested(() => engine.cancel()); - - return new Promise((c, e) => { - // Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned - const collector = new BatchedCollector(batchSize, progressCallback); - engine.search((matches) => { - const totalMatches = matches.reduce((acc, m) => acc + m.numMatches, 0); - collector.addItems(matches, totalMatches); - }, (progress) => { - progressCallback(progress); - }, (error, stats) => { - collector.flush(); - - if (error) { - e(error); - } else { - c({ - type: 'success', - limitHit: stats.limitHit, - stats: null - }); - } - }); }); } - private doSearch(engine: ISearchEngine, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): TPromise { - return new TPromise((c, e) => { + + + private doSearch(engine: ISearchEngine, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): Promise { + return new Promise((c, e) => { let batch: IRawFileMatch[] = []; if (token) { token.onCancellationRequested(() => engine.cancel()); @@ -425,9 +374,9 @@ export class SearchService implements IRawSearchService { }); } - public clearCache(cacheKey: string): TPromise { + public clearCache(cacheKey: string): Promise { delete this.caches[cacheKey]; - return TPromise.as(undefined); + return Promise.resolve(undefined); } /** @@ -477,3 +426,20 @@ const FileMatchItemAccessor = new class implements IItemAccessor return match.relativePath; // e.g. some/path/to/file/myFile.txt } }; + +function reviveQuery(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery { + return { + ...rawQuery, // TODO + ...{ + folderQueries: rawQuery.folderQueries && rawQuery.folderQueries.map(reviveFolderQuery), + extraFileResources: rawQuery.extraFileResources && rawQuery.extraFileResources.map(components => URI.revive(components)) + } + }; +} + +function reviveFolderQuery(rawFolderQuery: IFolderQuery): IFolderQuery { + return { + ...rawFolderQuery, + folder: URI.revive(rawFolderQuery.folder) + }; +} diff --git a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts index 1930112bca5..950b35ccb8a 100644 --- a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts @@ -11,16 +11,16 @@ import * as objects from 'vs/base/common/objects'; import * as paths from 'vs/base/common/paths'; import { isMacintosh as isMac } from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; -import { rgPath } from 'vscode-ripgrep'; -import { IFolderSearch, IRawSearch } from './search'; +import { IFileQuery, IFolderQuery } from 'vs/platform/search/common/search'; import { anchorGlob } from 'vs/workbench/services/search/node/ripgrepSearchUtils'; +import { rgPath } from 'vscode-ripgrep'; // If vscode-ripgrep is in an .asar file, then the binary is unpacked. const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked'); -export function spawnRipgrepCmd(config: IRawSearch, folderQuery: IFolderSearch, includePattern: glob.IExpression, excludePattern: glob.IExpression) { +export function spawnRipgrepCmd(config: IFileQuery, folderQuery: IFolderQuery, includePattern: glob.IExpression, excludePattern: glob.IExpression) { const rgArgs = getRgArgs(config, folderQuery, includePattern, excludePattern); - const cwd = folderQuery.folder; + const cwd = folderQuery.folder.fsPath; return { cmd: cp.spawn(rgDiskPath, rgArgs.args, { cwd }), siblingClauses: rgArgs.siblingClauses, @@ -29,7 +29,7 @@ export function spawnRipgrepCmd(config: IRawSearch, folderQuery: IFolderSearch, }; } -function getRgArgs(config: IRawSearch, folderQuery: IFolderSearch, includePattern: glob.IExpression, excludePattern: glob.IExpression) { +function getRgArgs(config: IFileQuery, folderQuery: IFolderQuery, includePattern: glob.IExpression, excludePattern: glob.IExpression) { const args = ['--files', '--hidden', '--case-sensitive']; // includePattern can't have siblingClauses @@ -67,7 +67,7 @@ function getRgArgs(config: IRawSearch, folderQuery: IFolderSearch, includePatter } // Follow symlinks - if (!config.ignoreSymlinks) { + if (!folderQuery.ignoreSymlinks) { args.push('--follow'); } @@ -76,7 +76,7 @@ function getRgArgs(config: IRawSearch, folderQuery: IFolderSearch, includePatter } args.push('--no-config'); - if (config.disregardGlobalIgnoreFiles) { + if (folderQuery.disregardGlobalIgnoreFiles) { args.push('--no-ignore-global'); } @@ -88,12 +88,12 @@ export interface IRgGlobResult { siblingClauses: glob.IExpression; } -export function foldersToRgExcludeGlobs(folderQueries: IFolderSearch[], globalExclude: glob.IExpression, excludesToSkip?: Set, absoluteGlobs = true): IRgGlobResult { +export function foldersToRgExcludeGlobs(folderQueries: IFolderQuery[], globalExclude: glob.IExpression, excludesToSkip?: Set, absoluteGlobs = true): IRgGlobResult { const globArgs: string[] = []; let siblingClauses: glob.IExpression = {}; folderQueries.forEach(folderQuery => { const totalExcludePattern = objects.assign({}, folderQuery.excludePattern || {}, globalExclude || {}); - const result = globExprsToRgGlobs(totalExcludePattern, absoluteGlobs && folderQuery.folder, excludesToSkip); + const result = globExprsToRgGlobs(totalExcludePattern, absoluteGlobs && folderQuery.folder.fsPath, excludesToSkip); globArgs.push(...result.globArgs); if (result.siblingClauses) { siblingClauses = objects.assign(siblingClauses, result.siblingClauses); @@ -103,11 +103,11 @@ export function foldersToRgExcludeGlobs(folderQueries: IFolderSearch[], globalEx return { globArgs, siblingClauses }; } -export function foldersToIncludeGlobs(folderQueries: IFolderSearch[], globalInclude: glob.IExpression, absoluteGlobs = true): string[] { +export function foldersToIncludeGlobs(folderQueries: IFolderQuery[], globalInclude: glob.IExpression, absoluteGlobs = true): string[] { const globArgs: string[] = []; folderQueries.forEach(folderQuery => { const totalIncludePattern = objects.assign({}, globalInclude || {}, folderQuery.includePattern || {}); - const result = globExprsToRgGlobs(totalIncludePattern, absoluteGlobs && folderQuery.folder); + const result = globExprsToRgGlobs(totalIncludePattern, absoluteGlobs && folderQuery.folder.fsPath); globArgs.push(...result.globArgs); }); diff --git a/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts b/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts index 601d679d551..9b2bad635c2 100644 --- a/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts +++ b/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts @@ -5,8 +5,9 @@ import { startsWith } from 'vs/base/common/strings'; import { ILogService } from 'vs/platform/log/common/log'; -import { SearchRange, TextSearchResult } from 'vs/platform/search/common/search'; +import { SearchRange, TextSearchMatch } from 'vs/platform/search/common/search'; import * as vscode from 'vscode'; +import { mapArrayOrNot } from 'vs/base/common/arrays'; export type Maybe = T | null | undefined; @@ -14,26 +15,32 @@ export function anchorGlob(glob: string): string { return startsWith(glob, '**') || startsWith(glob, '/') ? glob : `/${glob}`; } -export function createTextSearchResult(uri: vscode.Uri, text: string, range: Range, previewOptions?: vscode.TextSearchPreviewOptions): vscode.TextSearchResult { - const searchRange: SearchRange = { - startLineNumber: range.start.line, - startColumn: range.start.character, - endLineNumber: range.end.line, - endColumn: range.end.character, - }; +/** + * Create a vscode.TextSearchResult by using our internal TextSearchResult type for its previewOptions logic. + */ +export function createTextSearchResult(uri: vscode.Uri, text: string, range: Range | Range[], previewOptions?: vscode.TextSearchPreviewOptions): vscode.TextSearchMatch { + const searchRange = mapArrayOrNot(range, rangeToSearchRange); - const internalResult = new TextSearchResult(text, searchRange, previewOptions); - const internalPreviewRange = internalResult.preview.match; + const internalResult = new TextSearchMatch(text, searchRange, previewOptions); + const internalPreviewRange = internalResult.preview.matches; return { - range: new Range(internalResult.range.startLineNumber, internalResult.range.startColumn, internalResult.range.endLineNumber, internalResult.range.endColumn), + ranges: mapArrayOrNot(searchRange, searchRangeToRange), uri, preview: { text: internalResult.preview.text, - match: new Range(internalPreviewRange.startLineNumber, internalPreviewRange.startColumn, internalPreviewRange.endLineNumber, internalPreviewRange.endColumn), + matches: mapArrayOrNot(internalPreviewRange, searchRangeToRange) } }; } +function rangeToSearchRange(range: Range): SearchRange { + return new SearchRange(range.start.line, range.start.character, range.end.line, range.end.character); +} + +function searchRangeToRange(range: SearchRange): Range { + return new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); +} + export class Position { constructor(public readonly line, public readonly character) { } @@ -45,10 +52,10 @@ export class Position { compareTo(other: Position): number { return 0; } translate(lineDelta?: number, characterDelta?: number): Position; translate(change: { lineDelta?: number; characterDelta?: number; }): Position; - translate(_: any) { return null; } + translate(_?: any, _2?: any): Position { return new Position(0, 0); } with(line?: number, character?: number): Position; with(change: { line?: number; character?: number; }): Position; - with(_: any): Position { return null; } + with(_: any): Position { return new Position(0, 0); } } export class Range { @@ -64,12 +71,12 @@ export class Range { isSingleLine: boolean; contains(positionOrRange: Position | Range): boolean { return false; } isEqual(other: Range): boolean { return false; } - intersection(range: Range): Range | undefined { return null; } - union(other: Range): Range { return null; } + intersection(range: Range): Range | undefined { return undefined; } + union(other: Range): Range { return new Range(0, 0, 0, 0); } with(start?: Position, end?: Position): Range; with(change: { start?: Position, end?: Position }): Range; - with(_: any): Range { return null; } + with(_: any): Range { return new Range(0, 0, 0, 0); } } export interface IOutputChannel { diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts index 87cc6a75193..b97bb917dfa 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts @@ -7,8 +7,9 @@ import * as cp from 'child_process'; import { EventEmitter } from 'events'; import * as path from 'path'; import { NodeStringDecoder, StringDecoder } from 'string_decoder'; -import { startsWith } from 'vs/base/common/strings'; +import { createRegExp, startsWith, startsWithUTF8BOM, stripUTF8BOM } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; +import { IExtendedExtensionSearchOptions, SearchError, SearchErrorCode, serializeSearchError } from 'vs/platform/search/common/search'; import * as vscode from 'vscode'; import { rgPath } from 'vscode-ripgrep'; import { anchorGlob, createTextSearchResult, IOutputChannel, Maybe, Range } from './ripgrepSearchUtils'; @@ -44,7 +45,7 @@ export class RipgrepTextSearchEngine { rgProc.on('error', e => { console.error(e); this.outputChannel.appendLine('Error: ' + (e && e.message)); - reject(e); + reject(serializeSearchError(new SearchError(e && e.message, SearchErrorCode.rgProcessError))); }); let gotResult = false; @@ -97,9 +98,9 @@ export class RipgrepTextSearchEngine { // Trigger last result ripgrepParser.flush(); rgProc = null; - let displayMsg: Maybe; - if (stderr && !gotData && (displayMsg = rgErrorMsgForDisplay(stderr))) { - reject(new Error(displayMsg)); + let searchError: Maybe; + if (stderr && !gotData && (searchError = rgErrorMsgForDisplay(stderr))) { + reject(serializeSearchError(new SearchError(searchError.message, searchError.code))); } else { resolve({ limitHit }); } @@ -114,27 +115,26 @@ export class RipgrepTextSearchEngine { * Ripgrep produces stderr output which is not from a fatal error, and we only want the search to be * "failed" when a fatal error was produced. */ -export function rgErrorMsgForDisplay(msg: string): Maybe { +export function rgErrorMsgForDisplay(msg: string): Maybe { const firstLine = msg.split('\n')[0].trim(); - if (startsWith(firstLine, 'Error parsing regex')) { - return firstLine; + if (startsWith(firstLine, 'regex parse error')) { + return new SearchError('Regex parse error', SearchErrorCode.regexParseError); } - if (startsWith(firstLine, 'error parsing glob') || - startsWith(firstLine, 'unsupported encoding')) { + let match = firstLine.match(/grep config error: unknown encoding: (.*)/); + if (match) { + return new SearchError(`Unknown encoding: ${match[1]}`, SearchErrorCode.unknownEncoding); + } + + if (startsWith(firstLine, 'error parsing glob')) { // Uppercase first letter - return firstLine.charAt(0).toUpperCase() + firstLine.substr(1); + return new SearchError(firstLine.charAt(0).toUpperCase() + firstLine.substr(1), SearchErrorCode.globParseError); } - if (firstLine === `Literal '\\n' not allowed.`) { - // I won't localize this because none of the Ripgrep error messages are localized - return `Literal '\\n' currently not supported`; - } - - if (startsWith(firstLine, 'Literal ')) { - // Other unsupported chars - return firstLine; + if (startsWith(firstLine, 'the literal')) { + // Uppercase first letter + return new SearchError(firstLine.charAt(0).toUpperCase() + firstLine.substr(1), SearchErrorCode.invalidLiteral); } return undefined; @@ -143,6 +143,7 @@ export function rgErrorMsgForDisplay(msg: string): Maybe { export class RipgrepParser extends EventEmitter { private remainder = ''; private isDone = false; + private hitLimit = false; private stringDecoder: NodeStringDecoder; private numResults = 0; @@ -187,7 +188,7 @@ export class RipgrepParser extends EventEmitter { return; } - let parsedLine: any; + let parsedLine: IRgMessage; try { parsedLine = JSON.parse(outputLine); } catch (e) { @@ -195,49 +196,83 @@ export class RipgrepParser extends EventEmitter { } if (parsedLine.type === 'match') { - let hitLimit = false; - const uri = URI.file(path.join(this.rootFolder, parsedLine.data.path.text)); - parsedLine.data.submatches.map((match: any) => { - if (hitLimit) { - return null; - } + const matchPath = bytesOrTextToString(parsedLine.data.path); + const uri = URI.file(path.join(this.rootFolder, matchPath)); + const result = this.createTextSearchMatch(parsedLine.data, uri); + this.onResult(result); - if (this.numResults >= this.maxResults) { - // Finish the line, then report the result below - hitLimit = true; - } - - return this.submatchToResult(parsedLine, match, uri); - }).forEach((result: any) => { - if (result) { - this.onResult(result); - } - }); - - if (hitLimit) { + if (this.hitLimit) { this.cancel(); this.emit('hitLimit'); } + } else if (parsedLine.type === 'context') { + const contextPath = bytesOrTextToString(parsedLine.data.path); + const uri = URI.file(path.join(this.rootFolder, contextPath)); + const result = this.createTextSearchContext(parsedLine.data, uri); + result.forEach(r => this.onResult(r)); } } - private submatchToResult(parsedLine: any, match: any, uri: vscode.Uri): vscode.TextSearchResult { - const lineNumber = parsedLine.data.line_number - 1; - let matchText = parsedLine.data.lines.bytes ? - new Buffer(parsedLine.data.lines.bytes, 'base64').toString() : - parsedLine.data.lines.text; - let start = match.start; - let end = match.end; - if (lineNumber === 0) { - if (startsWithUTF8BOM(matchText)) { - matchText = stripUTF8BOM(matchText); - start -= 3; - end -= 3; - } - } + private createTextSearchMatch(data: IRgMatch, uri: vscode.Uri): vscode.TextSearchMatch { + const lineNumber = data.line_number - 1; + const fullText = bytesOrTextToString(data.lines); + const fullTextBytes = Buffer.from(fullText); - const range = new Range(lineNumber, start, lineNumber, end); - return createTextSearchResult(uri, matchText, range, this.previewOptions); + let prevMatchEnd = 0; + let prevMatchEndCol = 0; + let prevMatchEndLine = lineNumber; + const ranges = data.submatches.map((match, i) => { + if (this.hitLimit) { + return null; + } + + this.numResults++; + if (this.numResults >= this.maxResults) { + // Finish the line, then report the result below + this.hitLimit = true; + } + + let matchText = bytesOrTextToString(match.match); + const inBetweenChars = fullTextBytes.slice(prevMatchEnd, match.start).toString().length; + let startCol = prevMatchEndCol + inBetweenChars; + + const stats = getNumLinesAndLastNewlineLength(matchText); + let startLineNumber = prevMatchEndLine; + let endLineNumber = stats.numLines + startLineNumber; + let endCol = stats.numLines > 0 ? + stats.lastLineLength : + stats.lastLineLength + startCol; + + if (lineNumber === 0 && i === 0 && startsWithUTF8BOM(matchText)) { + matchText = stripUTF8BOM(matchText); + startCol -= 3; + endCol -= 3; + } + + prevMatchEnd = match.end; + prevMatchEndCol = endCol; + prevMatchEndLine = endLineNumber; + + return new Range(startLineNumber, startCol, endLineNumber, endCol); + }) + .filter(r => !!r); + + return createTextSearchResult(uri, fullText, ranges, this.previewOptions); + } + + private createTextSearchContext(data: IRgMatch, uri: URI): vscode.TextSearchContext[] { + const text = bytesOrTextToString(data.lines); + const startLine = data.line_number; + return text + .replace(/\r?\n$/, '') + .split('\n') + .map((line, i) => { + return { + text: line, + uri, + lineNumber: startLine + i + }; + }); } private onResult(match: vscode.TextSearchResult): void { @@ -245,8 +280,31 @@ export class RipgrepParser extends EventEmitter { } } +function bytesOrTextToString(obj: any): string { + return obj.bytes ? + Buffer.from(obj.bytes, 'base64').toString() : + obj.text; +} + +function getNumLinesAndLastNewlineLength(text: string): { numLines: number, lastLineLength: number } { + const re = /\n/g; + let numLines = 0; + let lastNewlineIdx = -1; + let match: ReturnType; + while (match = re.exec(text)) { + numLines++; + lastNewlineIdx = match.index; + } + + const lastLineLength = lastNewlineIdx >= 0 ? + text.length - lastNewlineIdx - 1 : + text.length; + + return { numLines, lastLineLength }; +} + function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions): string[] { - const args = ['--hidden', '--heading', '--line-number', '--color', 'ansi', '--colors', 'path:none', '--colors', 'line:none', '--colors', 'match:fg:red', '--colors', 'match:style:nobold']; + const args = ['--hidden']; args.push(query.isCaseSensitive ? '--case-sensitive' : '--ignore-case'); options.includes @@ -272,26 +330,36 @@ function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOpti args.push('--follow'); } - if (options.encoding) { + if (options.encoding && options.encoding !== 'utf8') { args.push('--encoding', options.encoding); } + let pattern = query.pattern; + // Ripgrep handles -- as a -- arg separator. Only --. - // - is ok, --- is ok, --some-flag is handled as query text. Need to special case. - if (query.pattern === '--') { + // - is ok, --- is ok, --some-flag is also ok. Need to special case. + if (pattern === '--') { query.isRegExp = true; - query.pattern = '\\-\\-'; + pattern = '\\-\\-'; + } + + if ((options).usePCRE2) { + args.push('--pcre2'); + + if (query.isRegExp) { + pattern = unicodeEscapesToPCRE2(pattern); + } } let searchPatternAfterDoubleDashes: Maybe; if (query.isWordMatch) { - const regexp = createRegExp(query.pattern, !!query.isRegExp, { wholeWord: query.isWordMatch }); + const regexp = createRegExp(pattern, !!query.isRegExp, { wholeWord: query.isWordMatch }); const regexpStr = regexp.source.replace(/\\\//g, '/'); // RegExp.source arbitrarily returns escaped slashes. Search and destroy. args.push('--regexp', regexpStr); } else if (query.isRegExp) { args.push('--regexp', fixRegexEndingPattern(query.pattern)); } else { - searchPatternAfterDoubleDashes = query.pattern; + searchPatternAfterDoubleDashes = pattern; args.push('--fixed-strings'); } @@ -306,6 +374,14 @@ function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOpti args.push('--multiline'); } + if (options.beforeContext) { + args.push('--before-context', options.beforeContext + ''); + } + + if (options.afterContext) { + args.push('--after-context', options.afterContext + ''); + } + // Folder to search args.push('--'); @@ -319,60 +395,37 @@ function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOpti return args; } -interface RegExpOptions { - matchCase?: boolean; - wholeWord?: boolean; - multiline?: boolean; - global?: boolean; +export function unicodeEscapesToPCRE2(pattern: string): string { + const reg = /((?:[^\\]|^)(?:\\\\)*)\\u([a-z0-9]{4})(?!\d)/g; + // Replace an unescaped $ at the end of the pattern with \r?$ + // Match $ preceeded by none or even number of literal \ + while (pattern.match(reg)) { + pattern = pattern.replace(reg, `$1\\x{$2}`); + } + + return pattern; } -function createRegExp(searchString: string, isRegex: boolean, options: RegExpOptions = {}): RegExp { - if (!searchString) { - throw new Error('Cannot create regex from empty string'); - } - if (!isRegex) { - searchString = escapeRegExpCharacters(searchString); - } - if (options.wholeWord) { - if (!/\B/.test(searchString.charAt(0))) { - searchString = '\\b' + searchString; - } - if (!/\B/.test(searchString.charAt(searchString.length - 1))) { - searchString = searchString + '\\b'; - } - } - let modifiers = ''; - if (options.global) { - modifiers += 'g'; - } - if (!options.matchCase) { - modifiers += 'i'; - } - if (options.multiline) { - modifiers += 'm'; - } - - return new RegExp(searchString, modifiers); +interface IRgMessage { + type: 'match' | 'context' | string; + data: IRgMatch; } -/** - * Escapes regular expression characters in a given string - */ -function escapeRegExpCharacters(value: string): string { - return value.replace(/[\-\\\{\}\*\+\?\|\^\$\.\[\]\(\)\#]/g, '\\$&'); +interface IRgMatch { + path: IRgBytesOrText; + lines: IRgBytesOrText; + line_number: number; + absolute_offset: number; + submatches: IRgSubmatch[]; } -// -- UTF-8 BOM - -const UTF8_BOM = 65279; - -function startsWithUTF8BOM(str: string): boolean { - return !!(str && str.length > 0 && str.charCodeAt(0) === UTF8_BOM); +interface IRgSubmatch { + match: IRgBytesOrText; + start: number; + end: number; } -function stripUTF8BOM(str: string): string { - return startsWithUTF8BOM(str) ? str.substr(1) : str; -} +type IRgBytesOrText = { bytes: string } | { text: string }; export function fixRegexEndingPattern(pattern: string): string { // Replace an unescaped $ at the end of the pattern with \r?$ @@ -380,4 +433,24 @@ export function fixRegexEndingPattern(pattern: string): string { return pattern.match(/([^\\]|^)(\\\\)*\$$/) ? pattern.replace(/\$$/, '\\r?$') : pattern; -} \ No newline at end of file +} + +export function fixRegexCRMatchingWhitespaceClass(pattern: string, isMultiline: boolean): string { + return isMultiline ? + pattern.replace(/([^\\]|^)((?:\\\\)*)\\s/, (prefix1, prefix2) => { + return prefix1 + prefix2 + '(\\r?\\n|[^\\S\\r])'; + }) : + pattern.replace(/([^\\]|^)((?:\\\\)*)\\s/, (prefix1, prefix2) => { + return prefix1 + prefix2 + '[ \\t\\f]'; + }); +} + +export function fixRegexCRMatchingNonWordClass(pattern: string, isMultiline: boolean): string { + return isMultiline ? + pattern.replace(/([^\\]|^)((?:\\\\)*)\\W/, (prefix1, prefix2) => { + return prefix1 + prefix2 + '(\\r?\\n|[^\\w\\r])'; + }) : + pattern.replace(/([^\\]|^)((?:\\\\)*)\\W/, (prefix1, prefix2) => { + return prefix1 + prefix2 + '[^\\w\\r]'; + }); +} diff --git a/src/vs/workbench/services/search/node/search.ts b/src/vs/workbench/services/search/node/search.ts index ca4103d568a..22cd4800928 100644 --- a/src/vs/workbench/services/search/node/search.ts +++ b/src/vs/workbench/services/search/node/search.ts @@ -5,47 +5,18 @@ import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IFileSearchStats, IPatternInfo, IProgress, ISearchEngineStats, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, ISearchQuery, IFolderQuery } from 'vs/platform/search/common/search'; +import { IFileSearchStats, IFolderQuery, IProgress, IRawFileQuery, IRawTextQuery, ISearchEngineStats, ISearchQuery, ITextSearchMatch, ITextSearchStats, ITextSearchResult } from 'vs/platform/search/common/search'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; -export interface IFolderSearch { - folder: string; - excludePattern?: glob.IExpression; - includePattern?: glob.IExpression; - fileEncoding?: string; - disregardIgnoreFiles?: boolean; - disregardGlobalIgnoreFiles?: boolean; -} - -export interface IRawSearch { - folderQueries: IFolderSearch[]; - ignoreSymlinks?: boolean; - extraFiles?: string[]; - filePattern?: string; - excludePattern?: glob.IExpression; - includePattern?: glob.IExpression; - contentPattern?: IPatternInfo; - maxResults?: number; - exists?: boolean; - sortByScore?: boolean; - cacheKey?: string; - maxFilesize?: number; - useRipgrep?: boolean; - disregardIgnoreFiles?: boolean; - previewOptions?: ITextSearchPreviewOptions; - disregardGlobalIgnoreFiles?: boolean; -} - export interface ITelemetryEvent { eventName: string; data: ITelemetryData; } export interface IRawSearchService { - fileSearch(search: IRawSearch): Event; - textSearch(search: IRawSearch): Event; - clearCache(cacheKey: string): TPromise; + fileSearch(search: IRawFileQuery): Event; + textSearch(search: IRawTextQuery): Event; + clearCache(cacheKey: string): Promise; } export interface IRawFileMatch { @@ -101,7 +72,7 @@ export function isSerializedFileMatch(arg: ISerializedSearchProgressItem): arg i export interface ISerializedFileMatch { path: string; - matches?: ITextSearchResult[]; + results?: ITextSearchResult[]; numMatches?: number; } @@ -112,22 +83,22 @@ export type IFileSearchProgressItem = IRawFileMatch | IRawFileMatch[] | IProgres export class FileMatch implements ISerializedFileMatch { path: string; - matches: ITextSearchResult[]; + results: ITextSearchMatch[]; constructor(path: string) { this.path = path; - this.matches = []; + this.results = []; } - addMatch(match: ITextSearchResult): void { - this.matches.push(match); + addMatch(match: ITextSearchMatch): void { + this.results.push(match); } serialize(): ISerializedFileMatch { return { path: this.path, - matches: this.matches, - numMatches: this.matches.length + results: this.results, + numMatches: this.results.length }; } } @@ -135,7 +106,7 @@ export class FileMatch implements ISerializedFileMatch { /** * Computes the patterns that the provider handles. Discards sibling clauses and 'false' patterns */ -export function resolvePatternsForProvider(globalPattern: glob.IExpression, folderPattern: glob.IExpression): string[] { +export function resolvePatternsForProvider(globalPattern: glob.IExpression | undefined, folderPattern: glob.IExpression | undefined): string[] { const merged = { ...(globalPattern || {}), ...(folderPattern || {}) @@ -198,10 +169,10 @@ export class QueryGlobTester { /** * Guaranteed async. */ - public includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise): TPromise { + public includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | Promise): Promise { const excludeP = this._parsedExcludeExpression ? - TPromise.as(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) : - TPromise.wrap(false); + Promise.resolve(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) : + Promise.resolve(false); return excludeP.then(excluded => { if (excluded) { @@ -209,8 +180,8 @@ export class QueryGlobTester { } return this._parsedIncludeExpression ? - TPromise.as(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) : - TPromise.wrap(true); + Promise.resolve(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) : + Promise.resolve(true); }).then(included => { return included; }); diff --git a/src/vs/workbench/services/search/node/searchIpc.ts b/src/vs/workbench/services/search/node/searchIpc.ts index b1a5da50433..618f2f8c82c 100644 --- a/src/vs/workbench/services/search/node/searchIpc.ts +++ b/src/vs/workbench/services/search/node/searchIpc.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/node/ipc'; -import { IRawSearch, IRawSearchService, ISerializedSearchComplete, ISerializedSearchProgressItem } from './search'; +import { IRawFileQuery, IRawTextQuery } from 'vs/platform/search/common/search'; +import { IRawSearchService, ISerializedSearchComplete, ISerializedSearchProgressItem } from './search'; export interface ISearchChannel extends IChannel { - listen(event: 'fileSearch', search: IRawSearch): Event; - listen(event: 'textSearch', search: IRawSearch): Event; - call(command: 'clearCache', cacheKey: string): TPromise; - call(command: string, arg: any): TPromise; + listen(event: 'fileSearch', search: IRawFileQuery): Event; + listen(event: 'textSearch', search: IRawTextQuery): Event; + call(command: 'clearCache', cacheKey: string): Promise; + call(command: string, arg: any): Promise; } export class SearchChannel implements ISearchChannel { @@ -27,7 +27,7 @@ export class SearchChannel implements ISearchChannel { throw new Error('Event not found'); } - call(command: string, arg?: any): TPromise { + call(command: string, arg?: any): Promise { switch (command) { case 'clearCache': return this.service.clearCache(arg); } @@ -39,15 +39,15 @@ export class SearchChannelClient implements IRawSearchService { constructor(private channel: ISearchChannel) { } - fileSearch(search: IRawSearch): Event { + fileSearch(search: IRawFileQuery): Event { return this.channel.listen('fileSearch', search); } - textSearch(search: IRawSearch): Event { + textSearch(search: IRawTextQuery): Event { return this.channel.listen('textSearch', search); } - clearCache(cacheKey: string): TPromise { + clearCache(cacheKey: string): Promise { return this.channel.call('clearCache', cacheKey); } } \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index b8cec951e08..207fed068ad 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -9,28 +9,26 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ResourceMap, values } from 'vs/base/common/map'; +import { keys, ResourceMap, values } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import * as objects from 'vs/base/common/objects'; import { StopWatch } from 'vs/base/common/stopwatch'; -import * as strings from 'vs/base/common/strings'; import { URI as uri } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { Promise } from 'vs/base/common/winjs.base'; import * as pfs from 'vs/base/node/pfs'; import { getNextTickChannel } from 'vs/base/parts/ipc/node/ipc'; import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; -import { Range } from 'vs/editor/common/core/range'; -import { FindMatch, ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; -import { FileMatch, ICachedSearchStats, IFileMatch, IFileSearchStats, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextSearchPreviewOptions, pathIncludedInQuery, QueryType, SearchProviderType, TextSearchResult } from 'vs/platform/search/common/search'; +import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IRawSearch, IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search'; +import { IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search'; import { ISearchChannel, SearchChannelClient } from './searchIpc'; export class SearchService extends Disposable implements ISearchService { @@ -72,15 +70,9 @@ export class SearchService extends Disposable implements ISearchService { }); } - public extendQuery(query: ISearchQuery): void { + public extendQuery(query: IFileQuery): void { const configuration = this.configurationService.getValue(); - // Configuration: Encoding - if (!query.fileEncoding) { - const fileEncoding = configuration && configuration.files && configuration.files.encoding; - query.fileEncoding = fileEncoding; - } - // Configuration: File Excludes if (!query.disregardExcludeSettings) { const fileExcludes = objects.deepClone(configuration && configuration.files && configuration.files.exclude); @@ -94,7 +86,7 @@ export class SearchService extends Disposable implements ISearchService { } } - public search(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): TPromise { + public textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise { // Get local results from dirty/untitled const localResults = this.getLocalResults(query); @@ -120,21 +112,39 @@ export class SearchService extends Disposable implements ISearchService { } }; + return this.doSearch(query, token, onProviderProgress); + } + + public fileSearch(query: IFileQuery, token?: CancellationToken): Promise { + return this.doSearch(query, token); + } + + private doSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise { const schemesInQuery = this.getSchemesInQuery(query); - const providerActivations: TPromise[] = [TPromise.wrap(null)]; + const providerActivations: Promise[] = [Promise.wrap(null)]; schemesInQuery.forEach(scheme => providerActivations.push(this.extensionService.activateByEvent(`onSearch:${scheme}`))); providerActivations.push(this.extensionService.activateByEvent('onSearch:file')); - const providerPromise = TPromise.join(providerActivations) + const providerPromise = Promise.join(providerActivations) .then(() => this.extensionService.whenInstalledExtensionsRegistered()) .then(() => { // Cancel faster if search was canceled while waiting for extensions if (token && token.isCancellationRequested) { - return TPromise.wrapError(canceled()); + return Promise.wrapError(canceled()); } - return this.searchWithProviders(query, onProviderProgress, token); + const progressCallback = (item: ISearchProgressItem) => { + if (token && token.isCancellationRequested) { + return; + } + + if (onProgress) { + onProgress(item); + } + }; + + return this.searchWithProviders(query, progressCallback, token); }) .then(completes => { completes = completes.filter(c => !!c); @@ -147,52 +157,16 @@ export class SearchService extends Disposable implements ISearchService { stats: completes[0].stats, results: arrays.flatten(completes.map(c => c.results)) }; - }, errs => { - if (!Array.isArray(errs)) { - errs = [errs]; - } - - errs = errs.filter(e => !!e); - return TPromise.wrapError(errs[0]); }); - const searchP = providerPromise.then(value => { - const values = [value]; - - const result: ISearchComplete = { - limitHit: false, - results: [], - stats: undefined - }; - - // TODO@joh - // sorting, disjunct results - for (const value of values) { - if (!value) { - continue; - } - // TODO@joh individual stats/limit - result.stats = value.stats || result.stats; - result.limitHit = value.limitHit || result.limitHit; - - for (const match of value.results) { - if (!localResults.has(match.resource)) { - result.results.push(match); - } - } - } - - return result; - }); - - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { if (token) { token.onCancellationRequested(() => { reject(canceled()); }); } - searchP.then(resolve, reject); + providerPromise.then(resolve, reject); }); } @@ -213,30 +187,34 @@ export class SearchService extends Disposable implements ISearchService { const e2eSW = StopWatch.create(false); const diskSearchQueries: IFolderQuery[] = []; - const searchPs: TPromise[] = []; + const searchPs: Promise[] = []; - query.folderQueries.forEach(fq => { + const fqs = this.groupFolderQueriesByScheme(query); + keys(fqs).forEach(scheme => { + const schemeFQs = fqs.get(scheme); let provider = query.type === QueryType.File ? - this.fileSearchProviders.get(fq.folder.scheme) || this.fileIndexProviders.get(fq.folder.scheme) : - this.textSearchProviders.get(fq.folder.scheme); + this.fileSearchProviders.get(scheme) || this.fileIndexProviders.get(scheme) : + this.textSearchProviders.get(scheme); - if (!provider && fq.folder.scheme === 'file') { - diskSearchQueries.push(fq); + if (!provider && scheme === 'file') { + diskSearchQueries.push(...schemeFQs); } else if (!provider) { - throw new Error('No search provider registered for scheme: ' + fq.folder.scheme); + throw new Error('No search provider registered for scheme: ' + scheme); } else { - const oneFolderQuery = { + const oneSchemeQuery: ISearchQuery = { ...query, ...{ - folderQueries: [fq] + folderQueries: schemeFQs } }; - searchPs.push(provider.search(oneFolderQuery, onProviderProgress, token)); + searchPs.push(query.type === QueryType.File ? + provider.fileSearch(oneSchemeQuery, token) : + provider.textSearch(oneSchemeQuery, onProviderProgress, token)); } }); - const diskSearchExtraFileResources = query.extraFileResources && query.extraFileResources.filter(res => res.scheme === 'file'); + const diskSearchExtraFileResources = query.extraFileResources && query.extraFileResources.filter(res => res.scheme === Schemas.file); if (diskSearchQueries.length || diskSearchExtraFileResources) { const diskSearchQuery: ISearchQuery = { @@ -247,33 +225,56 @@ export class SearchService extends Disposable implements ISearchService { extraFileResources: diskSearchExtraFileResources }; - searchPs.push(this.diskSearch.search(diskSearchQuery, onProviderProgress, token)); + searchPs.push(diskSearchQuery.type === QueryType.File ? + this.diskSearch.fileSearch(diskSearchQuery, token) : + this.diskSearch.textSearch(diskSearchQuery, onProviderProgress, token)); } - return TPromise.join(searchPs).then(completes => { + return Promise.join(searchPs).then(completes => { const endToEndTime = e2eSW.elapsed(); this.logService.trace(`SearchService#search: ${endToEndTime}ms`); completes.forEach(complete => { this.sendTelemetry(query, endToEndTime, complete); }); return completes; + }, err => { + const endToEndTime = e2eSW.elapsed(); + this.logService.trace(`SearchService#search: ${endToEndTime}ms`); + const searchError = deserializeSearchError(err.message); + this.sendTelemetry(query, endToEndTime, null, searchError); + + throw searchError; }); } - private sendTelemetry(query: ISearchQuery, endToEndTime: number, complete: ISearchComplete): void { + private groupFolderQueriesByScheme(query: ISearchQuery): Map { + const queries = new Map(); + + query.folderQueries.forEach(fq => { + const schemeFQs = queries.get(fq.folder.scheme) || []; + schemeFQs.push(fq); + + queries.set(fq.folder.scheme, schemeFQs); + }); + + return queries; + } + + private sendTelemetry(query: ISearchQuery, endToEndTime: number, complete?: ISearchComplete, err?: SearchError): void { const fileSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme === 'file'); const otherSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme !== 'file'); const scheme = fileSchemeOnly ? 'file' : otherSchemeOnly ? 'other' : 'mixed'; - if (query.type === QueryType.File && complete.stats) { + if (query.type === QueryType.File && complete && complete.stats) { const fileSearchStats = complete.stats as IFileSearchStats; if (fileSearchStats.fromCache) { const cacheStats: ICachedSearchStats = fileSearchStats.detailStats as ICachedSearchStats; /* __GDPR__ "cachedSearchComplete" : { + "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, @@ -287,6 +288,7 @@ export class SearchService extends Disposable implements ISearchService { } */ this.telemetryService.publicLog('cachedSearchComplete', { + reason: query._reason, resultCount: fileSearchStats.resultCount, workspaceFolderCount: query.folderQueries.length, type: fileSearchStats.type, @@ -303,6 +305,7 @@ export class SearchService extends Disposable implements ISearchService { /* __GDPR__ "searchComplete" : { + "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, @@ -314,10 +317,12 @@ export class SearchService extends Disposable implements ISearchService { "filesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "cmdTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "cmdResultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "useRipgrep" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ this.telemetryService.publicLog('searchComplete', { + reason: query._reason, resultCount: fileSearchStats.resultCount, workspaceFolderCount: query.folderQueries.length, type: fileSearchStats.type, @@ -329,13 +334,45 @@ export class SearchService extends Disposable implements ISearchService { filesWalked: searchEngineStats.filesWalked, cmdTime: searchEngineStats.cmdTime, cmdResultCount: searchEngineStats.cmdResultCount, - scheme + scheme, + useRipgrep: query.useRipgrep }); } + } else if (query.type === QueryType.Text) { + let errorType: string; + if (err) { + errorType = err.code === SearchErrorCode.regexParseError ? 'regex' : + err.code === SearchErrorCode.unknownEncoding ? 'encoding' : + err.code === SearchErrorCode.globParseError ? 'glob' : + err.code === SearchErrorCode.invalidLiteral ? 'literal' : + err.code === SearchErrorCode.other ? 'other' : + 'unknown'; + } + + /* __GDPR__ + "textSearchComplete" : { + "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "error" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "useRipgrep" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "usePCRE2" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + this.telemetryService.publicLog('textSearchComplete', { + reason: query._reason, + workspaceFolderCount: query.folderQueries.length, + endToEndTime: endToEndTime, + scheme, + error: errorType, + useRipgrep: query.useRipgrep, + usePCRE2: !!query.usePCRE2 + }); } } - private getLocalResults(query: ISearchQuery): ResourceMap { + private getLocalResults(query: ITextQuery): ResourceMap { const localResults = new ResourceMap(); if (query.type === QueryType.Text) { @@ -375,9 +412,8 @@ export class SearchService extends Disposable implements ISearchService { let fileMatch = new FileMatch(resource); localResults.set(resource, fileMatch); - matches.forEach((match) => { - fileMatch.matches.push(editorMatchToTextSearchResult(match, model, query.previewOptions)); - }); + const textSearchResults = editorMatchesToTextSearchResults(matches, model, query.previewOptions); + fileMatch.results = addContextToEditorMatches(textSearchResults, model, query); } else { localResults.set(resource, null); } @@ -387,18 +423,7 @@ export class SearchService extends Disposable implements ISearchService { return localResults; } - private matches(resource: uri, query: ISearchQuery): boolean { - // file pattern - if (query.filePattern) { - if (resource.scheme !== Schemas.file) { - return false; // if we match on file pattern, we have to ignore non file resources - } - - if (!strings.fuzzyContains(resource.fsPath, strings.stripWildcards(query.filePattern).toLowerCase())) { - return false; - } - } - + private matches(resource: uri, query: ITextQuery): boolean { // includes if (query.includePattern) { if (resource.scheme !== Schemas.file) { @@ -409,19 +434,18 @@ export class SearchService extends Disposable implements ISearchService { return pathIncludedInQuery(query, resource.fsPath); } - public clearCache(cacheKey: string): TPromise { + public clearCache(cacheKey: string): Promise { const clearPs = [ this.diskSearch, ...values(this.fileIndexProviders) ].map(provider => provider && provider.clearCache(cacheKey)); - return TPromise.join(clearPs) + return Promise.join(clearPs) .then(() => { }); } } export class DiskSearch implements ISearchResultProvider { - private raw: IRawSearchService; constructor(verboseLogging: boolean, timeout: number = 60 * 60 * 1000, searchDebug?: IDebugParams) { @@ -457,77 +481,42 @@ export class DiskSearch implements ISearchResultProvider { this.raw = new SearchChannelClient(channel); } - public search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise { + textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): Promise { const folderQueries = query.folderQueries || []; - return TPromise.join(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) + return Promise.join(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) .then(exists => { if (token && token.isCancellationRequested) { throw canceled(); } - const existingFolders = folderQueries.filter((q, index) => exists[index]); - const rawSearch = this.rawSearchQuery(query, existingFolders); - - let event: Event; - if (query.type === QueryType.File) { - event = this.raw.fileSearch(rawSearch); - } else { - event = this.raw.textSearch(rawSearch); - } + query.folderQueries = folderQueries.filter((q, index) => exists[index]); + const event: Event = this.raw.textSearch(query); return DiskSearch.collectResultsFromEvent(event, onProgress, token); }); } - private rawSearchQuery(query: ISearchQuery, existingFolders: IFolderQuery[]) { - let rawSearch: IRawSearch = { - folderQueries: [], - extraFiles: [], - filePattern: query.filePattern, - excludePattern: query.excludePattern, - includePattern: query.includePattern, - maxResults: query.maxResults, - exists: query.exists, - sortByScore: query.sortByScore, - cacheKey: query.cacheKey, - useRipgrep: query.useRipgrep, - disregardIgnoreFiles: query.disregardIgnoreFiles, - disregardGlobalIgnoreFiles: query.disregardGlobalIgnoreFiles, - ignoreSymlinks: query.ignoreSymlinks, - previewOptions: query.previewOptions - }; - - for (const q of existingFolders) { - rawSearch.folderQueries.push({ - excludePattern: q.excludePattern, - includePattern: q.includePattern, - fileEncoding: q.fileEncoding, - disregardIgnoreFiles: q.disregardIgnoreFiles, - disregardGlobalIgnoreFiles: q.disregardGlobalIgnoreFiles, - folder: q.folder.fsPath - }); - } - - if (query.extraFileResources) { - for (const r of query.extraFileResources) { - if (r.scheme === Schemas.file) { - rawSearch.extraFiles.push(r.fsPath); + fileSearch(query: IFileQuery, token?: CancellationToken): Promise { + const folderQueries = query.folderQueries || []; + return Promise.join(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) + .then(exists => { + if (token && token.isCancellationRequested) { + throw canceled(); } - } - } - if (query.type === QueryType.Text) { - rawSearch.contentPattern = query.contentPattern; - } + query.folderQueries = folderQueries.filter((q, index) => exists[index]); + let event: Event; + event = this.raw.fileSearch(query); - return rawSearch; + return DiskSearch.collectResultsFromEvent(event, null, token); + }); } - public static collectResultsFromEvent(event: Event, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise { + public static collectResultsFromEvent(event: Event, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): Promise { let result: IFileMatch[] = []; let listener: IDisposable; - return new TPromise((c, e) => { + return new Promise((c, e) => { if (token) { token.onCancellationRequested(() => { if (listener) { @@ -581,21 +570,15 @@ export class DiskSearch implements ISearchResultProvider { } private static createFileMatch(data: ISerializedFileMatch): FileMatch { - let fileMatch = new FileMatch(uri.file(data.path)); - if (data.matches) { - fileMatch.matches.push(...data.matches); // TODO why + const fileMatch = new FileMatch(uri.file(data.path)); + if (data.results) { + // const matches = data.results.filter(resultIsMatch); + fileMatch.results.push(...data.results); } return fileMatch; } - public clearCache(cacheKey: string): TPromise { + public clearCache(cacheKey: string): Promise { return this.raw.clearCache(cacheKey); } } - -function editorMatchToTextSearchResult(match: FindMatch, model: ITextModel, previewOptions: ITextSearchPreviewOptions): TextSearchResult { - return new TextSearchResult( - model.getLineContent(match.range.startLineNumber), - new Range(match.range.startLineNumber - 1, match.range.startColumn - 1, match.range.endLineNumber - 1, match.range.endColumn - 1), - previewOptions); -} diff --git a/src/vs/workbench/services/search/node/textSearchAdapter.ts b/src/vs/workbench/services/search/node/textSearchAdapter.ts index 20dbb3c90c5..c953c7866e9 100644 --- a/src/vs/workbench/services/search/node/textSearchAdapter.ts +++ b/src/vs/workbench/services/search/node/textSearchAdapter.ts @@ -4,78 +4,59 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; -import { URI } from 'vs/base/common/uri'; import * as extfs from 'vs/base/node/extfs'; -import { IFolderQuery, IProgress, ISearchQuery, ITextSearchStats, QueryType, IFileMatch } from 'vs/platform/search/common/search'; +import { IFileMatch, IProgress, ITextQuery, ITextSearchStats, ITextSearchMatch } from 'vs/platform/search/common/search'; import { RipgrepTextSearchEngine } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine'; import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager'; -import { IRawSearch, ISerializedFileMatch, ISerializedSearchSuccess } from './search'; +import { ISerializedFileMatch, ISerializedSearchSuccess } from './search'; export class TextSearchEngineAdapter { - constructor(private config: IRawSearch) { + constructor(private query: ITextQuery) { } - // TODO@Rob - make promise-based once the old search is gone, and I don't need them to have matching interfaces anymore - search(token: CancellationToken, onResult: (matches: ISerializedFileMatch[]) => void, onMessage: (message: IProgress) => void, done: (error: Error, complete: ISerializedSearchSuccess) => void): void { - if (!this.config.folderQueries.length && !this.config.extraFiles.length) { - done(null, { + search(token: CancellationToken, onResult: (matches: ISerializedFileMatch[]) => void, onMessage: (message: IProgress) => void): Promise { + if (!this.query.folderQueries.length && !this.query.extraFileResources.length) { + return Promise.resolve({ type: 'success', limitHit: false, stats: { type: 'searchProcess' } }); - return; } - const query: ISearchQuery = { - type: QueryType.Text, - cacheKey: this.config.cacheKey, - contentPattern: this.config.contentPattern, - - excludePattern: this.config.excludePattern, - includePattern: this.config.includePattern, - extraFileResources: this.config.extraFiles && this.config.extraFiles.map(f => URI.file(f)), - fileEncoding: this.config.folderQueries[0].fileEncoding, // ? - maxResults: this.config.maxResults, - exists: this.config.exists, - sortByScore: this.config.sortByScore, - disregardIgnoreFiles: this.config.disregardIgnoreFiles, - disregardGlobalIgnoreFiles: this.config.disregardGlobalIgnoreFiles, - ignoreSymlinks: this.config.ignoreSymlinks, - maxFileSize: this.config.maxFilesize, - previewOptions: this.config.previewOptions - }; - query.folderQueries = this.config.folderQueries.map(fq => { - disregardGlobalIgnoreFiles: fq.disregardGlobalIgnoreFiles, - disregardIgnoreFiles: fq.disregardIgnoreFiles, - excludePattern: fq.excludePattern, - fileEncoding: fq.fileEncoding, - folder: URI.file(fq.folder), - includePattern: fq.includePattern - }); - const pretendOutputChannel = { appendLine(msg) { onMessage({ message: msg }); } }; - const textSearchManager = new TextSearchManager(this.config.contentPattern, query, new RipgrepTextSearchEngine(pretendOutputChannel), extfs); - textSearchManager - .search( - matches => { - onResult(matches.map(fileMatchToSerialized)); - }, - token) - .then(() => done(null, { limitHit: false, stats: null, type: 'success' })); + const textSearchManager = new TextSearchManager(this.query, new RipgrepTextSearchEngine(pretendOutputChannel), extfs); + return new Promise((resolve, reject) => { + return textSearchManager + .search( + matches => { + onResult(matches.map(fileMatchToSerialized)); + }, + token) + .then( + c => resolve({ limitHit: c.limitHit, stats: null, type: 'success' }), + reject); + }); } } function fileMatchToSerialized(match: IFileMatch): ISerializedFileMatch { return { path: match.resource.fsPath, - matches: match.matches, - numMatches: match.matches.length + results: match.results, + numMatches: match.results.reduce((sum, r) => { + if (!!(r).ranges) { + const m = r; + return sum + (Array.isArray(m.ranges) ? m.ranges.length : 1); + } else { + return sum + 1; + } + }, 0) }; -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/search/node/textSearchManager.ts b/src/vs/workbench/services/search/node/textSearchManager.ts index 828819afaad..0f15a7709be 100644 --- a/src/vs/workbench/services/search/node/textSearchManager.ts +++ b/src/vs/workbench/services/search/node/textSearchManager.ts @@ -4,16 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; +import { mapArrayOrNot } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import * as glob from 'vs/base/common/glob'; import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { toCanonicalName } from 'vs/base/node/encoding'; import * as extfs from 'vs/base/node/extfs'; -import { IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ISearchQuery, ITextSearchResult } from 'vs/platform/search/common/search'; -import * as vscode from 'vscode'; +import { IExtendedExtensionSearchOptions, IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchMatch, ITextSearchContext, ITextSearchResult } from 'vs/platform/search/common/search'; import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search'; +import * as vscode from 'vscode'; export class TextSearchManager { @@ -22,15 +23,15 @@ export class TextSearchManager { private isLimitHit: boolean; private resultCount = 0; - constructor(private pattern: IPatternInfo, private config: ISearchQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs) { + constructor(private query: ITextQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs = extfs) { } - public search(onProgress: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { - const folderQueries = this.config.folderQueries; + public search(onProgress: (matches: IFileMatch[]) => void, token: CancellationToken): Promise { + const folderQueries = this.query.folderQueries || []; const tokenSource = new CancellationTokenSource(); token.onCancellationRequested(() => tokenSource.cancel()); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { this.collector = new TextSearchResultsCollector(onProgress); let isCanceled = false; @@ -39,7 +40,7 @@ export class TextSearchManager { return; } - if (this.resultCount >= this.config.maxResults) { + if (typeof this.query.maxResults === 'number' && this.resultCount >= this.query.maxResults) { this.isLimitHit = true; isCanceled = true; tokenSource.cancel(); @@ -52,38 +53,39 @@ export class TextSearchManager { }; // For each root folder - TPromise.join(folderQueries.map((fq, i) => { + Promise.all(folderQueries.map((fq, i) => { return this.searchInFolder(fq, r => onResult(r, i), tokenSource.token); })).then(results => { tokenSource.dispose(); this.collector.flush(); - const someFolderHitLImit = results.some(result => result && result.limitHit); + const someFolderHitLImit = results.some(result => !!result && !!result.limitHit); resolve({ limitHit: this.isLimitHit || someFolderHitLImit, stats: { type: 'textSearchProvider' } }); - }, (errs: Error[]) => { + }, (err: Error) => { tokenSource.dispose(); - const errMsg = errs - .map(err => toErrorMessage(err)) - .filter(msg => !!msg)[0]; - + const errMsg = toErrorMessage(err); reject(new Error(errMsg)); }); }); } - private searchInFolder(folderQuery: IFolderQuery, onResult: (result: vscode.TextSearchResult) => void, token: CancellationToken): TPromise { - const queryTester = new QueryGlobTester(this.config, folderQuery); - const testingPs = []; + private searchInFolder(folderQuery: IFolderQuery, onResult: (result: vscode.TextSearchResult) => void, token: CancellationToken): Promise { + const queryTester = new QueryGlobTester(this.query, folderQuery); + const testingPs: Promise[] = []; const progress = { report: (result: vscode.TextSearchResult) => { - const hasSibling = folderQuery.folder.scheme === 'file' && glob.hasSiblingPromiseFn(() => { - return this.readdir(path.dirname(result.uri.fsPath)); - }); + // TODO: validate result.ranges vs result.preview.matches + + const hasSibling = folderQuery.folder.scheme === 'file' ? + glob.hasSiblingPromiseFn(() => { + return this.readdir(path.dirname(result.uri.fsPath)); + }) : + undefined; const relativePath = path.relative(folderQuery.folder.fsPath, result.uri.fsPath); testingPs.push( @@ -97,16 +99,16 @@ export class TextSearchManager { }; const searchOptions = this.getSearchOptionsForFolder(folderQuery); - return new TPromise(resolve => process.nextTick(resolve)) - .then(() => this.provider.provideTextSearchResults(patternInfoToQuery(this.pattern), searchOptions, progress, token)) + return new Promise(resolve => process.nextTick(resolve)) + .then(() => this.provider.provideTextSearchResults(patternInfoToQuery(this.query.contentPattern), searchOptions, progress, token)) .then(result => { - return TPromise.join(testingPs) + return Promise.all(testingPs) .then(() => result); }); } - private readdir(dirname: string): TPromise { - return new TPromise((resolve, reject) => { + private readdir(dirname: string): Promise { + return new Promise((resolve, reject) => { this._extfs.readdir(dirname, (err, files) => { if (err) { return reject(err); @@ -118,21 +120,25 @@ export class TextSearchManager { } private getSearchOptionsForFolder(fq: IFolderQuery): vscode.TextSearchOptions { - const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern); - const excludes = resolvePatternsForProvider(this.config.excludePattern, fq.excludePattern); + const includes = resolvePatternsForProvider(this.query.includePattern, fq.includePattern); + const excludes = resolvePatternsForProvider(this.query.excludePattern, fq.excludePattern); - return { + const options = { folder: URI.from(fq.folder), excludes, includes, - useIgnoreFiles: !this.config.disregardIgnoreFiles, - useGlobalIgnoreFiles: !this.config.disregardGlobalIgnoreFiles, - followSymlinks: !this.config.ignoreSymlinks, - encoding: this.config.fileEncoding, - maxFileSize: this.config.maxFileSize, - maxResults: this.config.maxResults, - previewOptions: this.config.previewOptions + useIgnoreFiles: !fq.disregardIgnoreFiles, + useGlobalIgnoreFiles: !fq.disregardGlobalIgnoreFiles, + followSymlinks: !fq.ignoreSymlinks, + encoding: fq.fileEncoding && toCanonicalName(fq.fileEncoding), + maxFileSize: this.query.maxFileSize, + maxResults: this.query.maxResults, + previewOptions: this.query.previewOptions, + afterContext: this.query.afterContext, + beforeContext: this.query.beforeContext }; + (options).usePCRE2 = this.query.usePCRE2; + return options; } } @@ -151,7 +157,7 @@ export class TextSearchResultsCollector { private _currentFolderIdx: number; private _currentUri: URI; - private _currentFileMatch: IFileMatch; + private _currentFileMatch: IFileMatch | null = null; constructor(private _onResult: (result: IFileMatch[]) => void) { this._batchedCollector = new BatchedCollector(512, items => this.sendItems(items)); @@ -170,18 +176,18 @@ export class TextSearchResultsCollector { this._currentFolderIdx = folderIdx; this._currentFileMatch = { resource: data.uri, - matches: [] + results: [] }; } - this._currentFileMatch.matches.push(extensionResultToFrontendResult(data)); + this._currentFileMatch.results!.push(extensionResultToFrontendResult(data)); } private pushToCollector(): void { - const size = this._currentFileMatch ? - this._currentFileMatch.matches.length : + const size = this._currentFileMatch && this._currentFileMatch.results ? + this._currentFileMatch.results.length : 0; - this._batchedCollector.addItem(this._currentFileMatch, size); + this._batchedCollector.addItem(this._currentFileMatch!, size); } flush(): void { @@ -196,23 +202,34 @@ export class TextSearchResultsCollector { function extensionResultToFrontendResult(data: vscode.TextSearchResult): ITextSearchResult { // Warning: result from RipgrepTextSearchEH has fake vscode.Range. Don't depend on any other props beyond these... - return { - preview: { - match: { - startLineNumber: data.preview.match.start.line, - startColumn: data.preview.match.start.character, - endLineNumber: data.preview.match.end.line, - endColumn: data.preview.match.end.character + if (extensionResultIsMatch(data)) { + return { + preview: { + matches: mapArrayOrNot(data.preview.matches, m => ({ + startLineNumber: m.start.line, + startColumn: m.start.character, + endLineNumber: m.end.line, + endColumn: m.end.character + })), + text: data.preview.text }, - text: data.preview.text - }, - range: { - startLineNumber: data.range.start.line, - startColumn: data.range.start.character, - endLineNumber: data.range.end.line, - endColumn: data.range.end.character - } - }; + ranges: mapArrayOrNot(data.ranges, r => ({ + startLineNumber: r.start.line, + startColumn: r.start.character, + endLineNumber: r.end.line, + endColumn: r.end.character + })) + }; + } else { + return { + text: data.text, + lineNumber: data.lineNumber + }; + } +} + +export function extensionResultIsMatch(data: vscode.TextSearchResult): data is vscode.TextSearchMatch { + return !!(data).preview; } /** diff --git a/src/vs/workbench/services/search/test/common/searchHelpers.test.ts b/src/vs/workbench/services/search/test/common/searchHelpers.test.ts new file mode 100644 index 00000000000..758ccd272d1 --- /dev/null +++ b/src/vs/workbench/services/search/test/common/searchHelpers.test.ts @@ -0,0 +1,190 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ITextModel, FindMatch } from 'vs/editor/common/model'; +import { editorMatchesToTextSearchResults, addContextToEditorMatches } from 'vs/workbench/services/search/common/searchHelpers'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextQuery, QueryType, ITextSearchContext } from 'vs/platform/search/common/search'; + +suite('SearchHelpers', () => { + suite('editorMatchesToTextSearchResults', () => { + const mockTextModel: ITextModel = { + getLineContent(lineNumber: number): string { + return '' + lineNumber; + } + }; + + test('simple', () => { + const results = editorMatchesToTextSearchResults([new FindMatch(new Range(6, 1, 6, 2), null)], mockTextModel); + assert.equal(results.length, 1); + assert.equal(results[0].preview.text, '6\n'); + assert.deepEqual(results[0].preview.matches, [new Range(0, 0, 0, 1)]); + assert.deepEqual(results[0].ranges, [new Range(5, 0, 5, 1)]); + }); + + test('multiple', () => { + const results = editorMatchesToTextSearchResults( + [ + new FindMatch(new Range(6, 1, 6, 2), null), + new FindMatch(new Range(6, 4, 8, 2), null), + new FindMatch(new Range(9, 1, 10, 3), null), + ], + mockTextModel); + assert.equal(results.length, 2); + assert.deepEqual(results[0].preview.matches, [ + new Range(0, 0, 0, 1), + new Range(0, 3, 2, 1), + ]); + assert.deepEqual(results[0].ranges, [ + new Range(5, 0, 5, 1), + new Range(5, 3, 7, 1), + ]); + assert.equal(results[0].preview.text, '6\n7\n8\n'); + + assert.deepEqual(results[1].preview.matches, [ + new Range(0, 0, 1, 2), + ]); + assert.deepEqual(results[1].ranges, [ + new Range(8, 0, 9, 2), + ]); + assert.equal(results[1].preview.text, '9\n10\n'); + }); + }); + + suite('addContextToEditorMatches', () => { + const MOCK_LINE_COUNT = 100; + + const mockTextModel: ITextModel = { + getLineContent(lineNumber: number): string { + if (lineNumber < 1 || lineNumber > MOCK_LINE_COUNT) { + throw new Error(`invalid line count: ${lineNumber}`); + } + + return '' + lineNumber; + }, + + getLineCount(): number { + return MOCK_LINE_COUNT; + } + }; + + function getQuery(beforeContext?: number, afterContext?: number): ITextQuery { + return { + type: QueryType.Text, + contentPattern: { pattern: 'test' }, + beforeContext, + afterContext + }; + } + + test('no context', () => { + const matches = [{ + preview: { + text: 'foo', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(0, 0, 0, 10) + }]; + + assert.deepEqual(addContextToEditorMatches(matches, mockTextModel, getQuery()), matches); + }); + + test('simple', () => { + const matches = [{ + preview: { + text: 'foo', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(1, 0, 1, 10) + }]; + + assert.deepEqual(addContextToEditorMatches(matches, mockTextModel, getQuery(1, 2)), [ + { + text: '1', + lineNumber: 0 + }, + ...matches, + { + text: '3', + lineNumber: 2 + }, + { + text: '4', + lineNumber: 3 + }, + ]); + }); + + test('multiple matches next to each other', () => { + const matches = [ + { + preview: { + text: 'foo', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(1, 0, 1, 10) + }, + { + preview: { + text: 'bar', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(2, 0, 2, 10) + }]; + + assert.deepEqual(addContextToEditorMatches(matches, mockTextModel, getQuery(1, 2)), [ + { + text: '1', + lineNumber: 0 + }, + ...matches, + { + text: '4', + lineNumber: 3 + }, + { + text: '5', + lineNumber: 4 + }, + ]); + }); + + test('boundaries', () => { + const matches = [ + { + preview: { + text: 'foo', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(0, 0, 0, 10) + }, + { + preview: { + text: 'bar', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(MOCK_LINE_COUNT - 1, 0, MOCK_LINE_COUNT - 1, 10) + }]; + + assert.deepEqual(addContextToEditorMatches(matches, mockTextModel, getQuery(1, 2)), [ + matches[0], + { + text: '2', + lineNumber: 1 + }, + { + text: '3', + lineNumber: 2 + }, + { + text: '' + (MOCK_LINE_COUNT - 1), + lineNumber: MOCK_LINE_COUNT - 2 + }, + matches[1] + ]); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/search/test/node/searchService.test.ts b/src/vs/workbench/services/search/test/node/rawSearchService.test.ts similarity index 85% rename from src/vs/workbench/services/search/test/node/searchService.test.ts rename to src/vs/workbench/services/search/test/node/rawSearchService.test.ts index be347aa6c24..cf3b096d221 100644 --- a/src/vs/workbench/services/search/test/node/searchService.test.ts +++ b/src/vs/workbench/services/search/test/node/rawSearchService.test.ts @@ -8,19 +8,20 @@ import * as path from 'path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; -import { IProgress, ISearchEngineStats, IFileSearchStats } from 'vs/platform/search/common/search'; +import { URI } from 'vs/base/common/uri'; +import { IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchEngineStats, QueryType } from 'vs/platform/search/common/search'; import { SearchService as RawSearchService } from 'vs/workbench/services/search/node/rawSearchService'; -import { IFolderSearch, IRawFileMatch, IRawSearch, ISearchEngine, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess, ISearchEngineSuccess } from 'vs/workbench/services/search/node/search'; +import { IRawFileMatch, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/node/search'; import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; const TEST_FOLDER_QUERIES = [ - { folder: path.normalize('/some/where') } + { folder: URI.file(path.normalize('/some/where')) } ]; const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures')); -const MULTIROOT_QUERIES: IFolderSearch[] = [ - { folder: path.join(TEST_FIXTURES, 'examples') }, - { folder: path.join(TEST_FIXTURES, 'more') } +const MULTIROOT_QUERIES: IFolderQuery[] = [ + { folder: URI.file(path.join(TEST_FIXTURES, 'examples')) }, + { folder: URI.file(path.join(TEST_FIXTURES, 'more')) } ]; const stats: ISearchEngineStats = { @@ -37,7 +38,7 @@ class TestSearchEngine implements ISearchEngine { private isCanceled = false; - constructor(private result: () => IRawFileMatch, public config?: IRawSearch) { + constructor(private result: () => IRawFileMatch, public config?: IFileQuery) { TestSearchEngine.last = this; } @@ -73,9 +74,10 @@ class TestSearchEngine implements ISearchEngine { const testTimeout = 5000; -suite('SearchService', () => { +suite('RawSearchService', () => { - const rawSearch: IRawSearch = { + const rawSearch: IFileQuery = { + type: QueryType.File, folderQueries: TEST_FOLDER_QUERIES, filePattern: 'a' }; @@ -107,7 +109,7 @@ suite('SearchService', () => { } }; - await service.doFileSearch(Engine, rawSearch, cb); + await service.doFileSearchWithEngine(Engine, rawSearch, cb, null, 0); return assert.strictEqual(results, 5); }); @@ -129,7 +131,7 @@ suite('SearchService', () => { } }; - await service.doFileSearch(Engine, rawSearch, cb, undefined, 10); + await service.doFileSearchWithEngine(Engine, rawSearch, cb, undefined, 10); assert.deepStrictEqual(results, [10, 10, 5]); }); @@ -140,12 +142,12 @@ suite('SearchService', () => { const Engine = TestSearchEngine.bind(null, () => i-- && rawMatch); const service = new RawSearchService(); - function fileSearch(config: IRawSearch, batchSize: number): Event { + function fileSearch(config: IFileQuery, batchSize: number): Event { let promise: CancelablePromise; const emitter = new Emitter({ onFirstListenerAdd: () => { - promise = createCancelablePromise(token => service.doFileSearch(Engine, config, p => emitter.fire(p), token, batchSize) + promise = createCancelablePromise(token => service.doFileSearchWithEngine(Engine, config, p => emitter.fire(p), token, batchSize) .then(c => emitter.fire(c), err => emitter.fire({ type: 'error', error: err }))); }, onLastListenerRemove: () => { @@ -171,7 +173,8 @@ suite('SearchService', () => { this.timeout(testTimeout); const service = new RawSearchService(); - const query: IRawSearch = { + const query: IFileQuery = { + type: QueryType.File, folderQueries: MULTIROOT_QUERIES, maxResults: 1, includePattern: { @@ -188,7 +191,8 @@ suite('SearchService', () => { this.timeout(testTimeout); const service = new RawSearchService(); - const query: IRawSearch = { + const query: IFileQuery = { + type: QueryType.File, folderQueries: MULTIROOT_QUERIES, exists: true, includePattern: { @@ -223,7 +227,8 @@ suite('SearchService', () => { } }; - await service.doFileSearch(Engine, { + await service.doFileSearchWithEngine(Engine, { + type: QueryType.File, folderQueries: TEST_FOLDER_QUERIES, filePattern: 'bb', sortByScore: true, @@ -250,7 +255,8 @@ suite('SearchService', () => { assert.fail(JSON.stringify(value)); } }; - await service.doFileSearch(Engine, { + await service.doFileSearchWithEngine(Engine, { + type: QueryType.File, folderQueries: TEST_FOLDER_QUERIES, filePattern: 'a', sortByScore: true, @@ -279,7 +285,8 @@ suite('SearchService', () => { assert.fail(JSON.stringify(value)); } }; - return service.doFileSearch(Engine, { + return service.doFileSearchWithEngine(Engine, { + type: QueryType.File, folderQueries: TEST_FOLDER_QUERIES, filePattern: 'b', sortByScore: true, @@ -297,7 +304,8 @@ suite('SearchService', () => { } }; try { - const complete = await service.doFileSearch(Engine, { + const complete = await service.doFileSearchWithEngine(Engine, { + type: QueryType.File, folderQueries: TEST_FOLDER_QUERIES, filePattern: 'bc', sortByScore: true, @@ -324,7 +332,8 @@ suite('SearchService', () => { assert.fail(JSON.stringify(value)); } }; - const complete = await service.doFileSearch(Engine, { + const complete = await service.doFileSearchWithEngine(Engine, { + type: QueryType.File, folderQueries: TEST_FOLDER_QUERIES, filePattern: 'bc', sortByScore: true, diff --git a/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts similarity index 58% rename from src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts rename to src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts index 666116d9378..313dd31c1e5 100644 --- a/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts +++ b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts @@ -4,9 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { fixRegexEndingPattern } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine'; +import { unicodeEscapesToPCRE2, fixRegexEndingPattern } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine'; + +suite('RipgrepTextSearchEngine', () => { + test('unicodeEscapesToPCRE2', async () => { + assert.equal(unicodeEscapesToPCRE2('\\u1234'), '\\x{1234}'); + assert.equal(unicodeEscapesToPCRE2('\\u1234\\u0001'), '\\x{1234}\\x{0001}'); + assert.equal(unicodeEscapesToPCRE2('foo\\u1234bar'), 'foo\\x{1234}bar'); + assert.equal(unicodeEscapesToPCRE2('\\\\\\u1234'), '\\\\\\x{1234}'); + assert.equal(unicodeEscapesToPCRE2('foo\\\\\\u1234'), 'foo\\\\\\x{1234}'); + + assert.equal(unicodeEscapesToPCRE2('\\u123'), '\\u123'); + assert.equal(unicodeEscapesToPCRE2('\\u12345'), '\\u12345'); + assert.equal(unicodeEscapesToPCRE2('\\\\u12345'), '\\\\u12345'); + assert.equal(unicodeEscapesToPCRE2('foo'), 'foo'); + assert.equal(unicodeEscapesToPCRE2(''), ''); + }); -suite('RipgrepTextSearch - etc', () => { test('fixRegexEndingPattern', () => { function testFixRegexEndingPattern([input, expectedResult]: string[]): void { assert.equal(fixRegexEndingPattern(input), expectedResult); @@ -38,4 +52,4 @@ suite('RipgrepTextSearch - etc', () => { ['foo\\\\$', 'foo\\\\\\r?$'], ].forEach(testFixRegexEndingPattern); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/services/search/test/node/search.test.ts b/src/vs/workbench/services/search/test/node/search.test.ts index 3faddb9173d..1c32b06c8a6 100644 --- a/src/vs/workbench/services/search/test/node/search.test.ts +++ b/src/vs/workbench/services/search/test/node/search.test.ts @@ -3,29 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; import * as assert from 'assert'; - +import * as path from 'path'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; import { join, normalize } from 'vs/base/common/paths'; import * as platform from 'vs/base/common/platform'; - -import { FileWalker, Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; -import { IRawFileMatch, IFolderSearch } from 'vs/workbench/services/search/node/search'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { joinPath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { IFolderQuery, QueryType } from 'vs/platform/search/common/search'; +import { Engine as FileSearchEngine, FileWalker } from 'vs/workbench/services/search/node/fileSearch'; +import { IRawFileMatch } from 'vs/workbench/services/search/node/search'; const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures')); -const EXAMPLES_FIXTURES = path.join(TEST_FIXTURES, 'examples'); -const MORE_FIXTURES = path.join(TEST_FIXTURES, 'more'); -const TEST_ROOT_FOLDER: IFolderSearch = { folder: TEST_FIXTURES }; -const ROOT_FOLDER_QUERY: IFolderSearch[] = [ +const EXAMPLES_FIXTURES = URI.file(path.join(TEST_FIXTURES, 'examples')); +const MORE_FIXTURES = URI.file(path.join(TEST_FIXTURES, 'more')); +const TEST_ROOT_FOLDER: IFolderQuery = { folder: URI.file(TEST_FIXTURES) }; +const ROOT_FOLDER_QUERY: IFolderQuery[] = [ TEST_ROOT_FOLDER ]; -const ROOT_FOLDER_QUERY_36438: IFolderSearch[] = [ - { folder: path.normalize(getPathFromAmdModule(require, './fixtures2/36438')) } +const ROOT_FOLDER_QUERY_36438: IFolderQuery[] = [ + { folder: URI.file(path.normalize(getPathFromAmdModule(require, './fixtures2/36438'))) } ]; -const MULTIROOT_QUERIES: IFolderSearch[] = [ +const MULTIROOT_QUERIES: IFolderQuery[] = [ { folder: EXAMPLES_FIXTURES }, { folder: MORE_FIXTURES } ]; @@ -37,6 +38,7 @@ suite('FileSearchEngine', () => { test('Files: *.js', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.js' }); @@ -56,6 +58,7 @@ suite('FileSearchEngine', () => { test('Files: maxResults', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, maxResults: 1 }); @@ -75,6 +78,7 @@ suite('FileSearchEngine', () => { test('Files: maxResults without Ripgrep', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, maxResults: 1, useRipgrep: false @@ -95,6 +99,7 @@ suite('FileSearchEngine', () => { test('Files: exists', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, includePattern: { '**/file.txt': true }, exists: true @@ -116,6 +121,7 @@ suite('FileSearchEngine', () => { test('Files: not exists', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, includePattern: { '**/nofile.txt': true }, exists: true @@ -137,6 +143,7 @@ suite('FileSearchEngine', () => { test('Files: exists without Ripgrep', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, includePattern: { '**/file.txt': true }, exists: true, @@ -159,6 +166,7 @@ suite('FileSearchEngine', () => { test('Files: not exists without Ripgrep', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, includePattern: { '**/nofile.txt': true }, exists: true, @@ -181,6 +189,7 @@ suite('FileSearchEngine', () => { test('Files: examples/com*', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: normalize(join('examples', 'com*'), true) }); @@ -200,6 +209,7 @@ suite('FileSearchEngine', () => { test('Files: examples (fuzzy)', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: 'xl' }); @@ -219,6 +229,7 @@ suite('FileSearchEngine', () => { test('Files: multiroot', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: MULTIROOT_QUERIES, filePattern: 'file' }); @@ -238,6 +249,7 @@ suite('FileSearchEngine', () => { test('Files: multiroot with includePattern and maxResults', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: MULTIROOT_QUERIES, maxResults: 1, includePattern: { @@ -262,6 +274,7 @@ suite('FileSearchEngine', () => { test('Files: multiroot with includePattern and exists', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: MULTIROOT_QUERIES, exists: true, includePattern: { @@ -287,6 +300,7 @@ suite('FileSearchEngine', () => { test('Files: NPE (CamelCase)', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: 'NullPE' }); @@ -306,6 +320,7 @@ suite('FileSearchEngine', () => { test('Files: *.*', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*' }); @@ -325,6 +340,7 @@ suite('FileSearchEngine', () => { test('Files: *.as', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.as' }); @@ -344,6 +360,7 @@ suite('FileSearchEngine', () => { test('Files: *.* without derived', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: 'site.*', excludePattern: { '**/*.css': { 'when': '$(basename).less' } } @@ -367,6 +384,7 @@ suite('FileSearchEngine', () => { test('Files: *.* exclude folder without wildcard', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', excludePattern: { 'examples': true } @@ -387,6 +405,7 @@ suite('FileSearchEngine', () => { test('Files: exclude folder without wildcard #36438', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY_36438, excludePattern: { 'modules': true } }); @@ -406,6 +425,7 @@ suite('FileSearchEngine', () => { test('Files: include folder without wildcard #36438', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY_36438, includePattern: { 'modules/**': true } }); @@ -425,6 +445,7 @@ suite('FileSearchEngine', () => { test('Files: *.* exclude folder with leading wildcard', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', excludePattern: { '**/examples': true } @@ -445,6 +466,7 @@ suite('FileSearchEngine', () => { test('Files: *.* exclude folder with trailing wildcard', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', excludePattern: { 'examples/**': true } @@ -465,6 +487,7 @@ suite('FileSearchEngine', () => { test('Files: *.* exclude with unicode', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', excludePattern: { '**/üm laut汉语': true } @@ -485,6 +508,7 @@ suite('FileSearchEngine', () => { test('Files: *.* include with unicode', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', includePattern: { '**/üm laut汉语/*': true } @@ -504,7 +528,7 @@ suite('FileSearchEngine', () => { test('Files: multiroot with exclude', function (done: () => void) { this.timeout(testTimeout); - const folderQueries: IFolderSearch[] = [ + const folderQueries: IFolderQuery[] = [ { folder: EXAMPLES_FIXTURES, excludePattern: { @@ -520,6 +544,7 @@ suite('FileSearchEngine', () => { ]; const engine = new FileSearchEngine({ + type: QueryType.File, folderQueries, filePattern: '*' }); @@ -539,6 +564,7 @@ suite('FileSearchEngine', () => { test('Files: Unicode and Spaces', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: '汉语' }); @@ -561,6 +587,7 @@ suite('FileSearchEngine', () => { test('Files: no results', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: 'nofilematch' }); @@ -580,6 +607,7 @@ suite('FileSearchEngine', () => { test('Files: relative path matched once', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, filePattern: path.normalize(path.join('examples', 'company.js')) }); @@ -602,6 +630,7 @@ suite('FileSearchEngine', () => { test('Files: Include pattern, single files', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, includePattern: { 'site.css': true, @@ -626,11 +655,12 @@ suite('FileSearchEngine', () => { test('Files: extraFiles only', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: [], - extraFiles: [ - path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'site.css')), - path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'examples', 'company.js')), - path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'index.html')) + extraFileResources: [ + URI.file(path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'site.css'))), + URI.file(path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'examples', 'company.js'))), + URI.file(path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'index.html'))) ], filePattern: '*.js' }); @@ -653,11 +683,12 @@ suite('FileSearchEngine', () => { test('Files: extraFiles only (with include)', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: [], - extraFiles: [ - path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'site.css')), - path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'examples', 'company.js')), - path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'index.html')) + extraFileResources: [ + URI.file(path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'site.css'))), + URI.file(path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'examples', 'company.js'))), + URI.file(path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'index.html'))) ], filePattern: '*.*', includePattern: { '**/*.css': true } @@ -681,11 +712,12 @@ suite('FileSearchEngine', () => { test('Files: extraFiles only (with exclude)', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: [], - extraFiles: [ - path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'site.css')), - path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'examples', 'company.js')), - path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'index.html')) + extraFileResources: [ + URI.file(path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'site.css'))), + URI.file(path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'examples', 'company.js'))), + URI.file(path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'index.html'))) ], filePattern: '*.*', excludePattern: { '**/*.css': true } @@ -706,9 +738,10 @@ suite('FileSearchEngine', () => { test('Files: no dupes in nested folders', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ + type: QueryType.File, folderQueries: [ { folder: EXAMPLES_FIXTURES }, - { folder: path.join(EXAMPLES_FIXTURES, 'subfolder') } + { folder: joinPath(EXAMPLES_FIXTURES, 'subfolder') } ], filePattern: 'subfile.txt' }); @@ -738,14 +771,22 @@ suite('FileWalker', () => { const file0 = './more/file.txt'; const file1 = './examples/subfolder/subfile.txt'; - const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/something': true } }); + const walker = new FileWalker({ + type: QueryType.File, + folderQueries: ROOT_FOLDER_QUERY, + excludePattern: { '**/something': true } + }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); - const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder': true } }); + const walker = new FileWalker({ + type: QueryType.File, + folderQueries: ROOT_FOLDER_QUERY, + excludePattern: { '**/subfolder': true } + }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { assert.equal(err2, null); @@ -763,9 +804,9 @@ suite('FileWalker', () => { return; } - const folderQueries: IFolderSearch[] = [ + const folderQueries: IFolderQuery[] = [ { - folder: TEST_FIXTURES, + folder: URI.file(TEST_FIXTURES), excludePattern: { '**/subfolder': true } } ]; @@ -773,7 +814,7 @@ suite('FileWalker', () => { const file0 = './more/file.txt'; const file1 = './examples/subfolder/subfile.txt'; - const walker = new FileWalker({ folderQueries }); + const walker = new FileWalker({ type: QueryType.File, folderQueries }); const cmd1 = walker.spawnFindCmd(folderQueries[0]); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); @@ -794,7 +835,7 @@ suite('FileWalker', () => { const file1 = './examples/small.js'; const file2 = './more/file.txt'; - const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/something': true } }); + const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); @@ -802,7 +843,7 @@ suite('FileWalker', () => { assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file2), -1, stdout1); - const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '{**/examples,**/more}': true } }); + const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '{**/examples,**/more}': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { assert.equal(err2, null); @@ -824,14 +865,14 @@ suite('FileWalker', () => { const file0 = './examples/company.js'; const file1 = './examples/subfolder/subfile.txt'; - const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/examples/something': true } }); + const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/examples/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); - const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/examples/subfolder': true } }); + const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/examples/subfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { assert.equal(err2, null); @@ -852,14 +893,14 @@ suite('FileWalker', () => { const file0 = './examples/subfolder/subfile.txt'; const file1 = './examples/subfolder/anotherfolder/anotherfile.txt'; - const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/something': true } }); + const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); - const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/anotherfolder': true } }); + const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/anotherfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { assert.equal(err2, null); @@ -880,14 +921,14 @@ suite('FileWalker', () => { const file0 = './examples/company.js'; const file1 = './examples/subfolder/subfile.txt'; - const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { 'examples/something': true } }); + const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { 'examples/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); - const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { 'examples/subfolder': true } }); + const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { 'examples/subfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { assert.equal(err2, null); @@ -916,6 +957,7 @@ suite('FileWalker', () => { ]; const walker = new FileWalker({ + type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/anotherfolder': true, diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index 9e111e6d1c0..f4edf8a3bff 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -3,18 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; import * as assert from 'assert'; - -import * as glob from 'vs/base/common/glob'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; -import { ISerializedFileMatch, IRawSearch, IFolderSearch } from 'vs/workbench/services/search/node/search'; -import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch'; -import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; -import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider'; +import * as path from 'path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import * as glob from 'vs/base/common/glob'; +import { URI } from 'vs/base/common/uri'; +import { IFolderQuery, ISearchRange, ITextQuery, ITextSearchMatch, QueryType, ITextSearchContext, deserializeSearchError, SearchErrorCode } from 'vs/platform/search/common/search'; +import { LegacyTextSearchService } from 'vs/workbench/services/search/node/legacy/rawLegacyTextSearchService'; +import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search'; +import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; function countAll(matches: ISerializedFileMatch[]): number { return matches.reduce((acc, m) => acc + m.numMatches, 0); @@ -23,80 +21,65 @@ function countAll(matches: ISerializedFileMatch[]): number { const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures')); const EXAMPLES_FIXTURES = path.join(TEST_FIXTURES, 'examples'); const MORE_FIXTURES = path.join(TEST_FIXTURES, 'more'); -const TEST_ROOT_FOLDER: IFolderSearch = { folder: TEST_FIXTURES }; -const ROOT_FOLDER_QUERY: IFolderSearch[] = [ +const TEST_ROOT_FOLDER: IFolderQuery = { folder: URI.file(TEST_FIXTURES) }; +const ROOT_FOLDER_QUERY: IFolderQuery[] = [ TEST_ROOT_FOLDER ]; -const MULTIROOT_QUERIES: IFolderSearch[] = [ - { folder: EXAMPLES_FIXTURES }, - { folder: MORE_FIXTURES } +const MULTIROOT_QUERIES: IFolderQuery[] = [ + { folder: URI.file(EXAMPLES_FIXTURES) }, + { folder: URI.file(MORE_FIXTURES) } ]; -const textSearchWorkerProvider = new TextSearchWorkerProvider(); +function doLegacySearchTest(config: ITextQuery, expectedResultCount: number | Function): Promise { + const engine = new LegacyTextSearchService(); -function doLegacySearchTest(config: IRawSearch, expectedResultCount: number | Function): TPromise { - return new TPromise((resolve, reject) => { - let engine = new TextSearchEngine(config, new FileWalker({ ...config, useRipgrep: false }), textSearchWorkerProvider); - - let c = 0; - engine.search((result) => { - if (result) { - c += countAll(result); - } - }, () => { }, (error) => { - try { - assert.ok(!error); - if (typeof expectedResultCount === 'function') { - assert(expectedResultCount(c)); - } else { - assert.equal(c, expectedResultCount, 'legacy'); - } - } catch (e) { - reject(e); - } - - resolve(undefined); - }); + let c = 0; + return engine.textSearch(config, (result) => { + if (result && Array.isArray(result)) { + c += countAll(result); + } + }, null).then(() => { + if (typeof expectedResultCount === 'function') { + assert(expectedResultCount(c)); + } else { + assert.equal(c, expectedResultCount, 'legacy'); + } }); } -function doRipgrepSearchTest(config: IRawSearch, expectedResultCount: number | Function): TPromise { - return new TPromise((resolve, reject) => { - let engine = new TextSearchEngineAdapter(config); +function doRipgrepSearchTest(query: ITextQuery, expectedResultCount: number | Function): Promise { + let engine = new TextSearchEngineAdapter(query); - let c = 0; - engine.search(new CancellationTokenSource().token, (results) => { - if (results) { - c += results.reduce((acc, cur) => acc + cur.numMatches, 0); - } - }, () => { }, (error) => { - try { - assert.ok(!error); - if (typeof expectedResultCount === 'function') { - assert(expectedResultCount(c)); - } else { - assert.equal(c, expectedResultCount, `rg ${c} !== ${expectedResultCount}`); - } - } catch (e) { - reject(e); - } + let c = 0; + const results: ISerializedFileMatch[] = []; + return engine.search(new CancellationTokenSource().token, _results => { + if (_results) { + c += _results.reduce((acc, cur) => acc + cur.numMatches, 0); + results.push(..._results); + } + }, () => { }).then(() => { + if (typeof expectedResultCount === 'function') { + assert(expectedResultCount(c)); + } else { + assert.equal(c, expectedResultCount, `rg ${c} !== ${expectedResultCount}`); + } - resolve(undefined); - }); + return results; }); } -function doSearchTest(config: IRawSearch, expectedResultCount: number) { - return doLegacySearchTest(config, expectedResultCount) - .then(() => doRipgrepSearchTest(config, expectedResultCount)); +function doSearchTest(query: ITextQuery, expectedResultCount: number) { + return doLegacySearchTest(query, expectedResultCount) + .then(() => doRipgrepSearchTest(query, expectedResultCount)); } suite('Search-integration', function () { this.timeout(1000 * 60); // increase timeout for this suite test('Text: GameOfLife', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'GameOfLife' }, }; @@ -105,7 +88,8 @@ suite('Search-integration', function () { }); test('Text: GameOfLife (RegExp)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'Game.?fL\\w?fe', isRegExp: true } }; @@ -113,8 +97,20 @@ suite('Search-integration', function () { return doSearchTest(config, 4); }); + test('Text: GameOfLife (PCRE2 RegExp)', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + usePCRE2: true, + contentPattern: { pattern: 'Life(?!P)', isRegExp: true } + }; + + return doSearchTest(config, 8); + }); + test('Text: GameOfLife (RegExp to EOL)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'GameOfLife.*', isRegExp: true } }; @@ -123,7 +119,8 @@ suite('Search-integration', function () { }); test('Text: GameOfLife (Word Match, Case Sensitive)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'GameOfLife', isWordMatch: true, isCaseSensitive: true } }; @@ -132,7 +129,8 @@ suite('Search-integration', function () { }); test('Text: GameOfLife (Word Match, Spaces)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: ' GameOfLife ', isWordMatch: true } }; @@ -141,7 +139,8 @@ suite('Search-integration', function () { }); test('Text: GameOfLife (Word Match, Punctuation and Spaces)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: ', as =', isWordMatch: true } }; @@ -150,7 +149,8 @@ suite('Search-integration', function () { }); test('Text: Helvetica (UTF 16)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'Helvetica' } }; @@ -159,7 +159,8 @@ suite('Search-integration', function () { }); test('Text: e', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'e' } }; @@ -232,7 +233,8 @@ suite('Search-integration', function () { test('Text: a (capped)', () => { const maxResults = 520; - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'a' }, maxResults @@ -245,7 +247,8 @@ suite('Search-integration', function () { }); test('Text: a (no results)', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'ahsogehtdas' } }; @@ -254,7 +257,8 @@ suite('Search-integration', function () { }); test('Text: -size', () => { - const config = { + const config = { + type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: '-size' } }; @@ -263,7 +267,8 @@ suite('Search-integration', function () { }); test('Multiroot: Conway', () => { - const config: IRawSearch = { + const config: ITextQuery = { + type: QueryType.Text, folderQueries: MULTIROOT_QUERIES, contentPattern: { pattern: 'conway' } }; @@ -272,7 +277,8 @@ suite('Search-integration', function () { }); test('Multiroot: e with partial global exclude', () => { - const config: IRawSearch = { + const config: ITextQuery = { + type: QueryType.Text, folderQueries: MULTIROOT_QUERIES, contentPattern: { pattern: 'e' }, excludePattern: makeExpression('**/*.txt') @@ -282,7 +288,8 @@ suite('Search-integration', function () { }); test('Multiroot: e with global excludes', () => { - const config: IRawSearch = { + const config: ITextQuery = { + type: QueryType.Text, folderQueries: MULTIROOT_QUERIES, contentPattern: { pattern: 'e' }, excludePattern: makeExpression('**/*.txt', '**/*.js') @@ -292,16 +299,145 @@ suite('Search-integration', function () { }); test('Multiroot: e with folder exclude', () => { - const config: IRawSearch = { + const config: ITextQuery = { + type: QueryType.Text, folderQueries: [ - { folder: EXAMPLES_FIXTURES, excludePattern: makeExpression('**/e*.js') }, - { folder: MORE_FIXTURES } + { folder: URI.file(EXAMPLES_FIXTURES), excludePattern: makeExpression('**/e*.js') }, + { folder: URI.file(MORE_FIXTURES) } ], contentPattern: { pattern: 'e' } }; return doSearchTest(config, 286); }); + + test('Text: 语', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: '语' } + }; + + return doRipgrepSearchTest(config, 1).then(results => { + const matchRange = (results[0].results[0]).ranges; + assert.deepEqual(matchRange, [{ + startLineNumber: 0, + startColumn: 1, + endLineNumber: 0, + endColumn: 2 + }]); + }); + }); + + test('Multiple matches on line: h\\d,', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'h\\d,', isRegExp: true } + }; + + return doRipgrepSearchTest(config, 15).then(results => { + assert.equal(results.length, 3); + assert.equal(results[0].results.length, 1); + const match = results[0].results[0]; + assert.equal((match.ranges).length, 5); + }); + }); + + test('Search with context matches', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'compiler.typeCheck();' }, + beforeContext: 1, + afterContext: 2 + }; + + return doRipgrepSearchTest(config, 4).then(results => { + assert.equal(results.length, 4); + assert.equal((results[0].results[0]).lineNumber, 25); + assert.equal((results[0].results[0]).text, ' compiler.addUnit(prog,"input.ts");'); + // assert.equal((results[1].results[0]).preview.text, ' compiler.typeCheck();\n'); // See https://github.com/BurntSushi/ripgrep/issues/1095 + assert.equal((results[2].results[0]).lineNumber, 27); + assert.equal((results[2].results[0]).text, ' compiler.emit();'); + assert.equal((results[3].results[0]).lineNumber, 28); + assert.equal((results[3].results[0]).text, ''); + }); + }); + + suite('error messages', () => { + test('invalid encoding', () => { + const config = { + type: QueryType.Text, + folderQueries: [ + { + ...TEST_ROOT_FOLDER, + fileEncoding: 'invalidEncoding' + } + ], + contentPattern: { pattern: 'test' }, + }; + + return doRipgrepSearchTest(config, 0).then(() => { + throw new Error('expected fail'); + }, err => { + const searchError = deserializeSearchError(err.message); + assert.equal(searchError.message, 'Unknown encoding: invalidEncoding'); + assert.equal(searchError.code, SearchErrorCode.unknownEncoding); + }); + }); + + test('invalid regex', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: ')', isRegExp: true }, + }; + + return doRipgrepSearchTest(config, 0).then(() => { + throw new Error('expected fail'); + }, err => { + const searchError = deserializeSearchError(err.message); + assert.equal(searchError.message, 'Regex parse error'); + assert.equal(searchError.code, SearchErrorCode.regexParseError); + }); + }); + + test('invalid glob', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'foo' }, + includePattern: { + '***': true + } + }; + + return doRipgrepSearchTest(config, 0).then(() => { + throw new Error('expected fail'); + }, err => { + const searchError = deserializeSearchError(err.message); + assert.equal(searchError.message, 'Error parsing glob \'***\': invalid use of **; must be one path component'); + assert.equal(searchError.code, SearchErrorCode.globParseError); + }); + }); + + test('invalid literal', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'foo\nbar', isRegExp: true } + }; + + return doRipgrepSearchTest(config, 0).then(() => { + throw new Error('expected fail'); + }, err => { + const searchError = deserializeSearchError(err.message); + assert.equal(searchError.message, 'The literal \'"\\n"\' is not allowed in a regex'); + assert.equal(searchError.code, SearchErrorCode.invalidLiteral); + }); + }); + }); }); function makeExpression(...patterns: string[]): glob.IExpression { diff --git a/src/vs/workbench/services/search/test/node/textSearchManager.test.ts b/src/vs/workbench/services/search/test/node/textSearchManager.test.ts new file mode 100644 index 00000000000..2b2dcf13734 --- /dev/null +++ b/src/vs/workbench/services/search/test/node/textSearchManager.test.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { URI } from 'vs/base/common/uri'; +import { ITextQuery, QueryType } from 'vs/platform/search/common/search'; +import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager'; +import * as vscode from 'vscode'; + +suite('TextSearchManager', () => { + test('fixes encoding', async () => { + let correctEncoding = false; + const provider: vscode.TextSearchProvider = { + provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): vscode.ProviderResult { + correctEncoding = options.encoding === 'windows-1252'; + + return null; + } + }; + + const query: ITextQuery = { + type: QueryType.Text, + contentPattern: { + pattern: 'a' + }, + folderQueries: [{ + folder: URI.file('/some/folder'), + fileEncoding: 'windows1252' + }] + }; + + const m = new TextSearchManager(query, provider); + await m.search(() => { }, new CancellationTokenSource().token); + + assert.ok(correctEncoding); + }); +}); diff --git a/src/vs/workbench/services/textMate/electron-browser/TMGrammars.ts b/src/vs/workbench/services/textMate/electron-browser/TMGrammars.ts index 1fa0285bca2..3e6290686b4 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMGrammars.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMGrammars.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IExtensionPoint, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { ExtensionsRegistry, IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchModeService'; export interface IEmbeddedLanguagesMap { diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts index fec951db8c9..a0e060e0dde 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -5,26 +5,26 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; -import * as types from 'vs/base/common/types'; -import * as resources from 'vs/base/common/resources'; -import { Event, Emitter } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { Color } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ITokenizationSupport, TokenizationRegistry, IState, LanguageId, TokenMetadata } from 'vs/editor/common/modes'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { StackElement, IGrammar, Registry, IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2, ITokenTypeMap, StandardTokenType } from 'vscode-textmate'; -import { IWorkbenchThemeService, ITokenColorizationRule } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; -import { grammarsExtPoint, IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint, TokenTypesContribution } from 'vs/workbench/services/textMate/electron-browser/TMGrammars'; +import { Emitter, Event } from 'vs/base/common/event'; +import * as resources from 'vs/base/common/resources'; +import * as types from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; +import { IState, ITokenizationSupport, LanguageId, TokenMetadata, TokenizationRegistry } from 'vs/editor/common/modes'; import { nullTokenize2 } from 'vs/editor/common/modes/nullMode'; import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; -import { Color } from 'vs/base/common/color'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { URI } from 'vs/base/common/uri'; +import { IModeService } from 'vs/editor/common/services/modeService'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint, TokenTypesContribution, grammarsExtPoint } from 'vs/workbench/services/textMate/electron-browser/TMGrammars'; +import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; +import { ITokenColorizationRule, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2, IGrammar, ITokenTypeMap, Registry, StackElement, StandardTokenType } from 'vscode-textmate'; export class TMScopeRegistry { @@ -135,7 +135,7 @@ interface ICreateGrammarResult { export class TextMateService implements ITextMateService { public _serviceBrand: any; - private _grammarRegistry: TPromise<[Registry, StackElement]>; + private _grammarRegistry: Promise<[Registry, StackElement]>; private _modeService: IModeService; private _themeService: IWorkbenchThemeService; private _fileService: IFileService; @@ -157,7 +157,8 @@ export class TextMateService implements ITextMateService { @IWorkbenchThemeService themeService: IWorkbenchThemeService, @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, - @ILogService logService: ILogService + @ILogService logService: ILogService, + @IExtensionService extensionService: IExtensionService ) { this._styleElement = dom.createStyleSheet(); this._styleElement.className = 'vscode-tokens-styles'; @@ -202,15 +203,18 @@ export class TextMateService implements ITextMateService { this._modeService.onDidCreateMode((mode) => { let modeId = mode.getId(); - if (this._languageToScope.has(modeId)) { - this.registerDefinition(modeId); - } + // Modes can be instantiated before the extension points have finished registering + extensionService.whenInstalledExtensionsRegistered().then(() => { + if (this._languageToScope.has(modeId)) { + this.registerDefinition(modeId); + } + }); }); } - private _getOrCreateGrammarRegistry(): TPromise<[Registry, StackElement]> { + private _getOrCreateGrammarRegistry(): Promise<[Registry, StackElement]> { if (!this._grammarRegistry) { - this._grammarRegistry = TPromise.wrap(import('vscode-textmate')).then(({ Registry, INITIAL, parseRawGrammar }) => { + this._grammarRegistry = import('vscode-textmate').then(({ Registry, INITIAL, parseRawGrammar }) => { const grammarRegistry = new Registry({ loadGrammar: (scopeName: string) => { const location = this._scopeRegistry.getGrammarLocation(scopeName); @@ -226,7 +230,13 @@ export class TextMateService implements ITextMateService { }); }, getInjections: (scopeName: string) => { - return this._injections[scopeName]; + const scopeParts = scopeName.split('.'); + let injections: string[] = []; + for (let i = 1; i <= scopeParts.length; i++) { + const subScopeName = scopeParts.slice(0, i).join('.'); + injections = [...injections, ...this._injections[subScopeName]]; + } + return injections; } }); this._updateTheme(grammarRegistry); @@ -358,16 +368,16 @@ export class TextMateService implements ITextMateService { return result; } - public createGrammar(modeId: string): TPromise { + public createGrammar(modeId: string): Promise { return this._createGrammar(modeId).then(r => r.grammar); } - private _createGrammar(modeId: string): TPromise { + private _createGrammar(modeId: string): Promise { let scopeName = this._languageToScope.get(modeId); let languageRegistration = this._scopeRegistry.getLanguageRegistration(scopeName); if (!languageRegistration) { // No TM grammar defined - return TPromise.wrapError(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); + return Promise.reject(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); } let embeddedLanguages = this._resolveEmbeddedLanguages(languageRegistration.embeddedLanguages); let rawInjectedEmbeddedLanguages = this._injectedEmbeddedLanguages[scopeName]; diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts index f7aa3f42f5b..79c0a7e0623 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts @@ -3,11 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Event } from 'vs/base/common/event'; +import { LanguageId } from 'vs/editor/common/modes'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IGrammar } from 'vscode-textmate'; -import { LanguageId } from 'vs/editor/common/modes'; export const ITextMateService = createDecorator('textMateService'); @@ -16,5 +15,5 @@ export interface ITextMateService { onDidEncounterLanguage: Event; - createGrammar(modeId: string): TPromise; + createGrammar(modeId: string): Promise; } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index f6e908bede0..9bc56c2fce5 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -11,7 +11,6 @@ import { guessMimeTypes } from 'vs/base/common/mime'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { URI } from 'vs/base/common/uri'; import { isUndefinedOrNull } from 'vs/base/common/types'; -import { IMode } from 'vs/editor/common/modes'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, IRawTextContent, ILoadOptions, LoadReason } from 'vs/workbench/services/textfile/common/textfiles'; @@ -20,7 +19,7 @@ import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel' import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IFileService, IFileStat, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RunOnceScheduler, timeout } from 'vs/base/common/async'; @@ -32,6 +31,7 @@ import { isLinux } from 'vs/base/common/platform'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { isEqual, isEqualOrParent } from 'vs/base/common/resources'; +import { onUnexpectedError } from 'vs/base/common/errors'; /** * The text file editor model listens to changes to its underlying code editor model and saves these changes through the file service back to the disk. @@ -196,9 +196,9 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } const firstLineText = this.getFirstLineText(this.textEditorModel); - const mode = this.getOrCreateMode(this.modeService, void 0, firstLineText); + const languageSelection = this.getOrCreateMode(this.modeService, void 0, firstLineText); - this.modelService.setMode(this.textEditorModel, mode); + this.modelService.setMode(this.textEditorModel, languageSelection); } getVersionId(): number { @@ -492,8 +492,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.backupFileService.resolveBackupContent(backup).then(backupContent => backupContent, error => null /* ignore errors */); } - protected getOrCreateMode(modeService: IModeService, preferredModeIds: string, firstLineText?: string): Promise { - return modeService.getOrCreateModeByFilepathOrFirstLine(this.resource.fsPath, firstLineText); + protected getOrCreateMode(modeService: IModeService, preferredModeIds: string, firstLineText?: string): ILanguageSelection { + return modeService.createByFilepathOrFirstLine(this.resource.fsPath, firstLineText); } private onModelContentChanged(): void { @@ -846,7 +846,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Updated resolved stat with updated stat since touching it might have changed mtime this.updateLastResolvedDiskStat(stat); - }, () => void 0 /* gracefully ignore errors if just touching */)); + }, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */)); } private setDirty(dirty: boolean): () => void { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index e551eaa5671..766d4e482b3 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -32,7 +32,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { IModelService } from 'vs/editor/common/services/modelService'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { isEqualOrParent, isEqual, joinPath } from 'vs/base/common/resources'; +import { isEqualOrParent, isEqual, joinPath, dirname } from 'vs/base/common/resources'; export interface IBackupResult { didBackup: boolean; @@ -630,7 +630,8 @@ export abstract class TextFileService extends Disposable implements ITextFileSer const lastActiveFile = this.historyService.getLastActiveFile(schemeFilter); if (lastActiveFile) { - return joinPath(lastActiveFile, untitledFileName); + const lastDir = dirname(lastActiveFile); + return joinPath(lastDir, untitledFileName); } const lastActiveFolder = this.historyService.getLastActiveWorkspaceRoot(schemeFilter); diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 727fcccd182..05065018f26 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -15,7 +15,7 @@ import { ITextBufferFactory } from 'vs/editor/common/model'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; /** - * The save error handler can be installed on the text text file editor model to install code that executes when save errors occur. + * The save error handler can be installed on the text file editor model to install code that executes when save errors occur. */ export interface ISaveErrorHandler { @@ -34,7 +34,7 @@ export interface ISaveParticipant { } /** - * States the text text file editor model can be in. + * States the text file editor model can be in. */ export const enum ModelState { SAVED, diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index 6245c20d21c..78f73788172 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -17,7 +17,6 @@ import { ITextModelService, ITextModelContentProvider, ITextEditorModel } from ' import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { IFileService } from 'vs/platform/files/common/files'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; class ResourceModelCollection extends ReferenceCollection> { @@ -27,13 +26,12 @@ class ResourceModelCollection extends ReferenceCollection { + createReferencedObject(key: string, skipActivateProvider?: boolean): TPromise { this.modelsToDispose.delete(key); const resource = URI.parse(key); @@ -48,9 +46,9 @@ class ResourceModelCollection extends ReferenceCollection this.instantiationService.createInstance(ResourceEditorModel, resource)); } - // Either unknown schema, or not yet registered - if (!skipActivateExtensions) { - return this._extensionService.activateByEvent('onFileSystem:' + resource.scheme).then(() => this.createReferencedObject(key, true)); + // Either unknown schema, or not yet registered, try to activate + if (!skipActivateProvider) { + return this.fileService.activateProvider(resource.scheme).then(() => this.createReferencedObject(key, true)); } return TPromise.wrapError(new Error('resource is not available')); diff --git a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts index acf1e5bfce1..d36b54ecdc3 100644 --- a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts @@ -59,8 +59,8 @@ suite('Workbench - TextModelResolverService', () => { provideTextContent: function (resource: URI): TPromise { if (resource.scheme === 'test') { let modelContent = 'Hello Test'; - let mode = accessor.modeService.getOrCreateMode('json'); - return TPromise.as(accessor.modelService.createModel(modelContent, mode, resource)); + let languageSelection = accessor.modeService.create('json'); + return TPromise.as(accessor.modelService.createModel(modelContent, languageSelection, resource)); } return TPromise.as(null); @@ -131,14 +131,14 @@ suite('Workbench - TextModelResolverService', () => { test('even loading documents should be refcounted', async () => { let resolveModel: Function; - let waitForIt = new TPromise(c => resolveModel = c); + let waitForIt = new Promise(c => resolveModel = c); const disposable = accessor.textModelResolverService.registerTextModelContentProvider('test', { provideTextContent: (resource: URI): TPromise => { return waitForIt.then(_ => { let modelContent = 'Hello Test'; - let mode = accessor.modeService.getOrCreateMode('json'); - return accessor.modelService.createModel(modelContent, mode, resource); + let languageSelection = accessor.modeService.create('json'); + return accessor.modelService.createModel(modelContent, languageSelection, resource); }); } }); diff --git a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts index 99346badcb6..41795ef77f0 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts @@ -29,7 +29,7 @@ const schema: IJSONSchema = { }, folderNames: { type: 'object', - description: nls.localize('schema.folderNames', 'Associates folder names to icons. The object key is is the folder name, not including any path segments. No patterns or wildcards are allowed. Folder name matching is case insensitive.'), + description: nls.localize('schema.folderNames', 'Associates folder names to icons. The object key is the folder name, not including any path segments. No patterns or wildcards are allowed. Folder name matching is case insensitive.'), additionalProperties: { type: 'string', description: nls.localize('schema.folderName', 'The ID of the icon definition for the association.') @@ -37,7 +37,7 @@ const schema: IJSONSchema = { }, folderNamesExpanded: { type: 'object', - description: nls.localize('schema.folderNamesExpanded', 'Associates folder names to icons for expanded folders. The object key is is the folder name, not including any path segments. No patterns or wildcards are allowed. Folder name matching is case insensitive.'), + description: nls.localize('schema.folderNamesExpanded', 'Associates folder names to icons for expanded folders. The object key is the folder name, not including any path segments. No patterns or wildcards are allowed. Folder name matching is case insensitive.'), additionalProperties: { type: 'string', description: nls.localize('schema.folderNameExpanded', 'The ID of the icon definition for the association.') @@ -45,7 +45,7 @@ const schema: IJSONSchema = { }, fileExtensions: { type: 'object', - description: nls.localize('schema.fileExtensions', 'Associates file extensions to icons. The object key is is the file extension name. The extension name is the last segment of a file name after the last dot (not including the dot). Extensions are compared case insensitive.'), + description: nls.localize('schema.fileExtensions', 'Associates file extensions to icons. The object key is the file extension name. The extension name is the last segment of a file name after the last dot (not including the dot). Extensions are compared case insensitive.'), additionalProperties: { type: 'string', @@ -54,7 +54,7 @@ const schema: IJSONSchema = { }, fileNames: { type: 'object', - description: nls.localize('schema.fileNames', 'Associates file names to icons. The object key is is the full file name, but not including any path segments. File name can include dots and a possible file extension. No patterns or wildcards are allowed. File name matching is case insensitive.'), + description: nls.localize('schema.fileNames', 'Associates file names to icons. The object key is the full file name, but not including any path segments. File name can include dots and a possible file extension. No patterns or wildcards are allowed. File name matching is case insensitive.'), additionalProperties: { type: 'string', diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 287af47691f..71d5dd447e1 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -6,7 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { Color } from 'vs/base/common/color'; -import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; +import { ITheme, IThemeService, IIconTheme } from 'vs/platform/theme/common/themeService'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; export const IWorkbenchThemeService = createDecorator('themeService'); @@ -38,7 +38,7 @@ export interface IColorMap { [id: string]: Color; } -export interface IFileIconTheme { +export interface IFileIconTheme extends IIconTheme { readonly id: string; readonly label: string; readonly settingsId: string; @@ -46,9 +46,9 @@ export interface IFileIconTheme { readonly extensionData: ExtensionData; readonly isLoaded: boolean; - readonly hasFileIcons?: boolean; - readonly hasFolderIcons?: boolean; - readonly hidesExplorerArrows?: boolean; + readonly hasFileIcons: boolean; + readonly hasFolderIcons: boolean; + readonly hidesExplorerArrows: boolean; } export interface IWorkbenchThemeService extends IThemeService { diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts index 71404c276f3..5b4136989fb 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts @@ -17,9 +17,9 @@ export class FileIconThemeData implements IFileIconTheme { label: string; settingsId: string; description?: string; - hasFileIcons?: boolean; - hasFolderIcons?: boolean; - hidesExplorerArrows?: boolean; + hasFileIcons: boolean; + hasFolderIcons: boolean; + hidesExplorerArrows: boolean; isLoaded: boolean; location?: URI; extensionData: ExtensionData; diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 51525c6dd56..e312eda08d3 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -191,6 +191,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.onFileIconThemeChange.event; } + public get onIconThemeChange(): Event { + return this.onFileIconThemeChange.event; + } + public get onThemeChange(): Event { return this.onColorThemeChange.event; } @@ -394,6 +398,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.currentIconTheme; } + public getIconTheme() { + return this.currentIconTheme; + } + public setFileIconTheme(iconTheme: string, settingsTarget: ConfigurationTarget): Thenable { iconTheme = iconTheme || ''; if (iconTheme === this.currentIconTheme.id && this.currentIconTheme.isLoaded) { diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts index 96d717f4cbe..275296b9220 100644 --- a/src/vs/workbench/services/timer/electron-browser/timerService.ts +++ b/src/vs/workbench/services/timer/electron-browser/timerService.ts @@ -53,6 +53,8 @@ export interface IMemoryInfo { "timers.ellapsedExtensions" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedExtensionsReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWorkspaceStorageRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWorkspaceStorageInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedViewletRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedPanelRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedEditorRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, @@ -192,6 +194,23 @@ export interface IStartupMetrics { */ ellapsedWindowLoadToRequire: number; + /** + * The time it took to require the workspace storage DB. + * + * * Happens in the renderer-process + * * Measured with the `willRequireSQLite` and `didRequireSQLite` performance marks. + */ + ellapsedWorkspaceStorageRequire: number; + + /** + * The time it took to require the workspace storage DB, connect to it + * and load the initial set of values. + * + * * Happens in the renderer-process + * * Measured with the `willInitWorkspaceStorage` and `didInitWorkspaceStorage` performance marks. + */ + ellapsedWorkspaceStorageInit: number; + /** * The time it took to load the main-bundle of the workbench, e.g `workbench.main.js`. * @@ -369,6 +388,8 @@ class TimerService implements ITimerService { ellapsedWindowLoad: initialStartup ? perf.getDuration('main:appReady', 'main:loadWindow') : undefined, ellapsedWindowLoadToRequire: perf.getDuration('main:loadWindow', 'willLoadWorkbenchMain'), ellapsedRequire: perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'), + ellapsedWorkspaceStorageRequire: perf.getDuration('willRequireSQLite', 'didRequireSQLite'), + ellapsedWorkspaceStorageInit: perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage'), ellapsedExtensions: perf.getDuration('willLoadExtensions', 'didLoadExtensions'), ellapsedEditorRestore: perf.getDuration('willRestoreEditors', 'didRestoreEditors'), ellapsedViewletRestore: perf.getDuration('willRestoreViewlet', 'didRestoreViewlet'), @@ -397,7 +418,7 @@ class TimerService implements ITimerService { export const ITimerService = createDecorator('timerService'); -registerSingleton(ITimerService, TimerService); +registerSingleton(ITimerService, TimerService, true); //#region cached data logic diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index 7ae5e2acc4f..88e0be01c41 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -22,7 +22,7 @@ export interface IViewletService { /** * Opens a viewlet with the given identifier and pass keyboard focus to it if specified. */ - openViewlet(id: string, focus?: boolean): Promise; + openViewlet(id: string, focus?: boolean): Thenable; /** * Returns the current active viewlet or null if none. diff --git a/src/vs/workbench/services/viewlet/browser/viewletService.ts b/src/vs/workbench/services/viewlet/browser/viewletService.ts index cde5c0470bf..7b2a42f7390 100644 --- a/src/vs/workbench/services/viewlet/browser/viewletService.ts +++ b/src/vs/workbench/services/viewlet/browser/viewletService.ts @@ -68,17 +68,17 @@ export class ViewletService extends Disposable implements IViewletService { } } - openViewlet(id: string, focus?: boolean): Promise { + openViewlet(id: string, focus?: boolean): Thenable { if (this.getViewlet(id)) { return Promise.resolve(this.sidebarPart.openViewlet(id, focus)); } - return Promise.resolve(this.extensionService.whenInstalledExtensionsRegistered() + return this.extensionService.whenInstalledExtensionsRegistered() .then(() => { if (this.getViewlet(id)) { return this.sidebarPart.openViewlet(id, focus); } return null; - })); + }); } getActiveViewlet(): IViewlet { diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 443b591a4a9..b3b79954727 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -13,8 +13,8 @@ import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/ import { IWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; -import { migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/storageLegacyMigration'; -import { IStorageLegacyService, StorageLegacyService } from 'vs/platform/storage/common/storageLegacyService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { DelegatingStorageService } from 'vs/platform/storage/node/storageService'; import { ConfigurationScope, IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -25,6 +25,9 @@ import { distinct } from 'vs/base/common/arrays'; import { isLinux } from 'vs/base/common/platform'; import { isEqual } from 'vs/base/common/resources'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { join } from 'path'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { mkdirp } from 'vs/base/node/pfs'; export class WorkspaceEditingService implements IWorkspaceEditingService { @@ -35,11 +38,12 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { @IWorkspaceContextService private contextService: WorkspaceService, @IWindowService private windowService: IWindowService, @IWorkspaceConfigurationService private workspaceConfigurationService: IWorkspaceConfigurationService, - @IStorageLegacyService private storageLegacyService: IStorageLegacyService, + @IStorageService private storageService: IStorageService, @IExtensionService private extensionService: IExtensionService, @IBackupFileService private backupFileService: IBackupFileService, @INotificationService private notificationService: INotificationService, - @ICommandService private commandService: ICommandService + @ICommandService private commandService: ICommandService, + @IEnvironmentService private environmentService: IEnvironmentService ) { } @@ -163,6 +167,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return TPromise.as(void 0); } this.notificationService.error(error.message); + return TPromise.as(void 0); } @@ -218,29 +223,33 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { if (!extensionHostStarted) { startExtensionHost(); // start the extension host if not started } + return TPromise.wrapError(error); }); } - private migrate(toWorkspace: IWorkspaceIdentifier): TPromise { + private migrate(toWorkspace: IWorkspaceIdentifier): Thenable { - // Storage (UI State) migration - this.migrateStorage(toWorkspace); + // Storage migration + return this.migrateStorage(toWorkspace).then(() => { - // Settings migration (only if we come from a folder workspace) - if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { - return this.migrateWorkspaceSettings(toWorkspace); - } + // Settings migration (only if we come from a folder workspace) + if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { + return this.migrateWorkspaceSettings(toWorkspace); + } - return TPromise.as(void 0); + return void 0; + }); } - private migrateStorage(toWorkspace: IWorkspaceIdentifier): void { + private migrateStorage(toWorkspace: IWorkspaceIdentifier): Thenable { + const newWorkspaceStorageHome = join(this.environmentService.workspaceStorageHome, toWorkspace.id); - // TODO@Ben revisit this when we move away from local storage to a file based approach - const storageImpl = this.storageLegacyService as StorageLegacyService; - const newWorkspaceId = migrateStorageToMultiRootWorkspace(storageImpl.workspaceId, toWorkspace, storageImpl.workspaceStorage); - storageImpl.setWorkspaceId(newWorkspaceId); + return mkdirp(newWorkspaceStorageHome).then(() => { + const storageImpl = this.storageService as DelegatingStorageService; + + return storageImpl.storage.migrate(newWorkspaceStorageHome); + }); } private migrateWorkspaceSettings(toWorkspace: IWorkspaceIdentifier): TPromise { diff --git a/src/vs/workbench/test/common/editor/editorDiffModel.test.ts b/src/vs/workbench/test/common/editor/editorDiffModel.test.ts index 68953e620bc..4594a22b024 100644 --- a/src/vs/workbench/test/common/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/common/editor/editorDiffModel.test.ts @@ -41,8 +41,8 @@ suite('Workbench editor model', () => { provideTextContent: function (resource: URI): TPromise { if (resource.scheme === 'test') { let modelContent = 'Hello Test'; - let mode = accessor.modeService.getOrCreateMode('json'); - return TPromise.as(accessor.modelService.createModel(modelContent, mode, resource)); + let languageSelection = accessor.modeService.create('json'); + return TPromise.as(accessor.modelService.createModel(modelContent, languageSelection, resource)); } return TPromise.as(null); diff --git a/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts index b43a41d5264..873c498d0aa 100644 --- a/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts +++ b/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts @@ -33,7 +33,7 @@ suite('Workbench resource editor input', () => { test('simple', () => { let resource = URI.from({ scheme: 'inmemory', authority: null, path: 'thePath' }); - accessor.modelService.createModel('function test() {}', accessor.modeService.getOrCreateMode('text'), resource); + accessor.modelService.createModel('function test() {}', accessor.modeService.create('text'), resource); let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource); return input.resolve().then((model: ResourceEditorModel) => { diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index d69dd86e87b..8dfcbd3d4c4 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -30,6 +30,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import 'vs/workbench/parts/search/electron-browser/search.contribution'; import { NullLogService } from 'vs/platform/log/common/log'; import { ITextModel } from 'vs/editor/common/model'; +import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -158,7 +159,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('WorkspaceSymbols, back and forth', function () { - disposables.push(extHost.registerWorkspaceSymbolProvider({ + disposables.push(extHost.registerWorkspaceSymbolProvider(nullExtensionDescription, { provideWorkspaceSymbols(query): any { return [ new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/first')), @@ -167,7 +168,7 @@ suite('ExtHostLanguageFeatureCommands', function () { } })); - disposables.push(extHost.registerWorkspaceSymbolProvider({ + disposables.push(extHost.registerWorkspaceSymbolProvider(nullExtensionDescription, { provideWorkspaceSymbols(query): any { return [ new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/first')) @@ -190,7 +191,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('executeWorkspaceSymbolProvider should accept empty string, #39522', async function () { - disposables.push(extHost.registerWorkspaceSymbolProvider({ + disposables.push(extHost.registerWorkspaceSymbolProvider(nullExtensionDescription, { provideWorkspaceSymbols(query) { return [new types.SymbolInformation('hello', types.SymbolKind.Array, new types.Range(0, 0, 0, 0), URI.parse('foo:bar'))]; } @@ -220,12 +221,12 @@ suite('ExtHostLanguageFeatureCommands', function () { test('Definition, back and forth', function () { - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(nullExtensionDescription, defaultSelector, { provideDefinition(doc: any): any { return new types.Location(doc.uri, new types.Range(0, 0, 0, 0)); } })); - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(nullExtensionDescription, defaultSelector, { provideDefinition(doc: any): any { return [ new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), @@ -261,12 +262,12 @@ suite('ExtHostLanguageFeatureCommands', function () { test('Type Definition, back and forth', function () { - disposables.push(extHost.registerTypeDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerTypeDefinitionProvider(nullExtensionDescription, defaultSelector, { provideTypeDefinition(doc: any): any { return new types.Location(doc.uri, new types.Range(0, 0, 0, 0)); } })); - disposables.push(extHost.registerTypeDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerTypeDefinitionProvider(nullExtensionDescription, defaultSelector, { provideTypeDefinition(doc: any): any { return [ new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), @@ -291,7 +292,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('reference search, back and forth', function () { - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(nullExtensionDescription, defaultSelector, { provideReferences(doc: any) { return [ new types.Location(URI.parse('some:uri/path'), new types.Range(0, 1, 0, 5)) @@ -313,7 +314,7 @@ suite('ExtHostLanguageFeatureCommands', function () { // --- outline test('Outline, back and forth', function () { - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(nullExtensionDescription, defaultSelector, { provideDocumentSymbols(): any { return [ new types.SymbolInformation('testing1', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0)), @@ -335,14 +336,14 @@ suite('ExtHostLanguageFeatureCommands', function () { }); test('vscode.executeDocumentSymbolProvider command only returns SymbolInformation[] rather than DocumentSymbol[] #57984', function () { - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(nullExtensionDescription, defaultSelector, { provideDocumentSymbols(): any { return [ new types.SymbolInformation('SymbolInformation', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0)) ]; } })); - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(nullExtensionDescription, defaultSelector, { provideDocumentSymbols(): any { let root = new types.DocumentSymbol('DocumentSymbol', 'DocumentSymbol#detail', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0), new types.Range(1, 0, 1, 0)); root.children = [new types.DocumentSymbol('DocumentSymbol#child', 'DocumentSymbol#detail#child', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0), new types.Range(1, 0, 1, 0))]; @@ -367,7 +368,7 @@ suite('ExtHostLanguageFeatureCommands', function () { // --- suggest test('Suggest, back and forth', function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(doc, pos): any { let a = new types.CompletionItem('item1'); let b = new types.CompletionItem('item2'); @@ -425,7 +426,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); test('Suggest, return CompletionList !array', function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { let a = new types.CompletionItem('item1'); let b = new types.CompletionItem('item2'); @@ -445,7 +446,7 @@ suite('ExtHostLanguageFeatureCommands', function () { let resolveCount = 0; - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { let a = new types.CompletionItem('item1'); let b = new types.CompletionItem('item2'); @@ -475,7 +476,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); test('"vscode.executeCompletionItemProvider" doesnot return a preselect field #53749', async function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { let a = new types.CompletionItem('item1'); a.preselect = true; @@ -507,7 +508,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); test('executeCompletionItemProvider doesn\'t capture commitCharacters #58228', async function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { let a = new types.CompletionItem('item1'); a.commitCharacters = ['a', 'b']; @@ -536,7 +537,7 @@ suite('ExtHostLanguageFeatureCommands', function () { // --- quickfix test('QuickFix, back and forth', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { provideCodeActions(): vscode.Command[] { return [{ command: 'testing', title: 'Title', arguments: [1, 2, true] }]; } @@ -554,7 +555,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); test('vscode.executeCodeActionProvider results seem to be missing their `command` property #45124', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { provideCodeActions(document, range): vscode.CodeAction[] { return [{ command: { @@ -592,7 +593,7 @@ suite('ExtHostLanguageFeatureCommands', function () { big: extHost }; - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(nullExtensionDescription, defaultSelector, { provideCodeLenses(): any { return [new types.CodeLens(new types.Range(0, 0, 1, 1), { title: 'Title', command: 'cmd', arguments: [1, true, complexArg] })]; } @@ -616,7 +617,7 @@ suite('ExtHostLanguageFeatureCommands', function () { let resolveCount = 0; - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(nullExtensionDescription, defaultSelector, { provideCodeLenses(): any { return [ new types.CodeLens(new types.Range(0, 0, 1, 1)), @@ -648,7 +649,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('Links, back and forth', function () { - disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + disposables.push(extHost.registerDocumentLinkProvider(nullExtensionDescription, defaultSelector, { provideDocumentLinks(): any { return [new types.DocumentLink(new types.Range(0, 0, 0, 20), URI.parse('foo:bar'))]; } @@ -671,7 +672,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('Color provider', function () { - disposables.push(extHost.registerColorProvider(defaultSelector, { + disposables.push(extHost.registerColorProvider(nullExtensionDescription, defaultSelector, { provideDocumentColors(): vscode.ColorInformation[] { return [new types.ColorInformation(new types.Range(0, 0, 0, 20), new types.Color(0.1, 0.2, 0.3, 0.4))]; }, @@ -721,7 +722,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('"TypeError: e.onCancellationRequested is not a function" calling hover provider in Insiders #54174', function () { - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(nullExtensionDescription, defaultSelector, { provideHover(): any { return new types.Hover('fofofofo'); } diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 95ef6e612b1..6f1e04113c8 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -8,7 +8,6 @@ import { URI } from 'vs/base/common/uri'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { MainThreadConfigurationShape, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { TestRPCProtocol } from './testRPCProtocol'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; @@ -22,9 +21,9 @@ suite('ExtHostConfiguration', function () { class RecordingShape extends mock() { lastArgs: [ConfigurationTarget, string, any]; - $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise { + $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): Promise { this.lastArgs = [target, key, value]; - return TPromise.as(void 0); + return Promise.resolve(void 0); } } @@ -576,8 +575,8 @@ suite('ExtHostConfiguration', function () { test('update/error-state not OK', function () { const shape = new class extends mock() { - $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise { - return TPromise.wrapError(new Error('Unknown Key')); // something !== OK + $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): Promise { + return Promise.reject(new Error('Unknown Key')); // something !== OK } }; diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index f259a4d89b9..98562920079 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -44,6 +44,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { ITextModel, EndOfLineSequence } from 'vs/editor/common/model'; import { getColors } from 'vs/editor/contrib/colorPicker/color'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { nullExtensionDescription as defaultExtension } from 'vs/workbench/services/extensions/common/extensions'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -132,7 +133,7 @@ suite('ExtHostLanguageFeatures', function () { test('DocumentSymbols, register/deregister', function () { assert.equal(modes.DocumentSymbolProviderRegistry.all(model).length, 0); - let d1 = extHost.registerDocumentSymbolProvider(defaultSelector, { + let d1 = extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, { provideDocumentSymbols() { return []; } @@ -147,12 +148,12 @@ suite('ExtHostLanguageFeatures', function () { }); test('DocumentSymbols, evil provider', function () { - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, { provideDocumentSymbols(): any { throw new Error('evil document symbol provider'); } })); - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, { provideDocumentSymbols(): any { return [new types.SymbolInformation('test', types.SymbolKind.Field, new types.Range(0, 0, 0, 0))]; } @@ -167,7 +168,7 @@ suite('ExtHostLanguageFeatures', function () { }); test('DocumentSymbols, data conversion', function () { - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, { provideDocumentSymbols(): any { return [new types.SymbolInformation('test', types.SymbolKind.Field, new types.Range(0, 0, 0, 0))]; } @@ -189,12 +190,12 @@ suite('ExtHostLanguageFeatures', function () { test('CodeLens, evil provider', function () { - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, { provideCodeLenses(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, { provideCodeLenses() { return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; } @@ -209,7 +210,7 @@ suite('ExtHostLanguageFeatures', function () { test('CodeLens, do not resolve a resolved lens', function () { - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, { provideCodeLenses(): any { return [new types.CodeLens( new types.Range(0, 0, 0, 0), @@ -235,7 +236,7 @@ suite('ExtHostLanguageFeatures', function () { test('CodeLens, missing command', function () { - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, { provideCodeLenses() { return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; } @@ -260,7 +261,7 @@ suite('ExtHostLanguageFeatures', function () { test('Definition, data conversion', function () { - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; } @@ -279,12 +280,12 @@ suite('ExtHostLanguageFeatures', function () { test('Definition, one or many', function () { - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return [new types.Location(model.uri, new types.Range(1, 1, 1, 1))]; } })); - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return new types.Location(model.uri, new types.Range(1, 1, 1, 1)); } @@ -300,13 +301,13 @@ suite('ExtHostLanguageFeatures', function () { test('Definition, registration order', function () { - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return [new types.Location(URI.parse('far://first'), new types.Range(2, 3, 4, 5))]; } })); - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return new types.Location(URI.parse('far://second'), new types.Range(1, 2, 3, 4)); } @@ -326,12 +327,12 @@ suite('ExtHostLanguageFeatures', function () { test('Definition, evil provider', function () { - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { throw new Error('evil provider'); } })); - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return new types.Location(model.uri, new types.Range(1, 1, 1, 1)); } @@ -349,7 +350,7 @@ suite('ExtHostLanguageFeatures', function () { test('Implementation, data conversion', function () { - disposables.push(extHost.registerImplementationProvider(defaultSelector, { + disposables.push(extHost.registerImplementationProvider(defaultExtension, defaultSelector, { provideImplementation(): any { return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; } @@ -369,7 +370,7 @@ suite('ExtHostLanguageFeatures', function () { test('Type Definition, data conversion', function () { - disposables.push(extHost.registerTypeDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerTypeDefinitionProvider(defaultExtension, defaultSelector, { provideTypeDefinition(): any { return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; } @@ -389,7 +390,7 @@ suite('ExtHostLanguageFeatures', function () { test('HoverProvider, word range at pos', function () { - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { return new types.Hover('Hello'); } @@ -407,7 +408,7 @@ suite('ExtHostLanguageFeatures', function () { test('HoverProvider, given range', function () { - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { return new types.Hover('Hello', new types.Range(3, 0, 8, 7)); } @@ -425,14 +426,14 @@ suite('ExtHostLanguageFeatures', function () { test('HoverProvider, registration order', function () { - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { return new types.Hover('registered first'); } })); - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { return new types.Hover('registered second'); } @@ -451,12 +452,12 @@ suite('ExtHostLanguageFeatures', function () { test('HoverProvider, evil provider', function () { - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { return new types.Hover('Hello'); } @@ -475,7 +476,7 @@ suite('ExtHostLanguageFeatures', function () { test('Occurrences, data conversion', function () { - disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, { provideDocumentHighlights(): any { return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; } @@ -494,12 +495,12 @@ suite('ExtHostLanguageFeatures', function () { test('Occurrences, order 1/2', function () { - disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, { provideDocumentHighlights(): any { return []; } })); - disposables.push(extHost.registerDocumentHighlightProvider('*', { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, '*', { provideDocumentHighlights(): any { return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; } @@ -518,12 +519,12 @@ suite('ExtHostLanguageFeatures', function () { test('Occurrences, order 2/2', function () { - disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, { provideDocumentHighlights(): any { return [new types.DocumentHighlight(new types.Range(0, 0, 0, 2))]; } })); - disposables.push(extHost.registerDocumentHighlightProvider('*', { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, '*', { provideDocumentHighlights(): any { return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; } @@ -542,13 +543,13 @@ suite('ExtHostLanguageFeatures', function () { test('Occurrences, evil provider', function () { - disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, { provideDocumentHighlights(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, { provideDocumentHighlights(): any { return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; } @@ -566,13 +567,13 @@ suite('ExtHostLanguageFeatures', function () { test('References, registration order', function () { - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, { provideReferences(): any { return [new types.Location(URI.parse('far://register/first'), new types.Range(0, 0, 0, 0))]; } })); - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, { provideReferences(): any { return [new types.Location(URI.parse('far://register/second'), new types.Range(0, 0, 0, 0))]; } @@ -592,7 +593,7 @@ suite('ExtHostLanguageFeatures', function () { test('References, data conversion', function () { - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, { provideReferences(): any { return [new types.Location(model.uri, new types.Position(0, 0))]; } @@ -613,12 +614,12 @@ suite('ExtHostLanguageFeatures', function () { test('References, evil provider', function () { - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, { provideReferences(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, { provideReferences(): any { return [new types.Location(model.uri, new types.Range(0, 0, 0, 0))]; } @@ -637,7 +638,7 @@ suite('ExtHostLanguageFeatures', function () { test('Quick Fix, command data conversion', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): vscode.Command[] { return [ { command: 'test1', title: 'Testing1' }, @@ -661,7 +662,7 @@ suite('ExtHostLanguageFeatures', function () { test('Quick Fix, code action data conversion', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): vscode.CodeAction[] { return [ { @@ -689,7 +690,7 @@ suite('ExtHostLanguageFeatures', function () { test('Cannot read property \'id\' of undefined, #29469', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): any { return [ undefined, @@ -708,12 +709,12 @@ suite('ExtHostLanguageFeatures', function () { test('Quick Fix, evil provider', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): any { return [{ command: 'test', title: 'Testing' }]; } @@ -730,13 +731,13 @@ suite('ExtHostLanguageFeatures', function () { test('Navigate types, evil provider', function () { - disposables.push(extHost.registerWorkspaceSymbolProvider({ + disposables.push(extHost.registerWorkspaceSymbolProvider(defaultExtension, { provideWorkspaceSymbols(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerWorkspaceSymbolProvider({ + disposables.push(extHost.registerWorkspaceSymbolProvider(defaultExtension, { provideWorkspaceSymbols(): any { return [new types.SymbolInformation('testing', types.SymbolKind.Array, new types.Range(0, 0, 1, 1))]; } @@ -758,7 +759,7 @@ suite('ExtHostLanguageFeatures', function () { test('Rename, evil provider 0/2', function () { - disposables.push(extHost.registerRenameProvider(defaultSelector, { + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, { provideRenameEdits(): any { throw new class Foo { }; } @@ -776,7 +777,7 @@ suite('ExtHostLanguageFeatures', function () { test('Rename, evil provider 1/2', function () { - disposables.push(extHost.registerRenameProvider(defaultSelector, { + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, { provideRenameEdits(): any { throw Error('evil'); } @@ -792,13 +793,13 @@ suite('ExtHostLanguageFeatures', function () { test('Rename, evil provider 2/2', function () { - disposables.push(extHost.registerRenameProvider('*', { + disposables.push(extHost.registerRenameProvider(defaultExtension, '*', { provideRenameEdits(): any { throw Error('evil'); } })); - disposables.push(extHost.registerRenameProvider(defaultSelector, { + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, { provideRenameEdits(): any { let edit = new types.WorkspaceEdit(); edit.replace(model.uri, new types.Range(0, 0, 0, 0), 'testing'); @@ -816,7 +817,7 @@ suite('ExtHostLanguageFeatures', function () { test('Rename, ordering', function () { - disposables.push(extHost.registerRenameProvider('*', { + disposables.push(extHost.registerRenameProvider(defaultExtension, '*', { provideRenameEdits(): any { let edit = new types.WorkspaceEdit(); edit.replace(model.uri, new types.Range(0, 0, 0, 0), 'testing'); @@ -825,7 +826,7 @@ suite('ExtHostLanguageFeatures', function () { } })); - disposables.push(extHost.registerRenameProvider(defaultSelector, { + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, { provideRenameEdits(): any { return; } @@ -846,13 +847,13 @@ suite('ExtHostLanguageFeatures', function () { test('Parameter Hints, order', function () { - disposables.push(extHost.registerSignatureHelpProvider(defaultSelector, { + disposables.push(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, { provideSignatureHelp(): any { return undefined; } }, [])); - disposables.push(extHost.registerSignatureHelpProvider(defaultSelector, { + disposables.push(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, { provideSignatureHelp(): vscode.SignatureHelp { return { signatures: [], @@ -864,14 +865,14 @@ suite('ExtHostLanguageFeatures', function () { return rpcProtocol.sync().then(() => { - return provideSignatureHelp(model, new EditorPosition(1, 1), { triggerReason: modes.SignatureHelpTriggerReason.Invoke }, CancellationToken.None).then(value => { + return provideSignatureHelp(model, new EditorPosition(1, 1), { triggerReason: modes.SignatureHelpTriggerReason.Invoke, isRetrigger: false }, CancellationToken.None).then(value => { assert.ok(value); }); }); }); test('Parameter Hints, evil provider', function () { - disposables.push(extHost.registerSignatureHelpProvider(defaultSelector, { + disposables.push(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, { provideSignatureHelp(): any { throw new Error('evil'); } @@ -879,7 +880,7 @@ suite('ExtHostLanguageFeatures', function () { return rpcProtocol.sync().then(() => { - return provideSignatureHelp(model, new EditorPosition(1, 1), { triggerReason: modes.SignatureHelpTriggerReason.Invoke }, CancellationToken.None).then(value => { + return provideSignatureHelp(model, new EditorPosition(1, 1), { triggerReason: modes.SignatureHelpTriggerReason.Invoke, isRetrigger: false }, CancellationToken.None).then(value => { assert.equal(value, undefined); }); }); @@ -889,13 +890,13 @@ suite('ExtHostLanguageFeatures', function () { test('Suggest, order 1/3', function () { - disposables.push(extHost.registerCompletionItemProvider('*', { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, '*', { provideCompletionItems(): any { return [new types.CompletionItem('testing1')]; } }, [])); - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return [new types.CompletionItem('testing2')]; } @@ -911,13 +912,13 @@ suite('ExtHostLanguageFeatures', function () { test('Suggest, order 2/3', function () { - disposables.push(extHost.registerCompletionItemProvider('*', { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, '*', { provideCompletionItems(): any { return [new types.CompletionItem('weak-selector')]; // weaker selector but result } }, [])); - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return []; // stronger selector but not a good result; } @@ -933,13 +934,13 @@ suite('ExtHostLanguageFeatures', function () { test('Suggest, order 2/3', function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return [new types.CompletionItem('strong-1')]; } }, [])); - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return [new types.CompletionItem('strong-2')]; } @@ -956,13 +957,13 @@ suite('ExtHostLanguageFeatures', function () { test('Suggest, evil provider', function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { throw new Error('evil'); } }, [])); - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return [new types.CompletionItem('testing')]; } @@ -979,7 +980,7 @@ suite('ExtHostLanguageFeatures', function () { test('Suggest, CompletionList', function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return new types.CompletionList([new types.CompletionItem('hello')], true); } @@ -996,7 +997,7 @@ suite('ExtHostLanguageFeatures', function () { // --- format test('Format Doc, data conversion', function () { - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing'), types.TextEdit.setEndOfLine(types.EndOfLine.LF)]; } @@ -1017,7 +1018,7 @@ suite('ExtHostLanguageFeatures', function () { }); test('Format Doc, evil provider', function () { - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { throw new Error('evil'); } @@ -1030,19 +1031,19 @@ suite('ExtHostLanguageFeatures', function () { test('Format Doc, order', function () { - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { return undefined; } })); - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing')]; } })); - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { return undefined; } @@ -1059,7 +1060,7 @@ suite('ExtHostLanguageFeatures', function () { }); test('Format Range, data conversion', function () { - disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentRangeFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing')]; } @@ -1076,17 +1077,17 @@ suite('ExtHostLanguageFeatures', function () { }); test('Format Range, + format_doc', function () { - disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentRangeFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'range')]; } })); - disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentRangeFormattingEdits(): any { return [new types.TextEdit(new types.Range(2, 3, 4, 5), 'range2')]; } })); - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 1, 1), 'doc')]; } @@ -1105,7 +1106,7 @@ suite('ExtHostLanguageFeatures', function () { }); test('Format Range, evil provider', function () { - disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentRangeFormattingEdits(): any { throw new Error('evil'); } @@ -1118,7 +1119,7 @@ suite('ExtHostLanguageFeatures', function () { test('Format on Type, data conversion', function () { - disposables.push(extHost.registerOnTypeFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerOnTypeFormattingEditProvider(defaultExtension, defaultSelector, { provideOnTypeFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), arguments[2])]; } @@ -1137,7 +1138,7 @@ suite('ExtHostLanguageFeatures', function () { test('Links, data conversion', function () { - disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + disposables.push(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, { provideDocumentLinks() { return [new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3'))]; } @@ -1156,13 +1157,13 @@ suite('ExtHostLanguageFeatures', function () { test('Links, evil provider', function () { - disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + disposables.push(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, { provideDocumentLinks() { return [new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3'))]; } })); - disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + disposables.push(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, { provideDocumentLinks(): any { throw new Error(); } @@ -1181,7 +1182,7 @@ suite('ExtHostLanguageFeatures', function () { test('Document colors, data conversion', function () { - disposables.push(extHost.registerColorProvider(defaultSelector, { + disposables.push(extHost.registerColorProvider(defaultExtension, defaultSelector, { provideDocumentColors(): vscode.ColorInformation[] { return [new types.ColorInformation(new types.Range(0, 0, 0, 20), new types.Color(0.1, 0.2, 0.3, 0.4))]; }, diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index fc8f8e17a6a..18c5c7078ae 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -3,21 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { mapArrayOrNot } from 'vs/base/common/arrays'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { dispose } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; -import { IFileMatch, IPatternInfo, IRawFileMatch2, IRawSearchQuery, ISearchCompleteStats, ISearchQuery, QueryType } from 'vs/platform/search/common/search'; +import { IFileMatch, IFileQuery, IPatternInfo, IRawFileMatch2, ISearchCompleteStats, ISearchQuery, ITextQuery, QueryType, resultIsMatch } from 'vs/platform/search/common/search'; import { MainContext, MainThreadSearchShape } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; import { Range } from 'vs/workbench/api/node/extHostTypes'; +import { extensionResultIsMatch } from 'vs/workbench/services/search/node/textSearchManager'; import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; -import * as vscode from 'vscode'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { TestLogService } from 'vs/workbench/test/workbenchTestServices'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import * as vscode from 'vscode'; let rpcProtocol: TestRPCProtocol; let extHostSearch: ExtHostSearch; @@ -78,13 +79,13 @@ suite('ExtHostSearch', () => { await rpcProtocol.sync(); } - async function runFileSearch(query: IRawSearchQuery, cancel = false): Promise<{ results: URI[]; stats: ISearchCompleteStats }> { + async function runFileSearch(query: IFileQuery, cancel = false): Promise<{ results: URI[]; stats: ISearchCompleteStats }> { let stats: ISearchCompleteStats; try { const cancellation = new CancellationTokenSource(); const p = extHostSearch.$provideFileSearchResults(mockMainThreadSearch.lastHandle, 0, query, cancellation.token); if (cancel) { - await new TPromise(resolve => process.nextTick(resolve)); + await new Promise(resolve => process.nextTick(resolve)); cancellation.cancel(); } @@ -103,13 +104,13 @@ suite('ExtHostSearch', () => { }; } - async function runTextSearch(pattern: IPatternInfo, query: IRawSearchQuery, cancel = false): Promise<{ results: IFileMatch[], stats: ISearchCompleteStats }> { + async function runTextSearch(query: ITextQuery, cancel = false): Promise<{ results: IFileMatch[], stats: ISearchCompleteStats }> { let stats: ISearchCompleteStats; try { const cancellation = new CancellationTokenSource(); - const p = extHostSearch.$provideTextSearchResults(mockMainThreadSearch.lastHandle, 0, pattern, query, cancellation.token); + const p = extHostSearch.$provideTextSearchResults(mockMainThreadSearch.lastHandle, 0, query, cancellation.token); if (cancel) { - await new TPromise(resolve => process.nextTick(resolve)); + await new Promise(resolve => process.nextTick(resolve)); cancellation.cancel(); } @@ -157,7 +158,7 @@ suite('ExtHostSearch', () => { suite('File:', () => { - function getSimpleQuery(filePattern = ''): ISearchQuery { + function getSimpleQuery(filePattern = ''): IFileQuery { return { type: QueryType.File, @@ -179,7 +180,7 @@ suite('ExtHostSearch', () => { test('no results', async () => { await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { - return TPromise.wrap(null); + return Promise.resolve(null); } }); @@ -197,7 +198,7 @@ suite('ExtHostSearch', () => { await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }); @@ -211,7 +212,7 @@ suite('ExtHostSearch', () => { let cancelRequested = false; await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { token.onCancellationRequested(() => { cancelRequested = true; @@ -245,7 +246,7 @@ suite('ExtHostSearch', () => { await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { assert(options.excludes.length === 2 && options.includes.length === 2, 'Missing global include/excludes'); - return TPromise.wrap(null); + return Promise.resolve(null); } }); @@ -281,7 +282,7 @@ suite('ExtHostSearch', () => { assert.deepEqual(options.excludes.sort(), ['*.js']); } - return TPromise.wrap(null); + return Promise.resolve(null); } }); @@ -318,7 +319,7 @@ suite('ExtHostSearch', () => { assert.deepEqual(options.includes.sort(), ['*.jsx', '*.ts']); assert.deepEqual(options.excludes.sort(), []); - return TPromise.wrap(null); + return Promise.resolve(null); } }); @@ -358,7 +359,7 @@ suite('ExtHostSearch', () => { await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { - return TPromise.wrap(reportedResults + return Promise.resolve(reportedResults .map(relativePath => joinPath(options.folder, relativePath))); } }); @@ -404,7 +405,7 @@ suite('ExtHostSearch', () => { ].map(relativePath => joinPath(rootFolderB, relativePath)); } - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }); @@ -461,7 +462,7 @@ suite('ExtHostSearch', () => { provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }); @@ -497,7 +498,7 @@ suite('ExtHostSearch', () => { provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }); @@ -532,7 +533,7 @@ suite('ExtHostSearch', () => { provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }); @@ -563,7 +564,7 @@ suite('ExtHostSearch', () => { token.onCancellationRequested(() => cancels++); // Provice results async so it has a chance to invoke every provider - await new TPromise(r => process.nextTick(r)); + await new Promise(r => process.nextTick(r)); return [ 'file1.ts', 'file2.ts', @@ -603,7 +604,7 @@ suite('ExtHostSearch', () => { await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { - return TPromise.wrap(reportedResults); + return Promise.resolve(reportedResults); } }, fancyScheme); @@ -624,24 +625,25 @@ suite('ExtHostSearch', () => { suite('Text:', () => { - function makePreview(text: string): vscode.TextSearchResult['preview'] { + function makePreview(text: string): vscode.TextSearchMatch['preview'] { return { - match: new Range(0, 0, 0, text.length), + matches: new Range(0, 0, 0, text.length), text }; } - function makeTextResult(baseFolder: URI, relativePath: string): vscode.TextSearchResult { + function makeTextResult(baseFolder: URI, relativePath: string): vscode.TextSearchMatch { return { preview: makePreview('foo'), - range: new Range(0, 0, 0, 3), + ranges: new Range(0, 0, 0, 3), uri: joinPath(baseFolder, relativePath) }; } - function getSimpleQuery(): ISearchQuery { + function getSimpleQuery(queryText: string): ITextQuery { return { type: QueryType.Text, + contentPattern: getPattern(queryText), folderQueries: [ { folder: rootFolderA } @@ -659,33 +661,51 @@ suite('ExtHostSearch', () => { const actualTextSearchResults: vscode.TextSearchResult[] = []; for (let fileMatch of actual) { // Make relative - for (let lineMatch of fileMatch.matches) { - actualTextSearchResults.push({ - preview: { - text: lineMatch.preview.text, - match: new Range(lineMatch.preview.match.startLineNumber, lineMatch.preview.match.startColumn, lineMatch.preview.match.endLineNumber, lineMatch.preview.match.endColumn) - }, - range: new Range(lineMatch.range.startLineNumber, lineMatch.range.startColumn, lineMatch.range.endLineNumber, lineMatch.range.endColumn), - uri: fileMatch.resource - }); + for (let lineResult of fileMatch.results) { + if (resultIsMatch(lineResult)) { + actualTextSearchResults.push({ + preview: { + text: lineResult.preview.text, + matches: mapArrayOrNot( + lineResult.preview.matches, + m => new Range(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn)) + }, + ranges: mapArrayOrNot( + lineResult.ranges, + r => new Range(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn), + ), + uri: fileMatch.resource + }); + } else { + actualTextSearchResults.push({ + text: lineResult.text, + lineNumber: lineResult.lineNumber, + uri: fileMatch.resource + }); + } } } const rangeToString = (r: vscode.Range) => `(${r.start.line}, ${r.start.character}), (${r.end.line}, ${r.end.character})`; const makeComparable = (results: vscode.TextSearchResult[]) => results - .sort((a, b) => b.preview.text.localeCompare(a.preview.text)) - .map(r => ({ - ...r, - ...{ - uri: r.uri.toString(), - range: rangeToString(r.range), - preview: { - text: r.preview.text, - match: null // Don't care about this right now - } + .sort((a, b) => { + const compareKeyA = a.uri.toString() + ': ' + (extensionResultIsMatch(a) ? a.preview.text : a.text); + const compareKeyB = b.uri.toString() + ': ' + (extensionResultIsMatch(b) ? b.preview.text : b.text); + return compareKeyB.localeCompare(compareKeyA); + }) + .map(r => extensionResultIsMatch(r) ? { + uri: r.uri.toString(), + range: mapArrayOrNot(r.ranges, rangeToString), + preview: { + text: r.preview.text, + match: null // Don't care about this right now } - })); + } : { + uri: r.uri.toString(), + text: r.text, + lineNumber: r.lineNumber + }); return assert.deepEqual( makeComparable(actualTextSearchResults), @@ -695,11 +715,11 @@ suite('ExtHostSearch', () => { test('no results', async () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - return TPromise.wrap(null); + return Promise.resolve(null); } }); - const { results, stats } = await runTextSearch(getPattern('foo'), getSimpleQuery()); + const { results, stats } = await runTextSearch(getSimpleQuery('foo')); assert(!stats.limitHit); assert(!results.length); }); @@ -713,11 +733,11 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); - const { results, stats } = await runTextSearch(getPattern('foo'), getSimpleQuery()); + const { results, stats } = await runTextSearch(getSimpleQuery('foo')); assert(!stats.limitHit); assertResults(results, providedResults); }); @@ -727,12 +747,13 @@ suite('ExtHostSearch', () => { provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { assert.equal(options.includes.length, 1); assert.equal(options.excludes.length, 1); - return TPromise.wrap(null); + return Promise.resolve(null); } }); - const query: IRawSearchQuery = { + const query: ITextQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), includePattern: { '*.ts': true @@ -748,7 +769,7 @@ suite('ExtHostSearch', () => { ] }; - await runTextSearch(getPattern('foo'), query); + await runTextSearch(query); }); test('global/local include/excludes combined', async () => { @@ -762,12 +783,13 @@ suite('ExtHostSearch', () => { assert.deepEqual(options.excludes.sort(), ['*.js']); } - return TPromise.wrap(null); + return Promise.resolve(null); } }); - const query: IRawSearchQuery = { + const query: ITextQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), includePattern: { '*.ts': true @@ -789,7 +811,7 @@ suite('ExtHostSearch', () => { ] }; - await runTextSearch(getPattern('foo'), query); + await runTextSearch(query); }); test('include/excludes resolved correctly', async () => { @@ -798,12 +820,13 @@ suite('ExtHostSearch', () => { assert.deepEqual(options.includes.sort(), ['*.jsx', '*.ts']); assert.deepEqual(options.excludes.sort(), []); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), includePattern: { '*.ts': true, @@ -826,7 +849,7 @@ suite('ExtHostSearch', () => { ] }; - await runTextSearch(getPattern('foo'), query); + await runTextSearch(query); }); test('provider fail', async () => { @@ -837,7 +860,7 @@ suite('ExtHostSearch', () => { }); try { - await runTextSearch(getPattern('foo'), getSimpleQuery()); + await runTextSearch(getSimpleQuery('foo')); assert(false, 'Expected to fail'); } catch { // expected to fail @@ -864,12 +887,13 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), excludePattern: { '*.js': { @@ -882,7 +906,7 @@ suite('ExtHostSearch', () => { ] }; - const { results } = await runTextSearch(getPattern('foo'), query); + const { results } = await runTextSearch(query); assertResults(results, providedResults.slice(1)); }); @@ -923,12 +947,13 @@ suite('ExtHostSearch', () => { } reportedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), excludePattern: { '*.js': { @@ -954,7 +979,7 @@ suite('ExtHostSearch', () => { ] }; - const { results } = await runTextSearch(getPattern('foo'), query); + const { results } = await runTextSearch(query); assertResults(results, [ makeTextResult(rootFolderA, 'folder/fileA.scss'), makeTextResult(rootFolderA, 'folder/file2.css'), @@ -972,12 +997,13 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), includePattern: { '*.ts': true @@ -988,7 +1014,7 @@ suite('ExtHostSearch', () => { ] }; - const { results } = await runTextSearch(getPattern('foo'), query); + const { results } = await runTextSearch(query); assertResults(results, providedResults.slice(1)); }); @@ -1003,12 +1029,13 @@ suite('ExtHostSearch', () => { provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), maxResults: 1, @@ -1017,7 +1044,7 @@ suite('ExtHostSearch', () => { ] }; - const { results, stats } = await runTextSearch(getPattern('foo'), query); + const { results, stats } = await runTextSearch(query); assert(stats.limitHit, 'Expected to return limitHit'); assertResults(results, providedResults.slice(0, 1)); assert(wasCanceled, 'Expected to be canceled'); @@ -1035,12 +1062,13 @@ suite('ExtHostSearch', () => { provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), maxResults: 2, @@ -1049,7 +1077,7 @@ suite('ExtHostSearch', () => { ] }; - const { results, stats } = await runTextSearch(getPattern('foo'), query); + const { results, stats } = await runTextSearch(query); assert(stats.limitHit, 'Expected to return limitHit'); assertResults(results, providedResults.slice(0, 2)); assert(wasCanceled, 'Expected to be canceled'); @@ -1066,12 +1094,13 @@ suite('ExtHostSearch', () => { provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), maxResults: 2, @@ -1080,7 +1109,7 @@ suite('ExtHostSearch', () => { ] }; - const { results, stats } = await runTextSearch(getPattern('foo'), query); + const { results, stats } = await runTextSearch(query); assert(!stats.limitHit, 'Expected not to return limitHit'); assertResults(results, providedResults); assert(!wasCanceled, 'Expected not to be canceled'); @@ -1096,12 +1125,13 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); - return TPromise.wrap({ limitHit: true }); + return Promise.resolve({ limitHit: true }); } }); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), maxResults: 1000, @@ -1110,7 +1140,7 @@ suite('ExtHostSearch', () => { ] }; - const { results, stats } = await runTextSearch(getPattern('foo'), query); + const { results, stats } = await runTextSearch(query); assert(stats.limitHit, 'Expected to return limitHit'); assertResults(results, providedResults); }); @@ -1120,7 +1150,7 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ async provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Promise { token.onCancellationRequested(() => cancels++); - await new TPromise(r => process.nextTick(r)); + await new Promise(r => process.nextTick(r)); [ 'file1.ts', 'file2.ts', @@ -1132,6 +1162,7 @@ suite('ExtHostSearch', () => { const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), maxResults: 2, @@ -1141,7 +1172,7 @@ suite('ExtHostSearch', () => { ] }; - const { results } = await runTextSearch(getPattern('foo'), query); + const { results } = await runTextSearch(query); assert.equal(results.length, 2); assert.equal(cancels, 2); }); @@ -1156,19 +1187,20 @@ suite('ExtHostSearch', () => { await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return Promise.resolve(null); } }, fancyScheme); const query: ISearchQuery = { type: QueryType.Text, + contentPattern: getPattern('foo'), folderQueries: [ { folder: fancySchemeFolderA } ] }; - const { results } = await runTextSearch(getPattern('foo'), query); + const { results } = await runTextSearch(query); assertResults(results, providedResults); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts index 4f28c65d369..b19dac9a834 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -16,9 +16,9 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; -import { TPromise } from 'vs/base/common/winjs.base'; import { TreeItemCollapsibleState, ITreeItem } from 'vs/workbench/common/views'; import { NullLogService } from 'vs/platform/log/common/log'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; suite('ExtHostTreeView', function () { @@ -29,11 +29,11 @@ suite('ExtHostTreeView', function () { $registerTreeViewDataProvider(treeViewId: string): void { } - $refresh(viewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): TPromise { - return TPromise.as(null).then(() => this.onRefresh.fire(itemsToRefresh)); + $refresh(viewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise { + return Promise.resolve(null).then(() => this.onRefresh.fire(itemsToRefresh)); } - $reveal(): TPromise { + $reveal(): Promise { return null; } @@ -73,8 +73,9 @@ suite('ExtHostTreeView', function () { testObject = new ExtHostTreeViews(target, new ExtHostCommands(rpcProtocol, new ExtHostHeapService(), new NullLogService()), new NullLogService()); onDidChangeTreeNode = new Emitter<{ key: string }>(); onDidChangeTreeNodeWithId = new Emitter<{ key: string }>(); - testObject.createTreeView('testNodeTreeProvider', { treeDataProvider: aNodeTreeDataProvider() }); - testObject.createTreeView('testNodeWithIdTreeProvider', { treeDataProvider: aNodeWithIdTreeDataProvider() }); + testObject.createTreeView('testNodeTreeProvider', { treeDataProvider: aNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); + testObject.createTreeView('testNodeWithIdTreeProvider', { treeDataProvider: aNodeWithIdTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); + testObject.createTreeView('testNodeWithHighlightsTreeProvider', { treeDataProvider: aNodeWithHighlightedLabelTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return loadCompleteTree('testNodeTreeProvider'); }); @@ -84,12 +85,12 @@ suite('ExtHostTreeView', function () { .then(elements => { const actuals = elements.map(e => e.handle); assert.deepEqual(actuals, ['0/0:a', '0/0:b']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeTreeProvider', '0/0:a') .then(children => { const actuals = children.map(e => e.handle); assert.deepEqual(actuals, ['0/0:a/0:aa', '0/0:a/0:ab']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeTreeProvider', '0/0:a/0:aa').then(children => assert.equal(children.length, 0)), testObject.$getChildren('testNodeTreeProvider', '0/0:a/0:ab').then(children => assert.equal(children.length, 0)) ]); @@ -98,7 +99,7 @@ suite('ExtHostTreeView', function () { .then(children => { const actuals = children.map(e => e.handle); assert.deepEqual(actuals, ['0/0:b/0:ba', '0/0:b/0:bb']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeTreeProvider', '0/0:b/0:ba').then(children => assert.equal(children.length, 0)), testObject.$getChildren('testNodeTreeProvider', '0/0:b/0:bb').then(children => assert.equal(children.length, 0)) ]); @@ -112,12 +113,12 @@ suite('ExtHostTreeView', function () { .then(elements => { const actuals = elements.map(e => e.handle); assert.deepEqual(actuals, ['1/a', '1/b']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeWithIdTreeProvider', '1/a') .then(children => { const actuals = children.map(e => e.handle); assert.deepEqual(actuals, ['1/aa', '1/ab']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeWithIdTreeProvider', '1/aa').then(children => assert.equal(children.length, 0)), testObject.$getChildren('testNodeWithIdTreeProvider', '1/ab').then(children => assert.equal(children.length, 0)) ]); @@ -126,7 +127,7 @@ suite('ExtHostTreeView', function () { .then(children => { const actuals = children.map(e => e.handle); assert.deepEqual(actuals, ['1/ba', '1/bb']); - return TPromise.join([ + return Promise.all([ testObject.$getChildren('testNodeWithIdTreeProvider', '1/ba').then(children => assert.equal(children.length, 0)), testObject.$getChildren('testNodeWithIdTreeProvider', '1/bb').then(children => assert.equal(children.length, 0)) ]); @@ -135,6 +136,51 @@ suite('ExtHostTreeView', function () { }); }); + test('construct highlights tree', () => { + return testObject.$getChildren('testNodeWithHighlightsTreeProvider') + .then(elements => { + assert.deepEqual(removeUnsetKeys(elements), [{ + handle: '1/a', + label: { label: 'a', highlights: [[0, 2], [3, 5]] }, + collapsibleState: TreeItemCollapsibleState.Collapsed + }, { + handle: '1/b', + label: { label: 'b', highlights: [[0, 2], [3, 5]] }, + collapsibleState: TreeItemCollapsibleState.Collapsed + }]); + return Promise.all([ + testObject.$getChildren('testNodeWithHighlightsTreeProvider', '1/a') + .then(children => { + assert.deepEqual(removeUnsetKeys(children), [{ + handle: '1/aa', + parentHandle: '1/a', + label: { label: 'aa', highlights: [[0, 2], [3, 5]] }, + collapsibleState: TreeItemCollapsibleState.None + }, { + handle: '1/ab', + parentHandle: '1/a', + label: { label: 'ab', highlights: [[0, 2], [3, 5]] }, + collapsibleState: TreeItemCollapsibleState.None + }]); + }), + testObject.$getChildren('testNodeWithHighlightsTreeProvider', '1/b') + .then(children => { + assert.deepEqual(removeUnsetKeys(children), [{ + handle: '1/ba', + parentHandle: '1/b', + label: { label: 'ba', highlights: [[0, 2], [3, 5]] }, + collapsibleState: TreeItemCollapsibleState.None + }, { + handle: '1/bb', + parentHandle: '1/b', + label: { label: 'bb', highlights: [[0, 2], [3, 5]] }, + collapsibleState: TreeItemCollapsibleState.None + }]); + }) + ]); + }); + }); + test('error is thrown if id is not unique', (done) => { tree['a'] = { 'aa': {}, @@ -165,12 +211,12 @@ suite('ExtHostTreeView', function () { }); test('refresh a parent node', () => { - return new TPromise((c, e) => { + return new Promise((c, e) => { target.onRefresh.event(actuals => { assert.deepEqual(['0/0:b'], Object.keys(actuals)); assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), { handle: '0/0:b', - label: 'b', + label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }); c(null); @@ -185,7 +231,7 @@ suite('ExtHostTreeView', function () { assert.deepEqual(removeUnsetKeys(actuals['0/0:b/0:bb']), { handle: '0/0:b/0:bb', parentHandle: '0/0:b', - label: 'bb', + label: { label: 'bb' }, collapsibleState: TreeItemCollapsibleState.None }); done(); @@ -198,13 +244,13 @@ suite('ExtHostTreeView', function () { assert.deepEqual(['0/0:b', '0/0:a/0:aa'], Object.keys(actuals)); assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), { handle: '0/0:b', - label: 'b', + label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }); assert.deepEqual(removeUnsetKeys(actuals['0/0:a/0:aa']), { handle: '0/0:a/0:aa', parentHandle: '0/0:a', - label: 'aa', + label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.None }); done(); @@ -219,13 +265,13 @@ suite('ExtHostTreeView', function () { assert.deepEqual(['0/0:a/0:aa', '0/0:b'], Object.keys(actuals)); assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), { handle: '0/0:b', - label: 'b', + label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }); assert.deepEqual(removeUnsetKeys(actuals['0/0:a/0:aa']), { handle: '0/0:a/0:aa', parentHandle: '0/0:a', - label: 'aa', + label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.None }); done(); @@ -241,7 +287,7 @@ suite('ExtHostTreeView', function () { assert.deepEqual(['0/0:a'], Object.keys(actuals)); assert.deepEqual(removeUnsetKeys(actuals['0/0:a']), { handle: '0/0:aa', - label: 'aa', + label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.Collapsed }); done(); @@ -400,49 +446,49 @@ suite('ExtHostTreeView', function () { }); test('reveal will throw an error if getParent is not implemented', () => { - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return treeView.reveal({ key: 'a' }) .then(() => assert.fail('Reveal should throw an error as getParent is not implemented'), () => null); }); test('reveal will return empty array for root element', () => { const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return treeView.reveal({ key: 'a' }) .then(() => { assert.ok(revealTarget.calledOnce); assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); - assert.deepEqual({ handle: '0/0:a', label: 'a', collapsibleState: TreeItemCollapsibleState.Collapsed }, removeUnsetKeys(revealTarget.args[0][1])); + assert.deepEqual({ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }, removeUnsetKeys(revealTarget.args[0][1])); assert.deepEqual([], revealTarget.args[0][2]); - assert.deepEqual({ select: true, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); }); }); test('reveal will return parents array for an element when hierarchy is not loaded', () => { const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return treeView.reveal({ key: 'aa' }) .then(() => { assert.ok(revealTarget.calledOnce); assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); - assert.deepEqual({ handle: '0/0:a/0:aa', label: 'aa', collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1])); - assert.deepEqual([{ handle: '0/0:a', label: 'a', collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepEqual({ select: true, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ handle: '0/0:a/0:aa', label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1])); + assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); + assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); }); }); test('reveal will return parents array for an element when hierarchy is loaded', () => { const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return testObject.$getChildren('treeDataProvider') .then(() => testObject.$getChildren('treeDataProvider', '0/0:a')) .then(() => treeView.reveal({ key: 'aa' }) .then(() => { assert.ok(revealTarget.calledOnce); assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); - assert.deepEqual({ handle: '0/0:a/0:aa', label: 'aa', collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1])); - assert.deepEqual([{ handle: '0/0:a', label: 'a', collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepEqual({ select: true, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ handle: '0/0:a/0:aa', label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1])); + assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); + assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); })); }); @@ -455,23 +501,23 @@ suite('ExtHostTreeView', function () { } }; const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); - return treeView.reveal({ key: 'bac' }, { select: false, focus: false }) + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); + return treeView.reveal({ key: 'bac' }, { select: false, focus: false, expand: false }) .then(() => { assert.ok(revealTarget.calledOnce); assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); - assert.deepEqual({ handle: '0/0:b/0:ba/0:bac', label: 'bac', collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b/0:ba' }, removeUnsetKeys(revealTarget.args[0][1])); + assert.deepEqual({ handle: '0/0:b/0:ba/0:bac', label: { label: 'bac' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b/0:ba' }, removeUnsetKeys(revealTarget.args[0][1])); assert.deepEqual([ - { handle: '0/0:b', label: 'b', collapsibleState: TreeItemCollapsibleState.Collapsed }, - { handle: '0/0:b/0:ba', label: 'ba', collapsibleState: TreeItemCollapsibleState.Collapsed, parentHandle: '0/0:b' } + { handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }, + { handle: '0/0:b/0:ba', label: { label: 'ba' }, collapsibleState: TreeItemCollapsibleState.Collapsed, parentHandle: '0/0:b' } ], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepEqual({ select: false, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ select: false, focus: false, expand: false }, revealTarget.args[0][3]); }); }); test('reveal after first udpate', () => { const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return loadCompleteTree('treeDataProvider') .then(() => { tree = { @@ -490,16 +536,16 @@ suite('ExtHostTreeView', function () { .then(() => { assert.ok(revealTarget.calledOnce); assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); - assert.deepEqual({ handle: '0/0:a/0:ac', label: 'ac', collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1])); - assert.deepEqual([{ handle: '0/0:a', label: 'a', collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepEqual({ select: true, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ handle: '0/0:a/0:ac', label: { label: 'ac' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1])); + assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); + assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); }); }); }); test('reveal after second udpate', () => { const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return loadCompleteTree('treeDataProvider') .then(() => { tree = { @@ -529,9 +575,9 @@ suite('ExtHostTreeView', function () { .then(() => { assert.ok(revealTarget.calledOnce); assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); - assert.deepEqual({ handle: '0/0:b/0:bc', label: 'bc', collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1])); - assert.deepEqual([{ handle: '0/0:b', label: 'b', collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepEqual({ select: true, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ handle: '0/0:b/0:bc', label: { label: 'bc' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1])); + assert.deepEqual([{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); + assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); }); }); }); @@ -543,13 +589,20 @@ suite('ExtHostTreeView', function () { } function removeUnsetKeys(obj: any): any { - const result = {}; - for (const key of Object.keys(obj)) { - if (obj[key] !== void 0) { - result[key] = obj[key]; - } + if (Array.isArray(obj)) { + return obj.map(o => removeUnsetKeys(o)); } - return result; + + if (typeof obj === 'object') { + const result = {}; + for (const key of Object.keys(obj)) { + if (obj[key] !== void 0) { + result[key] = removeUnsetKeys(obj[key]); + } + } + return result; + } + return obj; } function aNodeTreeDataProvider(): TreeDataProvider<{ key: string }> { @@ -594,6 +647,20 @@ suite('ExtHostTreeView', function () { }; } + function aNodeWithHighlightedLabelTreeDataProvider(): TreeDataProvider<{ key: string }> { + return { + getChildren: (element: { key: string }): { key: string }[] => { + return getChildren(element ? element.key : undefined).map(key => getNode(key)); + }, + getTreeItem: (element: { key: string }): TreeItem => { + const treeItem = getTreeItem(element.key, [[0, 2], [3, 5]]); + treeItem.id = element.key; + return treeItem; + }, + onDidChangeTreeData: onDidChangeTreeNodeWithId.event + }; + } + function getTreeElement(element): any { let parent = tree; for (let i = 0; i < element.length; i++) { @@ -616,10 +683,10 @@ suite('ExtHostTreeView', function () { return []; } - function getTreeItem(key: string): TreeItem { + function getTreeItem(key: string, highlights?: [number, number][]): TreeItem { const treeElement = getTreeElement(key); return { - label: labels[key] || key, + label: { label: labels[key] || key, highlights }, collapsibleState: treeElement && Object.keys(treeElement).length ? TreeItemCollapsibleState.Collapsed : TreeItemCollapsibleState.None }; } diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index 18a42e3bb18..80ddda65980 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -20,14 +20,14 @@ suite('ExtHostTypes', function () { let uri = URI.parse('file:///path/test.file'); assert.deepEqual(uri.toJSON(), { - $mid: 100, + $mid: 1, scheme: 'file', path: '/path/test.file' }); assert.ok(uri.fsPath); assert.deepEqual(uri.toJSON(), { - $mid: 100, + $mid: 1, scheme: 'file', path: '/path/test.file', fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'), @@ -35,7 +35,7 @@ suite('ExtHostTypes', function () { assert.ok(uri.toString()); assert.deepEqual(uri.toJSON(), { - $mid: 100, + $mid: 1, scheme: 'file', path: '/path/test.file', fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'), diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts index 2c39ea8c487..4ec159ed8f3 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -19,7 +19,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; import { IModelService } from 'vs/editor/common/services/modelService'; import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { TestFileService, TestEditorService, TestEditorGroupsService, TestEnvironmentService, TestContextService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; +import { TestFileService, TestEditorService, TestEditorGroupsService, TestEnvironmentService, TestContextService, TestTextResourcePropertiesService, TestWindowService } from 'vs/workbench/test/workbenchTestServices'; import { ResourceTextEdit } from 'vs/editor/common/modes'; import { BulkEditService } from 'vs/workbench/services/bulkEdit/electron-browser/bulkEditService'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -82,7 +82,7 @@ suite('MainThreadEditors', () => { } }; - const bulkEditService = new BulkEditService(new NullLogService(), modelService, new TestEditorService(), textModelService, new TestFileService(), textFileService, new LabelService(TestEnvironmentService, new TestContextService()), configService); + const bulkEditService = new BulkEditService(new NullLogService(), modelService, new TestEditorService(), textModelService, new TestFileService(), textFileService, new LabelService(TestEnvironmentService, new TestContextService(), new TestWindowService()), configService); const rpcProtocol = new TestRPCProtocol(); rpcProtocol.set(ExtHostContext.ExtHostDocuments, new class extends mock() { diff --git a/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts b/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts index 3e7225bb485..d2a5259f9f1 100644 --- a/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts +++ b/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { CharCode } from 'vs/base/common/charCode'; import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; @@ -11,6 +10,7 @@ import { isThenable } from 'vs/base/common/async'; export function SingleProxyRPCProtocol(thing: any): IExtHostContext { return { + remoteAuthority: null, getProxy(): T { return thing; }, @@ -23,6 +23,8 @@ export function SingleProxyRPCProtocol(thing: any): IExtHostContext { export class TestRPCProtocol implements IExtHostContext { + public remoteAuthority = null; + private _callCountValue: number = 0; private _idle: Promise; private _completeIdle: Function; @@ -91,10 +93,10 @@ export class TestRPCProtocol implements IExtHostContext { return value; } - protected _remoteCall(proxyId: string, path: string, args: any[]): TPromise { + protected _remoteCall(proxyId: string, path: string, args: any[]): Promise { this._callCount++; - return new TPromise((c) => { + return new Promise((c) => { setTimeout(c, 0); }).then(() => { const instance = this._locals[proxyId]; @@ -103,9 +105,9 @@ export class TestRPCProtocol implements IExtHostContext { let p: Thenable; try { let result = (instance[path]).apply(instance, wireArgs); - p = isThenable(result) ? result : TPromise.as(result); + p = isThenable(result) ? result : Promise.resolve(result); } catch (err) { - p = TPromise.wrapError(err); + p = Promise.reject(err); } return p.then(result => { @@ -115,7 +117,7 @@ export class TestRPCProtocol implements IExtHostContext { return wireResult; }, err => { this._callCount--; - return TPromise.wrapError(err); + return Promise.reject(err); }); }); } diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts index b763fdf321e..4b2d8d4387f 100644 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -9,7 +9,7 @@ import * as fs from 'fs'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; -import { ISearchService, IQueryOptions } from 'vs/platform/search/common/search'; +import { ISearchService } from 'vs/platform/search/common/search'; import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -19,7 +19,6 @@ import { SearchService } from 'vs/workbench/services/search/node/searchService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TestEnvironmentService, TestContextService, TestEditorService, TestEditorGroupsService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -28,7 +27,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; -import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { QueryBuilder, ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import * as event from 'vs/base/common/event'; import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; @@ -73,12 +72,12 @@ suite.skip('TextSearch performance (integration)', () => { [ILogService, new NullLogService()] )); - const queryOptions: IQueryOptions = { + const queryOptions: ITextQueryBuilderOptions = { maxResults: 2048 }; const searchModel: SearchModel = instantiationService.createInstance(SearchModel); - function runSearch(): TPromise { + function runSearch(): Promise { const queryBuilder: QueryBuilder = instantiationService.createInstance(QueryBuilder); const query = queryBuilder.text({ pattern: 'static_library(' }, [URI.file(testWorkspacePath)], queryOptions); @@ -107,7 +106,7 @@ suite.skip('TextSearch performance (integration)', () => { let resolve; let error; - return new TPromise((_resolve, _error) => { + return new Promise((_resolve, _error) => { resolve = _resolve; error = _error; @@ -155,15 +154,15 @@ class TestTelemetryService implements ITelemetryService { return this.emitter.event; } - public publicLog(eventName: string, data?: any): TPromise { + public publicLog(eventName: string, data?: any): Promise { const event = { name: eventName, data: data }; this.events.push(event); this.emitter.fire(event); - return TPromise.wrap(null); + return Promise.resolve(); } - public getTelemetryInfo(): TPromise { - return TPromise.wrap({ + public getTelemetryInfo(): Promise { + return Promise.resolve({ instanceId: 'someValue.instanceId', sessionId: 'someValue.sessionId', machineId: 'someValue.machineId' diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 57c10d6c5ce..09f2228e539 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -58,7 +58,7 @@ import { Range } from 'vs/editor/common/core/range'; import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; -import { IExtensionService, ProfileSession, IExtensionsStatus, ExtensionPointContribution, IExtensionDescription } from '../services/extensions/common/extensions'; +import { IExtensionService, ProfileSession, IExtensionsStatus, ExtensionPointContribution, IExtensionDescription, IWillActivateEvent } from '../services/extensions/common/extensions'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations'; @@ -77,7 +77,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { StorageService } from 'vs/platform/storage/electron-browser/storageService'; +import { StorageService } from 'vs/platform/storage/node/storageService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; @@ -310,6 +310,7 @@ export class TestExtensionService implements IExtensionService { _serviceBrand: any; onDidRegisterExtensions: Event = Event.None; onDidChangeExtensionsStatus: Event = Event.None; + onWillActivateByEvent: Event = Event.None; activateByEvent(_activationEvent: string): TPromise { return TPromise.as(void 0); } whenInstalledExtensionsRegistered(): TPromise { return TPromise.as(true); } getExtensions(): TPromise { return TPromise.as([]); } @@ -530,7 +531,7 @@ export class TestPartService implements IPartService { export class TestStorageService extends StorageService { constructor() { - super(':memory:', new NullLogService(), TestEnvironmentService); + super(':memory:', false, new NullLogService(), TestEnvironmentService); } } @@ -882,6 +883,10 @@ export class TestFileService implements IFileService { return { dispose() { } }; } + activateProvider(_scheme: string) { + return Promise.resolve(null); + } + canHandleResource(resource: URI): boolean { return resource.scheme === 'file'; } @@ -1120,6 +1125,10 @@ export class TestWindowService implements IWindowService { updateTouchBar(_items: ISerializableCommandAction[][]): TPromise { return TPromise.as(void 0); } + + resolveProxy(url: string): Promise { + return Promise.resolve(void 0); + } } export class TestLifecycleService implements ILifecycleService { @@ -1376,6 +1385,10 @@ export class TestWindowsService implements IWindowsService { openAboutDialog(): TPromise { return TPromise.as(void 0); } + + resolveProxy(windowId: number, url: string): Promise { + return Promise.resolve(void 0); + } } export class TestTextResourceConfigurationService implements ITextResourceConfigurationService { diff --git a/test/smoke/README.md b/test/smoke/README.md index b3a137953d4..c61944fee9e 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -2,7 +2,7 @@ ### Run -``` +```bash # Dev yarn smoketest @@ -14,7 +14,7 @@ yarn smoketest --build PATH_TO_BUILD You must always run the smoketest version which matches the release you are testing. So, if you want to run the smoketest for a release build (eg `release/1.22`), you need that version of the smoke tests too: -``` +```bash git checkout release/1.22 yarn yarn smoketest --build PATH_TO_RELEASE_BUILD @@ -30,7 +30,7 @@ yarn smoketest --build PATH_TO_RELEASE_BUILD Start a watch task in `test/smoke`: -``` +```bash cd test/smoke yarn watch ``` diff --git a/test/smoke/package.json b/test/smoke/package.json index ef0dd229f41..044201ce0bd 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -22,7 +22,7 @@ "@types/webdriverio": "4.6.1", "concurrently": "^3.5.1", "cpx": "^1.5.0", - "electron": "^2.0.11", + "electron": "^2.0.12", "htmlparser2": "^3.9.2", "mkdirp": "^0.5.1", "mocha": "^5.2.0", diff --git a/test/smoke/src/areas/extensions/extensions.ts b/test/smoke/src/areas/extensions/extensions.ts index b3ad16827c8..632077c2bdd 100644 --- a/test/smoke/src/areas/extensions/extensions.ts +++ b/test/smoke/src/areas/extensions/extensions.ts @@ -34,6 +34,6 @@ export class Extensions extends Viewlet { await this.searchForExtension(name); const ariaLabel = `${name}. Press enter for extension details.`; await this.code.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${ariaLabel}"] .extension li[class='action-item'] .extension-action.install`); - await this.code.waitForElement(`.extension-editor .monaco-action-bar .action-item:not(.disabled) .extension-action.reload`); + await this.code.waitForElement(`.extension-editor .monaco-action-bar .action-item:not(.disabled) .extension-action.uninstall`); } } \ No newline at end of file diff --git a/test/smoke/src/areas/problems/problems.ts b/test/smoke/src/areas/problems/problems.ts index bffd6a95fca..29c330dc7d5 100644 --- a/test/smoke/src/areas/problems/problems.ts +++ b/test/smoke/src/areas/problems/problems.ts @@ -40,7 +40,7 @@ export class Problems { public static getSelectorInProblemsView(problemType: ProblemSeverity): string { let selector = problemType === ProblemSeverity.WARNING ? 'warning' : 'error'; - return `div[aria-label="Problems grouped by files"] .icon.${selector}`; + return `div[id="workbench.panel.markers"] .monaco-tl-contents .icon.${selector}`; } public static getSelectorInEditor(problemType: ProblemSeverity): string { diff --git a/test/smoke/src/areas/workbench/viewlet.ts b/test/smoke/src/areas/workbench/viewlet.ts index 84ccb601012..5d478e5a8cf 100644 --- a/test/smoke/src/areas/workbench/viewlet.ts +++ b/test/smoke/src/areas/workbench/viewlet.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { Code } from '../../vscode/code'; export abstract class Viewlet { diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index 0f246945f20..6fd6af733be 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -584,10 +584,10 @@ electron-download@^3.0.1: semver "^5.3.0" sumchecker "^1.2.0" -electron@^2.0.11: - version "2.0.11" - resolved "https://registry.yarnpkg.com/electron/-/electron-2.0.11.tgz#8e352a83ec607471e82d323a2adc7e7b53698e79" - integrity sha512-bFTMDQN3epfiymqTPdgffyTxuy/7A52sIkW7Hos+hY5XLPArOXLXAKx1JtB3dM7CcPfZa+5qp/J3cPCidh5WXg== +electron@^2.0.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/electron/-/electron-2.0.12.tgz#04b11ef3246cd2a5839a8f16314051341dc5c449" + integrity sha512-mw8hoM/GPtFPP8FGiJcVNe8Rx63YJ7O8bf7McQj21HAvrXGAwReGFrpIe5xN6ec10fDXNSNyfzRucjFXtOtLcg== dependencies: "@types/node" "^8.0.24" electron-download "^3.0.1" diff --git a/test/tree/public/index.html b/test/tree/public/index.html index cc04550a183..5113851803f 100644 --- a/test/tree/public/index.html +++ b/test/tree/public/index.html @@ -51,7 +51,7 @@ const renderer = { templateId: 'template', renderTemplate(container) { return container; }, - renderElement(element, index, container) { container.textContent = element; }, + renderElement(element, index, container) { container.textContent = element.element; }, disposeElement() { }, disposeTemplate() { } }; @@ -159,10 +159,10 @@ const { tree, treeFilter } = createIndexTree(); const files = []; - for (let i = 0; i < 10000; i++) { + for (let i = 0; i < 100000; i++) { const errors = []; - for (let j = 1; j <= (i % 5) + 1; j++) { + for (let j = 1; j <= 3; j++) { errors.push({ element: `error #${j}` }); } diff --git a/tslint.json b/tslint.json index 7f1388dfa76..476f99d2237 100644 --- a/tslint.json +++ b/tslint.json @@ -523,6 +523,7 @@ } ], "duplicate-imports": true, + "no-new-buffer": true, "translation-remind": true, "no-standalone-editor": true }, diff --git a/yarn.lock b/yarn.lock index e39e168182b..bf17a883209 100644 --- a/yarn.lock +++ b/yarn.lock @@ -284,6 +284,13 @@ agent-base@4, agent-base@^4.1.0: dependencies: es6-promisify "^5.0.0" +agent-base@4.2.1, agent-base@~4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + ajv-keywords@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" @@ -4097,7 +4104,7 @@ http-errors@1.6.2, http-errors@~1.6.2: setprototypeof "1.0.3" statuses ">= 1.3.1 < 2" -http-proxy-agent@^2.1.0: +http-proxy-agent@2.1.0, http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== @@ -4128,7 +4135,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@^2.2.1: +https-proxy-agent@2.2.1, https-proxy-agent@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== @@ -4306,6 +4313,11 @@ invert-kv@^1.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= +ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + ipaddr.js@1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" @@ -7963,6 +7975,11 @@ slice-ansi@0.0.4: resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= +smart-buffer@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.1.tgz#07ea1ca8d4db24eb4cac86537d7d18995221ace3" + integrity sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg== + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -8007,6 +8024,22 @@ sntp@2.x.x: dependencies: hoek "4.x.x" +socks-proxy-agent@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473" + integrity sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw== + dependencies: + agent-base "~4.2.0" + socks "~2.2.0" + +socks@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.2.1.tgz#68ad678b3642fbc5d99c64c165bc561eab0215f9" + integrity sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w== + dependencies: + ip "^1.1.5" + smart-buffer "^4.0.1" + sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" @@ -8780,10 +8813,10 @@ typescript-formatter@7.1.0: commandpost "^1.0.0" editorconfig "^0.15.0" -typescript@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.1.tgz#3362ba9dd1e482ebb2355b02dfe8bcd19a2c7c96" - integrity sha512-Veu0w4dTc/9wlWNf2jeRInNodKlcdLgemvPsrNpfu5Pq39sgfFjvIIgTsvUHCoLBnMhPoUA+tFxsXjU6VexVRQ== +typescript@3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.4.tgz#c74ef7b3c2da65beff548b903022cb8c3cd997ed" + integrity sha512-JZHJtA6ZL15+Q3Dqkbh8iCUmvxD3iJ7ujXS+fVkKnwIVAdHc5BJTDNM0aTrnr2luKulFjU7W+SRhDZvi66Ru7Q== typescript@^2.6.2: version "2.6.2" @@ -9262,18 +9295,18 @@ vsce@1.48.0: yauzl "^2.3.1" yazl "^2.2.2" -vscode-anymatch@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/vscode-anymatch/-/vscode-anymatch-1.3.2.tgz#fff357422ea59cc4d12a074f347957f08d251003" - integrity sha512-TKJ8jTIvurXCjhCjhpTmu4m6Z852JTRZdCNT9OmQ7OJjR/kTf0P5E2swRvGNbM9jtmgeiFJ3MN64prFLCIzChQ== +vscode-anymatch@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/vscode-anymatch/-/vscode-anymatch-1.3.3.tgz#0613d31a949c8025473bbdad848d219f47a44f86" + integrity sha512-LQ4vF4BWb9gwAvbMtN+3HC4HKDxLd+ZyWmAjACOdD05O/ZMcgvvnjO24GseEIQ6cWn8gW+Ft08gHFihnQy1eSw== dependencies: micromatch "^2.1.5" normalize-path "^2.0.0" -vscode-chokidar@1.6.4: - version "1.6.4" - resolved "https://registry.yarnpkg.com/vscode-chokidar/-/vscode-chokidar-1.6.4.tgz#03e5b5f755a1e73b4f15310e66f59b11673fbdd2" - integrity sha512-2A4YQsY2Mm6VAxushKwJTIWCxnDe+1BPTSa+4zQGWcFvyu5W1q+WCp+hTIG6eZ+hc7JHdrHeKzZ0mgUsUFwQgQ== +vscode-chokidar@1.6.5: + version "1.6.5" + resolved "https://registry.yarnpkg.com/vscode-chokidar/-/vscode-chokidar-1.6.5.tgz#f38a1f909fa364a5429a4d4d70ecb40a15b54d0b" + integrity sha512-bc5dNF8tW2R+oesYT/Au//qumyd/0HTwSRqVXcg8ADQW1MsWKFwv+IxfSIuCHckaEy4I81GpSDaRWVGQqtsELw== dependencies: async-each "^1.0.0" glob-parent "^2.0.0" @@ -9282,19 +9315,19 @@ vscode-chokidar@1.6.4: is-glob "^2.0.0" path-is-absolute "^1.0.0" readdirp "^2.0.0" - vscode-anymatch "^1.3.0" + vscode-anymatch "1.3.3" optionalDependencies: - vscode-fsevents "0.3.9" + vscode-fsevents "0.3.10" vscode-debugprotocol@1.32.0: version "1.32.0" resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.32.0.tgz#cca9eccb3f73ded5e525e01621a72ca2bb577dc3" integrity sha512-x3+HV+BkLqfl1ZuDJEILAv1sT5mDceuPThDXD12hwXAEjAdfc6MLQFvaoVPuO6C6gb+lHQTd0R9FNkCflEJHbA== -vscode-fsevents@0.3.9: - version "0.3.9" - resolved "https://registry.yarnpkg.com/vscode-fsevents/-/vscode-fsevents-0.3.9.tgz#edbb66ea2c4eeb102b9194bb602e73bd9512c64c" - integrity sha512-ykvsVNFXeSc8aBNzwp0hIq41i80njAfpCwbQ3h04x69VC4xSK/PqVStNlOg7oRnrGHu8acchZv++k6WHXpSp1A== +vscode-fsevents@0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/vscode-fsevents/-/vscode-fsevents-0.3.10.tgz#65a586c3c6df3080bea20482146963a0f3a2c58d" + integrity sha512-iNlCKNgEB9A2JZkLf4h4sJlOS1u0lbe4QjM0Dr0PHaTmsttkJEfOaQeci2Ja1wUA7hUUAF6sNbei/Qp2DacFLw== dependencies: nan "^2.10.0" @@ -9325,10 +9358,21 @@ vscode-nsfw@1.1.1: lodash.isundefined "^3.0.1" nan "^2.10.0" -vscode-ripgrep@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.2.2.tgz#c21b91bb558638a7124fe3d9719b2666b12dbfdc" - integrity sha512-rPbCj4LUnPzivezk3pC94G5sB17HbayjV9mSvNTDYFg5llHN/XozAmkoDOC0KG9UveuQ71MADX1//CeHtUTmZw== +vscode-proxy-agent@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.1.1.tgz#dabf44726afe838143d445a88ae03455928cc4a7" + integrity sha512-/sC95JmVy+HjJIFzza131muUAyCy0aT8a5zIgu9bkrntzsh3V/sKtbVX64Ig59dvgyOghsSEF4D9wQKGbmrAQg== + dependencies: + agent-base "4.2.1" + debug "3.1.0" + http-proxy-agent "2.1.0" + https-proxy-agent "2.2.1" + socks-proxy-agent "4.0.1" + +vscode-ripgrep@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.2.4.tgz#b3cfbe08ed13f6cf6b134147ea4d982970ab4f70" + integrity sha512-TysaK20aCSfsFIQGd0DfMshjkHof0fG6zx7DoO0tdWNAZgsvoqLtOWdqHcocICRZ3RSpdiMiEJRaMK+iOzx16w== vscode-sqlite3@4.0.2: version "4.0.2" @@ -9344,10 +9388,10 @@ vscode-textmate@^4.0.1: dependencies: oniguruma "^7.0.0" -vscode-xterm@3.9.0-beta9: - version "3.9.0-beta9" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.9.0-beta9.tgz#4b03dc89603bab4ed5e65b5dd3c26d73275f6113" - integrity sha512-l4Gvqm7unG4JoGgkOPydmDadl81zpqK7PRfmilSlO7EIYhPYvVjxoU2OhdsPfPRkyinLLbSfI9MGuR+6no7UEw== +vscode-xterm@3.9.0-beta13: + version "3.9.0-beta13" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.9.0-beta13.tgz#ede9b4141eee579ec62f890df23c08b48ada17e8" + integrity sha512-honMQHY3AObEZomW83xrQt4paBFJ3aqCdF7jCABEM/9PQ0DjV7BDJmUQwdPwbKXYCvgOIC3BmATmVOxGey4Gmg== vso-node-api@6.1.2-preview: version "6.1.2-preview"