diff --git a/.editorconfig b/.editorconfig index b4180a87dd7c..e7e99b5bcb52 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# EditorConfig is awesome: http://EditorConfig.org +# EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true @@ -6,7 +6,6 @@ root = true # Tab indentation [*] indent_style = tab -indent_size = 4 trim_trailing_whitespace = true # The indent size used in the `package.json` file cannot be changed diff --git a/.nvmrc b/.nvmrc index 32c861f970d8..45a4fb75db86 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -8.9.2 +8 diff --git a/.vscode/launch.json b/.vscode/launch.json index fea405af38b9..04058a1233d7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -66,7 +66,8 @@ "request": "launch", "name": "Launch azuredatastudio", "windows": { - "runtimeExecutable": "${workspaceFolder}/scripts/sql.bat" + "runtimeExecutable": "${workspaceFolder}/scripts/sql.bat", + "timeout": 20000 }, "osx": { "runtimeExecutable": "${workspaceFolder}/scripts/sql.sh" @@ -74,15 +75,25 @@ "linux": { "runtimeExecutable": "${workspaceFolder}/scripts/sql.sh" }, + "breakOnLoad": false, "urlFilter": "*workbench.html*", "runtimeArgs": [ - "--inspect=5875", "--no-cached-data" + "--inspect=5875", + "--no-cached-data" ], - "skipFiles": [ - "**/winjs*.js" + "webRoot": "${workspaceFolder}" + }, + { + "type": "node", + "request": "launch", + "name": "Launch ADS (Main Process)", + "runtimeExecutable": "${workspaceFolder}/scripts/sql.sh", + "runtimeArgs": [ + "--no-cached-data" ], - "webRoot": "${workspaceFolder}", - "timeout": 45000 + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ] }, { "type": "chrome", diff --git a/.vscode/settings.json b/.vscode/settings.json index 6e4b7b9f968d..df4d1aa2ada3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -50,5 +50,7 @@ }, { "fileMatch": [ "cglicenses.json" ], "url": "./.vscode/cglicenses.schema.json" - }] + } +], +"git.ignoreLimitWarning": true } diff --git a/.vscode/shared.code-snippets b/.vscode/shared.code-snippets index aa0b82c4e70c..25cffc136673 100644 --- a/.vscode/shared.code-snippets +++ b/.vscode/shared.code-snippets @@ -23,11 +23,13 @@ "description": "Insert Copyright Statement" }, "TS -> Inject Service": { + "scope": "typescript", "description": "Constructor Injection Pattern", "prefix": "@inject", "body": "@$1 private readonly _$2: ${1},$0" }, "TS -> Event & Emitter": { + "scope": "typescript", "prefix": "emitter", "description": "Add emitter and event properties", "body": [ diff --git a/.yarnrc b/.yarnrc index 8e1ac6b6caca..190252d15daa 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "2.0.12" +target "3.1.2" runtime "electron" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2f93c0c8dc05..2176a2480bf0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,9 @@ The built-in tool for reporting an issue, which you can access by using `Report Please include the following with each issue. -* Version of Azure Data Studio (formerly SQL Operations Studio). +* Version of Azure Data Studio (formerly SQL Operations Studio) + +* Your operating system > **Tip:** You can easily create an issue using `Report Issues` from Azure Data Studio Help menu. diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 1b83653f659b..3cef20bd7d2f 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -2440,7 +2440,6 @@ General Public License. -------------------------------START OF THIRD-PARTY NOTICES------------------------------------------- -===================================ExcelDataReader (BEGIN) The MIT License (MIT) Copyright (c) 2014 ExcelDataReader diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index 91891e6da94d..fc124c5bf450 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -2,7 +2,7 @@ steps: - script: | set -e sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 libgconf-2-4 dbus xvfb libgtk-3-0 + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb sudo chmod +x /etc/init.d/xvfb sudo update-rc.d xvfb defaults diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index a535f8dbff9b..5ca1fdaa4e49 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -95,7 +95,7 @@ gulp.task('extract-editor-src', ['clean-editor-src'], function () { 'vs/base/browser/ui/octiconLabel/octiconLabel': 'vs/base/browser/ui/octiconLabel/octiconLabel.mock', }, shakeLevel: 2, // 0-Files, 1-InnerFile, 2-ClassMembers - importIgnorePattern: /^vs\/css!/, + importIgnorePattern: /(^vs\/css!)|(promise-polyfill\/polyfill)/, destRoot: path.join(root, 'out-editor-src') }); }); diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index f9847959cfc3..f21c579004af 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -50,7 +50,6 @@ const indentationFilter = [ '!src/vs/css.build.js', '!src/vs/loader.js', '!src/vs/base/common/marked/marked.js', - '!src/vs/base/common/winjs.base.js', '!src/vs/base/node/terminateProcess.sh', '!src/vs/base/node/cpuUsage.sh', '!test/assert.js', @@ -110,10 +109,12 @@ const copyrightFilter = [ '!**/*.opts', '!**/*.disabled', '!**/*.code-workspace', + '!**/promise-polyfill/polyfill.js', '!build/**/*.init', '!resources/linux/snap/snapcraft.yaml', '!resources/linux/snap/electron-launch', '!resources/win32/bin/code.js', + '!resources/completions/**', '!extensions/markdown-language-features/media/highlight.css', '!extensions/html-language-features/server/src/modes/typescript/*', '!extensions/*/server/bin/*' @@ -127,7 +128,6 @@ const eslintFilter = [ '!src/vs/nls.js', '!src/vs/css.build.js', '!src/vs/nls.build.js', - '!src/**/winjs.base.js', '!src/**/marked.js', '!**/test/**' ]; diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index a83dc438b6bf..ed5eb6ad0346 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -135,7 +135,7 @@ gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compil gulp.task('optimize-index-js', ['optimize-vscode'], () => { - const fullpath = path.join(process.cwd(), 'out-vscode/vs/code/electron-browser/workbench/workbench.js'); + const fullpath = path.join(process.cwd(), 'out-vscode/bootstrap-window.js'); const contents = fs.readFileSync(fullpath).toString(); const newContents = contents.replace('[/*BUILD->INSERT_NODE_MODULES*/]', JSON.stringify(nodeModules)); fs.writeFileSync(fullpath, newContents); @@ -164,7 +164,7 @@ const config = { version: getElectronVersion(), productAppName: product.nameLong, companyName: 'Microsoft Corporation', - copyright: 'Copyright (C) 2018 Microsoft. All rights reserved', + copyright: 'Copyright (C) 2019 Microsoft. All rights reserved', darwinIcon: 'resources/darwin/code.icns', darwinBundleIdentifier: product.darwinBundleIdentifier, darwinApplicationCategoryType: 'public.app-category.developer-tools', @@ -180,13 +180,13 @@ const config = { urlSchemes: [product.urlProtocol] }], darwinForceDarkModeSupport: true, - darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : void 0, + darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : undefined, linuxExecutableName: product.applicationName, winIcon: 'resources/win32/code.ico', - token: process.env['VSCODE_MIXIN_PASSWORD'] || process.env['GITHUB_TOKEN'] || void 0, + token: process.env['VSCODE_MIXIN_PASSWORD'] || process.env['GITHUB_TOKEN'] || undefined, // @ts-ignore JSON checking: electronRepository is optional - repo: product.electronRepository || void 0 + repo: product.electronRepository || undefined }; function getElectron(arch) { @@ -423,6 +423,8 @@ function packageTask(platform, arch, opts) { .pipe(electron(_.extend({}, config, { platform, arch, ffmpegChromium: true }))) .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'])); + // result = es.merge(result, gulp.src('resources/completions/**', { base: '.' })); + if (platform === 'win32') { result = es.merge(result, gulp.src('resources/win32/bin/code.js', { base: 'resources/win32' })); diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 7c55577c888a..822d139093b0 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -15,6 +15,9 @@ const util = require('./lib/util'); const packageJson = require('../package.json'); const product = require('../product.json'); const rpmDependencies = require('../resources/linux/rpm/dependencies.json'); +const path = require('path'); +const root = path.dirname(__dirname); +const commit = util.getVersion(root); const linuxPackageRevision = Math.floor(new Date().getTime() / 1000); @@ -51,6 +54,12 @@ function prepareDebPackage(arch) { const icon = gulp.src('resources/linux/code.png', { base: '.' }) .pipe(rename('usr/share/pixmaps/' + product.applicationName + '.png')); + // const bash_completion = gulp.src('resources/completions/bash/code') + // .pipe(rename('usr/share/bash-completion/completions/code')); + + // const zsh_completion = gulp.src('resources/completions/zsh/_code') + // .pipe(rename('usr/share/zsh/vendor-completions/_code')); + const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) .pipe(rename(function (p) { p.dirname = 'usr/share/' + product.applicationName + '/' + p.dirname; })); @@ -85,7 +94,7 @@ function prepareDebPackage(arch) { .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) .pipe(rename('DEBIAN/postinst')); - const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, icon, code); + const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, icon, /* bash_completion, zsh_completion, */ code); return all.pipe(vfs.dest(destination)); }; @@ -136,6 +145,12 @@ function prepareRpmPackage(arch) { const icon = gulp.src('resources/linux/code.png', { base: '.' }) .pipe(rename('BUILD/usr/share/pixmaps/' + product.applicationName + '.png')); + // const bash_completion = gulp.src('resources/completions/bash/code') + // .pipe(rename('BUILD/usr/share/bash-completion/completions/code')); + + // const zsh_completion = gulp.src('resources/completions/zsh/_code') + // .pipe(rename('BUILD/usr/share/zsh/site-functions/_code')); + const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) .pipe(rename(function (p) { p.dirname = 'BUILD/usr/share/' + product.applicationName + '/' + p.dirname; })); @@ -156,7 +171,7 @@ function prepareRpmPackage(arch) { const specIcon = gulp.src('resources/linux/rpm/code.xpm', { base: '.' }) .pipe(rename('SOURCES/' + product.applicationName + '.xpm')); - const all = es.merge(code, desktops, appdata, icon, spec, specIcon); + const all = es.merge(code, desktops, appdata, icon, /* bash_completion, zsh_completion, */ spec, specIcon); return all.pipe(vfs.dest(getRpmBuildPath(rpmArch))); }; @@ -199,17 +214,13 @@ function prepareSnapPackage(arch) { const snapcraft = gulp.src('resources/linux/snap/snapcraft.yaml', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@VERSION@@', `${packageJson.version}-${linuxPackageRevision}`)) + .pipe(replace('@@VERSION@@', commit.substr(0, 8))) .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, snapUpdate); + const all = es.merge(desktop, icon, code, snapcraft, electronLaunch); return all.pipe(vfs.dest(destination)); }; diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 0b10f700bd64..66173d52ccb7 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -126,6 +126,20 @@ class MonacoGenerator { this._declarationResolver.invalidateCache(moduleId); this._executeSoon(); }); + watcher.addListener('error', (err) => { + console.error(`Encountered error while watching ${filePath}.`); + console.log(err); + delete this._watchedFiles[filePath]; + for (let i = 0; i < this._watchers.length; i++) { + if (this._watchers[i] === watcher) { + this._watchers.splice(i, 1); + break; + } + } + watcher.close(); + this._declarationResolver.invalidateCache(moduleId); + this._executeSoon(); + }); this._watchers.push(watcher); }; this._fsProvider = new class extends monacodts.FSProvider { diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index c395ce64bdd3..189ef7afdfea 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -159,6 +159,20 @@ class MonacoGenerator { this._declarationResolver.invalidateCache(moduleId); this._executeSoon(); }); + watcher.addListener('error', (err) => { + console.error(`Encountered error while watching ${filePath}.`); + console.log(err); + delete this._watchedFiles[filePath]; + for (let i = 0; i < this._watchers.length; i++) { + if (this._watchers[i] === watcher) { + this._watchers.splice(i, 1); + break; + } + } + watcher.close(); + this._declarationResolver.invalidateCache(moduleId); + this._executeSoon(); + }); this._watchers.push(watcher); }; this._fsProvider = new class extends monacodts.FSProvider { diff --git a/build/lib/git.js b/build/lib/git.js index 4b740f07effb..212d21f79062 100644 --- a/build/lib/git.js +++ b/build/lib/git.js @@ -17,14 +17,14 @@ function getVersion(repo) { head = fs.readFileSync(headPath, 'utf8').trim(); } catch (e) { - return void 0; + return undefined; } if (/^[0-9a-f]{40}$/i.test(head)) { return head; } const refMatch = /^ref: (.*)$/.exec(head); if (!refMatch) { - return void 0; + return undefined; } const ref = refMatch[1]; const refPath = path.join(git, ref); @@ -40,7 +40,7 @@ function getVersion(repo) { refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim(); } catch (e) { - return void 0; + return undefined; } const refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; let refsMatch; diff --git a/build/lib/git.ts b/build/lib/git.ts index 825ab4658a36..366d642626ee 100644 --- a/build/lib/git.ts +++ b/build/lib/git.ts @@ -18,7 +18,7 @@ export function getVersion(repo: string): string | undefined { try { head = fs.readFileSync(headPath, 'utf8').trim(); } catch (e) { - return void 0; + return undefined; } if (/^[0-9a-f]{40}$/i.test(head)) { @@ -28,7 +28,7 @@ export function getVersion(repo: string): string | undefined { const refMatch = /^ref: (.*)$/.exec(head); if (!refMatch) { - return void 0; + return undefined; } const ref = refMatch[1]; @@ -46,7 +46,7 @@ export function getVersion(repo: string): string | undefined { try { refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim(); } catch (e) { - return void 0; + return undefined; } const refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 296d1286fbb8..f8a57d73f6b5 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -238,6 +238,10 @@ "name": "vs/workbench/services/decorations", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/label", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/preferences", "project": "vscode-preferences" diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 15e9a7f230e1..7f2fa4297895 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -119,8 +119,7 @@ function createESMSourcesAndResources2(options) { return path.join(OUT_RESOURCES_FOLDER, dest); }; const allFiles = walkDirRecursive(SRC_FOLDER); - for (let i = 0; i < allFiles.length; i++) { - const file = allFiles[i]; + for (const file of allFiles) { if (options.ignores.indexOf(file.replace(/\\/g, '/')) >= 0) { continue; } diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index e37e8c311306..e8b096cb736e 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -139,8 +139,7 @@ export function createESMSourcesAndResources2(options: IOptions2): void { }; const allFiles = walkDirRecursive(SRC_FOLDER); - for (let i = 0; i < allFiles.length; i++) { - const file = allFiles[i]; + for (const file of allFiles) { if (options.ignores.indexOf(file.replace(/\\/g, '/')) >= 0) { continue; @@ -244,7 +243,6 @@ export function createESMSourcesAndResources2(options: IOptions2): void { let mode = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i]; - if (mode === 0) { if (/\/\/ ESM-comment-begin/.test(line)) { mode = 1; diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index 28d07d7c6152..4aa2419c8107 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -15,8 +15,7 @@ var ShakeLevel; ShakeLevel[ShakeLevel["ClassMembers"] = 2] = "ClassMembers"; })(ShakeLevel = exports.ShakeLevel || (exports.ShakeLevel = {})); function printDiagnostics(diagnostics) { - for (let i = 0; i < diagnostics.length; i++) { - const diag = diagnostics[i]; + for (const diag of diagnostics) { let result = ''; if (diag.file) { result += `${diag.file.fileName}: `; @@ -97,6 +96,11 @@ function discoverAndReadFiles(options) { FILES[`${moduleId}.d.ts`] = dts_filecontents; continue; } + const js_filename = path.join(options.sourcesRoot, moduleId + '.js'); + if (fs.existsSync(js_filename)) { + // This is an import for a .js file, so ignore it... + continue; + } let ts_filename; if (options.redirects[moduleId]) { ts_filename = path.join(options.sourcesRoot, options.redirects[moduleId] + '.ts'); @@ -475,8 +479,7 @@ function generateResult(languageService, shakeLevel) { } else { let survivingImports = []; - for (let i = 0; i < node.importClause.namedBindings.elements.length; i++) { - const importNode = node.importClause.namedBindings.elements[i]; + for (const importNode of node.importClause.namedBindings.elements) { if (getColor(importNode) === 2 /* Black */) { survivingImports.push(importNode.getFullText(sourceFile)); } diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index 54f1f4633214..6a314fa6d71b 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -61,15 +61,14 @@ export interface ITreeShakingResult { } function printDiagnostics(diagnostics: ReadonlyArray): void { - for (let i = 0; i < diagnostics.length; i++) { - const diag = diagnostics[i]; + for (const diag of diagnostics) { let result = ''; if (diag.file) { result += `${diag.file.fileName}: `; } if (diag.file && diag.start) { let location = diag.file.getLineAndCharacterOfPosition(diag.start); - result += `- ${location.line + 1},${location.character} - ` + result += `- ${location.line + 1},${location.character} - `; } result += JSON.stringify(diag.messageText); console.log(result); @@ -160,6 +159,12 @@ function discoverAndReadFiles(options: ITreeShakingOptions): IFileMap { continue; } + const js_filename = path.join(options.sourcesRoot, moduleId + '.js'); + if (fs.existsSync(js_filename)) { + // This is an import for a .js file, so ignore it... + continue; + } + let ts_filename: string; if (options.redirects[moduleId]) { ts_filename = path.join(options.sourcesRoot, options.redirects[moduleId] + '.ts'); @@ -459,7 +464,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt } if (black_queue.length === 0) { - for (let i = 0; i < gray_queue.length; i++) { + for (let i = 0; i< gray_queue.length; i++) { const node = gray_queue[i]; const nodeParent = node.parent; if ((ts.isClassDeclaration(nodeParent) || ts.isInterfaceDeclaration(nodeParent)) && nodeOrChildIsBlack(nodeParent)) { @@ -604,8 +609,7 @@ function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLe } } else { let survivingImports: string[] = []; - for (let i = 0; i < node.importClause.namedBindings.elements.length; i++) { - const importNode = node.importClause.namedBindings.elements[i]; + for (const importNode of node.importClause.namedBindings.elements) { if (getColor(importNode) === NodeColor.Black) { survivingImports.push(importNode.getFullText(sourceFile)); } diff --git a/build/lib/tslint/noUnexternalizedStringsRule.js b/build/lib/tslint/noUnexternalizedStringsRule.js index 9715f1d86688..9ae2da6121d3 100644 --- a/build/lib/tslint/noUnexternalizedStringsRule.js +++ b/build/lib/tslint/noUnexternalizedStringsRule.js @@ -109,8 +109,7 @@ class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker { this.recordKey(keyArg, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined); } else if (isObjectLiteral(keyArg)) { - for (let i = 0; i < keyArg.properties.length; i++) { - const property = keyArg.properties[i]; + for (const property of keyArg.properties) { if (isPropertyAssignment(property)) { const name = property.name.getText(); if (name === 'key') { diff --git a/build/lib/tslint/noUnexternalizedStringsRule.ts b/build/lib/tslint/noUnexternalizedStringsRule.ts index 144260620f89..1a2108f553a1 100644 --- a/build/lib/tslint/noUnexternalizedStringsRule.ts +++ b/build/lib/tslint/noUnexternalizedStringsRule.ts @@ -148,8 +148,7 @@ class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker { if (isStringLiteral(keyArg)) { this.recordKey(keyArg, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined); } else if (isObjectLiteral(keyArg)) { - for (let i = 0; i < keyArg.properties.length; i++) { - const property = keyArg.properties[i]; + for (const property of keyArg.properties) { if (isPropertyAssignment(property)) { const name = property.name.getText(); if (name === 'key') { diff --git a/build/lib/watch/index.js b/build/lib/watch/index.js index afeccf1f065d..1f5b3c60eed3 100644 --- a/build/lib/watch/index.js +++ b/build/lib/watch/index.js @@ -17,7 +17,7 @@ function handleDeletions() { }); } -let watch = void 0; +let watch = undefined; if (!watch) { watch = process.platform === 'win32' ? require('./watch-win32') : require('gulp-watch'); diff --git a/build/monaco/ThirdPartyNotices.txt b/build/monaco/ThirdPartyNotices.txt index 45eeffb9f329..a459893cc979 100644 --- a/build/monaco/ThirdPartyNotices.txt +++ b/build/monaco/ThirdPartyNotices.txt @@ -8,22 +8,28 @@ herein, whether by implication, estoppel or otherwise. -%% winjs version 4.4.0 (https://github.com/winjs/winjs) +%% promise-polyfill version 8.1.0 (https://github.com/taylorhakes/promise-polyfill) ========================================= -WinJS +Copyright (c) 2014 Taylor Hakes +Copyright (c) 2014 Forbes Lindesay -Copyright (c) Microsoft Corporation - -All rights reserved. - -MIT License - -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. +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 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. +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. ========================================= END OF winjs NOTICES AND INFORMATION diff --git a/build/monaco/api.js b/build/monaco/api.js index 79632e2b2c09..4cbe5474ecc8 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -456,11 +456,20 @@ class FSProvider { existsSync(filePath) { return fs.existsSync(filePath); } + statSync(filePath) { + return fs.statSync(filePath); + } readFileSync(_moduleId, filePath) { return fs.readFileSync(filePath); } } exports.FSProvider = FSProvider; +class CacheEntry { + constructor(sourceFile, mtime) { + this.sourceFile = sourceFile; + this.mtime = mtime; + } +} class DeclarationResolver { constructor(_fsProvider) { this._fsProvider = _fsProvider; @@ -470,31 +479,43 @@ class DeclarationResolver { this._sourceFileCache[moduleId] = null; } getDeclarationSourceFile(moduleId) { + if (this._sourceFileCache[moduleId]) { + // Since we cannot trust file watching to invalidate the cache, check also the mtime + const fileName = this._getFileName(moduleId); + const mtime = this._fsProvider.statSync(fileName).mtime.getTime(); + if (this._sourceFileCache[moduleId].mtime !== mtime) { + this._sourceFileCache[moduleId] = null; + } + } if (!this._sourceFileCache[moduleId]) { this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId); } - return this._sourceFileCache[moduleId]; + return this._sourceFileCache[moduleId] ? this._sourceFileCache[moduleId].sourceFile : null; } - _getDeclarationSourceFile(moduleId) { + _getFileName(moduleId) { if (/\.d\.ts$/.test(moduleId)) { - const fileName = path.join(SRC, moduleId); - if (!this._fsProvider.existsSync(fileName)) { - return null; - } - const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); - return ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5); + return path.join(SRC, moduleId); } - const fileName = path.join(SRC, `${moduleId}.ts`); + return path.join(SRC, `${moduleId}.ts`); + } + _getDeclarationSourceFile(moduleId) { + const fileName = this._getFileName(moduleId); if (!this._fsProvider.existsSync(fileName)) { return null; } + const mtime = this._fsProvider.statSync(fileName).mtime.getTime(); + if (/\.d\.ts$/.test(moduleId)) { + // const mtime = this._fsProvider.statFileSync() + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + return new CacheEntry(ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5), mtime); + } 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); + return new CacheEntry(ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5), mtime); } } exports.DeclarationResolver = DeclarationResolver; diff --git a/build/monaco/api.ts b/build/monaco/api.ts index 9775f741ad3c..c45a35e01e3f 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -547,14 +547,24 @@ export class FSProvider { public existsSync(filePath: string): boolean { return fs.existsSync(filePath); } + public statSync(filePath: string): fs.Stats { + return fs.statSync(filePath); + } public readFileSync(_moduleId: string, filePath: string): Buffer { return fs.readFileSync(filePath); } } +class CacheEntry { + constructor( + public readonly sourceFile: ts.SourceFile, + public readonly mtime: number + ) {} +} + export class DeclarationResolver { - private _sourceFileCache: { [moduleId: string]: ts.SourceFile | null; }; + private _sourceFileCache: { [moduleId: string]: CacheEntry | null; }; constructor(private readonly _fsProvider: FSProvider) { this._sourceFileCache = Object.create(null); @@ -565,32 +575,51 @@ export class DeclarationResolver { } public getDeclarationSourceFile(moduleId: string): ts.SourceFile | null { + if (this._sourceFileCache[moduleId]) { + // Since we cannot trust file watching to invalidate the cache, check also the mtime + const fileName = this._getFileName(moduleId); + const mtime = this._fsProvider.statSync(fileName).mtime.getTime(); + if (this._sourceFileCache[moduleId]!.mtime !== mtime) { + this._sourceFileCache[moduleId] = null; + } + } if (!this._sourceFileCache[moduleId]) { this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId); } - return this._sourceFileCache[moduleId]; + return this._sourceFileCache[moduleId] ? this._sourceFileCache[moduleId]!.sourceFile : null; } - private _getDeclarationSourceFile(moduleId: string): ts.SourceFile | null { + private _getFileName(moduleId: string): string { if (/\.d\.ts$/.test(moduleId)) { - const fileName = path.join(SRC, moduleId); - if (!this._fsProvider.existsSync(fileName)) { - return null; - } - const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); - return ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5); + return path.join(SRC, moduleId); } - const fileName = path.join(SRC, `${moduleId}.ts`); + return path.join(SRC, `${moduleId}.ts`); + } + + private _getDeclarationSourceFile(moduleId: string): CacheEntry | null { + const fileName = this._getFileName(moduleId); if (!this._fsProvider.existsSync(fileName)) { return null; } + const mtime = this._fsProvider.statSync(fileName).mtime.getTime(); + if (/\.d\.ts$/.test(moduleId)) { + // const mtime = this._fsProvider.statFileSync() + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + return new CacheEntry( + ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5), + mtime + ); + } 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); + return new CacheEntry( + ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5), + mtime + ); } } diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 204d7bb9288b..a379a7522ada 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -21,12 +21,11 @@ declare namespace monaco { export class Emitter { constructor(); readonly event: Event; - fire(event?: T): void; + fire(event: T): void; dispose(): void; } #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/base/common/keyCodes): KeyCode @@ -86,4 +85,4 @@ declare namespace monaco.worker { } -//dtsv=2 \ No newline at end of file +//dtsv=2 diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe index 663f7b7cc268..e75b8585a2df 100644 --- a/build/monaco/monaco.usage.recipe +++ b/build/monaco/monaco.usage.recipe @@ -11,7 +11,6 @@ import { SimpleWorkerClient, create as create1 } from './vs/base/common/worker/s import { create as create2 } from './vs/editor/common/services/editorSimpleWorker'; import { QuickOpenWidget } from './vs/base/parts/quickopen/browser/quickOpenWidget'; import { SyncDescriptor0, SyncDescriptor1, SyncDescriptor2, SyncDescriptor3, SyncDescriptor4, SyncDescriptor5, SyncDescriptor6, SyncDescriptor7, SyncDescriptor8 } from './vs/platform/instantiation/common/descriptors'; -import { PolyfillPromise } from './vs/base/common/winjs.polyfill.promise'; import { DiffNavigator } from './vs/editor/browser/widget/diffNavigator'; import * as editorAPI from './vs/editor/editor.api'; @@ -32,14 +31,6 @@ import * as editorAPI from './vs/editor/editor.api'; a = create1; a = create2; - // promise polyfill - a = PolyfillPromise.all; - a = PolyfillPromise.race; - a = PolyfillPromise.resolve; - a = PolyfillPromise.reject; - a = (b).then; - a = (b).catch; - // injection madness a = (>b).ctor; a = (>b).bind; @@ -73,7 +64,6 @@ import * as editorAPI from './vs/editor/editor.api'; a = editorAPI.SelectionDirection; a = editorAPI.MarkerSeverity; a = editorAPI.MarkerTag; - a = editorAPI.Promise; a = editorAPI.Uri; a = editorAPI.Token; a = editorAPI.editor; diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index ab18ce3cb817..45bd052cd5c9 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -17,6 +17,7 @@ function yarnInstall(location, opts) { opts.cwd = location; opts.stdio = 'inherit'; + console.log('Installing dependencies in \'%s\'.', location); const result = cp.spawnSync(yarn, ['install'], opts); if (result.error || result.status !== 0) { diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index 38af111dad18..1cbc34b916bf 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -7,8 +7,8 @@ let err = false; const majorNodeVersion = parseInt(/^(\d+)\./.exec(process.versions.node)[1]); -if (majorNodeVersion < 8 || majorNodeVersion >= 9) { - console.error('\033[1;31m*** Please use node >=8 and <9.\033[0;0m'); +if (majorNodeVersion < 8 || majorNodeVersion >= 11) { + console.error('\033[1;31m*** Please use node >=8 and <11.\033[0;0m'); err = true; } diff --git a/build/npm/update-grammar.js b/build/npm/update-grammar.js index 7c6e11a6d896..3f0b65e688df 100644 --- a/build/npm/update-grammar.js +++ b/build/npm/update-grammar.js @@ -120,6 +120,28 @@ exports.update = function (repoId, repoPath, dest, modifyGrammar, version = 'mas try { fs.writeFileSync(dest, JSON.stringify(result, null, '\t').replace(/\n/g, '\r\n')); + // Add commit sha to cgmanifest + let cgmanifestRead = JSON.parse(fs.readFileSync('./cgmanifest.json').toString()); + let promises = new Array(); + let packageJsonPath = 'https://raw.githubusercontent.com/' + repoId + `/${info.commitSha}/package.json`; + for (let i = 0; i < cgmanifestRead.registrations.length; i++) { + if (cgmanifestRead.registrations[i].component.git.repositoryUrl.substr(cgmanifestRead.registrations[i].component.git.repositoryUrl.length - repoId.length, repoId.length) === repoId) { + cgmanifestRead.registrations[i].component.git.commitHash = info.commitSha; + promises.push(download(packageJsonPath).then(function (packageJson) { + if (packageJson) { + try { + cgmanifestRead.registrations[i].version = JSON.parse(packageJson).version; + } catch (e) { + console.log('File does not exist at' + packageJsonPath); + } + } + })); + break; + } + } + Promise.all(promises).then(function (allResult) { + fs.writeFileSync('./cgmanifest.json', JSON.stringify(cgmanifestRead, 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 a260f47763db..c8e7a5e07090 100644 --- a/build/package.json +++ b/build/package.json @@ -42,7 +42,7 @@ "request": "^2.85.0", "tslint": "^5.9.1", "service-downloader": "github:anthonydresser/service-downloader#0.1.5", - "typescript": "3.1.4", + "typescript": "3.2.2", "vsce": "1.48.0", "xml2js": "^0.4.17" }, diff --git a/build/yarn.lock b/build/yarn.lock index e65082edc4b8..3eaaa8e3cf8a 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2084,10 +2084,10 @@ typed-rest-client@^0.9.0: 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== +typescript@3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5" + integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" diff --git a/cgmanifest.json b/cgmanifest.json index 637c7d2f5ab5..b7e74e394565 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33" + "commitHash": "164c37e3f235134c88e80fac2a182cfba3f07f00" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "61.0.3163.100" + "version": "66.0.3359.181" }, { "component": { @@ -48,12 +48,12 @@ "git": { "name": "libchromiumcontent", "repositoryUrl": "https://github.com/electron/libchromiumcontent", - "commitHash": "ccdb085454b0a387ee96e0f81a7ca9a8ce07a710" + "commitHash": "7ea271f92018b1eeb8e70ec6de8c29f9758a0c05" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "61.0.3163.100" + "version": "66.0.3359.181" }, { "component": { @@ -61,11 +61,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "8a44289089a08b7b19fa3c4651b5f1f5d1edd71b" + "commitHash": "5cbb905c1af7cea2d709932d59827d7c6d03ef4a" } }, "isOnlyProductionDependency": true, - "version": "8.9.3" + "version": "10.2.0" }, { "component": { @@ -73,12 +73,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "d281859cf59f12c7107a540a9f4cba0ecf5eff41" + "commitHash": "bb28fa8e8e797db249a66405146ad0501eaf411a" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "2.0.12" + "version": "3.1.2" }, { "component": { @@ -105,6 +105,30 @@ "license": "MIT", "version": "0.14.0" }, + { + "component": { + "type": "git", + "git": { + "name": "vscode-octicons-font", + "repositoryUrl": "https://github.com/Microsoft/vscode-octicons-font", + "commitHash": "5095860bb929919670646e2dfa0ee47d9b93bcb9" + } + }, + "license": "MIT", + "version": "1.0.0" + }, + { + "component": { + "type": "git", + "git": { + "name": "octicons", + "repositoryUrl": "https://github.com/primer/octicons", + "commitHash": "d120bf97bc9a12fb415f69fedaf31fe58427ca56" + } + }, + "license": "MIT", + "version": "8.3.0" + }, { "component": { "type": "npm", @@ -493,6 +517,19 @@ " defined by the Mozilla Public License, v. 2.0." ], "license": "MPL" + }, + { + "component": { + "type": "git", + "git": { + "name": "ripgrep", + "repositoryUrl": "https://github.com/BurntSushi/ripgrep", + "commitHash": "8a7db1a918e969b85cd933d8ed9fa5285b281ba4" + } + }, + "isOnlyProductionDependency": true, + "license": "MIT", + "version": "0.10.0" } ], "version": 1 diff --git a/extensions/admin-pack/package.json b/extensions/admin-pack/package.json index be40f9484cea..48813e8d1ac3 100644 --- a/extensions/admin-pack/package.json +++ b/extensions/admin-pack/package.json @@ -1,24 +1,29 @@ { - "name": "admin-pack", - "displayName": "Admin Pack for SQL Server", - "description": "", - "version": "0.0.2", - "publisher": "Microsoft", - "engines": { - "vscode": "*", - "azdata": "*" - }, - "extensionPack": [ - "Microsoft.agent", - "Microsoft.profiler", - "Microsoft.import", - "Microsoft.dacpac" - ], - "repository": { - "type": "git", - "url": "https://github.com/Microsoft/azuredatastudio.git" - }, - "bugs": { - "url": "https://github.com/Microsoft/azuredatastudio/issues" - } + "name": "admin-pack", + "displayName": "Admin Pack for SQL Server", + "description": "", + "version": "0.0.2", + "publisher": "Microsoft", + "engines": { + "vscode": "*", + "azdata": "*" + }, + "extensionPack": [ + "Microsoft.agent", + "Microsoft.profiler", + "Microsoft.import", + "Microsoft.dacpac" + ], + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/azuredatastudio.git" + }, + "bugs": { + "url": "https://github.com/Microsoft/azuredatastudio/issues" + }, + "__metadata": { + "id": "32", + "publisherDisplayName": "Microsoft", + "publisherId": "Microsoft" + } } \ No newline at end of file diff --git a/extensions/agent/package.json b/extensions/agent/package.json index cfdcb5f27205..5e13af26ec59 100644 --- a/extensions/agent/package.json +++ b/extensions/agent/package.json @@ -1,51 +1,56 @@ { - "name": "agent", - "displayName": "SQL Server Agent", - "description": "Manage and troubleshoot SQL Server Agent jobs", - "version": "0.37.0", - "publisher": "Microsoft", - "preview": true, - "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt", - "icon": "images/sqlserver.png", - "aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412", - "engines": { - "vscode": "0.10.x" - }, - "activationEvents": [ - "*" - ], - "main": "./out/main", - "repository": { - "type": "git", - "url": "https://github.com/Microsoft/azuredatastudio.git" - }, - "extensionDependencies": [ - "Microsoft.mssql" - ], - "contributes": { - "outputChannels": [ - "sqlagent" - ], - "dashboard.tabs": [ - { - "id": "data-management-agent", - "description": "Manage and troubleshoot SQL Agent jobs", - "provider": "MSSQL", - "title": "SQL Agent", - "when": "connectionProvider == 'MSSQL' && !mssql:iscloud", - "container": { - "controlhost-container": { - "type": "agent" - } - } - } - ] - }, - "dependencies": { - "vscode-nls": "^3.2.1" - }, - "devDependencies": { - "mocha-junit-reporter": "^1.17.0", - "mocha-multi-reporters": "^1.1.7" - } -} + "name": "agent", + "displayName": "SQL Server Agent", + "description": "Manage and troubleshoot SQL Server Agent jobs", + "version": "0.37.0", + "publisher": "Microsoft", + "preview": true, + "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt", + "icon": "images/sqlserver.png", + "aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412", + "engines": { + "vscode": "0.10.x" + }, + "activationEvents": [ + "*" + ], + "main": "./out/main", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/azuredatastudio.git" + }, + "extensionDependencies": [ + "Microsoft.mssql" + ], + "contributes": { + "outputChannels": [ + "sqlagent" + ], + "dashboard.tabs": [ + { + "id": "data-management-agent", + "description": "Manage and troubleshoot SQL Agent jobs", + "provider": "MSSQL", + "title": "SQL Agent", + "when": "connectionProvider == 'MSSQL' && !mssql:iscloud", + "container": { + "controlhost-container": { + "type": "agent" + } + } + } + ] + }, + "dependencies": { + "vscode-nls": "^3.2.1" + }, + "devDependencies": { + "mocha-junit-reporter": "^1.17.0", + "mocha-multi-reporters": "^1.1.7" + }, + "__metadata": { + "id": "10", + "publisherDisplayName": "Microsoft", + "publisherId": "Microsoft" + } +} \ No newline at end of file diff --git a/extensions/azurecore/src/azureResource/tree/accountNotSignedInTreeNode.ts b/extensions/azurecore/src/azureResource/tree/accountNotSignedInTreeNode.ts index b7e9c91d302f..65cf600ef7b1 100644 --- a/extensions/azurecore/src/azureResource/tree/accountNotSignedInTreeNode.ts +++ b/extensions/azurecore/src/azureResource/tree/accountNotSignedInTreeNode.ts @@ -47,5 +47,5 @@ export class AzureResourceAccountNotSignedInTreeNode extends TreeNode { return 'message_accountNotSignedIn'; } - private static readonly signInLabel = localize('azure.resource.tree.accountNotSignedInTreeNode.signInLabel', 'Sign in to Azure ...'); + private static readonly signInLabel = localize('azure.resource.tree.accountNotSignedInTreeNode.signInLabel', 'Sign in to Azure...'); } diff --git a/extensions/azurecore/src/test/azureResource/tree/accountNotSignedInTreeNode.test.ts b/extensions/azurecore/src/test/azureResource/tree/accountNotSignedInTreeNode.test.ts index 5cb0d1cd1505..a7539934e745 100644 --- a/extensions/azurecore/src/test/azureResource/tree/accountNotSignedInTreeNode.test.ts +++ b/extensions/azurecore/src/test/azureResource/tree/accountNotSignedInTreeNode.test.ts @@ -14,7 +14,7 @@ import { AzureResourceAccountNotSignedInTreeNode } from '../../../azureResource/ describe('AzureResourceAccountNotSignedInTreeNode.info', function(): void { it('Should be correct.', async function(): Promise { - const label = 'Sign in to Azure ...'; + const label = 'Sign in to Azure...'; const treeNode = new AzureResourceAccountNotSignedInTreeNode(); diff --git a/extensions/bat/cgmanifest.json b/extensions/bat/cgmanifest.json index 32d7db263cc8..1e21679d7b2d 100644 --- a/extensions/bat/cgmanifest.json +++ b/extensions/bat/cgmanifest.json @@ -10,8 +10,8 @@ } }, "license": "MIT", - "version": "0.0.0" + "version": "0.7.4" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts index d6847a99693d..dee6fe8f7e23 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -44,8 +44,11 @@ export class SettingsDocument { const completions: vscode.CompletionItem[] = []; completions.push(this.newSimpleCompletionItem('${activeEditorShort}', range, localize('activeEditorShort', "the file name (e.g. myFile.txt)"))); - completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFile.txt)"))); - completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myProject/myFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem('${activeFolderShort}', range, localize('activeFolderShort', "the name of the folder the file is contained in (e.g. myFileFolder)"))); + completions.push(this.newSimpleCompletionItem('${activeFolderMedium}', range, localize('activeFolderMedium', "the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder)"))); + completions.push(this.newSimpleCompletionItem('${activeFolderLong}', range, localize('activeFolderLong', "the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder)"))); completions.push(this.newSimpleCompletionItem('${rootName}', range, localize('rootName', "name of the workspace (e.g. myFolder or myWorkspace)"))); completions.push(this.newSimpleCompletionItem('${rootPath}', range, localize('rootPath', "file path of the workspace (e.g. /Users/Development/myWorkspace)"))); completions.push(this.newSimpleCompletionItem('${folderName}', range, localize('folderName', "name of the workspace folder the file is contained in (e.g. myFolder)"))); diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json index 0caec7607a61..8ceab79d04e7 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -41,6 +41,14 @@ "fileMatch": "*color-theme.json", "url": "vscode://schemas/color-theme" } + ], + "languages": [ + { + "id": "ignore", + "filenames": [ + ".vscodeignore" + ] + } ] }, "devDependencies": { diff --git a/extensions/git/package.json b/extensions/git/package.json index ef4385ef3db4..fdc08df06a86 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -661,11 +661,21 @@ "group": "1_sync", "when": "scmProvider == git" }, + { + "command": "git.pushForce", + "group": "1_sync", + "when": "scmProvider == git && config.git.allowForcePush" + }, { "command": "git.pushTo", "group": "1_sync", "when": "scmProvider == git" }, + { + "command": "git.pushToForce", + "group": "1_sync", + "when": "scmProvider == git && config.git.allowForcePush" + }, { "command": "git.publish", "group": "2_publish", @@ -1018,12 +1028,19 @@ }, "git.autofetch": { "type": "boolean", + "scope": "resource", "description": "%config.autofetch%", "default": false, "tags": [ "usesOnlineServices" ] }, + "git.autofetchPeriod": { + "type": "number", + "scope": "resource", + "description": "%config.autofetchPeriod%", + "default": 180 + }, "git.branchValidationRegex": { "type": "string", "description": "%config.branchValidationRegex%", @@ -1160,6 +1177,14 @@ "default": 72, "description": "%config.inputValidationLength%" }, + "git.inputValidationSubjectLength": { + "type": [ + "number", + "null" + ], + "default": null, + "description": "%config.inputValidationSubjectLength%" + }, "git.detectSubmodules": { "type": "boolean", "scope": "resource", @@ -1214,6 +1239,12 @@ "default": false, "description": "%config.fetchOnPull%" }, + "git.autoStash": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.autoStash%" + }, "git.allowForcePush": { "type": "boolean", "default": false, @@ -1231,6 +1262,7 @@ }, "git.openDiffOnClick": { "type": "boolean", + "scope": "resource", "default": true, "description": "%config.openDiffOnClick%" } @@ -1385,7 +1417,7 @@ "file-type": "^7.2.0", "iconv-lite": "^0.4.24", "jschardet": "^1.6.0", - "vscode-extension-telemetry": "0.1.0", + "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0", "which": "^1.3.0" }, diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 356d1473bbca..4f749189fc77 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -68,6 +68,7 @@ "config.autoRepositoryDetection.openEditors": "Scan for parent folders of open files.", "config.autorefresh": "Whether auto refreshing is enabled.", "config.autofetch": "When enabled, commits will automatically be fetched from the default remote of the current Git repository.", + "config.autofetchPeriod": "Duration in seconds between each automatic git fetch, when `git.autofetch` is enabled.", "config.confirmSync": "Confirm before synchronizing git repositories.", "config.countBadge": "Controls the git badge counter.", "config.countBadge.all": "Count all changes.", @@ -97,6 +98,7 @@ "config.showPushSuccessNotification": "Controls whether to show a notification when a push is successful.", "config.inputValidation": "Controls when to show commit message input validation.", "config.inputValidationLength": "Controls the commit message length threshold for showing a warning.", + "config.inputValidationSubjectLength": "Controls the commit message subject length threshold for showing a warning. Unset it to inherit the value of `config.inputValidationLength`.", "config.detectSubmodules": "Controls whether to automatically detect git submodules.", "config.detectSubmodulesLimit": "Controls the limit of git submodules detected.", "config.alwaysShowStagedChangesResourceGroup": "Always show the Staged Changes resource group.", @@ -107,6 +109,7 @@ "config.rebaseWhenSync": "Force git to use rebase when running the sync command.", "config.confirmEmptyCommits": "Always confirm the creation of empty commits.", "config.fetchOnPull": "Fetch all branches when pulling or just the current one.", + "config.autoStash": "Stash any changes before pulling and restore them after successful pull.", "config.allowForcePush": "Controls whether force push (with or without lease) is enabled.", "config.useForcePushWithLease": "Controls whether force pushing uses the safer force-with-lease variant.", "config.confirmForcePush": "Controls whether to ask for confirmation before force-pushing.", diff --git a/extensions/git/resources/icons/dark/open-change.svg b/extensions/git/resources/icons/dark/open-change.svg index e43ba7616c50..6f785c26a5eb 100644 --- a/extensions/git/resources/icons/dark/open-change.svg +++ b/extensions/git/resources/icons/dark/open-change.svg @@ -1 +1 @@ -Compare_16x \ No newline at end of file + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/open-change.svg b/extensions/git/resources/icons/light/open-change.svg index e53964f3b816..873b93d81062 100644 --- a/extensions/git/resources/icons/light/open-change.svg +++ b/extensions/git/resources/icons/light/open-change.svg @@ -1 +1 @@ -Compare_16x \ No newline at end of file + \ No newline at end of file diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 433a46cd6319..0f0df0650d13 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, Status } from './git'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions } from './git'; import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode'; import { mapEvent } from '../util'; @@ -76,6 +76,10 @@ export class ApiRepository implements Repository { return this._repository.setConfig(key, value); } + getGlobalConfig(key: string): Promise { + return this._repository.getGlobalConfig(key); + } + getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number; }> { return this._repository.getObjectDetails(treeish, path); } @@ -104,19 +108,27 @@ export class ApiRepository implements Repository { return this._repository.diff(cached); } - diffWithHEAD(path: string): Promise { + diffWithHEAD(): Promise; + diffWithHEAD(path: string): Promise; + diffWithHEAD(path?: string): Promise { return this._repository.diffWithHEAD(path); } - diffWith(ref: string, path: string): Promise { + diffWith(ref: string): Promise; + diffWith(ref: string, path: string): Promise; + diffWith(ref: string, path?: string): Promise { return this._repository.diffWith(ref, path); } - diffIndexWithHEAD(path: string): Promise { + diffIndexWithHEAD(): Promise; + diffIndexWithHEAD(path: string): Promise; + diffIndexWithHEAD(path?: string): Promise { return this._repository.diffIndexWithHEAD(path); } - diffIndexWith(ref: string, path: string): Promise { + diffIndexWith(ref: string): Promise; + diffIndexWith(ref: string, path: string): Promise; + diffIndexWith(ref: string, path?: string): Promise { return this._repository.diffIndexWith(ref, path); } @@ -124,7 +136,9 @@ export class ApiRepository implements Repository { return this._repository.diffBlobs(object1, object2); } - diffBetween(ref1: string, ref2: string, path: string): Promise { + diffBetween(ref1: string, ref2: string): Promise; + diffBetween(ref1: string, ref2: string, path: string): Promise; + diffBetween(ref1: string, ref2: string, path?: string): Promise { return this._repository.diffBetween(ref1, ref2, path); } @@ -168,17 +182,25 @@ export class ApiRepository implements Repository { return this._repository.removeRemote(name); } - fetch(remote?: string | undefined, ref?: string | undefined): Promise { - return this._repository.fetch(remote, ref); + fetch(remote?: string | undefined, ref?: string | undefined, depth?: number | undefined): Promise { + return this._repository.fetch(remote, ref, depth); } - pull(): Promise { - return this._repository.pull(); + pull(unshallow?: boolean): Promise { + return this._repository.pull(undefined, unshallow); } push(remoteName?: string, branchName?: string, setUpstream: boolean = false): Promise { return this._repository.pushTo(remoteName, branchName, setUpstream); } + + blame(path: string): Promise { + return this._repository.blame(path); + } + + log(options?: LogOptions): Promise { + return this._repository.log(options); + } } export class ApiGit implements Git { diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 000b6d2b2edf..767aacd3c9f3 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -41,6 +41,7 @@ export interface Commit { readonly hash: string; readonly message: string; readonly parents: string[]; + readonly authorEmail?: string | undefined; } export interface Submodule { @@ -67,6 +68,7 @@ export const enum Status { DELETED, UNTRACKED, IGNORED, + INTENT_TO_ADD, ADDED_BY_US, ADDED_BY_THEM, @@ -109,6 +111,14 @@ export interface RepositoryUIState { readonly onDidChange: Event; } +/** + * Log options. + */ +export interface LogOptions { + /** Max number of log entries to retrieve. If not specified, the default is 32. */ + readonly maxEntries?: number; +} + export interface Repository { readonly rootUri: Uri; @@ -119,6 +129,7 @@ export interface Repository { getConfigs(): Promise<{ key: string; value: string; }[]>; getConfig(key: string): Promise; setConfig(key: string, value: string): Promise; + getGlobalConfig(key: string): Promise; getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }>; detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }>; @@ -130,11 +141,16 @@ export interface Repository { apply(patch: string, reverse?: boolean): Promise; diff(cached?: boolean): Promise; + diffWithHEAD(): Promise; diffWithHEAD(path: string): Promise; + diffWith(ref: string): Promise; diffWith(ref: string, path: string): Promise; + diffIndexWithHEAD(): Promise; diffIndexWithHEAD(path: string): Promise; + diffIndexWith(ref: string): Promise; diffIndexWith(ref: string, path: string): Promise; diffBlobs(object1: string, object2: string): Promise; + diffBetween(ref1: string, ref2: string): Promise; diffBetween(ref1: string, ref2: string, path: string): Promise; hashObject(data: string): Promise; @@ -152,9 +168,12 @@ export interface Repository { addRemote(name: string, url: string): Promise; removeRemote(name: string): Promise; - fetch(remote?: string, ref?: string): Promise; - pull(): Promise; + fetch(remote?: string, ref?: string, depth?: number): Promise; + pull(unshallow?: boolean): Promise; push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise; + + blame(path: string): Promise; + log(options?: LogOptions): Promise; } export interface API { @@ -214,4 +233,6 @@ export const enum GitErrorCodes { WrongCase = 'WrongCase', CantLockRef = 'CantLockRef', CantRebaseMultipleBranches = 'CantRebaseMultipleBranches', + PatchDoesNotApply = 'PatchDoesNotApply', + NoPathFound = 'NoPathFound' } \ No newline at end of file diff --git a/extensions/git/src/autofetch.ts b/extensions/git/src/autofetch.ts index 0cf3664e037a..0ab990e59020 100644 --- a/extensions/git/src/autofetch.ts +++ b/extensions/git/src/autofetch.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workspace, Disposable, EventEmitter, Memento, window, MessageItem, ConfigurationTarget } from 'vscode'; +import { workspace, Disposable, EventEmitter, Memento, window, MessageItem, ConfigurationTarget, Uri } from 'vscode'; import { Repository, Operation } from './repository'; import { eventToPromise, filterEvent, onceEvent } from './util'; import * as nls from 'vscode-nls'; @@ -17,7 +17,6 @@ function isRemoteOperation(operation: Operation): boolean { export class AutoFetcher { - private static readonly Period = 3 * 60 * 1000 /* three minutes */; private static DidInformUser = 'autofetch.didInformUser'; private _onDidChange = new EventEmitter(); @@ -62,7 +61,7 @@ export class AutoFetcher { } if (result === yes) { - const gitConfig = workspace.getConfiguration('git'); + const gitConfig = workspace.getConfiguration('git', Uri.file(this.repository.root)); gitConfig.update('autofetch', true, ConfigurationTarget.Global); } @@ -70,7 +69,7 @@ export class AutoFetcher { } private onConfiguration(): void { - const gitConfig = workspace.getConfiguration('git'); + const gitConfig = workspace.getConfiguration('git', Uri.file(this.repository.root)); if (gitConfig.get('autofetch') === false) { this.disable(); @@ -112,8 +111,10 @@ export class AutoFetcher { return; } - const timeout = new Promise(c => setTimeout(c, AutoFetcher.Period)); + const period = workspace.getConfiguration('git', Uri.file(this.repository.root)).get('autofetchPeriod', 180) * 1000; + const timeout = new Promise(c => setTimeout(c, period)); const whenDisabled = eventToPromise(filterEvent(this.onDidChange, enabled => !enabled)); + await Promise.race([timeout, whenDisabled]); } } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 18b95637cdc2..bc616c682bbb 100755 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -103,6 +103,15 @@ class CreateBranchItem implements QuickPickItem { } } +class HEADItem implements QuickPickItem { + + constructor(private repository: Repository) { } + + get label(): string { return 'HEAD'; } + get description(): string { return (this.repository.HEAD && this.repository.HEAD.commit || '').substr(0, 8); } + get alwaysShow(): boolean { return true; } +} + interface CommandOptions { repository?: boolean; diff?: boolean; @@ -154,6 +163,22 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ return { merge, resolved, unresolved, deletionConflicts }; } +function createCheckoutItems(repository: Repository): CheckoutItem[] { + const config = workspace.getConfiguration('git'); + const checkoutType = config.get('checkoutType') || 'all'; + const includeTags = checkoutType === 'all' || checkoutType === 'tags'; + const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; + + const heads = repository.refs.filter(ref => ref.type === RefType.Head) + .map(ref => new CheckoutItem(ref)); + const tags = (includeTags ? repository.refs.filter(ref => ref.type === RefType.Tag) : []) + .map(ref => new CheckoutTagItem(ref)); + const remoteHeads = (includeRemotes ? repository.refs.filter(ref => ref.type === RefType.RemoteHead) : []) + .map(ref => new CheckoutRemoteHeadItem(ref)); + + return [...heads, ...tags, ...remoteHeads]; +} + enum PushType { Push, PushTo, @@ -340,6 +365,7 @@ export class CommandCenter { case Status.MODIFIED: case Status.UNTRACKED: case Status.IGNORED: + case Status.INTENT_TO_ADD: const repository = this.model.getRepository(resource.resourceUri); if (!repository) { @@ -1393,56 +1419,52 @@ export class CommandCenter { await repository.checkout(treeish); return true; } - - const config = workspace.getConfiguration('git'); - const checkoutType = config.get('checkoutType') || 'all'; - const includeTags = checkoutType === 'all' || checkoutType === 'tags'; - const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; - const createBranch = new CreateBranchItem(this); - const heads = repository.refs.filter(ref => ref.type === RefType.Head) - .map(ref => new CheckoutItem(ref)); + const picks = [createBranch, ...createCheckoutItems(repository)]; + const placeHolder = localize('select a ref to checkout', 'Select a ref to checkout'); - const tags = (includeTags ? repository.refs.filter(ref => ref.type === RefType.Tag) : []) - .map(ref => new CheckoutTagItem(ref)); + const quickpick = window.createQuickPick(); + quickpick.items = picks; + quickpick.placeholder = placeHolder; + quickpick.show(); - const remoteHeads = (includeRemotes ? repository.refs.filter(ref => ref.type === RefType.RemoteHead) : []) - .map(ref => new CheckoutRemoteHeadItem(ref)); - - const picks = [createBranch, ...heads, ...tags, ...remoteHeads]; - const placeHolder = localize('select a ref to checkout', 'Select a ref to checkout'); - const choice = await window.showQuickPick(picks, { placeHolder }); + const choice = await new Promise(c => quickpick.onDidAccept(() => c(quickpick.activeItems[0]))); + quickpick.hide(); if (!choice) { return false; } - await choice.run(repository); + if (choice === createBranch) { + await this._branch(repository, quickpick.value); + } else { + await (choice as CheckoutItem).run(repository); + } + return true; } @command('git.branch', { repository: true }) async branch(repository: Repository): Promise { + await this._branch(repository); + } + + private async _branch(repository: Repository, defaultName?: string): Promise { const config = workspace.getConfiguration('git'); - const branchValidationRegex = config.get('branchValidationRegex')!; const branchWhitespaceChar = config.get('branchWhitespaceChar')!; - const validateName = new RegExp(branchValidationRegex); - const sanitize = (name: string) => { - name = name.trim(); - - if (!name) { - return name; - } - - return name.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, branchWhitespaceChar); - }; + const branchValidationRegex = config.get('branchValidationRegex')!; + const sanitize = (name: string) => name ? + name.trim().replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, branchWhitespaceChar) + : name; - const result = await window.showInputBox({ + const rawBranchName = await window.showInputBox({ + value: defaultName, placeHolder: localize('branch name', "Branch name"), prompt: localize('provide branch name', "Please provide a branch name"), ignoreFocusOut: true, validateInput: (name: string) => { + const validateName = new RegExp(branchValidationRegex); if (validateName.test(sanitize(name))) { return null; } @@ -1451,13 +1473,21 @@ export class CommandCenter { } }); - const name = sanitize(result || ''); + const branchName = sanitize(rawBranchName || ''); + + if (!branchName) { + return; + } + + const picks = [new HEADItem(repository), ...createCheckoutItems(repository)]; + const placeHolder = localize('select a ref to create a new branch from', 'Select a ref to create a new branch from'); + const target = await window.showQuickPick(picks, { placeHolder }); - if (!name) { + if (!target) { return; } - await repository.branch(name, true); + await repository.branch(branchName, true, target.label); } @command('git.deleteBranch', { repository: true }) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 6dbe74f78288..d38d9b58db8e 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,9 +12,9 @@ import { EventEmitter } from 'events'; import iconv = require('iconv-lite'); import * as filetype from 'file-type'; import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util'; -import { CancellationToken } from 'vscode'; +import { CancellationToken, Uri } from 'vscode'; import { detectEncoding } from './encoding'; -import { Ref, RefType, Branch, Remote, GitErrorCodes } from './api/git'; +import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status } from './api/git'; const readfile = denodeify(fs.readFile); @@ -114,17 +114,17 @@ function findGitWin32InPath(onLookup: (path: string) => void): Promise { function findGitWin32(onLookup: (path: string) => void): Promise { return findSystemGitWin32(process.env['ProgramW6432'] as string, onLookup) - .then(void 0, () => findSystemGitWin32(process.env['ProgramFiles(x86)'] as string, onLookup)) - .then(void 0, () => findSystemGitWin32(process.env['ProgramFiles'] as string, onLookup)) - .then(void 0, () => findSystemGitWin32(path.join(process.env['LocalAppData'] as string, 'Programs'), onLookup)) - .then(void 0, () => findGitWin32InPath(onLookup)); + .then(undefined, () => findSystemGitWin32(process.env['ProgramFiles(x86)'] as string, onLookup)) + .then(undefined, () => findSystemGitWin32(process.env['ProgramFiles'] as string, onLookup)) + .then(undefined, () => findSystemGitWin32(path.join(process.env['LocalAppData'] as string, 'Programs'), onLookup)) + .then(undefined, () => findGitWin32InPath(onLookup)); } export function findGit(hint: string | undefined, onLookup: (path: string) => void): Promise { const first = hint ? findSpecificGit(hint, onLookup) : Promise.reject(null); return first - .then(void 0, () => { + .then(undefined, () => { switch (process.platform) { case 'darwin': return findGitDarwin(onLookup); case 'win32': return findGitWin32(onLookup); @@ -248,7 +248,7 @@ export class GitError { this.error = data.error; this.message = data.error.message; } else { - this.error = void 0; + this.error = undefined; this.message = ''; } @@ -308,9 +308,11 @@ function getGitErrorCode(stderr: string): string | undefined { return GitErrorCodes.InvalidBranchName; } - return void 0; + return undefined; } +const COMMIT_FORMAT = '%H\n%ae\n%P\n%B'; + export class Git { readonly path: string; @@ -450,6 +452,7 @@ export interface Commit { hash: string; message: string; parents: string[]; + authorEmail?: string | undefined; } export class GitStatusParser { @@ -581,13 +584,13 @@ export function parseGitmodules(raw: string): Submodule[] { } export function parseGitCommit(raw: string): Commit | null { - const match = /^([0-9a-f]{40})\n(.*)\n([^]*)$/m.exec(raw.trim()); + const match = /^([0-9a-f]{40})\n(.*)\n(.*)\n([^]*)$/m.exec(raw.trim()); if (!match) { return null; } - const parents = match[2] ? match[2].split(' ') : []; - return { hash: match[1], message: match[3], parents }; + const parents = match[3] ? match[3].split(' ') : []; + return { hash: match[1], message: match[4], parents, authorEmail: match[2] }; } interface LsTreeElement { @@ -629,6 +632,10 @@ export interface CommitOptions { empty?: boolean; } +export interface PullOptions { + unshallow?: boolean; +} + export enum ForcePushMode { Force, ForceWithLease @@ -697,6 +704,41 @@ export class Repository { }); } + async log(options?: LogOptions): Promise { + const maxEntries = options && typeof options.maxEntries === 'number' && options.maxEntries > 0 ? options.maxEntries : 32; + const args = ['log', '-' + maxEntries, `--pretty=format:${COMMIT_FORMAT}%x00%x00`]; + const gitResult = await this.run(args); + if (gitResult.exitCode) { + // An empty repo. + return []; + } + + const s = gitResult.stdout; + const result: Commit[] = []; + let index = 0; + while (index < s.length) { + let nextIndex = s.indexOf('\x00\x00', index); + if (nextIndex === -1) { + nextIndex = s.length; + } + + let entry = s.substr(index, nextIndex - index); + if (entry.startsWith('\n')) { + entry = entry.substring(1); + } + + const commit = parseGitCommit(entry); + if (!commit) { + break; + } + + result.push(commit); + index = nextIndex + 2; + } + + return result; + } + async bufferString(object: string, encoding: string = 'utf8', autoGuessEncoding = false): Promise { const stdout = await this.buffer(object); @@ -829,7 +871,15 @@ export class Repository { args.push('-R'); } - await this.run(args); + try { + await this.run(args); + } catch (err) { + if (/patch does not apply/.test(err.stderr)) { + err.gitErrorCode = GitErrorCodes.PatchDoesNotApply; + } + + throw err; + } } async diff(cached = false): Promise { @@ -843,25 +893,53 @@ export class Repository { return result.stdout; } - async diffWithHEAD(path: string): Promise { + diffWithHEAD(): Promise; + diffWithHEAD(path: string): Promise; + diffWithHEAD(path?: string | undefined): Promise; + async diffWithHEAD(path?: string | undefined): Promise { + if (!path) { + return await this.diffFiles(false); + } + const args = ['diff', '--', path]; const result = await this.run(args); return result.stdout; } - async diffWith(ref: string, path: string): Promise { + diffWith(ref: string): Promise; + diffWith(ref: string, path: string): Promise; + diffWith(ref: string, path?: string | undefined): Promise; + async diffWith(ref: string, path?: string): Promise { + if (!path) { + return await this.diffFiles(false, ref); + } + const args = ['diff', ref, '--', path]; const result = await this.run(args); return result.stdout; } - async diffIndexWithHEAD(path: string): Promise { + diffIndexWithHEAD(): Promise; + diffIndexWithHEAD(path: string): Promise; + diffIndexWithHEAD(path?: string | undefined): Promise; + async diffIndexWithHEAD(path?: string): Promise { + if (!path) { + return await this.diffFiles(true); + } + const args = ['diff', '--cached', '--', path]; const result = await this.run(args); return result.stdout; } - async diffIndexWith(ref: string, path: string): Promise { + diffIndexWith(ref: string): Promise; + diffIndexWith(ref: string, path: string): Promise; + diffIndexWith(ref: string, path?: string | undefined): Promise; + async diffIndexWith(ref: string, path?: string): Promise { + if (!path) { + return await this.diffFiles(true, ref); + } + const args = ['diff', '--cached', ref, '--', path]; const result = await this.run(args); return result.stdout; @@ -873,13 +951,102 @@ export class Repository { return result.stdout; } - async diffBetween(ref1: string, ref2: string, path: string): Promise { - const args = ['diff', `${ref1}...${ref2}`, '--', path]; + diffBetween(ref1: string, ref2: string): Promise; + diffBetween(ref1: string, ref2: string, path: string): Promise; + diffBetween(ref1: string, ref2: string, path?: string | undefined): Promise; + async diffBetween(ref1: string, ref2: string, path?: string): Promise { + const range = `${ref1}...${ref2}`; + if (!path) { + return await this.diffFiles(false, range); + } + + const args = ['diff', range, '--', path]; const result = await this.run(args); return result.stdout.trim(); } + private async diffFiles(cached: boolean, ref?: string): Promise { + const args = ['diff', '--name-status', '-z', '--diff-filter=ADMR']; + if (cached) { + args.push('--cached'); + } + + if (ref) { + args.push(ref); + } + + const gitResult = await this.run(args); + if (gitResult.exitCode) { + return []; + } + + const entries = gitResult.stdout.split('\x00'); + let index = 0; + const result: Change[] = []; + + entriesLoop: + while (index < entries.length - 1) { + const change = entries[index++]; + const resourcePath = entries[index++]; + if (!change || !resourcePath) { + break; + } + + const originalUri = Uri.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(this.repositoryRoot, resourcePath)); + let status: Status = Status.UNTRACKED; + + // Copy or Rename status comes with a number, e.g. 'R100'. We don't need the number, so we use only first character of the status. + switch (change[0]) { + case 'M': + status = Status.MODIFIED; + break; + + case 'A': + status = Status.INDEX_ADDED; + break; + + case 'D': + status = Status.DELETED; + break; + + // Rename contains two paths, the second one is what the file is renamed/copied to. + case 'R': + if (index >= entries.length) { + break; + } + + const newPath = entries[index++]; + if (!newPath) { + break; + } + + const uri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(this.repositoryRoot, newPath)); + result.push({ + uri, + renameUri: uri, + originalUri, + status: Status.INDEX_RENAMED + }); + + continue; + + default: + // Unknown status + break entriesLoop; + } + + result.push({ + status, + originalUri, + uri: originalUri, + renameUri: originalUri, + }); + } + + return result; + } + async getMergeBase(ref1: string, ref2: string): Promise { const args = ['merge-base', ref1, ref2]; const result = await this.run(args); @@ -1158,7 +1325,7 @@ export class Repository { await this.run(args); } - async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean } = {}): Promise { + async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number } = {}): Promise { const args = ['fetch']; if (options.remote) { @@ -1175,6 +1342,9 @@ export class Repository { args.push('--prune'); } + if (typeof options.depth === 'number') { + args.push(`--depth=${options.depth}`); + } try { await this.run(args); @@ -1189,9 +1359,13 @@ export class Repository { } } - async pull(rebase?: boolean, remote?: string, branch?: string): Promise { + async pull(rebase?: boolean, remote?: string, branch?: string, options: PullOptions = {}): Promise { const args = ['pull', '--tags']; + if (options.unshallow) { + args.push('--unshallow'); + } + if (rebase) { args.push('-r'); } @@ -1263,6 +1437,23 @@ export class Repository { } } + async blame(path: string): Promise { + try { + const args = ['blame']; + args.push(path); + + let result = await this.run(args); + + return result.stdout.trim(); + } catch (err) { + if (/^fatal: no such path/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.NoPathFound; + } + + throw err; + } + } + async createStash(message?: string, includeUntracked?: boolean): Promise { try { const args = ['stash', 'save']; @@ -1368,7 +1559,7 @@ export class Repository { throw new Error('Not in a branch'); } - return { name: result.stdout.trim(), commit: void 0, type: RefType.Head }; + return { name: result.stdout.trim(), commit: undefined, type: RefType.Head }; } catch (err) { const result = await this.run(['rev-parse', 'HEAD']); @@ -1376,7 +1567,7 @@ export class Repository { throw new Error('Error parsing HEAD'); } - return { name: void 0, commit: result.stdout.trim(), type: RefType.Head }; + return { name: undefined, commit: result.stdout.trim(), type: RefType.Head }; } } @@ -1521,7 +1712,7 @@ export class Repository { } async getCommit(ref: string): Promise { - const result = await this.run(['show', '-s', '--format=%H\n%P\n%B', ref]); + const result = await this.run(['show', '-s', `--format=${COMMIT_FORMAT}`, ref]); return parseGitCommit(result.stdout) || Promise.reject('bad commit format'); } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 8ca4f81cbc98..4010fc0f4a47 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, Status } from './api/git'; +import { Branch, Ref, Remote, RefType, GitErrorCodes, Status, LogOptions, Change } from './api/git'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -94,6 +94,7 @@ export class Resource implements SourceControlResourceState { case Status.INDEX_COPIED: return Resource.Icons[theme].Copied; case Status.UNTRACKED: return Resource.Icons[theme].Untracked; case Status.IGNORED: return Resource.Icons[theme].Ignored; + case Status.INTENT_TO_ADD: return Resource.Icons[theme].Added; case Status.BOTH_DELETED: return Resource.Icons[theme].Conflict; case Status.ADDED_BY_US: return Resource.Icons[theme].Conflict; case Status.DELETED_BY_THEM: return Resource.Icons[theme].Conflict; @@ -116,6 +117,7 @@ export class Resource implements SourceControlResourceState { case Status.INDEX_COPIED: return localize('index copied', "Index Copied"); case Status.UNTRACKED: return localize('untracked', "Untracked"); case Status.IGNORED: return localize('ignored', "Ignored"); + case Status.INTENT_TO_ADD: return localize('intent to add', "Intent to Add"); case Status.BOTH_DELETED: return localize('both deleted', "Both Deleted"); case Status.ADDED_BY_US: return localize('added by us', "Added By Us"); case Status.DELETED_BY_THEM: return localize('deleted by them', "Deleted By Them"); @@ -166,6 +168,7 @@ export class Resource implements SourceControlResourceState { case Status.MODIFIED: return 'M'; case Status.INDEX_ADDED: + case Status.INTENT_TO_ADD: return 'A'; case Status.INDEX_DELETED: case Status.DELETED: @@ -201,6 +204,7 @@ export class Resource implements SourceControlResourceState { case Status.DELETED: return new ThemeColor('gitDecoration.deletedResourceForeground'); case Status.INDEX_ADDED: + case Status.INTENT_TO_ADD: return new ThemeColor('gitDecoration.addedResourceForeground'); case Status.INDEX_RENAMED: // todo@joh - special color? case Status.UNTRACKED: @@ -295,7 +299,9 @@ export const enum Operation { GetObjectDetails = 'GetObjectDetails', SubmoduleUpdate = 'SubmoduleUpdate', RebaseContinue = 'RebaseContinue', - Apply = 'Apply' + Apply = 'Apply', + Blame = 'Blame', + Log = 'Log', } function isReadOnly(operation: Operation): boolean { @@ -643,18 +649,43 @@ export class Repository implements Disposable { }; } + let lineNumber = 0; let start = 0, end; let match: RegExpExecArray | null; const regex = /\r?\n/g; while ((match = regex.exec(text)) && position > match.index) { start = match.index + match[0].length; + lineNumber++; } end = match ? match.index : text.length; const line = text.substring(start, end); - const threshold = Math.max(config.get('inputValidationLength') || 72, 0) || 72; + + let threshold = config.get('inputValidationLength', 50); + + if (lineNumber === 0) { + const inputValidationSubjectLength = config.get('inputValidationSubjectLength', null); + + if (inputValidationSubjectLength !== null) { + threshold = inputValidationSubjectLength; + } + } + + + + + + + + + + + // const subjectThreshold = + + + // Math.max(config.get('inputValidationLength') || 50, config.get('subjectValidationLength') || 50, 0) || 50; if (line.length <= threshold) { if (setting !== 'always') { @@ -697,10 +728,18 @@ export class Repository implements Disposable { return this.run(Operation.Config, () => this.repository.config('local', key)); } + getGlobalConfig(key: string): Promise { + return this.run(Operation.Config, () => this.repository.config('global', key)); + } + setConfig(key: string, value: string): Promise { return this.run(Operation.Config, () => this.repository.config('local', key, value)); } + log(options?: LogOptions): Promise { + return this.run(Operation.Log, () => this.repository.log(options)); + } + @throttle async status(): Promise { await this.run(Operation.Status); @@ -710,19 +749,31 @@ export class Repository implements Disposable { return this.run(Operation.Diff, () => this.repository.diff(cached)); } - diffWithHEAD(path: string): Promise { + diffWithHEAD(): Promise; + diffWithHEAD(path: string): Promise; + diffWithHEAD(path?: string | undefined): Promise; + diffWithHEAD(path?: string | undefined): Promise { return this.run(Operation.Diff, () => this.repository.diffWithHEAD(path)); } - diffWith(ref: string, path: string): Promise { + diffWith(ref: string): Promise; + diffWith(ref: string, path: string): Promise; + diffWith(ref: string, path?: string | undefined): Promise; + diffWith(ref: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffWith(ref, path)); } - diffIndexWithHEAD(path: string): Promise { + diffIndexWithHEAD(): Promise; + diffIndexWithHEAD(path: string): Promise; + diffIndexWithHEAD(path?: string | undefined): Promise; + diffIndexWithHEAD(path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffIndexWithHEAD(path)); } - diffIndexWith(ref: string, path: string): Promise { + diffIndexWith(ref: string): Promise; + diffIndexWith(ref: string, path: string): Promise; + diffIndexWith(ref: string, path?: string | undefined): Promise; + diffIndexWith(ref: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffIndexWith(ref, path)); } @@ -730,7 +781,10 @@ export class Repository implements Disposable { return this.run(Operation.Diff, () => this.repository.diffBlobs(object1, object2)); } - diffBetween(ref1: string, ref2: string, path: string): Promise { + diffBetween(ref1: string, ref2: string): Promise; + diffBetween(ref1: string, ref2: string, path: string): Promise; + diffBetween(ref1: string, ref2: string, path?: string | undefined): Promise; + diffBetween(ref1: string, ref2: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffBetween(ref1, ref2, path)); } @@ -904,8 +958,8 @@ export class Repository implements Disposable { await this.run(Operation.Fetch, () => this.repository.fetch({ all: true })); } - async fetch(remote?: string, ref?: string): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ remote, ref })); + async fetch(remote?: string, ref?: string, depth?: number): Promise { + await this.run(Operation.Fetch, () => this.repository.fetch({ remote, ref, depth })); } @throttle @@ -918,18 +972,11 @@ export class Repository implements Disposable { branch = `${head.upstream.name}`; } - const config = workspace.getConfiguration('git', Uri.file(this.root)); - const fetchOnPull = config.get('fetchOnPull'); - - if (fetchOnPull) { - await this.run(Operation.Pull, () => this.repository.pull(true)); - } else { - await this.run(Operation.Pull, () => this.repository.pull(true, remote, branch)); - } + return this.pullFrom(true, remote, branch); } @throttle - async pull(head?: Branch): Promise { + async pull(head?: Branch, unshallow?: boolean): Promise { let remote: string | undefined; let branch: string | undefined; @@ -938,25 +985,22 @@ export class Repository implements Disposable { branch = `${head.upstream.name}`; } - const config = workspace.getConfiguration('git', Uri.file(this.root)); - const fetchOnPull = config.get('fetchOnPull'); - - if (fetchOnPull) { - await this.run(Operation.Pull, () => this.repository.pull(false)); - } else { - await this.run(Operation.Pull, () => this.repository.pull(false, remote, branch)); - } + return this.pullFrom(false, remote, branch, unshallow); } - async pullFrom(rebase?: boolean, remote?: string, branch?: string): Promise { - const config = workspace.getConfiguration('git', Uri.file(this.root)); - const fetchOnPull = config.get('fetchOnPull'); + async pullFrom(rebase?: boolean, remote?: string, branch?: string, unshallow?: boolean): Promise { + await this.run(Operation.Pull, async () => { + await this.maybeAutoStash(async () => { + const config = workspace.getConfiguration('git', Uri.file(this.root)); + const fetchOnPull = config.get('fetchOnPull'); - if (fetchOnPull) { - await this.run(Operation.Pull, () => this.repository.pull(rebase)); - } else { - await this.run(Operation.Pull, () => this.repository.pull(rebase, remote, branch)); - } + if (fetchOnPull) { + await this.repository.pull(rebase, undefined, undefined, { unshallow }); + } else { + await this.repository.pull(rebase, remote, branch, { unshallow }); + } + }); + }); } @throttle @@ -980,6 +1024,10 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true, forcePushMode)); } + async blame(path: string): Promise { + return await this.run(Operation.Blame, () => this.repository.blame(path)); + } + @throttle sync(head: Branch): Promise { return this._sync(head, false); @@ -1002,26 +1050,28 @@ export class Repository implements Disposable { } await this.run(Operation.Sync, async () => { - const config = workspace.getConfiguration('git', Uri.file(this.root)); - const fetchOnPull = config.get('fetchOnPull'); + await this.maybeAutoStash(async () => { + const config = workspace.getConfiguration('git', Uri.file(this.root)); + const fetchOnPull = config.get('fetchOnPull'); - if (fetchOnPull) { - await this.repository.pull(rebase); - } else { - await this.repository.pull(rebase, remoteName, pullBranch); - } + if (fetchOnPull) { + await this.repository.pull(rebase); + } else { + await this.repository.pull(rebase, remoteName, pullBranch); + } - const remote = this.remotes.find(r => r.name === remoteName); + const remote = this.remotes.find(r => r.name === remoteName); - if (remote && remote.isReadOnly) { - return; - } + if (remote && remote.isReadOnly) { + return; + } - const shouldPush = this.HEAD && (typeof this.HEAD.ahead === 'number' ? this.HEAD.ahead > 0 : true); + const shouldPush = this.HEAD && (typeof this.HEAD.ahead === 'number' ? this.HEAD.ahead > 0 : true); - if (shouldPush) { - await this.repository.push(remoteName, pushBranch); - } + if (shouldPush) { + await this.repository.push(remoteName, pushBranch); + } + }); }); } @@ -1102,7 +1152,8 @@ export class Repository implements Disposable { const text = lastLine.isEmptyOrWhitespace ? `${textToAppend}\n` : `\n${textToAppend}\n`; edit.insert(document.uri, lastLine.range.end, text); - workspace.applyEdit(edit); + await workspace.applyEdit(edit); + await document.save(); }); } @@ -1211,6 +1262,24 @@ export class Repository implements Disposable { } } + private static KnownHugeFolderNames = ['node_modules']; + + private async findKnownHugeFolderPathsToIgnore(): Promise { + const folderPaths: string[] = []; + + for (const folderName of Repository.KnownHugeFolderNames) { + const folderPath = path.join(this.repository.root, folderName); + + if (await new Promise(c => fs.exists(folderPath, c))) { + folderPaths.push(folderPath); + } + } + + const ignored = await this.checkIgnore(folderPaths); + + return folderPaths.filter(p => !ignored.has(p)); + } + @throttle private async updateModelState(): Promise { const { status, didHitLimit } = await this.repository.getStatus(); @@ -1221,15 +1290,34 @@ export class Repository implements Disposable { this.isRepositoryHuge = didHitLimit; if (didHitLimit && !shouldIgnore && !this.didWarnAboutLimit) { + const knownHugeFolderPaths = await this.findKnownHugeFolderPathsToIgnore(); + const gitWarn = localize('huge', "The git repository at '{0}' has too many active changes, only a subset of Git features will be enabled.", this.repository.root); const neverAgain = { title: localize('neveragain', "Don't Show Again") }; - window.showWarningMessage(localize('huge', "The git repository at '{0}' has too many active changes, only a subset of Git features will be enabled.", this.repository.root), neverAgain).then(result => { + if (knownHugeFolderPaths.length > 0) { + const folderPath = knownHugeFolderPaths[0]; + const folderName = path.basename(folderPath); + + const addKnown = localize('add known', "Would you like to add '{0}' to .gitignore?", folderName); + const yes = { title: localize('yes', "Yes") }; + + const result = await window.showWarningMessage(`${gitWarn} ${addKnown}`, yes, neverAgain); + + if (result === neverAgain) { + config.update('ignoreLimitWarning', true, false); + this.didWarnAboutLimit = true; + } else if (result === yes) { + this.ignore([Uri.file(folderPath)]); + } + } else { + const result = await window.showWarningMessage(gitWarn, neverAgain); + if (result === neverAgain) { config.update('ignoreLimitWarning', true, false); } - }); - this.didWarnAboutLimit = true; + this.didWarnAboutLimit = true; + } } let HEAD: Branch | undefined; @@ -1287,6 +1375,7 @@ export class Repository implements Disposable { switch (raw.y) { case 'M': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); break; case 'D': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; + case 'A': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; } return undefined; }); @@ -1339,6 +1428,22 @@ export class Repository implements Disposable { } } + private async maybeAutoStash(runOperation: () => Promise): Promise { + const config = workspace.getConfiguration('git', Uri.file(this.root)); + const shouldAutoStash = config.get('autoStash') + && this.workingTreeGroup.resourceStates.some(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED); + + if (!shouldAutoStash) { + return await runOperation(); + } + + await this.repository.createStash(undefined, true); + const result = await runOperation(); + await this.repository.popStash(); + + return result; + } + private onFSChange(_uri: Uri): void { const config = workspace.getConfiguration('git'); const autorefresh = config.get('autorefresh'); diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts index 42cd51c518aa..f41542066811 100644 --- a/extensions/git/src/staging.ts +++ b/extensions/git/src/staging.ts @@ -13,7 +13,18 @@ export function applyLineChanges(original: TextDocument, modified: TextDocument, const isInsertion = diff.originalEndLineNumber === 0; const isDeletion = diff.modifiedEndLineNumber === 0; - result.push(original.getText(new Range(currentLine, 0, isInsertion ? diff.originalStartLineNumber : diff.originalStartLineNumber - 1, 0))); + let endLine = isInsertion ? diff.originalStartLineNumber : diff.originalStartLineNumber - 1; + let endCharacter = 0; + + // if this is a deletion at the very end of the document,then we need to account + // for a newline at the end of the last line which may have been deleted + // https://github.com/Microsoft/vscode/issues/59670 + if (isDeletion && diff.originalStartLineNumber === original.lineCount) { + endLine -= 1; + endCharacter = original.lineAt(endLine).range.end.character; + } + + result.push(original.getText(new Range(currentLine, 0, endLine, endCharacter))); if (!isDeletion) { let fromLine = diff.modifiedStartLineNumber - 1; @@ -114,4 +125,4 @@ export function invertLineChange(diff: LineChange): LineChange { originalStartLineNumber: diff.modifiedStartLineNumber, originalEndLineNumber: diff.modifiedEndLineNumber }; -} \ No newline at end of file +} diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts index 827ae7c491cc..8e27a374fa19 100644 --- a/extensions/git/src/test/git.test.ts +++ b/extensions/git/src/test/git.test.ts @@ -177,37 +177,43 @@ suite('git', () => { suite('parseGitCommit', () => { test('single parent commit', function () { const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1 +john.doe@mail.com 8e5a374372b8393906c7e380dbb09349c5385554 This is a commit message.`; assert.deepEqual(parseGitCommit(GIT_OUTPUT_SINGLE_PARENT), { hash: '52c293a05038d865604c2284aa8698bd087915a1', message: 'This is a commit message.', - parents: ['8e5a374372b8393906c7e380dbb09349c5385554'] + parents: ['8e5a374372b8393906c7e380dbb09349c5385554'], + authorEmail: 'john.doe@mail.com', }); }); test('multiple parent commits', function () { const GIT_OUTPUT_MULTIPLE_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1 +john.doe@mail.com 8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217 This is a commit message.`; assert.deepEqual(parseGitCommit(GIT_OUTPUT_MULTIPLE_PARENTS), { hash: '52c293a05038d865604c2284aa8698bd087915a1', message: 'This is a commit message.', - parents: ['8e5a374372b8393906c7e380dbb09349c5385554', 'df27d8c75b129ab9b178b386077da2822101b217'] + parents: ['8e5a374372b8393906c7e380dbb09349c5385554', 'df27d8c75b129ab9b178b386077da2822101b217'], + authorEmail: 'john.doe@mail.com', }); }); test('no parent commits', function () { const GIT_OUTPUT_NO_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1 +john.doe@mail.com This is a commit message.`; assert.deepEqual(parseGitCommit(GIT_OUTPUT_NO_PARENTS), { hash: '52c293a05038d865604c2284aa8698bd087915a1', message: 'This is a commit message.', - parents: [] + parents: [], + authorEmail: 'john.doe@mail.com', }); }); }); diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 653dffdb4561..70a145638049 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -69,7 +69,7 @@ export function anyEvent(...events: Event[]): Event { } export function done(promise: Promise): Promise { - return promise.then(() => void 0); + return promise.then(() => undefined); } export function onceEvent(event: Event): Event { diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 616d42caddf1..1d859c5e73be 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -36,10 +36,10 @@ resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" integrity sha1-AW44dim4gXvtZT/jLqtdESecjfY= -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== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -313,12 +313,12 @@ supports-color@3.1.2: dependencies: has-flag "^1.0.0" -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/import/package.json b/extensions/import/package.json index d27419156728..af8dad991ce3 100644 --- a/extensions/import/package.json +++ b/extensions/import/package.json @@ -1,64 +1,69 @@ { - "name": "import", - "displayName": "SQL Server Import", - "description": "SQL Server Import for Azure Data Studio supports importing CSV or JSON files into SQL Server.", - "version": "0.7.0", - "publisher": "Microsoft", - "preview": true, - "engines": { - "vscode": "^1.25.0", - "azdata": "*" - }, - "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/extensions/import/Microsoft_SQL_Server_Import_Extension_and_Tools_Import_Flat_File_Preview.docx", - "icon": "images/sqlserver.png", - "aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412", - "activationEvents": [ - "*" - ], - "main": "./out/main", - "repository": { - "type": "git", - "url": "https://github.com/Microsoft/azuredatastudio.git" - }, - "extensionDependencies": [ - "Microsoft.mssql" - ], - "contributes": { - "commands": [ - { - "command": "flatFileImport.start", - "title": "Import wizard", - "category": "Flat File Import", - "icon": { - "light": "./images/light_icon.svg", - "dark": "./images/dark_icon.svg" - } - } - ], - "keybindings": [ - { - "command": "flatFileImport.start", - "key": "ctrl+i", - "mac": "ctrl+i" - } - ], - "menus": { - "objectExplorer/item/context": [ - { - "command": "flatFileImport.start", - "when": "connectionProvider == MSSQL && nodeType && nodeType == Database", - "group": "import" - } - ] - } - }, - "dependencies": { - "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.15", - "htmlparser2": "^3.10.1", - "opener": "^1.4.3", - "service-downloader": "github:anthonydresser/service-downloader#0.1.5", - "vscode-extension-telemetry": "0.0.18", - "vscode-nls": "^3.2.1" - }, - "devDependencies": {} -} + "name": "import", + "displayName": "SQL Server Import", + "description": "SQL Server Import for Azure Data Studio supports importing CSV or JSON files into SQL Server.", + "version": "0.7.0", + "publisher": "Microsoft", + "preview": true, + "engines": { + "vscode": "^1.25.0", + "azdata": "*" + }, + "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/extensions/import/Microsoft_SQL_Server_Import_Extension_and_Tools_Import_Flat_File_Preview.docx", + "icon": "images/sqlserver.png", + "aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412", + "activationEvents": [ + "*" + ], + "main": "./out/main", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/azuredatastudio.git" + }, + "extensionDependencies": [ + "Microsoft.mssql" + ], + "contributes": { + "commands": [ + { + "command": "flatFileImport.start", + "title": "Import wizard", + "category": "Flat File Import", + "icon": { + "light": "./images/light_icon.svg", + "dark": "./images/dark_icon.svg" + } + } + ], + "keybindings": [ + { + "command": "flatFileImport.start", + "key": "ctrl+i", + "mac": "ctrl+i" + } + ], + "menus": { + "objectExplorer/item/context": [ + { + "command": "flatFileImport.start", + "when": "connectionProvider == MSSQL && nodeType && nodeType == Database", + "group": "import" + } + ] + } + }, + "dependencies": { + "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.15", + "htmlparser2": "^3.10.1", + "opener": "^1.4.3", + "service-downloader": "github:anthonydresser/service-downloader#0.1.5", + "vscode-extension-telemetry": "0.0.18", + "vscode-nls": "^3.2.1" + }, + "devDependencies": {}, + "__metadata": { + "id": "23", + "publisherDisplayName": "Microsoft", + "publisherId": "Microsoft" + } +} \ 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 2b43e8643083..83f0e137838d 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -8,7 +8,7 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, Diagnostic, StatusBarAlignment, TextEditor } from 'vscode'; +import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, Position, SelectionRange, Range, SelectionRangeKind } from 'vscode'; import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature } from 'vscode-languageclient'; import TelemetryReporter from 'vscode-extension-telemetry'; @@ -193,8 +193,32 @@ export function activate(context: ExtensionContext) { toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); + + extensions.onDidChange(_ => { + client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); + }); + + documentSelector.forEach(selector => { + toDispose.push(languages.registerSelectionRangeProvider(selector, { + async provideSelectionRanges(document: TextDocument, position: Position): Promise { + const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); + const rawRanges = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); + if (Array.isArray(rawRanges)) { + return rawRanges.map(r => { + return { + range: client.protocol2CodeConverter.asRange(r), + kind: SelectionRangeKind.Declaration + }; + }); + } + return []; + } + })); + }); }); + + let languageConfiguration: LanguageConfiguration = { wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/, indentationRules: { @@ -338,7 +362,7 @@ function getPackageInfo(context: ExtensionContext): IPackageInfo | undefined { aiKey: extensionPackage.aiKey }; } - return void 0; + return undefined; } function readJSONFile(location: string) { 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 7bae423786a8..6689fa17e794 100644 --- a/extensions/json-language-features/client/src/typings/ref.d.ts +++ b/extensions/json-language-features/client/src/typings/ref.d.ts @@ -4,3 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// +/// diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index a1a5204c8b5b..6e982dac417e 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -14,6 +14,7 @@ "onLanguage:jsonc" ], "main": "./client/out/jsonMain", + "enableProposedApi": true, "scripts": { "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", @@ -100,11 +101,11 @@ } }, "dependencies": { - "vscode-extension-telemetry": "0.1.0", + "vscode-extension-telemetry": "0.1.1", "vscode-languageclient": "^5.1.0", "vscode-nls": "^4.0.0" }, "devDependencies": { "@types/node": "^8.10.25" } -} +} \ No newline at end of file diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index db0182fa0888..0868d9aa049b 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.1", + "vscode-json-languageservice": "^3.3.0-next.0", "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 60d671c9dd9c..15e7ea72d4f5 100644 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/jsonServerMain.ts @@ -143,7 +143,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { const capabilities: ServerCapabilities = { // Tell the client that the server works in FULL text document sync mode textDocumentSync: documents.syncKind, - completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : void 0, + completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : undefined, hoverProvider: true, documentSymbolProvider: true, documentRangeFormattingProvider: false, @@ -174,13 +174,13 @@ interface JSONSchemaSettings { schema?: JSONSchema; } -let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = void 0; -let schemaAssociations: ISchemaAssociations | undefined = void 0; +let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined; +let schemaAssociations: ISchemaAssociations | undefined = undefined; let formatterRegistration: Thenable | null = null; // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration((change) => { - var settings = change.settings; + let settings = change.settings; configureHttpRequests(settings.http && settings.http.proxy, settings.http && settings.http.proxyStrictSSL); jsonConfigurationSettings = settings.json && settings.json.schemas; @@ -233,7 +233,7 @@ function updateConfiguration() { schemas: new Array() }; if (schemaAssociations) { - for (var pattern in schemaAssociations) { + for (const pattern in schemaAssociations) { const association = schemaAssociations[pattern]; if (Array.isArray(association)) { association.forEach(uri => { @@ -427,5 +427,16 @@ connection.onFoldingRanges((params, token) => { }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); }); +connection.onRequest('$/textDocument/selectionRange', async (params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + if (document) { + const jsonDocument = getJSONDocument(document); + return languageService.getSelectionRanges(document, params.position, jsonDocument); + } + return []; + }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); +}); + // Listen on the connection connection.listen(); diff --git a/extensions/json-language-features/server/src/languageModelCache.ts b/extensions/json-language-features/server/src/languageModelCache.ts index 407844e580b4..81bf9dfb421c 100644 --- a/extensions/json-language-features/server/src/languageModelCache.ts +++ b/extensions/json-language-features/server/src/languageModelCache.ts @@ -15,7 +15,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime let languageModels: { [uri: string]: { version: number, languageId: string, cTime: number, languageModel: T } } = {}; let nModels = 0; - let cleanupInterval: NodeJS.Timer | undefined = void 0; + let cleanupInterval: NodeJS.Timer | undefined = undefined; if (cleanupIntervalTimeInSec > 0) { cleanupInterval = setInterval(() => { let cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; @@ -73,7 +73,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime dispose() { if (typeof cleanupInterval !== 'undefined') { clearInterval(cleanupInterval); - cleanupInterval = void 0; + cleanupInterval = undefined; languageModels = {}; nModels = 0; } diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 012e346360a3..1c933ebfb3c7 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.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== +vscode-json-languageservice@^3.3.0-next.0: + version "3.3.0-next.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.3.0-next.0.tgz#c17db95d0eacc24f80d3b3f120ab5e03943769a0" + integrity sha512-YZXL3yHzbr0/Ar5dGdeM/f5Y0l41z/Y4QSQTdL3Hl3ScuY76IPcDEnf7iuk9yx+QoPfEHFCBDv5Rg6XVcMl8Tg== dependencies: jsonc-parser "^2.0.2" vscode-languageserver-types "^3.13.0" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index cfcbd4f4cbe1..019393942b3d 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== -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== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -38,12 +38,12 @@ semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-jsonrpc@^4.0.0: version "4.0.0" diff --git a/extensions/markdown-basics/cgmanifest.json b/extensions/markdown-basics/cgmanifest.json index 5e7930de00bb..71df78ef41f7 100644 --- a/extensions/markdown-basics/cgmanifest.json +++ b/extensions/markdown-basics/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/markdown-basics/package.json b/extensions/markdown-basics/package.json index a81c76255311..8d1ef2828c74 100644 --- a/extensions/markdown-basics/package.json +++ b/extensions/markdown-basics/package.json @@ -17,13 +17,13 @@ ], "extensions": [ ".md", - ".mkd", - ".mdwn", + ".mkd", + ".mdwn", ".mdown", ".markdown", ".markdn", - ".mdtxt", - ".mdtext", + ".mdtxt", + ".mdtext", ".workbook" ], "configuration": "./language-configuration.json" @@ -91,4 +91,4 @@ "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js microsoft/vscode-markdown-tm-grammar syntaxes/markdown.tmLanguage ./syntaxes/markdown.tmLanguage.json" } -} +} \ No newline at end of file diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 36d2fbf3a588..01dcaeaaedfc 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -908,7 +908,8 @@ function scrollToRevealSourceLine(line) { scrollTo = previousTop + betweenProgress * elementOffset; } else { - scrollTo = previousTop; + const progressInElement = line - Math.floor(line); + scrollTo = previousTop + (rect.height * progressInElement); } window.scroll(window.scrollX, Math.max(1, window.scrollY + scrollTo)); } @@ -978,4 +979,4 @@ exports.getSettings = getSettings; /***/ }) /******/ }); -//# 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 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsT0FBTztBQUNsQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLDhDQUE4QyxrQkFBa0I7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsb0JBQW9CO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsRUFBRTtBQUNiLGFBQWEsUUFBUTtBQUNyQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxFQUFFO0FBQ2IsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOzs7Ozs7Ozs7Ozs7O0FDdGJBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNENBQTRDOztBQUU1Qzs7Ozs7Ozs7Ozs7Ozs7O0FDbkJBOzs7Z0dBR2dHO0FBQ2hHLCtGQUF5RDtBQUV6RDtJQUdDLDhCQUE4QixDQUFDLElBQVk7UUFDMUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLHNDQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxJQUFJLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRUQsT0FBTyxDQUFDLE1BQStCO1FBQ3RDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQ3hCLENBQUM7SUFFRCxvQkFBb0IsQ0FBQyxPQUFnQztRQUNwRCxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDZCxNQUFNLENBQUM7UUFDUixDQUFDO1FBQ0QsT0FBTyxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUM1RSxDQUFDO0lBRUQsa0JBQWtCLENBQUMsT0FBZ0M7UUFDbEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ2QsTUFBTSxDQUFDO1FBQ1IsQ0FBQztRQUNELE9BQU8sQ0FBQyxTQUFTLElBQUksbUJBQW1CLENBQUM7SUFDMUMsQ0FBQztDQUNEO0FBM0JELDRDQTJCQzs7Ozs7Ozs7Ozs7Ozs7QUNqQ0Q7OztnR0FHZ0c7O0FBRWhHLDRCQUFtQyxDQUFhO0lBQy9DLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEtBQUssU0FBUyxJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssZUFBZSxDQUFDLENBQUMsQ0FBQztRQUNsRixRQUFRLENBQUMsZ0JBQWdCLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxFQUFFLENBQUM7SUFDTCxDQUFDO0FBQ0YsQ0FBQztBQU5ELGdEQU1DOzs7Ozs7Ozs7Ozs7OztBQ1hEOzs7Z0dBR2dHOztBQUVoRyw4R0FBc0Q7QUFDdEQsZ0ZBQThDO0FBQzlDLHlGQUFvRDtBQUNwRCwrRkFBMkY7QUFDM0Ysc0ZBQWtEO0FBQ2xELHVHQUE2QztBQUk3QyxJQUFJLGNBQWMsR0FBRyxJQUFJLENBQUM7QUFDMUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxtQ0FBZ0IsRUFBRSxDQUFDO0FBQ3RDLE1BQU0sUUFBUSxHQUFHLHNCQUFXLEVBQUUsQ0FBQztBQUUvQixNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsRUFBRSxDQUFDO0FBRWxDLG9CQUFvQjtBQUNwQixNQUFNLEtBQUssR0FBRyxrQkFBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO0FBQ3BDLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7QUFFdkIsTUFBTSxTQUFTLEdBQUcsaUNBQXFCLENBQUMsTUFBTSxDQUFDLENBQUM7QUFFaEQsTUFBTSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDdkMsTUFBTSxDQUFDLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUVoRCxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsRUFBRTtJQUNwQixnQkFBZ0IsRUFBRSxDQUFDO0FBQ3BCLENBQUMsQ0FBQztBQUVGLDJCQUFrQixDQUFDLEdBQUcsRUFBRTtJQUN2QixFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDZixNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7WUFDbkMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUN6QixjQUFjLEdBQUcsSUFBSSxDQUFDO2dCQUN0QixzQ0FBd0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUN2QyxDQUFDO1FBQ0YsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztBQUNGLENBQUMsQ0FBQyxDQUFDO0FBRUgsTUFBTSxZQUFZLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDMUIsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLENBQUMsSUFBWSxFQUFFLEVBQUU7UUFDMUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUN0QixzQ0FBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNoQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFUCxNQUFNLENBQUMsQ0FBQyxJQUFZLEVBQUUsUUFBYSxFQUFFLEVBQUU7UUFDdEMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2xCLFFBQVEsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1lBQ3JCLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNoQixDQUFDO0lBQ0YsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMLElBQUksZ0JBQWdCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRTtJQUNwQyxNQUFNLFNBQVMsR0FBb0QsRUFBRSxDQUFDO0lBQ3RFLElBQUksTUFBTSxHQUFHLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNsRCxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ1osSUFBSSxDQUFDLENBQUM7UUFDTixHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDcEMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRXRCLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDdkMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDakMsQ0FBQztZQUVELFNBQVMsQ0FBQyxJQUFJLENBQUM7Z0JBQ2QsRUFBRSxFQUFFLEdBQUcsQ0FBQyxFQUFFO2dCQUNWLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTTtnQkFDbEIsS0FBSyxFQUFFLEdBQUcsQ0FBQyxLQUFLO2FBQ2hCLENBQUMsQ0FBQztRQUNKLENBQUM7UUFFRCxTQUFTLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQ3JELENBQUM7QUFDRixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFFUCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUN0QyxjQUFjLEdBQUcsSUFBSSxDQUFDO0lBQ3RCLGdCQUFnQixFQUFFLENBQUM7QUFDcEIsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBRVQsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsRUFBRTtJQUMxQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUMzQyxNQUFNLENBQUM7SUFDUixDQUFDO0lBRUQsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3pCLEtBQUssZ0NBQWdDO1lBQ3BDLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZELEtBQUssQ0FBQztRQUVQLEtBQUssWUFBWTtZQUNoQixZQUFZLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDeEMsS0FBSyxDQUFDO0lBQ1IsQ0FBQztBQUNGLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztBQUVWLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLEVBQUU7SUFDN0MsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsMkJBQTJCLENBQUMsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCx5QkFBeUI7SUFDekIsR0FBRyxDQUFDLENBQUMsSUFBSSxJQUFJLEdBQUcsS0FBSyxDQUFDLE1BQXFCLEVBQUUsSUFBSSxFQUFFLElBQUksR0FBRyxJQUFJLENBQUMsVUFBeUIsRUFBRSxDQUFDO1FBQzFGLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUMxQixNQUFNLENBQUM7UUFDUixDQUFDO0lBQ0YsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUM7SUFDM0IsTUFBTSxJQUFJLEdBQUcsOENBQWdDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdEQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM5QyxTQUFTLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUMvRCxDQUFDO0FBQ0YsQ0FBQyxDQUFDLENBQUM7QUFFSCxRQUFRLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxFQUFFO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNaLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCxJQUFJLElBQUksR0FBUSxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQzdCLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDYixFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3ZELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDL0MsS0FBSyxDQUFDO1lBQ1AsQ0FBQztZQUNELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNqRixNQUFNLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGdDQUFnQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDNUYsU0FBUyxDQUFDLFdBQVcsQ0FBQyxXQUFXLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDdkQsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUN2QixLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7Z0JBQ3hCLEtBQUssQ0FBQztZQUNQLENBQUM7WUFDRCxLQUFLLENBQUM7UUFDUCxDQUFDO1FBQ0QsSUFBSSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDeEIsQ0FBQztBQUNGLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUVULEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLENBQUM7SUFDdEMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQy9DLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7WUFDcEIsY0FBYyxHQUFHLEtBQUssQ0FBQztRQUN4QixDQUFDO1FBQUMsSUFBSSxDQUFDLENBQUM7WUFDUCxNQUFNLElBQUksR0FBRyw4Q0FBZ0MsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDOUQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDOUMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxZQUFZLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQy9DLENBQUM7UUFDRixDQUFDO0lBQ0YsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDVCxDQUFDOzs7Ozs7Ozs7Ozs7OztBQzdKRDs7O2dHQUdnRzs7QUFFaEcsc0ZBQXlDO0FBUzVCLDZCQUFxQixHQUFHLENBQUMsTUFBVyxFQUFFLEVBQUU7SUFDcEQsTUFBTSxDQUFDLElBQUk7UUFDVixXQUFXLENBQUMsSUFBWSxFQUFFLElBQVk7WUFDckMsTUFBTSxDQUFDLFdBQVcsQ0FBQztnQkFDbEIsSUFBSTtnQkFDSixNQUFNLEVBQUUsc0JBQVcsRUFBRSxDQUFDLE1BQU07Z0JBQzVCLElBQUk7YUFDSixDQUFDLENBQUM7UUFDSixDQUFDO0tBQ0QsQ0FBQztBQUNILENBQUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUN4QkY7OztnR0FHZ0c7O0FBRWhHLHNGQUF5QztBQUd6QyxlQUFlLEdBQVcsRUFBRSxHQUFXLEVBQUUsS0FBYTtJQUNyRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBRUQsbUJBQW1CLElBQVk7SUFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsc0JBQVcsRUFBRSxDQUFDLFNBQVMsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQVFELE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDakMsSUFBSSxRQUEyQixDQUFDO0lBQ2hDLE1BQU0sQ0FBQyxHQUFHLEVBQUU7UUFDWCxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDZixRQUFRLEdBQUcsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLFFBQVEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUNqRixRQUFRLENBQUMsc0JBQXNCLENBQUMsV0FBVyxDQUFDLEVBQzVDLENBQUMsT0FBWSxFQUFFLEVBQUU7Z0JBQ2hCLE1BQU0sSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDaEQsTUFBTSxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDO1lBQzFCLENBQUMsQ0FBQztpQkFDRCxNQUFNLENBQUMsQ0FBQyxDQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdkMsQ0FBQztRQUNELE1BQU0sQ0FBQyxRQUFRLENBQUM7SUFDakIsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMOzs7OztHQUtHO0FBQ0gsa0NBQXlDLFVBQWtCO0lBQzFELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDMUMsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sS0FBSyxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDM0IsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBQy9CLE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO1FBQzdDLENBQUM7UUFBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBQ3BDLE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFDbEMsQ0FBQztRQUNELFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDbEIsQ0FBQztJQUNELE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDO0FBQ3JCLENBQUM7QUFiRCw0REFhQztBQUVEOztHQUVHO0FBQ0gscUNBQTRDLE1BQWM7SUFDekQsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxNQUFNLFFBQVEsR0FBRyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQztJQUN6QyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNaLElBQUksRUFBRSxHQUFHLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQzFCLE9BQU8sRUFBRSxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQztRQUNwQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUMxRCxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxHQUFHLE1BQU0sQ0FBQyxNQUFNLElBQUksUUFBUSxDQUFDLENBQUMsQ0FBQztZQUM1QyxFQUFFLEdBQUcsR0FBRyxDQUFDO1FBQ1YsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsRUFBRSxHQUFHLEdBQUcsQ0FBQztRQUNWLENBQUM7SUFDRixDQUFDO0lBQ0QsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzVCLE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUMzRCxFQUFFLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxJQUFJLFFBQVEsQ0FBQyxHQUFHLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUN4QyxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDNUIsTUFBTSxDQUFDLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7SUFDakQsQ0FBQztJQUNELE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBdEJELGtFQXNCQztBQUVEOztHQUVHO0FBQ0gsa0NBQXlDLElBQVk7SUFDcEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxzQkFBVyxFQUFFLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCxFQUFFLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNmLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNqQyxNQUFNLENBQUM7SUFDUixDQUFDO0lBRUQsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsR0FBRyx3QkFBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMxRCxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDZixNQUFNLENBQUM7SUFDUixDQUFDO0lBQ0QsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO0lBQ2pCLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUN0RCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO0lBQzdCLEVBQUUsQ0FBQyxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3pDLDhEQUE4RDtRQUM5RCxNQUFNLGVBQWUsR0FBRyxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3RSxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLFdBQVcsQ0FBQztRQUM3RSxRQUFRLEdBQUcsV0FBVyxHQUFHLGVBQWUsR0FBRyxhQUFhLENBQUM7SUFDMUQsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ1AsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNsRCxRQUFRLEdBQUcsV0FBVyxHQUFHLENBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxDQUFDO0lBQzVELENBQUM7SUFDRCxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDO0FBQ3ZFLENBQUM7QUEzQkQsNERBMkJDO0FBRUQsMENBQWlELE1BQWM7SUFDOUQsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsR0FBRywyQkFBMkIsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMvRCxFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO1FBQ2QsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBQ2hFLE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDMUUsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztZQUNWLE1BQU0sdUJBQXVCLEdBQUcsa0JBQWtCLEdBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNySCxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxHQUFHLHVCQUF1QixHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDbkYsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN4QixDQUFDO1FBQ0QsSUFBSSxDQUFDLENBQUM7WUFDTCxNQUFNLHFCQUFxQixHQUFHLGtCQUFrQixHQUFHLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQzNFLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLEdBQUcscUJBQXFCLENBQUM7WUFDbkQsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN4QixDQUFDO0lBQ0YsQ0FBQztJQUNELE1BQU0sQ0FBQyxJQUFJLENBQUM7QUFDYixDQUFDO0FBakJELDRFQWlCQzs7Ozs7Ozs7Ozs7Ozs7QUN2SUQ7OztnR0FHZ0c7O0FBWWhHLElBQUksY0FBYyxHQUFnQyxTQUFTLENBQUM7QUFFNUQsaUJBQXdCLEdBQVc7SUFDbEMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3hFLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7UUFDYixNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDVixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixDQUFDO0lBQ0YsQ0FBQztJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMsMkJBQTJCLEdBQUcsRUFBRSxDQUFDLENBQUM7QUFDbkQsQ0FBQztBQVZELDBCQVVDO0FBRUQ7SUFDQyxFQUFFLENBQUMsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDO1FBQ3BCLE1BQU0sQ0FBQyxjQUFjLENBQUM7SUFDdkIsQ0FBQztJQUVELGNBQWMsR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDMUMsRUFBRSxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQztRQUNwQixNQUFNLENBQUMsY0FBYyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxNQUFNLElBQUksS0FBSyxDQUFDLHlCQUF5QixDQUFDLENBQUM7QUFDNUMsQ0FBQztBQVhELGtDQVdDIiwiZmlsZSI6ImluZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiIFx0Ly8gVGhlIG1vZHVsZSBjYWNoZVxuIFx0dmFyIGluc3RhbGxlZE1vZHVsZXMgPSB7fTtcblxuIFx0Ly8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbiBcdGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuIFx0XHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcbiBcdFx0aWYoaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0pIHtcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbiBcdFx0fVxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0aTogbW9kdWxlSWQsXG4gXHRcdFx0bDogZmFsc2UsXG4gXHRcdFx0ZXhwb3J0czoge31cbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubCA9IHRydWU7XG5cbiBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbiBcdFx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xuIFx0fVxuXG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlcyBvYmplY3QgKF9fd2VicGFja19tb2R1bGVzX18pXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcblxuIFx0Ly8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbiBcdFx0aWYoIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBuYW1lKSkge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7XG4gXHRcdFx0XHRjb25maWd1cmFibGU6IGZhbHNlLFxuIFx0XHRcdFx0ZW51bWVyYWJsZTogdHJ1ZSxcbiBcdFx0XHRcdGdldDogZ2V0dGVyXG4gXHRcdFx0fSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGdldERlZmF1bHRFeHBvcnQgZnVuY3Rpb24gZm9yIGNvbXBhdGliaWxpdHkgd2l0aCBub24taGFybW9ueSBtb2R1bGVzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm4gPSBmdW5jdGlvbihtb2R1bGUpIHtcbiBcdFx0dmFyIGdldHRlciA9IG1vZHVsZSAmJiBtb2R1bGUuX19lc01vZHVsZSA/XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0RGVmYXVsdCgpIHsgcmV0dXJuIG1vZHVsZVsnZGVmYXVsdCddOyB9IDpcbiBcdFx0XHRmdW5jdGlvbiBnZXRNb2R1bGVFeHBvcnRzKCkgeyByZXR1cm4gbW9kdWxlOyB9O1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQoZ2V0dGVyLCAnYScsIGdldHRlcik7XG4gXHRcdHJldHVybiBnZXR0ZXI7XG4gXHR9O1xuXG4gXHQvLyBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGxcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubyA9IGZ1bmN0aW9uKG9iamVjdCwgcHJvcGVydHkpIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmplY3QsIHByb3BlcnR5KTsgfTtcblxuIFx0Ly8gX193ZWJwYWNrX3B1YmxpY19wYXRoX19cbiBcdF9fd2VicGFja19yZXF1aXJlX18ucCA9IFwiXCI7XG5cblxuIFx0Ly8gTG9hZCBlbnRyeSBtb2R1bGUgYW5kIHJldHVybiBleHBvcnRzXG4gXHRyZXR1cm4gX193ZWJwYWNrX3JlcXVpcmVfXyhfX3dlYnBhY2tfcmVxdWlyZV9fLnMgPSBcIi4vcHJldmlldy1zcmMvaW5kZXgudHNcIik7XG4iLCIvKipcbiAqIGxvZGFzaCAoQ3VzdG9tIEJ1aWxkKSA8aHR0cHM6Ly9sb2Rhc2guY29tLz5cbiAqIEJ1aWxkOiBgbG9kYXNoIG1vZHVsYXJpemUgZXhwb3J0cz1cIm5wbVwiIC1vIC4vYFxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnMgPGh0dHBzOi8vanF1ZXJ5Lm9yZy8+XG4gKiBSZWxlYXNlZCB1bmRlciBNSVQgbGljZW5zZSA8aHR0cHM6Ly9sb2Rhc2guY29tL2xpY2Vuc2U+XG4gKiBCYXNlZCBvbiBVbmRlcnNjb3JlLmpzIDEuOC4zIDxodHRwOi8vdW5kZXJzY29yZWpzLm9yZy9MSUNFTlNFPlxuICogQ29weXJpZ2h0IEplcmVteSBBc2hrZW5hcywgRG9jdW1lbnRDbG91ZCBhbmQgSW52ZXN0aWdhdGl2ZSBSZXBvcnRlcnMgJiBFZGl0b3JzXG4gKi9cblxuLyoqIFVzZWQgYXMgdGhlIGBUeXBlRXJyb3JgIG1lc3NhZ2UgZm9yIFwiRnVuY3Rpb25zXCIgbWV0aG9kcy4gKi9cbnZhciBGVU5DX0VSUk9SX1RFWFQgPSAnRXhwZWN0ZWQgYSBmdW5jdGlvbic7XG5cbi8qKiBVc2VkIGFzIHJlZmVyZW5jZXMgZm9yIHZhcmlvdXMgYE51bWJlcmAgY29uc3RhbnRzLiAqL1xudmFyIE5BTiA9IDAgLyAwO1xuXG4vKiogYE9iamVjdCN0b1N0cmluZ2AgcmVzdWx0IHJlZmVyZW5jZXMuICovXG52YXIgc3ltYm9sVGFnID0gJ1tvYmplY3QgU3ltYm9sXSc7XG5cbi8qKiBVc2VkIHRvIG1hdGNoIGxlYWRpbmcgYW5kIHRyYWlsaW5nIHdoaXRlc3BhY2UuICovXG52YXIgcmVUcmltID0gL15cXHMrfFxccyskL2c7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiYWQgc2lnbmVkIGhleGFkZWNpbWFsIHN0cmluZyB2YWx1ZXMuICovXG52YXIgcmVJc0JhZEhleCA9IC9eWy0rXTB4WzAtOWEtZl0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmluYXJ5IHN0cmluZyB2YWx1ZXMuICovXG52YXIgcmVJc0JpbmFyeSA9IC9eMGJbMDFdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IG9jdGFsIHN0cmluZyB2YWx1ZXMuICovXG52YXIgcmVJc09jdGFsID0gL14wb1swLTddKyQvaTtcblxuLyoqIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIHdpdGhvdXQgYSBkZXBlbmRlbmN5IG9uIGByb290YC4gKi9cbnZhciBmcmVlUGFyc2VJbnQgPSBwYXJzZUludDtcblxuLyoqIERldGVjdCBmcmVlIHZhcmlhYmxlIGBnbG9iYWxgIGZyb20gTm9kZS5qcy4gKi9cbnZhciBmcmVlR2xvYmFsID0gdHlwZW9mIGdsb2JhbCA9PSAnb2JqZWN0JyAmJiBnbG9iYWwgJiYgZ2xvYmFsLk9iamVjdCA9PT0gT2JqZWN0ICYmIGdsb2JhbDtcblxuLyoqIERldGVjdCBmcmVlIHZhcmlhYmxlIGBzZWxmYC4gKi9cbnZhciBmcmVlU2VsZiA9IHR5cGVvZiBzZWxmID09ICdvYmplY3QnICYmIHNlbGYgJiYgc2VsZi5PYmplY3QgPT09IE9iamVjdCAmJiBzZWxmO1xuXG4vKiogVXNlZCBhcyBhIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsIG9iamVjdC4gKi9cbnZhciByb290ID0gZnJlZUdsb2JhbCB8fCBmcmVlU2VsZiB8fCBGdW5jdGlvbigncmV0dXJuIHRoaXMnKSgpO1xuXG4vKiogVXNlZCBmb3IgYnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgb2JqZWN0UHJvdG8gPSBPYmplY3QucHJvdG90eXBlO1xuXG4vKipcbiAqIFVzZWQgdG8gcmVzb2x2ZSB0aGVcbiAqIFtgdG9TdHJpbmdUYWdgXShodHRwOi8vZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1vYmplY3QucHJvdG90eXBlLnRvc3RyaW5nKVxuICogb2YgdmFsdWVzLlxuICovXG52YXIgb2JqZWN0VG9TdHJpbmcgPSBvYmplY3RQcm90by50b1N0cmluZztcblxuLyogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgZm9yIHRob3NlIHdpdGggdGhlIHNhbWUgbmFtZSBhcyBvdGhlciBgbG9kYXNoYCBtZXRob2RzLiAqL1xudmFyIG5hdGl2ZU1heCA9IE1hdGgubWF4LFxuICAgIG5hdGl2ZU1pbiA9IE1hdGgubWluO1xuXG4vKipcbiAqIEdldHMgdGhlIHRpbWVzdGFtcCBvZiB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0aGF0IGhhdmUgZWxhcHNlZCBzaW5jZVxuICogdGhlIFVuaXggZXBvY2ggKDEgSmFudWFyeSAxOTcwIDAwOjAwOjAwIFVUQykuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAyLjQuMFxuICogQGNhdGVnb3J5IERhdGVcbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIHRpbWVzdGFtcC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5kZWZlcihmdW5jdGlvbihzdGFtcCkge1xuICogICBjb25zb2xlLmxvZyhfLm5vdygpIC0gc3RhbXApO1xuICogfSwgXy5ub3coKSk7XG4gKiAvLyA9PiBMb2dzIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGl0IHRvb2sgZm9yIHRoZSBkZWZlcnJlZCBpbnZvY2F0aW9uLlxuICovXG52YXIgbm93ID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiByb290LkRhdGUubm93KCk7XG59O1xuXG4vKipcbiAqIENyZWF0ZXMgYSBkZWJvdW5jZWQgZnVuY3Rpb24gdGhhdCBkZWxheXMgaW52b2tpbmcgYGZ1bmNgIHVudGlsIGFmdGVyIGB3YWl0YFxuICogbWlsbGlzZWNvbmRzIGhhdmUgZWxhcHNlZCBzaW5jZSB0aGUgbGFzdCB0aW1lIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gd2FzXG4gKiBpbnZva2VkLiBUaGUgZGVib3VuY2VkIGZ1bmN0aW9uIGNvbWVzIHdpdGggYSBgY2FuY2VsYCBtZXRob2QgdG8gY2FuY2VsXG4gKiBkZWxheWVkIGBmdW5jYCBpbnZvY2F0aW9ucyBhbmQgYSBgZmx1c2hgIG1ldGhvZCB0byBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS5cbiAqIFByb3ZpZGUgYG9wdGlvbnNgIHRvIGluZGljYXRlIHdoZXRoZXIgYGZ1bmNgIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZVxuICogbGVhZGluZyBhbmQvb3IgdHJhaWxpbmcgZWRnZSBvZiB0aGUgYHdhaXRgIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZFxuICogd2l0aCB0aGUgbGFzdCBhcmd1bWVudHMgcHJvdmlkZWQgdG8gdGhlIGRlYm91bmNlZCBmdW5jdGlvbi4gU3Vic2VxdWVudFxuICogY2FsbHMgdG8gdGhlIGRlYm91bmNlZCBmdW5jdGlvbiByZXR1cm4gdGhlIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2BcbiAqIGludm9jYXRpb24uXG4gKlxuICogKipOb3RlOioqIElmIGBsZWFkaW5nYCBhbmQgYHRyYWlsaW5nYCBvcHRpb25zIGFyZSBgdHJ1ZWAsIGBmdW5jYCBpc1xuICogaW52b2tlZCBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dCBvbmx5IGlmIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb25cbiAqIGlzIGludm9rZWQgbW9yZSB0aGFuIG9uY2UgZHVyaW5nIHRoZSBgd2FpdGAgdGltZW91dC5cbiAqXG4gKiBJZiBgd2FpdGAgaXMgYDBgIGFuZCBgbGVhZGluZ2AgaXMgYGZhbHNlYCwgYGZ1bmNgIGludm9jYXRpb24gaXMgZGVmZXJyZWRcbiAqIHVudGlsIHRvIHRoZSBuZXh0IHRpY2ssIHNpbWlsYXIgdG8gYHNldFRpbWVvdXRgIHdpdGggYSB0aW1lb3V0IG9mIGAwYC5cbiAqXG4gKiBTZWUgW0RhdmlkIENvcmJhY2hvJ3MgYXJ0aWNsZV0oaHR0cHM6Ly9jc3MtdHJpY2tzLmNvbS9kZWJvdW5jaW5nLXRocm90dGxpbmctZXhwbGFpbmVkLWV4YW1wbGVzLylcbiAqIGZvciBkZXRhaWxzIG92ZXIgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gYF8uZGVib3VuY2VgIGFuZCBgXy50aHJvdHRsZWAuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IEZ1bmN0aW9uXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byBkZWJvdW5jZS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbd2FpdD0wXSBUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byBkZWxheS5cbiAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0aW9ucz17fV0gVGhlIG9wdGlvbnMgb2JqZWN0LlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy5sZWFkaW5nPWZhbHNlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0aW9ucy5tYXhXYWl0XVxuICogIFRoZSBtYXhpbXVtIHRpbWUgYGZ1bmNgIGlzIGFsbG93ZWQgdG8gYmUgZGVsYXllZCBiZWZvcmUgaXQncyBpbnZva2VkLlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy50cmFpbGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyBkZWJvdW5jZWQgZnVuY3Rpb24uXG4gKiBAZXhhbXBsZVxuICpcbiAqIC8vIEF2b2lkIGNvc3RseSBjYWxjdWxhdGlvbnMgd2hpbGUgdGhlIHdpbmRvdyBzaXplIGlzIGluIGZsdXguXG4gKiBqUXVlcnkod2luZG93KS5vbigncmVzaXplJywgXy5kZWJvdW5jZShjYWxjdWxhdGVMYXlvdXQsIDE1MCkpO1xuICpcbiAqIC8vIEludm9rZSBgc2VuZE1haWxgIHdoZW4gY2xpY2tlZCwgZGVib3VuY2luZyBzdWJzZXF1ZW50IGNhbGxzLlxuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIF8uZGVib3VuY2Uoc2VuZE1haWwsIDMwMCwge1xuICogICAnbGVhZGluZyc6IHRydWUsXG4gKiAgICd0cmFpbGluZyc6IGZhbHNlXG4gKiB9KSk7XG4gKlxuICogLy8gRW5zdXJlIGBiYXRjaExvZ2AgaXMgaW52b2tlZCBvbmNlIGFmdGVyIDEgc2Vjb25kIG9mIGRlYm91bmNlZCBjYWxscy5cbiAqIHZhciBkZWJvdW5jZWQgPSBfLmRlYm91bmNlKGJhdGNoTG9nLCAyNTAsIHsgJ21heFdhaXQnOiAxMDAwIH0pO1xuICogdmFyIHNvdXJjZSA9IG5ldyBFdmVudFNvdXJjZSgnL3N0cmVhbScpO1xuICogalF1ZXJ5KHNvdXJjZSkub24oJ21lc3NhZ2UnLCBkZWJvdW5jZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgZGVib3VuY2VkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCBkZWJvdW5jZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gZGVib3VuY2UoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGFzdEFyZ3MsXG4gICAgICBsYXN0VGhpcyxcbiAgICAgIG1heFdhaXQsXG4gICAgICByZXN1bHQsXG4gICAgICB0aW1lcklkLFxuICAgICAgbGFzdENhbGxUaW1lLFxuICAgICAgbGFzdEludm9rZVRpbWUgPSAwLFxuICAgICAgbGVhZGluZyA9IGZhbHNlLFxuICAgICAgbWF4aW5nID0gZmFsc2UsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgd2FpdCA9IHRvTnVtYmVyKHdhaXQpIHx8IDA7XG4gIGlmIChpc09iamVjdChvcHRpb25zKSkge1xuICAgIGxlYWRpbmcgPSAhIW9wdGlvbnMubGVhZGluZztcbiAgICBtYXhpbmcgPSAnbWF4V2FpdCcgaW4gb3B0aW9ucztcbiAgICBtYXhXYWl0ID0gbWF4aW5nID8gbmF0aXZlTWF4KHRvTnVtYmVyKG9wdGlvbnMubWF4V2FpdCkgfHwgMCwgd2FpdCkgOiBtYXhXYWl0O1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cblxuICBmdW5jdGlvbiBpbnZva2VGdW5jKHRpbWUpIHtcbiAgICB2YXIgYXJncyA9IGxhc3RBcmdzLFxuICAgICAgICB0aGlzQXJnID0gbGFzdFRoaXM7XG5cbiAgICBsYXN0QXJncyA9IGxhc3RUaGlzID0gdW5kZWZpbmVkO1xuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICByZXN1bHQgPSBmdW5jLmFwcGx5KHRoaXNBcmcsIGFyZ3MpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiBsZWFkaW5nRWRnZSh0aW1lKSB7XG4gICAgLy8gUmVzZXQgYW55IGBtYXhXYWl0YCB0aW1lci5cbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgLy8gU3RhcnQgdGhlIHRpbWVyIGZvciB0aGUgdHJhaWxpbmcgZWRnZS5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIC8vIEludm9rZSB0aGUgbGVhZGluZyBlZGdlLlxuICAgIHJldHVybiBsZWFkaW5nID8gaW52b2tlRnVuYyh0aW1lKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbWFpbmluZ1dhaXQodGltZSkge1xuICAgIHZhciB0aW1lU2luY2VMYXN0Q2FsbCA9IHRpbWUgLSBsYXN0Q2FsbFRpbWUsXG4gICAgICAgIHRpbWVTaW5jZUxhc3RJbnZva2UgPSB0aW1lIC0gbGFzdEludm9rZVRpbWUsXG4gICAgICAgIHJlc3VsdCA9IHdhaXQgLSB0aW1lU2luY2VMYXN0Q2FsbDtcblxuICAgIHJldHVybiBtYXhpbmcgPyBuYXRpdmVNaW4ocmVzdWx0LCBtYXhXYWl0IC0gdGltZVNpbmNlTGFzdEludm9rZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiBzaG91bGRJbnZva2UodGltZSkge1xuICAgIHZhciB0aW1lU2luY2VMYXN0Q2FsbCA9IHRpbWUgLSBsYXN0Q2FsbFRpbWUsXG4gICAgICAgIHRpbWVTaW5jZUxhc3RJbnZva2UgPSB0aW1lIC0gbGFzdEludm9rZVRpbWU7XG5cbiAgICAvLyBFaXRoZXIgdGhpcyBpcyB0aGUgZmlyc3QgY2FsbCwgYWN0aXZpdHkgaGFzIHN0b3BwZWQgYW5kIHdlJ3JlIGF0IHRoZVxuICAgIC8vIHRyYWlsaW5nIGVkZ2UsIHRoZSBzeXN0ZW0gdGltZSBoYXMgZ29uZSBiYWNrd2FyZHMgYW5kIHdlJ3JlIHRyZWF0aW5nXG4gICAgLy8gaXQgYXMgdGhlIHRyYWlsaW5nIGVkZ2UsIG9yIHdlJ3ZlIGhpdCB0aGUgYG1heFdhaXRgIGxpbWl0LlxuICAgIHJldHVybiAobGFzdENhbGxUaW1lID09PSB1bmRlZmluZWQgfHwgKHRpbWVTaW5jZUxhc3RDYWxsID49IHdhaXQpIHx8XG4gICAgICAodGltZVNpbmNlTGFzdENhbGwgPCAwKSB8fCAobWF4aW5nICYmIHRpbWVTaW5jZUxhc3RJbnZva2UgPj0gbWF4V2FpdCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdGltZXJFeHBpcmVkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCk7XG4gICAgaWYgKHNob3VsZEludm9rZSh0aW1lKSkge1xuICAgICAgcmV0dXJuIHRyYWlsaW5nRWRnZSh0aW1lKTtcbiAgICB9XG4gICAgLy8gUmVzdGFydCB0aGUgdGltZXIuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCByZW1haW5pbmdXYWl0KHRpbWUpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRyYWlsaW5nRWRnZSh0aW1lKSB7XG4gICAgdGltZXJJZCA9IHVuZGVmaW5lZDtcblxuICAgIC8vIE9ubHkgaW52b2tlIGlmIHdlIGhhdmUgYGxhc3RBcmdzYCB3aGljaCBtZWFucyBgZnVuY2AgaGFzIGJlZW5cbiAgICAvLyBkZWJvdW5jZWQgYXQgbGVhc3Qgb25jZS5cbiAgICBpZiAodHJhaWxpbmcgJiYgbGFzdEFyZ3MpIHtcbiAgICAgIHJldHVybiBpbnZva2VGdW5jKHRpbWUpO1xuICAgIH1cbiAgICBsYXN0QXJncyA9IGxhc3RUaGlzID0gdW5kZWZpbmVkO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiBjYW5jZWwoKSB7XG4gICAgaWYgKHRpbWVySWQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVySWQpO1xuICAgIH1cbiAgICBsYXN0SW52b2tlVGltZSA9IDA7XG4gICAgbGFzdEFyZ3MgPSBsYXN0Q2FsbFRpbWUgPSBsYXN0VGhpcyA9IHRpbWVySWQgPSB1bmRlZmluZWQ7XG4gIH1cblxuICBmdW5jdGlvbiBmbHVzaCgpIHtcbiAgICByZXR1cm4gdGltZXJJZCA9PT0gdW5kZWZpbmVkID8gcmVzdWx0IDogdHJhaWxpbmdFZGdlKG5vdygpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGRlYm91bmNlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpLFxuICAgICAgICBpc0ludm9raW5nID0gc2hvdWxkSW52b2tlKHRpbWUpO1xuXG4gICAgbGFzdEFyZ3MgPSBhcmd1bWVudHM7XG4gICAgbGFzdFRoaXMgPSB0aGlzO1xuICAgIGxhc3RDYWxsVGltZSA9IHRpbWU7XG5cbiAgICBpZiAoaXNJbnZva2luZykge1xuICAgICAgaWYgKHRpbWVySWQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gbGVhZGluZ0VkZ2UobGFzdENhbGxUaW1lKTtcbiAgICAgIH1cbiAgICAgIGlmIChtYXhpbmcpIHtcbiAgICAgICAgLy8gSGFuZGxlIGludm9jYXRpb25zIGluIGEgdGlnaHQgbG9vcC5cbiAgICAgICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAgICAgcmV0dXJuIGludm9rZUZ1bmMobGFzdENhbGxUaW1lKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHRpbWVySWQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuICBkZWJvdW5jZWQuY2FuY2VsID0gY2FuY2VsO1xuICBkZWJvdW5jZWQuZmx1c2ggPSBmbHVzaDtcbiAgcmV0dXJuIGRlYm91bmNlZDtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgdGhyb3R0bGVkIGZ1bmN0aW9uIHRoYXQgb25seSBpbnZva2VzIGBmdW5jYCBhdCBtb3N0IG9uY2UgcGVyXG4gKiBldmVyeSBgd2FpdGAgbWlsbGlzZWNvbmRzLiBUaGUgdGhyb3R0bGVkIGZ1bmN0aW9uIGNvbWVzIHdpdGggYSBgY2FuY2VsYFxuICogbWV0aG9kIHRvIGNhbmNlbCBkZWxheWVkIGBmdW5jYCBpbnZvY2F0aW9ucyBhbmQgYSBgZmx1c2hgIG1ldGhvZCB0b1xuICogaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uIFByb3ZpZGUgYG9wdGlvbnNgIHRvIGluZGljYXRlIHdoZXRoZXIgYGZ1bmNgXG4gKiBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGUgbGVhZGluZyBhbmQvb3IgdHJhaWxpbmcgZWRnZSBvZiB0aGUgYHdhaXRgXG4gKiB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWQgd2l0aCB0aGUgbGFzdCBhcmd1bWVudHMgcHJvdmlkZWQgdG8gdGhlXG4gKiB0aHJvdHRsZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnQgY2FsbHMgdG8gdGhlIHRocm90dGxlZCBmdW5jdGlvbiByZXR1cm4gdGhlXG4gKiByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgIGludm9jYXRpb24uXG4gKlxuICogKipOb3RlOioqIElmIGBsZWFkaW5nYCBhbmQgYHRyYWlsaW5nYCBvcHRpb25zIGFyZSBgdHJ1ZWAsIGBmdW5jYCBpc1xuICogaW52b2tlZCBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dCBvbmx5IGlmIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb25cbiAqIGlzIGludm9rZWQgbW9yZSB0aGFuIG9uY2UgZHVyaW5nIHRoZSBgd2FpdGAgdGltZW91dC5cbiAqXG4gKiBJZiBgd2FpdGAgaXMgYDBgIGFuZCBgbGVhZGluZ2AgaXMgYGZhbHNlYCwgYGZ1bmNgIGludm9jYXRpb24gaXMgZGVmZXJyZWRcbiAqIHVudGlsIHRvIHRoZSBuZXh0IHRpY2ssIHNpbWlsYXIgdG8gYHNldFRpbWVvdXRgIHdpdGggYSB0aW1lb3V0IG9mIGAwYC5cbiAqXG4gKiBTZWUgW0RhdmlkIENvcmJhY2hvJ3MgYXJ0aWNsZV0oaHR0cHM6Ly9jc3MtdHJpY2tzLmNvbS9kZWJvdW5jaW5nLXRocm90dGxpbmctZXhwbGFpbmVkLWV4YW1wbGVzLylcbiAqIGZvciBkZXRhaWxzIG92ZXIgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gYF8udGhyb3R0bGVgIGFuZCBgXy5kZWJvdW5jZWAuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IEZ1bmN0aW9uXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byB0aHJvdHRsZS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbd2FpdD0wXSBUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB0aHJvdHRsZSBpbnZvY2F0aW9ucyB0by5cbiAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0aW9ucz17fV0gVGhlIG9wdGlvbnMgb2JqZWN0LlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy5sZWFkaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy50cmFpbGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyB0aHJvdHRsZWQgZnVuY3Rpb24uXG4gKiBAZXhhbXBsZVxuICpcbiAqIC8vIEF2b2lkIGV4Y2Vzc2l2ZWx5IHVwZGF0aW5nIHRoZSBwb3NpdGlvbiB3aGlsZSBzY3JvbGxpbmcuXG4gKiBqUXVlcnkod2luZG93KS5vbignc2Nyb2xsJywgXy50aHJvdHRsZSh1cGRhdGVQb3NpdGlvbiwgMTAwKSk7XG4gKlxuICogLy8gSW52b2tlIGByZW5ld1Rva2VuYCB3aGVuIHRoZSBjbGljayBldmVudCBpcyBmaXJlZCwgYnV0IG5vdCBtb3JlIHRoYW4gb25jZSBldmVyeSA1IG1pbnV0ZXMuXG4gKiB2YXIgdGhyb3R0bGVkID0gXy50aHJvdHRsZShyZW5ld1Rva2VuLCAzMDAwMDAsIHsgJ3RyYWlsaW5nJzogZmFsc2UgfSk7XG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgdGhyb3R0bGVkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIHRocm90dGxlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgdGhyb3R0bGVkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIHRocm90dGxlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxlYWRpbmcgPSB0cnVlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIGlmIChpc09iamVjdChvcHRpb25zKSkge1xuICAgIGxlYWRpbmcgPSAnbGVhZGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy5sZWFkaW5nIDogbGVhZGluZztcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG4gIHJldHVybiBkZWJvdW5jZShmdW5jLCB3YWl0LCB7XG4gICAgJ2xlYWRpbmcnOiBsZWFkaW5nLFxuICAgICdtYXhXYWl0Jzogd2FpdCxcbiAgICAndHJhaWxpbmcnOiB0cmFpbGluZ1xuICB9KTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyB0aGVcbiAqIFtsYW5ndWFnZSB0eXBlXShodHRwOi8vd3d3LmVjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtZWNtYXNjcmlwdC1sYW5ndWFnZS10eXBlcylcbiAqIG9mIGBPYmplY3RgLiAoZS5nLiBhcnJheXMsIGZ1bmN0aW9ucywgb2JqZWN0cywgcmVnZXhlcywgYG5ldyBOdW1iZXIoMClgLCBhbmQgYG5ldyBTdHJpbmcoJycpYClcbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhbiBvYmplY3QsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc09iamVjdCh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoXy5ub29wKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3QodmFsdWUpIHtcbiAgdmFyIHR5cGUgPSB0eXBlb2YgdmFsdWU7XG4gIHJldHVybiAhIXZhbHVlICYmICh0eXBlID09ICdvYmplY3QnIHx8IHR5cGUgPT0gJ2Z1bmN0aW9uJyk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgb2JqZWN0LWxpa2UuIEEgdmFsdWUgaXMgb2JqZWN0LWxpa2UgaWYgaXQncyBub3QgYG51bGxgXG4gKiBhbmQgaGFzIGEgYHR5cGVvZmAgcmVzdWx0IG9mIFwib2JqZWN0XCIuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgb2JqZWN0LWxpa2UsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc09iamVjdExpa2Uoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoXy5ub29wKTtcbiAqIC8vID0+IGZhbHNlXG4gKlxuICogXy5pc09iamVjdExpa2UobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdExpa2UodmFsdWUpIHtcbiAgcmV0dXJuICEhdmFsdWUgJiYgdHlwZW9mIHZhbHVlID09ICdvYmplY3QnO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIGNsYXNzaWZpZWQgYXMgYSBgU3ltYm9sYCBwcmltaXRpdmUgb3Igb2JqZWN0LlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGEgc3ltYm9sLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNTeW1ib2woU3ltYm9sLml0ZXJhdG9yKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzU3ltYm9sKCdhYmMnKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzU3ltYm9sKHZhbHVlKSB7XG4gIHJldHVybiB0eXBlb2YgdmFsdWUgPT0gJ3N5bWJvbCcgfHxcbiAgICAoaXNPYmplY3RMaWtlKHZhbHVlKSAmJiBvYmplY3RUb1N0cmluZy5jYWxsKHZhbHVlKSA9PSBzeW1ib2xUYWcpO1xufVxuXG4vKipcbiAqIENvbnZlcnRzIGB2YWx1ZWAgdG8gYSBudW1iZXIuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIHByb2Nlc3MuXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSBudW1iZXIuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8udG9OdW1iZXIoMy4yKTtcbiAqIC8vID0+IDMuMlxuICpcbiAqIF8udG9OdW1iZXIoTnVtYmVyLk1JTl9WQUxVRSk7XG4gKiAvLyA9PiA1ZS0zMjRcbiAqXG4gKiBfLnRvTnVtYmVyKEluZmluaXR5KTtcbiAqIC8vID0+IEluZmluaXR5XG4gKlxuICogXy50b051bWJlcignMy4yJyk7XG4gKiAvLyA9PiAzLjJcbiAqL1xuZnVuY3Rpb24gdG9OdW1iZXIodmFsdWUpIHtcbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PSAnbnVtYmVyJykge1xuICAgIHJldHVybiB2YWx1ZTtcbiAgfVxuICBpZiAoaXNTeW1ib2wodmFsdWUpKSB7XG4gICAgcmV0dXJuIE5BTjtcbiAgfVxuICBpZiAoaXNPYmplY3QodmFsdWUpKSB7XG4gICAgdmFyIG90aGVyID0gdHlwZW9mIHZhbHVlLnZhbHVlT2YgPT0gJ2Z1bmN0aW9uJyA/IHZhbHVlLnZhbHVlT2YoKSA6IHZhbHVlO1xuICAgIHZhbHVlID0gaXNPYmplY3Qob3RoZXIpID8gKG90aGVyICsgJycpIDogb3RoZXI7XG4gIH1cbiAgaWYgKHR5cGVvZiB2YWx1ZSAhPSAnc3RyaW5nJykge1xuICAgIHJldHVybiB2YWx1ZSA9PT0gMCA/IHZhbHVlIDogK3ZhbHVlO1xuICB9XG4gIHZhbHVlID0gdmFsdWUucmVwbGFjZShyZVRyaW0sICcnKTtcbiAgdmFyIGlzQmluYXJ5ID0gcmVJc0JpbmFyeS50ZXN0KHZhbHVlKTtcbiAgcmV0dXJuIChpc0JpbmFyeSB8fCByZUlzT2N0YWwudGVzdCh2YWx1ZSkpXG4gICAgPyBmcmVlUGFyc2VJbnQodmFsdWUuc2xpY2UoMiksIGlzQmluYXJ5ID8gMiA6IDgpXG4gICAgOiAocmVJc0JhZEhleC50ZXN0KHZhbHVlKSA/IE5BTiA6ICt2YWx1ZSk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gdGhyb3R0bGU7XG4iLCJ2YXIgZztcclxuXHJcbi8vIFRoaXMgd29ya3MgaW4gbm9uLXN0cmljdCBtb2RlXHJcbmcgPSAoZnVuY3Rpb24oKSB7XHJcblx0cmV0dXJuIHRoaXM7XHJcbn0pKCk7XHJcblxyXG50cnkge1xyXG5cdC8vIFRoaXMgd29ya3MgaWYgZXZhbCBpcyBhbGxvd2VkIChzZWUgQ1NQKVxyXG5cdGcgPSBnIHx8IEZ1bmN0aW9uKFwicmV0dXJuIHRoaXNcIikoKSB8fCAoMSwgZXZhbCkoXCJ0aGlzXCIpO1xyXG59IGNhdGNoIChlKSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiB0aGUgd2luZG93IHJlZmVyZW5jZSBpcyBhdmFpbGFibGVcclxuXHRpZiAodHlwZW9mIHdpbmRvdyA9PT0gXCJvYmplY3RcIikgZyA9IHdpbmRvdztcclxufVxyXG5cclxuLy8gZyBjYW4gc3RpbGwgYmUgdW5kZWZpbmVkLCBidXQgbm90aGluZyB0byBkbyBhYm91dCBpdC4uLlxyXG4vLyBXZSByZXR1cm4gdW5kZWZpbmVkLCBpbnN0ZWFkIG9mIG5vdGhpbmcgaGVyZSwgc28gaXQnc1xyXG4vLyBlYXNpZXIgdG8gaGFuZGxlIHRoaXMgY2FzZS4gaWYoIWdsb2JhbCkgeyAuLi59XHJcblxyXG5tb2R1bGUuZXhwb3J0cyA9IGc7XHJcbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuaW1wb3J0IHsgZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lIH0gZnJvbSAnLi9zY3JvbGwtc3luYyc7XG5cbmV4cG9ydCBjbGFzcyBBY3RpdmVMaW5lTWFya2VyIHtcblx0cHJpdmF0ZSBfY3VycmVudDogYW55O1xuXG5cdG9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbihsaW5lOiBudW1iZXIpIHtcblx0XHRjb25zdCB7IHByZXZpb3VzIH0gPSBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG5cdFx0dGhpcy5fdXBkYXRlKHByZXZpb3VzICYmIHByZXZpb3VzLmVsZW1lbnQpO1xuXHR9XG5cblx0X3VwZGF0ZShiZWZvcmU6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkKSB7XG5cdFx0dGhpcy5fdW5tYXJrQWN0aXZlRWxlbWVudCh0aGlzLl9jdXJyZW50KTtcblx0XHR0aGlzLl9tYXJrQWN0aXZlRWxlbWVudChiZWZvcmUpO1xuXHRcdHRoaXMuX2N1cnJlbnQgPSBiZWZvcmU7XG5cdH1cblxuXHRfdW5tYXJrQWN0aXZlRWxlbWVudChlbGVtZW50OiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdGlmICghZWxlbWVudCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRlbGVtZW50LmNsYXNzTmFtZSA9IGVsZW1lbnQuY2xhc3NOYW1lLnJlcGxhY2UoL1xcYmNvZGUtYWN0aXZlLWxpbmVcXGIvZywgJycpO1xuXHR9XG5cblx0X21hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQ6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkKSB7XG5cdFx0aWYgKCFlbGVtZW50KSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGVsZW1lbnQuY2xhc3NOYW1lICs9ICcgY29kZS1hY3RpdmUtbGluZSc7XG5cdH1cbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuZXhwb3J0IGZ1bmN0aW9uIG9uY2VEb2N1bWVudExvYWRlZChmOiAoKSA9PiB2b2lkKSB7XG5cdGlmIChkb2N1bWVudC5yZWFkeVN0YXRlID09PSAnbG9hZGluZycgfHwgZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ3VuaW5pdGlhbGl6ZWQnKSB7XG5cdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGYpO1xuXHR9IGVsc2Uge1xuXHRcdGYoKTtcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBBY3RpdmVMaW5lTWFya2VyIH0gZnJvbSAnLi9hY3RpdmVMaW5lTWFya2VyJztcbmltcG9ydCB7IG9uY2VEb2N1bWVudExvYWRlZCB9IGZyb20gJy4vZXZlbnRzJztcbmltcG9ydCB7IGNyZWF0ZVBvc3RlckZvclZzQ29kZSB9IGZyb20gJy4vbWVzc2FnaW5nJztcbmltcG9ydCB7IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0LCBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcbmltcG9ydCB7IGdldFNldHRpbmdzLCBnZXREYXRhIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5pbXBvcnQgdGhyb3R0bGUgPSByZXF1aXJlKCdsb2Rhc2gudGhyb3R0bGUnKTtcblxuZGVjbGFyZSB2YXIgYWNxdWlyZVZzQ29kZUFwaTogYW55O1xuXG52YXIgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IEFjdGl2ZUxpbmVNYXJrZXIoKTtcbmNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuY29uc3QgdnNjb2RlID0gYWNxdWlyZVZzQ29kZUFwaSgpO1xuXG4vLyBTZXQgVlMgQ29kZSBzdGF0ZVxuY29uc3Qgc3RhdGUgPSBnZXREYXRhKCdkYXRhLXN0YXRlJyk7XG52c2NvZGUuc2V0U3RhdGUoc3RhdGUpO1xuXG5jb25zdCBtZXNzYWdpbmcgPSBjcmVhdGVQb3N0ZXJGb3JWc0NvZGUodnNjb2RlKTtcblxud2luZG93LmNzcEFsZXJ0ZXIuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG53aW5kb3cuc3R5bGVMb2FkaW5nTW9uaXRvci5zZXRQb3N0ZXIobWVzc2FnaW5nKTtcblxud2luZG93Lm9ubG9hZCA9ICgpID0+IHtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufTtcblxub25jZURvY3VtZW50TG9hZGVkKCgpID0+IHtcblx0aWYgKHNldHRpbmdzLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG5cdFx0c2V0VGltZW91dCgoKSA9PiB7XG5cdFx0XHRjb25zdCBpbml0aWFsTGluZSA9ICtzZXR0aW5ncy5saW5lO1xuXHRcdFx0aWYgKCFpc05hTihpbml0aWFsTGluZSkpIHtcblx0XHRcdFx0c2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuXHRcdFx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUoaW5pdGlhbExpbmUpO1xuXHRcdFx0fVxuXHRcdH0sIDApO1xuXHR9XG59KTtcblxuY29uc3Qgb25VcGRhdGVWaWV3ID0gKCgpID0+IHtcblx0Y29uc3QgZG9TY3JvbGwgPSB0aHJvdHRsZSgobGluZTogbnVtYmVyKSA9PiB7XG5cdFx0c2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuXHRcdHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lKTtcblx0fSwgNTApO1xuXG5cdHJldHVybiAobGluZTogbnVtYmVyLCBzZXR0aW5nczogYW55KSA9PiB7XG5cdFx0aWYgKCFpc05hTihsaW5lKSkge1xuXHRcdFx0c2V0dGluZ3MubGluZSA9IGxpbmU7XG5cdFx0XHRkb1Njcm9sbChsaW5lKTtcblx0XHR9XG5cdH07XG59KSgpO1xuXG5sZXQgdXBkYXRlSW1hZ2VTaXplcyA9IHRocm90dGxlKCgpID0+IHtcblx0Y29uc3QgaW1hZ2VJbmZvOiB7IGlkOiBzdHJpbmcsIGhlaWdodDogbnVtYmVyLCB3aWR0aDogbnVtYmVyIH1bXSA9IFtdO1xuXHRsZXQgaW1hZ2VzID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2ltZycpO1xuXHRpZiAoaW1hZ2VzKSB7XG5cdFx0bGV0IGk7XG5cdFx0Zm9yIChpID0gMDsgaSA8IGltYWdlcy5sZW5ndGg7IGkrKykge1xuXHRcdFx0Y29uc3QgaW1nID0gaW1hZ2VzW2ldO1xuXG5cdFx0XHRpZiAoaW1nLmNsYXNzTGlzdC5jb250YWlucygnbG9hZGluZycpKSB7XG5cdFx0XHRcdGltZy5jbGFzc0xpc3QucmVtb3ZlKCdsb2FkaW5nJyk7XG5cdFx0XHR9XG5cblx0XHRcdGltYWdlSW5mby5wdXNoKHtcblx0XHRcdFx0aWQ6IGltZy5pZCxcblx0XHRcdFx0aGVpZ2h0OiBpbWcuaGVpZ2h0LFxuXHRcdFx0XHR3aWR0aDogaW1nLndpZHRoXG5cdFx0XHR9KTtcblx0XHR9XG5cblx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NhY2hlSW1hZ2VTaXplcycsIGltYWdlSW5mbyk7XG5cdH1cbn0sIDUwKTtcblxud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsICgpID0+IHtcblx0c2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuXHR1cGRhdGVJbWFnZVNpemVzKCk7XG59LCB0cnVlKTtcblxud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCBldmVudCA9PiB7XG5cdGlmIChldmVudC5kYXRhLnNvdXJjZSAhPT0gc2V0dGluZ3Muc291cmNlKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0c3dpdGNoIChldmVudC5kYXRhLnR5cGUpIHtcblx0XHRjYXNlICdvbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24nOlxuXHRcdFx0bWFya2VyLm9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbihldmVudC5kYXRhLmxpbmUpO1xuXHRcdFx0YnJlYWs7XG5cblx0XHRjYXNlICd1cGRhdGVWaWV3Jzpcblx0XHRcdG9uVXBkYXRlVmlldyhldmVudC5kYXRhLmxpbmUsIHNldHRpbmdzKTtcblx0XHRcdGJyZWFrO1xuXHR9XG59LCBmYWxzZSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2RibGNsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIXNldHRpbmdzLmRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcikge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdC8vIElnbm9yZSBjbGlja3Mgb24gbGlua3Ncblx0Zm9yIChsZXQgbm9kZSA9IGV2ZW50LnRhcmdldCBhcyBIVE1MRWxlbWVudDsgbm9kZTsgbm9kZSA9IG5vZGUucGFyZW50Tm9kZSBhcyBIVE1MRWxlbWVudCkge1xuXHRcdGlmIChub2RlLnRhZ05hbWUgPT09ICdBJykge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0fVxuXG5cdGNvbnN0IG9mZnNldCA9IGV2ZW50LnBhZ2VZO1xuXHRjb25zdCBsaW5lID0gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0KTtcblx0aWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcblx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2RpZENsaWNrJywgeyBsaW5lOiBNYXRoLmZsb29yKGxpbmUpIH0pO1xuXHR9XG59KTtcblxuZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBldmVudCA9PiB7XG5cdGlmICghZXZlbnQpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRsZXQgbm9kZTogYW55ID0gZXZlbnQudGFyZ2V0O1xuXHR3aGlsZSAobm9kZSkge1xuXHRcdGlmIChub2RlLnRhZ05hbWUgJiYgbm9kZS50YWdOYW1lID09PSAnQScgJiYgbm9kZS5ocmVmKSB7XG5cdFx0XHRpZiAobm9kZS5nZXRBdHRyaWJ1dGUoJ2hyZWYnKS5zdGFydHNXaXRoKCcjJykpIHtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0XHRpZiAobm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ2ZpbGU6Ly8nKSB8fCBub2RlLmhyZWYuc3RhcnRzV2l0aCgndnNjb2RlLXJlc291cmNlOicpKSB7XG5cdFx0XHRcdGNvbnN0IFtwYXRoLCBmcmFnbWVudF0gPSBub2RlLmhyZWYucmVwbGFjZSgvXihmaWxlOlxcL1xcL3x2c2NvZGUtcmVzb3VyY2U6KS9pLCAnJykuc3BsaXQoJyMnKTtcblx0XHRcdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjbGlja0xpbmsnLCB7IHBhdGgsIGZyYWdtZW50IH0pO1xuXHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0XHRldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0XHRicmVhaztcblx0XHR9XG5cdFx0bm9kZSA9IG5vZGUucGFyZW50Tm9kZTtcblx0fVxufSwgdHJ1ZSk7XG5cbmlmIChzZXR0aW5ncy5zY3JvbGxFZGl0b3JXaXRoUHJldmlldykge1xuXHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgdGhyb3R0bGUoKCkgPT4ge1xuXHRcdGlmIChzY3JvbGxEaXNhYmxlZCkge1xuXHRcdFx0c2Nyb2xsRGlzYWJsZWQgPSBmYWxzZTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KHdpbmRvdy5zY3JvbGxZKTtcblx0XHRcdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0XHRcdG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgncmV2ZWFsTGluZScsIHsgbGluZSB9KTtcblx0XHRcdH1cblx0XHR9XG5cdH0sIDUwKSk7XG59IiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IGdldFNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTWVzc2FnZVBvc3RlciB7XG5cdC8qKlxuXHQgKiBQb3N0IGEgbWVzc2FnZSB0byB0aGUgbWFya2Rvd24gZXh0ZW5zaW9uXG5cdCAqL1xuXHRwb3N0TWVzc2FnZSh0eXBlOiBzdHJpbmcsIGJvZHk6IG9iamVjdCk6IHZvaWQ7XG59XG5cbmV4cG9ydCBjb25zdCBjcmVhdGVQb3N0ZXJGb3JWc0NvZGUgPSAodnNjb2RlOiBhbnkpID0+IHtcblx0cmV0dXJuIG5ldyBjbGFzcyBpbXBsZW1lbnRzIE1lc3NhZ2VQb3N0ZXIge1xuXHRcdHBvc3RNZXNzYWdlKHR5cGU6IHN0cmluZywgYm9keTogb2JqZWN0KTogdm9pZCB7XG5cdFx0XHR2c2NvZGUucG9zdE1lc3NhZ2Uoe1xuXHRcdFx0XHR0eXBlLFxuXHRcdFx0XHRzb3VyY2U6IGdldFNldHRpbmdzKCkuc291cmNlLFxuXHRcdFx0XHRib2R5XG5cdFx0XHR9KTtcblx0XHR9XG5cdH07XG59O1xuXG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgZ2V0U2V0dGluZ3MgfSBmcm9tICcuL3NldHRpbmdzJztcblxuXG5mdW5jdGlvbiBjbGFtcChtaW46IG51bWJlciwgbWF4OiBudW1iZXIsIHZhbHVlOiBudW1iZXIpIHtcblx0cmV0dXJuIE1hdGgubWluKG1heCwgTWF0aC5tYXgobWluLCB2YWx1ZSkpO1xufVxuXG5mdW5jdGlvbiBjbGFtcExpbmUobGluZTogbnVtYmVyKSB7XG5cdHJldHVybiBjbGFtcCgwLCBnZXRTZXR0aW5ncygpLmxpbmVDb3VudCAtIDEsIGxpbmUpO1xufVxuXG5cbmV4cG9ydCBpbnRlcmZhY2UgQ29kZUxpbmVFbGVtZW50IHtcblx0ZWxlbWVudDogSFRNTEVsZW1lbnQ7XG5cdGxpbmU6IG51bWJlcjtcbn1cblxuY29uc3QgZ2V0Q29kZUxpbmVFbGVtZW50cyA9ICgoKSA9PiB7XG5cdGxldCBlbGVtZW50czogQ29kZUxpbmVFbGVtZW50W107XG5cdHJldHVybiAoKSA9PiB7XG5cdFx0aWYgKCFlbGVtZW50cykge1xuXHRcdFx0ZWxlbWVudHMgPSAoW3sgZWxlbWVudDogZG9jdW1lbnQuYm9keSwgbGluZTogMCB9XSkuY29uY2F0KEFycmF5LnByb3RvdHlwZS5tYXAuY2FsbChcblx0XHRcdFx0ZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS1saW5lJyksXG5cdFx0XHRcdChlbGVtZW50OiBhbnkpID0+IHtcblx0XHRcdFx0XHRjb25zdCBsaW5lID0gK2VsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLWxpbmUnKTtcblx0XHRcdFx0XHRyZXR1cm4geyBlbGVtZW50LCBsaW5lIH07XG5cdFx0XHRcdH0pXG5cdFx0XHRcdC5maWx0ZXIoKHg6IGFueSkgPT4gIWlzTmFOKHgubGluZSkpKTtcblx0XHR9XG5cdFx0cmV0dXJuIGVsZW1lbnRzO1xuXHR9O1xufSkoKTtcblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgbWFwIHRvIGEgc3BlY2lmaWMgdGFyZ2V0IGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqXG4gKiBJZiBhbiBleGFjdCBtYXRjaCwgcmV0dXJucyBhIHNpbmdsZSBlbGVtZW50LiBJZiB0aGUgbGluZSBpcyBiZXR3ZWVuIGVsZW1lbnRzLFxuICogcmV0dXJucyB0aGUgZWxlbWVudCBwcmlvciB0byBhbmQgdGhlIGVsZW1lbnQgYWZ0ZXIgdGhlIGdpdmVuIGxpbmUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUodGFyZ2V0TGluZTogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZU51bWJlciA9IE1hdGguZmxvb3IodGFyZ2V0TGluZSk7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRsZXQgcHJldmlvdXMgPSBsaW5lc1swXSB8fCBudWxsO1xuXHRmb3IgKGNvbnN0IGVudHJ5IG9mIGxpbmVzKSB7XG5cdFx0aWYgKGVudHJ5LmxpbmUgPT09IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzOiBlbnRyeSwgbmV4dDogdW5kZWZpbmVkIH07XG5cdFx0fSBlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuXHRcdFx0cmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG5cdFx0fVxuXHRcdHByZXZpb3VzID0gZW50cnk7XG5cdH1cblx0cmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG5cdGNvbnN0IHBvc2l0aW9uID0gb2Zmc2V0IC0gd2luZG93LnNjcm9sbFk7XG5cdGxldCBsbyA9IC0xO1xuXHRsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuXHR3aGlsZSAobG8gKyAxIDwgaGkpIHtcblx0XHRjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuXHRcdGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcblx0XHRcdGhpID0gbWlkO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGxvID0gbWlkO1xuXHRcdH1cblx0fVxuXHRjb25zdCBoaUVsZW1lbnQgPSBsaW5lc1toaV07XG5cdGNvbnN0IGhpQm91bmRzID0gaGlFbGVtZW50LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG5cdFx0Y29uc3QgbG9FbGVtZW50ID0gbGluZXNbbG9dO1xuXHRcdHJldHVybiB7IHByZXZpb3VzOiBsb0VsZW1lbnQsIG5leHQ6IGhpRWxlbWVudCB9O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmU6IG51bWJlcikge1xuXHRpZiAoIWdldFNldHRpbmdzKCkuc2Nyb2xsUHJldmlld1dpdGhFZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRpZiAobGluZSA8PSAwKSB7XG5cdFx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRpZiAoIXByZXZpb3VzKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdGxldCBzY3JvbGxUbyA9IDA7XG5cdGNvbnN0IHJlY3QgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuXHRpZiAobmV4dCAmJiBuZXh0LmxpbmUgIT09IHByZXZpb3VzLmxpbmUpIHtcblx0XHQvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuXHRcdGNvbnN0IGJldHdlZW5Qcm9ncmVzcyA9IChsaW5lIC0gcHJldmlvdXMubGluZSkgLyAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0Y29uc3QgZWxlbWVudE9mZnNldCA9IG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c1RvcDtcblx0XHRzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcblx0fSBlbHNlIHtcblx0XHRjb25zdCBwcm9ncmVzc0luRWxlbWVudCA9IGxpbmUgLSBNYXRoLmZsb29yKGxpbmUpO1xuXHRcdHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG5cdH1cblx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgTWF0aC5tYXgoMSwgd2luZG93LnNjcm9sbFkgKyBzY3JvbGxUbykpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0OiBudW1iZXIpIHtcblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmIChwcmV2aW91cykge1xuXHRcdGNvbnN0IHByZXZpb3VzQm91bmRzID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRjb25zdCBvZmZzZXRGcm9tUHJldmlvdXMgPSAob2Zmc2V0IC0gd2luZG93LnNjcm9sbFkgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuXHRcdGlmIChuZXh0KSB7XG5cdFx0XHRjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcblx0XHRcdGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgKiAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuXHRcdFx0Y29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0fVxuXHRyZXR1cm4gbnVsbDtcbn1cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgaW50ZXJmYWNlIFByZXZpZXdTZXR0aW5ncyB7XG5cdHNvdXJjZTogc3RyaW5nO1xuXHRsaW5lOiBudW1iZXI7XG5cdGxpbmVDb3VudDogbnVtYmVyO1xuXHRzY3JvbGxQcmV2aWV3V2l0aEVkaXRvcj86IGJvb2xlYW47XG5cdHNjcm9sbEVkaXRvcldpdGhQcmV2aWV3OiBib29sZWFuO1xuXHRkaXNhYmxlU2VjdXJpdHlXYXJuaW5nczogYm9vbGVhbjtcblx0ZG91YmxlQ2xpY2tUb1N3aXRjaFRvRWRpdG9yOiBib29sZWFuO1xufVxuXG5sZXQgY2FjaGVkU2V0dGluZ3M6IFByZXZpZXdTZXR0aW5ncyB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcblxuZXhwb3J0IGZ1bmN0aW9uIGdldERhdGEoa2V5OiBzdHJpbmcpOiBQcmV2aWV3U2V0dGluZ3Mge1xuXHRjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcblx0aWYgKGVsZW1lbnQpIHtcblx0XHRjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcblx0XHRpZiAoZGF0YSkge1xuXHRcdFx0cmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG5cdFx0fVxuXHR9XG5cblx0dGhyb3cgbmV3IEVycm9yKGBDb3VsZCBub3QgbG9hZCBkYXRhIGZvciAke2tleX1gKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFNldHRpbmdzKCk6IFByZXZpZXdTZXR0aW5ncyB7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuXHRpZiAoY2FjaGVkU2V0dGluZ3MpIHtcblx0XHRyZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBsb2FkIHNldHRpbmdzJyk7XG59XG4iXSwic291cmNlUm9vdCI6IiJ9 \ No newline at end of file diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index 99f173f111df..da0640b24434 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ body { - font-family: "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; font-size: 14px; padding: 0 26px; line-height: 22px; diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 623e96fdca36..34494ab24137 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -191,16 +191,6 @@ "description": "%markdown.styles.dec%", "scope": "resource" }, - "markdown.previewFrontMatter": { - "type": "string", - "enum": [ - "hide", - "show" - ], - "default": "hide", - "description": "%markdown.previewFrontMatter.dec%", - "scope": "resource" - }, "markdown.preview.breaks": { "type": "boolean", "default": false, @@ -215,7 +205,7 @@ }, "markdown.preview.fontFamily": { "type": "string", - "default": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'HelveticaNeue-Light', 'Ubuntu', 'Droid Sans', sans-serif", + "default": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'Ubuntu', 'Droid Sans', sans-serif", "description": "%markdown.preview.fontFamily.desc%", "scope": "resource" }, @@ -315,13 +305,14 @@ "build-preview": "webpack --mode development" }, "dependencies": { - "highlight.js": "9.12.0", - "markdown-it": "^8.4.1", - "vscode-extension-telemetry": "0.1.0", + "highlight.js": "9.13.1", + "markdown-it": "^8.4.2", + "markdown-it-front-matter": "^0.1.2", + "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/highlight.js": "9.1.10", + "@types/highlight.js": "9.12.3", "@types/lodash.throttle": "^4.1.3", "@types/markdown-it": "0.0.2", "@types/node": "^8.10.25", diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index ca3d7226f58e..7841caff810a 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -13,7 +13,6 @@ "markdown.preview.scrollPreviewWithEditorSelection.desc": "[Deprecated] Scrolls the markdown preview to reveal the currently selected line from the editor.", "markdown.preview.scrollPreviewWithEditorSelection.deprecationMessage": "This setting has been replaced by 'markdown.preview.scrollPreviewWithEditor' and no longer has any effect.", "markdown.preview.title": "Open Preview", - "markdown.previewFrontMatter.dec": "Sets how YAML front matter should be rendered in the markdown preview. 'hide' removes the front matter. Otherwise, the front matter is treated as markdown content.", "markdown.previewSide.title": "Open Preview to the Side", "markdown.showLockedPreviewToSide.title": "Open Locked Preview to the Side", "markdown.showSource.title": "Show Source", diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 9a13c4b73628..3ae1b9e10353 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -12,7 +12,7 @@ import throttle = require('lodash.throttle'); declare var acquireVsCodeApi: any; -var scrollDisabled = true; +let scrollDisabled = true; const marker = new ActiveLineMarker(); const settings = getSettings(); diff --git a/extensions/markdown-language-features/preview-src/scroll-sync.ts b/extensions/markdown-language-features/preview-src/scroll-sync.ts index ab1cb7e1e836..36c284926e05 100644 --- a/extensions/markdown-language-features/preview-src/scroll-sync.ts +++ b/extensions/markdown-language-features/preview-src/scroll-sync.ts @@ -110,7 +110,8 @@ export function scrollToRevealSourceLine(line: number) { const elementOffset = next.element.getBoundingClientRect().top - previousTop; scrollTo = previousTop + betweenProgress * elementOffset; } else { - scrollTo = previousTop; + const progressInElement = line - Math.floor(line); + scrollTo = previousTop + (rect.height * progressInElement); } window.scroll(window.scrollX, Math.max(1, window.scrollY + scrollTo)); } diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index fe44f0a88423..2760dce59beb 100644 --- a/extensions/markdown-language-features/src/commands/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/commands/openDocumentLink.ts @@ -25,7 +25,7 @@ export class OpenDocumentLinkCommand implements Command { path: string, fragment: string ): vscode.Uri { - return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ path, fragment }))}`); + return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ path: encodeURIComponent(path), fragment }))}`); } public constructor( @@ -39,9 +39,9 @@ export class OpenDocumentLinkCommand implements Command { return this.tryOpen(p + '.md', args); } const resource = vscode.Uri.file(p); - return Promise.resolve(void 0) + return Promise.resolve(undefined) .then(() => vscode.commands.executeCommand('vscode.open', resource)) - .then(() => void 0); + .then(() => undefined); }); } diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 69cb066ec1fa..f2fbc173616d 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -40,6 +40,9 @@ export function activate(context: vscode.ExtensionContext) { const previewManager = new MarkdownPreviewManager(contentProvider, logger, contributions); context.subscriptions.push(previewManager); + context.subscriptions.push(vscode.languages.setLanguageConfiguration('markdown', { + wordPattern: new RegExp('(\\p{Alphabetic}|\\p{Number})+', 'ug'), + })); context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider)); context.subscriptions.push(vscode.languages.registerDocumentLinkProvider(selector, new LinkProvider())); context.subscriptions.push(vscode.languages.registerFoldingRangeProvider(selector, new MarkdownFoldingProvider(engine))); diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index 0ef52ead21d4..bec722c99e1e 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -50,8 +50,27 @@ function matchAll( return out; } +function extractDocumentLink( + document: vscode.TextDocument, + base: string, + pre: number, + link: string, + matchIndex: number | undefined +): vscode.DocumentLink | undefined { + const offset = (matchIndex || 0) + pre; + const linkStart = document.positionAt(offset); + const linkEnd = document.positionAt(offset + link.length); + try { + return new vscode.DocumentLink( + new vscode.Range(linkStart, linkEnd), + normalizeLink(document, link, base)); + } catch (e) { + return undefined; + } +} + export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[[^\]]*\]\(\s*)((([^\s\(\)]|\(\S*?\))+))\s*(".*?")?\)/g; + private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|[^\]]*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; private readonly definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm; @@ -73,20 +92,15 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { - const pre = match[1]; - const link = match[2]; - const offset = (match.index || 0) + pre.length; - const linkStart = document.positionAt(offset); - const linkEnd = document.positionAt(offset + link.length); - try { - results.push(new vscode.DocumentLink( - new vscode.Range(linkStart, linkEnd), - normalizeLink(document, link, base))); - } catch (e) { - // noop + const matchImage = match[4] && extractDocumentLink(document, base, match[3].length + 1, match[4], match.index); + if (matchImage) { + results.push(matchImage); + } + const matchLink = extractDocumentLink(document, base, match[1].length, match[5], match.index); + if (matchLink) { + results.push(matchLink); } } - return results; } @@ -159,4 +173,4 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { } return out; } -} +} \ No newline at end of file diff --git a/extensions/markdown-language-features/src/features/foldingProvider.ts b/extensions/markdown-language-features/src/features/foldingProvider.ts index fe252bff088f..426cba30d58a 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -25,7 +25,7 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi (isStartRegion(token.content) || isEndRegion(token.content)); - const tokens = await this.engine.parse(document.uri, document.getText()); + const tokens = await this.engine.parse(document); const regionMarkers = tokens.filter(isRegionMarker) .map(token => ({ line: token.map[0], isStart: isStartRegion(token.content) })); @@ -84,7 +84,7 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi } }; - const tokens = await this.engine.parse(document.uri, document.getText()); + const tokens = await this.engine.parse(document); const multiLineListItems = tokens.filter(isFoldableToken); return multiLineListItems.map(listItem => { const start = listItem.map[0]; diff --git a/extensions/markdown-language-features/src/features/previewConfig.ts b/extensions/markdown-language-features/src/features/previewConfig.ts index f58ed83d79e8..c277508f839f 100644 --- a/extensions/markdown-language-features/src/features/previewConfig.ts +++ b/extensions/markdown-language-features/src/features/previewConfig.ts @@ -12,7 +12,6 @@ export class MarkdownPreviewConfiguration { public readonly scrollBeyondLastLine: boolean; public readonly wordWrap: boolean; - public readonly previewFrontMatter: string; public readonly lineBreaks: boolean; public readonly doubleClickToSwitchToEditor: boolean; public readonly scrollEditorWithPreview: boolean; @@ -36,7 +35,6 @@ export class MarkdownPreviewConfiguration { this.wordWrap = markdownEditorConfig['editor.wordWrap'] !== 'off'; } - this.previewFrontMatter = markdownConfig.get('previewFrontMatter', 'hide'); this.scrollPreviewWithEditor = !!markdownConfig.get('preview.scrollPreviewWithEditor', true); this.scrollEditorWithPreview = !!markdownConfig.get('preview.scrollEditorWithPreview', true); this.lineBreaks = !!markdownConfig.get('preview.breaks', false); diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index c3fbf40d4adb..6dcebd011555 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -68,7 +68,7 @@ export class MarkdownContentProvider { const nonce = new Date().getTime() + '' + new Date().getMilliseconds(); const csp = this.getCspForResource(sourceUri, nonce); - const body = await this.engine.render(sourceUri, config.previewFrontMatter === 'hide', markdownDocument.getText()); + const body = await this.engine.render(markdownDocument); return ` diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index e1aa22becaa6..53f117779ea0 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -3,132 +3,158 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as crypto from 'crypto'; import { MarkdownIt, Token } from 'markdown-it'; import * as path from 'path'; import * as vscode from 'vscode'; -import * as crypto from 'crypto'; import { MarkdownContributions } from './markdownExtensions'; import { Slugifier } from './slugify'; +import { SkinnyTextDocument } from './tableOfContentsProvider'; import { getUriForLinkWithKnownExternalScheme } from './util/links'; -const FrontMatterRegex = /^---\s*[^]*?(-{3}|\.{3})\s*/; +const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; + +interface MarkdownItConfig { + readonly breaks: boolean; + readonly linkify: boolean; +} + +class TokenCache { + private cachedDocument?: { + readonly uri: vscode.Uri; + readonly version: number; + readonly config: MarkdownItConfig; + }; + private tokens?: Token[]; + + public tryGetCached(document: SkinnyTextDocument, config: MarkdownItConfig): Token[] | undefined { + if (this.cachedDocument + && this.cachedDocument.uri.toString() === document.uri.toString() + && this.cachedDocument.version === document.version + && this.cachedDocument.config.breaks === config.breaks + && this.cachedDocument.config.linkify === config.linkify + ) { + return this.tokens; + } + return undefined; + } + + public update(document: SkinnyTextDocument, config: MarkdownItConfig, tokens: Token[]) { + this.cachedDocument = { + uri: document.uri, + version: document.version, + config, + }; + this.tokens = tokens; + } +} export class MarkdownEngine { - private md?: MarkdownIt; + private md?: Promise; - private firstLine?: number; private currentDocument?: vscode.Uri; private _slugCount = new Map(); + private _tokenCache = new TokenCache(); public constructor( private readonly extensionPreviewResourceProvider: MarkdownContributions, private readonly slugifier: Slugifier, ) { } - private usePlugin(factory: (md: any) => any): void { - try { - this.md = factory(this.md); - } catch (e) { - // noop - } - } - - private async getEngine(resource: vscode.Uri): Promise { + private async getEngine(config: MarkdownItConfig): Promise { if (!this.md) { - const hljs = await import('highlight.js'); - this.md = (await import('markdown-it'))({ - html: true, - highlight: (str: string, lang?: string) => { - // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 - if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) { - lang = 'jsx'; - } - if (lang && lang.toLocaleLowerCase() === 'json5') { - lang = 'json'; - } - if (lang && lang.toLocaleLowerCase() === 'c#') { - lang = 'cs'; + this.md = import('markdown-it').then(async markdownIt => { + let md: MarkdownIt = markdownIt(await getMarkdownOptions(() => md)); + + for (const plugin of this.extensionPreviewResourceProvider.markdownItPlugins) { + try { + md = (await plugin)(md); + } catch { + // noop } - if (lang && hljs.getLanguage(lang)) { - try { - return `
${hljs.highlight(lang, str, true).value}
`; - } catch (error) { } - } - return `
${this.md!.utils.escapeHtml(str)}
`; } - }); - for (const plugin of this.extensionPreviewResourceProvider.markdownItPlugins) { - this.usePlugin(await plugin); - } + const frontMatterPlugin = require('markdown-it-front-matter'); + // Extract rules from front matter plugin and apply at a lower precedence + let fontMatterRule: any; + frontMatterPlugin({ + block: { + ruler: { + before: (_id: any, _id2: any, rule: any) => { fontMatterRule = rule; } + } + } + }, () => { /* noop */ }); - for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'fence', 'blockquote_open', 'list_item_open']) { - this.addLineNumberRenderer(this.md, renderName); - } + md.block.ruler.before('fence', 'front_matter', fontMatterRule, { + alt: ['paragraph', 'reference', 'blockquote', 'list'] + }); - this.addImageStabilizer(this.md); - this.addFencedRenderer(this.md); + for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'fence', 'blockquote_open', 'list_item_open']) { + this.addLineNumberRenderer(md, renderName); + } + + this.addImageStabilizer(md); + this.addFencedRenderer(md); - this.addLinkNormalizer(this.md); - this.addLinkValidator(this.md); - this.addNamedHeaders(this.md); + this.addLinkNormalizer(md); + this.addLinkValidator(md); + this.addNamedHeaders(md); + return md; + }); } - const config = vscode.workspace.getConfiguration('markdown', resource); - this.md.set({ - breaks: config.get('preview.breaks', false), - linkify: config.get('preview.linkify', true) - }); - return this.md; + const md = await this.md!; + md.set(config); + return md; } - private stripFrontmatter(text: string): { text: string, offset: number } { - let offset = 0; - const frontMatterMatch = FrontMatterRegex.exec(text); - if (frontMatterMatch) { - const frontMatter = frontMatterMatch[0]; - offset = frontMatter.split(/\r\n|\n|\r/g).length - 1; - text = text.substr(frontMatter.length); + private tokenize( + document: SkinnyTextDocument, + config: MarkdownItConfig, + engine: MarkdownIt + ): Token[] { + const cached = this._tokenCache.tryGetCached(document, config); + if (cached) { + return cached; } - return { text, offset }; + + this.currentDocument = document.uri; + this._slugCount = new Map(); + + const text = document.getText(); + const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); + this._tokenCache.update(document, config, tokens); + return tokens; } - // {{SQL CARBON EDIT}} + // {{SQL CARBON EDIT}} - Add renderText method public async renderText(document: vscode.Uri, text: string): Promise { - const engine = await this.getEngine(document); + const engine = await this.getEngine(this.getConfig(document)); return engine.render(text); } - - public async render(document: vscode.Uri, stripFrontmatter: boolean, text: string): Promise { - let offset = 0; - if (stripFrontmatter) { - const markdownContent = this.stripFrontmatter(text); - offset = markdownContent.offset; - text = markdownContent.text; - } - this.currentDocument = document; - this.firstLine = offset; - this._slugCount = new Map(); - - const engine = await this.getEngine(document); - return engine.render(text); + // {{SQL CARBON EDIT}} - End + + public async render(document: SkinnyTextDocument): Promise { + const config = this.getConfig(document.uri); + const engine = await this.getEngine(config); + return engine.renderer.render(this.tokenize(document, config, engine), { + ...(engine as any).options, + ...config + }, {}); } - public async parse(document: vscode.Uri, source: string): Promise { - const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; - const { text, offset } = this.stripFrontmatter(source); - this.currentDocument = document; - this._slugCount = new Map(); - - const engine = await this.getEngine(document); + public async parse(document: SkinnyTextDocument): Promise { + const config = this.getConfig(document.uri); + const engine = await this.getEngine(config); + return this.tokenize(document, config, engine); + } - return engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}).map(token => { - if (token.map) { - token.map[0] += offset; - token.map[1] += offset; - } - return token; - }); + private getConfig(resource: vscode.Uri): MarkdownItConfig { + const config = vscode.workspace.getConfiguration('markdown', resource); + return { + breaks: config.get('preview.breaks', false), + linkify: config.get('preview.linkify', true) + }; } private addLineNumberRenderer(md: any, ruleName: string): void { @@ -136,7 +162,7 @@ export class MarkdownEngine { md.renderer.rules[ruleName] = (tokens: any, idx: number, options: any, env: any, self: any) => { const token = tokens[idx]; if (token.map && token.map.length) { - token.attrSet('data-line', this.firstLine + token.map[0]); + token.attrSet('data-line', token.map[0]); token.attrJoin('class', 'code-line'); } @@ -257,4 +283,30 @@ export class MarkdownEngine { } }; } -} \ No newline at end of file +} + +async function getMarkdownOptions(md: () => MarkdownIt) { + const hljs = await import('highlight.js'); + return { + html: true, + highlight: (str: string, lang?: string) => { + // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 + if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) { + lang = 'jsx'; + } + 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}
`; + } + catch (error) { } + } + return `
${md().utils.escapeHtml(str)}
`; + } + }; +} diff --git a/extensions/markdown-language-features/src/security.ts b/extensions/markdown-language-features/src/security.ts index 245ac1a102f0..252879bbf911 100644 --- a/extensions/markdown-language-features/src/security.ts +++ b/extensions/markdown-language-features/src/security.ts @@ -149,6 +149,7 @@ export class PreviewSecuritySelector { if (selection.type === 'toggle') { this.cspArbiter.setShouldDisableSecurityWarning(!this.cspArbiter.shouldDisableSecurityWarnings()); + this.webviewManager.refresh(); return; } else { await this.cspArbiter.setSecurityLevelForResource(resource, selection.type); diff --git a/extensions/markdown-language-features/src/slugify.ts b/extensions/markdown-language-features/src/slugify.ts index c9549df65537..2baf1fd58644 100644 --- a/extensions/markdown-language-features/src/slugify.ts +++ b/extensions/markdown-language-features/src/slugify.ts @@ -23,7 +23,7 @@ export const githubSlugifier: Slugifier = new class implements Slugifier { heading.trim() .toLowerCase() .replace(/\s+/g, '-') // Replace whitespace with - - .replace(/[\]\[\!\'\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g, '') // Remove known punctuators + .replace(/[\]\[\!\'\#\$\%\&\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g, '') // Remove known punctuators .replace(/^\-+/, '') // Remove leading - .replace(/\-+$/, '') // Remove trailing - ); diff --git a/extensions/markdown-language-features/src/tableOfContentsProvider.ts b/extensions/markdown-language-features/src/tableOfContentsProvider.ts index f8987416f917..c24331ef02f7 100644 --- a/extensions/markdown-language-features/src/tableOfContentsProvider.ts +++ b/extensions/markdown-language-features/src/tableOfContentsProvider.ts @@ -17,6 +17,7 @@ export interface TocEntry { export interface SkinnyTextDocument { readonly uri: vscode.Uri; + readonly version: number; readonly lineCount: number; getText(): string; lineAt(line: number): vscode.TextLine; @@ -49,7 +50,7 @@ export class TableOfContentsProvider { private async buildToc(document: SkinnyTextDocument): Promise { const toc: TocEntry[] = []; - const tokens = await this.engine.parse(document.uri, document.getText()); + const tokens = await this.engine.parse(document); const slugCount = new Map(); diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 709d4769e800..5c0e985b63a9 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -103,6 +103,33 @@ suite('markdown.DocumentLinkProvider', () => { assertRangeEqual(link1.range, new vscode.Range(0, 10, 0, 14)); assertRangeEqual(link2.range, new vscode.Range(0, 23, 0, 28)); }); + + // #49238 + test('should handle hyperlinked images', () => { + { + const links = getLinksForFile('[![alt text](image.jpg)](https://example.com)'); + assert.strictEqual(links.length, 2); + const [link1, link2] = links; + assertRangeEqual(link1.range, new vscode.Range(0,13,0,22)); + assertRangeEqual(link2.range, new vscode.Range(0,25,0,44)); + } + { + const links = getLinksForFile('[![a]( whitespace.jpg )]( https://whitespace.com )'); + assert.strictEqual(links.length, 2); + const [link1, link2] = links; + assertRangeEqual(link1.range, new vscode.Range(0,7,0,21)); + assertRangeEqual(link2.range, new vscode.Range(0,26,0,48)); + } + { + const links = getLinksForFile('[![a](img1.jpg)](file1.txt) text [![a](img2.jpg)](file2.txt)'); + assert.strictEqual(links.length, 4); + const [link1, link2, link3, link4] = links; + assertRangeEqual(link1.range, new vscode.Range(0,6,0,14)); + assertRangeEqual(link2.range, new vscode.Range(0,17,0,26)); + assertRangeEqual(link3.range, new vscode.Range(0,39,0,47)); + assertRangeEqual(link4.range, new vscode.Range(0,50,0,59)); + } + }); }); diff --git a/extensions/markdown-language-features/src/test/inMemoryDocument.ts b/extensions/markdown-language-features/src/test/inMemoryDocument.ts index 960d2d9b2b8e..e8e5b088d623 100644 --- a/extensions/markdown-language-features/src/test/inMemoryDocument.ts +++ b/extensions/markdown-language-features/src/test/inMemoryDocument.ts @@ -10,7 +10,8 @@ export class InMemoryDocument implements vscode.TextDocument { constructor( public readonly uri: vscode.Uri, - private readonly _contents: string + private readonly _contents: string, + public readonly version = 1, ) { this._lines = this._contents.split(/\n/g); } @@ -18,7 +19,6 @@ export class InMemoryDocument implements vscode.TextDocument { isUntitled: boolean = false; languageId: string = ''; - version: number = 1; isDirty: boolean = false; isClosed: boolean = false; eol: vscode.EndOfLine = vscode.EndOfLine.LF; diff --git a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts index 83102ec1175d..b1f8e531a380 100644 --- a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts @@ -52,7 +52,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { const testFileName = vscode.Uri.file('test.md'); const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([ - new InMemoryDocument(testFileName, `# header1`) + new InMemoryDocument(testFileName, `# header1`, 1 /* version */) ]); const provider = new MarkdownWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); @@ -60,7 +60,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); // Update file - workspaceFileProvider.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`)); + workspaceFileProvider.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`, 2 /* version */)); const newSymbols = await provider.provideWorkspaceSymbols(''); assert.strictEqual(newSymbols.length, 2); assert.strictEqual(newSymbols[0].name, '# new header'); diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 74030b70a593..e63c5f4da912 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== -"@types/highlight.js@9.1.10": - version "9.1.10" - resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.1.10.tgz#b621f809cd9573b80992b90cffc5788208e3069c" - integrity sha512-3uQgLVw3ukDjrgi1h2qxSgsg2W7Sp/BN/P+IBgi8D019FdCcetJzJIxk0Wp1Qfcxzy3EreUnPI7/1HXhFNCRTg== +"@types/highlight.js@9.12.3": + version "9.12.3" + resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca" + integrity sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ== "@types/lodash.throttle@^4.1.3": version "4.1.3" @@ -159,10 +159,10 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -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== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -2933,10 +2933,10 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -highlight.js@9.12.0: - version "9.12.0" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" - integrity sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4= +highlight.js@9.13.1: + version "9.13.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" + integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== hmac-drbg@^1.0.0: version "1.0.1" @@ -3895,10 +3895,15 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-it@^8.4.1: - version "8.4.1" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.1.tgz#206fe59b0e4e1b78a7c73250af9b34a4ad0aaf44" - integrity sha512-CzzqSSNkFRUf9vlWvhK1awpJreMRqdCrBvZ8DIoDWTOkESMIF741UPAhuAmbyWmdiFPA6WARNhnu2M6Nrhwa+A== +markdown-it-front-matter@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.1.2.tgz#e50bf56e77e6a4f5ac4ffa894d4d45ccd9896b20" + integrity sha1-5Qv1bnfmpPWsT/qJTU1FzNmJayA= + +markdown-it@^8.4.2: + 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" @@ -6308,12 +6313,12 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/merge-conflict/src/commandHandler.ts b/extensions/merge-conflict/src/commandHandler.ts index 8d26fad6d6af..5807223e4e11 100644 --- a/extensions/merge-conflict/src/commandHandler.ts +++ b/extensions/merge-conflict/src/commandHandler.ts @@ -237,9 +237,9 @@ export default class CommandHandler implements vscode.Disposable { return null; } - for (let i = 0; i < conflicts.length; i++) { - if (conflicts[i].range.contains(editor.selection.active)) { - return conflicts[i]; + for (const conflict of conflicts) { + if (conflict.range.contains(editor.selection.active)) { + return conflict; } } @@ -282,11 +282,11 @@ export default class CommandHandler implements vscode.Disposable { throw new Error(`Unsupported direction ${direction}`); } - for (let i = 0; i < conflicts.length; i++) { - if (predicate(conflicts[i]) && !conflicts[i].range.contains(selection)) { + for (const conflict of conflicts) { + if (predicate(conflict) && !conflict.range.contains(selection)) { return { canNavigate: true, - conflict: conflicts[i] + conflict: conflict }; } } diff --git a/extensions/merge-conflict/src/delayer.ts b/extensions/merge-conflict/src/delayer.ts index 00302b50e80d..946b30f71a98 100644 --- a/extensions/merge-conflict/src/delayer.ts +++ b/extensions/merge-conflict/src/delayer.ts @@ -35,7 +35,7 @@ export class Delayer { }).then(() => { this.completionPromise = null; this.onSuccess = null; - var result = this.task!(); + let result = this.task!(); this.task = null; return result; }); diff --git a/extensions/merge-conflict/src/documentTracker.ts b/extensions/merge-conflict/src/documentTracker.ts index c691283faee9..9f01ee73752c 100644 --- a/extensions/merge-conflict/src/documentTracker.ts +++ b/extensions/merge-conflict/src/documentTracker.ts @@ -91,8 +91,7 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, return false; } - var task = this.cache.get(key); - + const task = this.cache.get(key); if (!task) { return false; } @@ -128,7 +127,7 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, } private getCacheKey(document: vscode.TextDocument): string | null { - if (document.uri && document.uri) { + if (document.uri) { return document.uri.toString(); } diff --git a/extensions/merge-conflict/src/mergeDecorator.ts b/extensions/merge-conflict/src/mergeDecorator.ts index 75b71a993bcb..b62df990c698 100644 --- a/extensions/merge-conflict/src/mergeDecorator.ts +++ b/extensions/merge-conflict/src/mergeDecorator.ts @@ -153,10 +153,10 @@ export default class MergeDecorator implements vscode.Disposable { } private applyDecorationsFromEvent(eventDocument: vscode.TextDocument) { - for (var i = 0; i < vscode.window.visibleTextEditors.length; i++) { - if (vscode.window.visibleTextEditors[i].document === eventDocument) { + for (const editor of vscode.window.visibleTextEditors) { + if (editor.document === eventDocument) { // Attempt to apply - this.applyDecorations(vscode.window.visibleTextEditors[i]); + this.applyDecorations(editor); } } } diff --git a/extensions/notebook/src/test/common/stubs.ts b/extensions/notebook/src/test/common/stubs.ts index 6c050d3d136b..25b6d0fd53fb 100644 --- a/extensions/notebook/src/test/common/stubs.ts +++ b/extensions/notebook/src/test/common/stubs.ts @@ -14,6 +14,7 @@ export class MockExtensionContext implements vscode.ExtensionContext { return relativePath; } storagePath: string; + globalStoragePath: string; constructor() { this.subscriptions = []; diff --git a/extensions/package.json b/extensions/package.json index 05bd5d082927..0f226217b7c8 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.2.2" + "typescript": "3.3.1" }, "scripts": { "postinstall": "node ./postinstall" } -} +} \ No newline at end of file diff --git a/extensions/powershell/cgmanifest.json b/extensions/powershell/cgmanifest.json index 9c01a619c59e..e9adf03ca045 100644 --- a/extensions/powershell/cgmanifest.json +++ b/extensions/powershell/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/profiler/package.json b/extensions/profiler/package.json index 7a5a258d81cb..1b9eaf04e3a0 100644 --- a/extensions/profiler/package.json +++ b/extensions/profiler/package.json @@ -1,84 +1,89 @@ { - "name": "profiler", - "displayName": "SQL Server Profiler", - "description": "SQL Server Profiler for Azure Data Studio", - "version": "0.7.0", - "publisher": "Microsoft", - "preview": true, - "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt", - "icon": "images/sqlserver.png", - "aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412", - "engines": { - "vscode": "0.10.x" - }, - "activationEvents": [ - "*" - ], - "main": "./client/out/main", - "scripts": { - "compile": "gulp compile-extension:profiler-client" - }, - "repository": { - "type": "git", - "url": "https://github.com/Microsoft/azuredatastudio.git" - }, - "extensionDependencies": [ - "Microsoft.mssql" - ], - "contributes": { - "commands": [ - { - "command": "profiler.newProfiler", - "title": "Launch Profiler", - "category": "Profiler" - }, - { - "command": "profiler.start", - "title": "Start", - "category": "Profiler" - }, - { - "command": "profiler.stop", - "title": "Stop", - "category": "Profiler" - }, - { - "command": "profiler.openCreateSessionDialog", - "title": "Create Profiler Session", - "category": "Profiler" - } - ], - "menus": { - "commandPalette": [ - { - "command": "profiler.start", - "when": "False" - }, - { - "command": "profiler.stop", - "when": "False" - }, - { - "command": "profiler.openCreateSessionDialog", - "when": "False" - } - ], - "objectExplorer/item/context": [ - { - "command": "profiler.newProfiler", - "when": "connectionProvider == MSSQL && nodeType && nodeType == Server", - "group": "profiler" - } - ] - }, - "outputChannels": [ - "sqlprofiler" - ] - }, - "dependencies": { - "vscode-nls": "^3.2.1" - }, - "devDependencies": { - "vscode": "1.0.1" - } -} + "name": "profiler", + "displayName": "SQL Server Profiler", + "description": "SQL Server Profiler for Azure Data Studio", + "version": "0.7.0", + "publisher": "Microsoft", + "preview": true, + "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt", + "icon": "images/sqlserver.png", + "aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412", + "engines": { + "vscode": "0.10.x" + }, + "activationEvents": [ + "*" + ], + "main": "./client/out/main", + "scripts": { + "compile": "gulp compile-extension:profiler-client" + }, + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/azuredatastudio.git" + }, + "extensionDependencies": [ + "Microsoft.mssql" + ], + "contributes": { + "commands": [ + { + "command": "profiler.newProfiler", + "title": "Launch Profiler", + "category": "Profiler" + }, + { + "command": "profiler.start", + "title": "Start", + "category": "Profiler" + }, + { + "command": "profiler.stop", + "title": "Stop", + "category": "Profiler" + }, + { + "command": "profiler.openCreateSessionDialog", + "title": "Create Profiler Session", + "category": "Profiler" + } + ], + "menus": { + "commandPalette": [ + { + "command": "profiler.start", + "when": "False" + }, + { + "command": "profiler.stop", + "when": "False" + }, + { + "command": "profiler.openCreateSessionDialog", + "when": "False" + } + ], + "objectExplorer/item/context": [ + { + "command": "profiler.newProfiler", + "when": "connectionProvider == MSSQL && nodeType && nodeType == Server", + "group": "profiler" + } + ] + }, + "outputChannels": [ + "sqlprofiler" + ] + }, + "dependencies": { + "vscode-nls": "^3.2.1" + }, + "devDependencies": { + "vscode": "1.0.1" + }, + "__metadata": { + "id": "18", + "publisherDisplayName": "Microsoft", + "publisherId": "Microsoft" + } +} \ No newline at end of file diff --git a/extensions/python/cgmanifest.json b/extensions/python/cgmanifest.json index 4f5dbadabc86..66490a1ec9af 100644 --- a/extensions/python/cgmanifest.json +++ b/extensions/python/cgmanifest.json @@ -10,8 +10,8 @@ } }, "license": "MIT", - "version": "0.0.0" + "version": "1.1.1" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/python/package.json b/extensions/python/package.json index 48151bd6245b..c81942af7641 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -10,7 +10,7 @@ "contributes": { "languages": [{ "id": "python", - "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".snakefile", ".smk"], + "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".snakefile", ".smk", ".pyi"], "aliases": [ "Python", "py" ], "firstLine": "^#!\\s*/.*\\bpython[0-9.-]*\\b", "configuration": "./language-configuration.json" diff --git a/extensions/r/cgmanifest.json b/extensions/r/cgmanifest.json index f520a2df9bf0..0781a1507229 100644 --- a/extensions/r/cgmanifest.json +++ b/extensions/r/cgmanifest.json @@ -10,8 +10,8 @@ } }, "license": "MIT", - "version": "0.0.0" + "version": "0.5.5" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index a40abdb6d89e..db3a5db2b061 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "Microsoft/vscode-mssql", "repositoryUrl": "https://github.com/Microsoft/vscode-mssql", - "commitHash": "68d4b740b6a9e12592a32f1c0c8a0dd987f19da8" + "commitHash": "3aa44d04b04d219ad5fa8f411ca9dd32294a7a06" } }, "license": "MIT", - "version": "0.0.0" + "version": "1.4.0" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index 8be7dbf822fc..5706724bb179 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.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/vscode-mssql/commit/68d4b740b6a9e12592a32f1c0c8a0dd987f19da8", + "version": "https://github.com/Microsoft/vscode-mssql/commit/3aa44d04b04d219ad5fa8f411ca9dd32294a7a06", "name": "SQL", "scopeName": "source.sql", "patterns": [ @@ -223,7 +223,7 @@ "name": "support.function.mathematical.sql" }, { - "match": "(?i)\\b(app_name|applock_mode|applock_test|assemblyproperty|col_length|col_name|columnproperty|database_principal_id|databasepropertyex|db_id|db_name|file_id|file_idex|file_name|filegroup_id|filegroup_name|filegroupproperty|fileproperty|fulltextcatalogproperty|fulltextserviceproperty|index_col|indexkey_property|indexproperty|object_definition|object_id|object_name|object_schema_name|objectproperty|objectpropertyex|original_db_name|parsename|schema_id|schema_name|scope_identity|severeproperty|stats_date|type_id|type_name|typeproperty)\\b", + "match": "(?i)\\b(app_name|applock_mode|applock_test|assemblyproperty|col_length|col_name|columnproperty|database_principal_id|databasepropertyex|db_id|db_name|file_id|file_idex|file_name|filegroup_id|filegroup_name|filegroupproperty|fileproperty|fulltextcatalogproperty|fulltextserviceproperty|index_col|indexkey_property|indexproperty|object_definition|object_id|object_name|object_schema_name|objectproperty|objectpropertyex|original_db_name|parsename|schema_id|schema_name|scope_identity|serverproperty|stats_date|type_id|type_name|typeproperty)\\b", "name": "support.function.metadata.sql" }, { diff --git a/extensions/theme-abyss/cgmanifest.json b/extensions/theme-abyss/cgmanifest.json index f3bd1db12240..e0aaef639b40 100644 --- a/extensions/theme-abyss/cgmanifest.json +++ b/extensions/theme-abyss/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "description": "The themes in this folders are copied from colorsublime.com. <<>>", diff --git a/extensions/theme-kimbie-dark/cgmanifest.json b/extensions/theme-kimbie-dark/cgmanifest.json index 6c169cecb07f..4968a0609a52 100644 --- a/extensions/theme-kimbie-dark/cgmanifest.json +++ b/extensions/theme-kimbie-dark/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-monokai-dimmed/cgmanifest.json b/extensions/theme-monokai-dimmed/cgmanifest.json index 6c169cecb07f..4968a0609a52 100644 --- a/extensions/theme-monokai-dimmed/cgmanifest.json +++ b/extensions/theme-monokai-dimmed/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-monokai/cgmanifest.json b/extensions/theme-monokai/cgmanifest.json index 6c169cecb07f..4968a0609a52 100644 --- a/extensions/theme-monokai/cgmanifest.json +++ b/extensions/theme-monokai/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-quietlight/cgmanifest.json b/extensions/theme-quietlight/cgmanifest.json index 6c169cecb07f..4968a0609a52 100644 --- a/extensions/theme-quietlight/cgmanifest.json +++ b/extensions/theme-quietlight/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-red/cgmanifest.json b/extensions/theme-red/cgmanifest.json index 6c169cecb07f..4968a0609a52 100644 --- a/extensions/theme-red/cgmanifest.json +++ b/extensions/theme-red/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index ad52f6bae660..4249da110744 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -35,16 +35,32 @@ let nonBuiltInLanguages = { // { fileNames, extensions } "todo": { fileNames: ['todo'] } }; -function getCommitSha(repoId, repoPath) { - let commitInfo = 'https://api.github.com/repos/' + repoId + '/commits?path=' + repoPath; +let FROM_DISK = false; // set to true to take content from a repo checkedout next to the vscode repo + +let font, fontMappingsFile, fileAssociationFile, colorsFile; +if (!FROM_DISK) { + font = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti/seti.woff'; + fontMappingsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti.less'; + fileAssociationFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/components/icons/mapping.less'; + colorsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/ui-variables.less'; +} else { + font = '../../../seti-ui/styles/_fonts/seti/seti.woff'; + fontMappingsFile = '../../../seti-ui/styles/_fonts/seti.less'; + fileAssociationFile = '../../../seti-ui/styles/components/icons/mapping.less'; + colorsFile = '../../../seti-ui/styles/ui-variables.less'; +} + +function getCommitSha(repoId) { + let commitInfo = 'https://api.github.com/repos/' + repoId + '/commits/master'; return download(commitInfo).then(function (content) { try { - let lastCommit = JSON.parse(content)[0]; + let lastCommit = JSON.parse(content); return Promise.resolve({ commitSha: lastCommit.sha, commitDate: lastCommit.commit.author.date }); } catch (e) { + console.error('Failed parsing ' + content); return Promise.resolve(null); } }, function () { @@ -189,21 +205,10 @@ function getLanguageMappings() { return langMappings; } -//let font = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti/seti.woff'; -let font = '../../../seti-ui/styles/_fonts/seti/seti.woff'; - exports.copyFont = function () { return downloadBinary(font, './icons/seti.woff'); }; -//let fontMappings = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti.less'; -//let mappings = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/components/icons/mapping.less'; -//let colors = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/ui-variables.less'; - -let fontMappingsFile = '../../../seti-ui/styles/_fonts/seti.less'; -let fileAssociationFile = '../../../seti-ui/styles/components/icons/mapping.less'; -let colorsFile = '../../../seti-ui/styles/ui-variables.less'; - exports.update = function () { console.log('Reading from ' + fontMappingsFile); @@ -358,12 +363,18 @@ exports.update = function () { while ((match = regex3.exec(content)) !== null) { colorId2Value[match[1]] = match[2]; } - return getCommitSha('jesseweed/seti-ui', 'styles/_fonts/seti.less').then(function (info) { + return getCommitSha('jesseweed/seti-ui').then(function (info) { try { writeFileIconContent(info); - if (info) { - console.log('Updated to jesseweed/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); - } + + let cgmanifestPath = './cgmanifest.json'; + let cgmanifest = fs.readFileSync(cgmanifestPath).toString(); + let cgmanifestContent = JSON.parse(cgmanifest); + cgmanifestContent['registrations'][0]['component']['git']['commitHash'] = info.commitSha; + fs.writeFileSync(cgmanifestPath, JSON.stringify(cgmanifestContent, null, '\t')); + console.log('updated ' + cgmanifestPath); + + console.log('Updated to jesseweed/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); } catch (e) { console.error(e); diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index 98fcf55e0e78..2b449830f504 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "0b576faae405d3cd8df6ac1a397f287aa6d8b3fe" + "commitHash": "89175d7f9e0c70cd325b80a18a3c77fc8eb7c798" } }, "version": "0.1.0" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index 2152db7f651c..06fe72e59f26 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -1839,5 +1839,5 @@ "profiler": "_csv_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/7714a720646300bb8f6d1690752cd71f50991414" + "version": "https://github.com/jesseweed/seti-ui/commit/89175d7f9e0c70cd325b80a18a3c77fc8eb7c798" } \ No newline at end of file diff --git a/extensions/theme-solarized-dark/cgmanifest.json b/extensions/theme-solarized-dark/cgmanifest.json index 6c169cecb07f..4968a0609a52 100644 --- a/extensions/theme-solarized-dark/cgmanifest.json +++ b/extensions/theme-solarized-dark/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-solarized-light/cgmanifest.json b/extensions/theme-solarized-light/cgmanifest.json index 6c169cecb07f..4968a0609a52 100644 --- a/extensions/theme-solarized-light/cgmanifest.json +++ b/extensions/theme-solarized-light/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-tomorrow-night-blue/cgmanifest.json b/extensions/theme-tomorrow-night-blue/cgmanifest.json index 6c169cecb07f..4968a0609a52 100644 --- a/extensions/theme-tomorrow-night-blue/cgmanifest.json +++ b/extensions/theme-tomorrow-night-blue/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/xml/cgmanifest.json b/extensions/xml/cgmanifest.json index b209abe6e8c4..a291bba7156d 100644 --- a/extensions/xml/cgmanifest.json +++ b/extensions/xml/cgmanifest.json @@ -11,8 +11,8 @@ }, "license": "MIT", "description": "The files syntaxes/xml.json and syntaxes/xsl.json were derived from the Atom package https://github.com/atom/language-xml which were originally converted from the TextMate bundle https://github.com/textmate/xml.tmbundle.", - "version": "0.0.0" + "version": "0.35.2" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/xml/package.json b/extensions/xml/package.json index f04aed5a2d37..985266edf908 100644 --- a/extensions/xml/package.json +++ b/extensions/xml/package.json @@ -15,7 +15,6 @@ ".atom", ".axml", ".bpmn", - ".config", ".cpt", ".csl", ".csproj", @@ -36,7 +35,7 @@ ".opml", ".owl", ".proj", - ".props", + ".props", ".pt", ".publishsettings", ".pubxml", diff --git a/extensions/yarn.lock b/extensions/yarn.lock index b5e82fef80f8..faf73e879037 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5" - integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg== +typescript@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" + integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== diff --git a/package.json b/package.json index bfbf59f3dc4c..8ce303dcb1e4 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "angular2-grid": "2.0.6", "angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.4.6", "ansi_up": "^3.0.0", - "applicationinsights": "1.0.6", + "applicationinsights": "1.0.8", "chart.js": "^2.6.0", "fast-plist": "0.1.2", "find-remove": "1.2.1", @@ -64,8 +64,8 @@ "native-keymap": "1.2.5", "native-watchdog": "1.0.0", "ng2-charts": "^1.6.0", - "node-pty": "0.7.8", - "nsfw": "1.0.16", + "node-pty": "0.8.1", + "nsfw": "1.1.0", "pretty-data": "^0.40.0", "reflect-metadata": "^0.1.8", "rxjs": "5.4.0", @@ -75,15 +75,15 @@ "spdlog": "0.7.2", "sudo-prompt": "8.2.0", "svg.js": "^2.2.5", - "v8-inspect-profiler": "^0.0.13", + "v8-inspect-profiler": "^0.0.20", "vscode-chokidar": "1.6.5", "vscode-debugprotocol": "1.33.0", "vscode-nsfw": "1.1.1", - "vscode-proxy-agent": "0.1.1", + "vscode-proxy-agent": "0.3.0", "vscode-ripgrep": "^1.2.5", - "vscode-sqlite3": "4.0.5", + "vscode-sqlite3": "4.0.7", "vscode-textmate": "^4.0.1", - "vscode-xterm": "3.9.0-beta13", + "vscode-xterm": "3.11.0-beta4", "winreg": "^1.2.4", "yauzl": "^2.9.1", "yazl": "^2.4.3", @@ -94,7 +94,7 @@ "@types/keytar": "^4.0.1", "@types/minimist": "^1.2.0", "@types/mocha": "2.2.39", - "@types/node": "^8.9.1", + "@types/node": "^10.12.12", "@types/sanitize-html": "^1.18.2", "@types/semver": "^5.5.0", "@types/should": "^13.0.0", @@ -165,7 +165,7 @@ "tslint": "^5.11.0", "tslint-microsoft-contrib": "^6.0.0", "typemoq": "^0.3.2", - "typescript": "3.1.4", + "typescript": "3.2.2", "typescript-formatter": "7.1.0", "typescript-tslint-plugin": "^0.0.7", "uglify-es": "^3.0.18", @@ -193,6 +193,7 @@ "resolutions": { "rc": "1.2.8", "event-stream": "3.3.4", - "@types/node": "8.10.34" + "natives": "1.1.6", + "@types/node": "10.12.12" } } diff --git a/product.json b/product.json index dc7135fa5a42..06e0b6e350cb 100644 --- a/product.json +++ b/product.json @@ -32,7 +32,7 @@ "gettingStartedUrl": "https://go.microsoft.com/fwlink/?linkid=862039", "releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=875578", "documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277", - "vscodeVersion": "1.30.1", + "vscodeVersion": "1.31.1", "commit": "9ca6200018fc206d67a47229f991901a8a453781", "date": "2017-12-15T12:00:00.000Z", "recommendedExtensions": [ diff --git a/resources/completions/bash/code b/resources/completions/bash/code new file mode 100644 index 000000000000..e377c5d24e23 --- /dev/null +++ b/resources/completions/bash/code @@ -0,0 +1,61 @@ +_code() +{ + local cur prev words cword split + _init_completion -s || return + + _expand || return + + case $prev in + -d|--diff) + _filedir + return + ;; + -a|--add|--user-data-dir|--extensions-dir) + _filedir -d + return + ;; + -g|--goto) + compopt -o nospace + _filedir + return + ;; + --locale) + COMPREPLY=( $( compgen -W 'de en en-US es fr it ja ko ru zh-CN zh-TW bg hu pt-br tr' ) ) + return + ;; + --install-extension|--uninstall-extension) + _filedir vsix + return + ;; + --log) + COMPREPLY=( $( compgen -W 'critical error warn info debug trace off' ) ) + return + ;; + --folder-uri|--disable-extension|--max-memory) + # argument required but no completions available + return 0 + ;; + --enable-proposed-api) + # argument optional but no completions available + ;; + esac + + $split && return + + if [[ $cur == -* ]]; then + COMPREPLY=( $( compgen -W '-d --diff --folder-uri -a --add -g + --goto -n --new-window -r --reuse-window -w --wait --locale= + --user-data-dir -v --version -h --help --extensions-dir + --list-extensions --show-versions --install-extension + --uninstall-extension --enable-proposed-api --verbose --log -s + --status -p --performance --prof-startup --disable-extensions + --disable-extension --inspect-extensions + --inspect-brk-extensions --disable-gpu --upload-logs + --max-memory=' -- "$cur") ) + [[ $COMPREPLY == *= ]] && compopt -o nospace + return + fi + + _filedir +} && +complete -F _code code diff --git a/resources/completions/zsh/_code b/resources/completions/zsh/_code new file mode 100644 index 000000000000..9579cffb2f64 --- /dev/null +++ b/resources/completions/zsh/_code @@ -0,0 +1,38 @@ +#compdef code + +local arguments + +arguments=( + '(-d --diff)'{-d,--diff}'[compare two files with each other]:file to compare:_files:file to compare with:_files' + \*'--folder-uri[open a window with given folder uri(s)]:folder uri: ' + \*{-a,--add}'[add folder(s) to the last active window]:directory:_directories' + '(-g --goto)'{-g,--goto}'[open a file at the path on the specified line and column position]:file\:line[\:column]:_files -r \:' + '(-n --new-window -r --reuse-window)'{-n,--new-window}'[force to open a new window]' + '(-n --new-window -r --reuse-window)'{-r,--reuse-window}'[force to open a file or folder in an already opened window]' + '(-w --wait)'{-w,--wait}'[wait for the files to be closed before returning]' + '--locale=[the locale to use (e.g. en-US or zh-TW)]:locale (e.g. en-US or zh-TW):(de en en-US es fr it ja ko ru zh-CN zh-TW bg hu pt-br tr)' + '--user-data-dir[specify the directory that user data is kept in]:directory:_directories' + '(- *)'{-v,--version}'[print version]' + '(- *)'{-h,--help}'[print usage]' + '--extensions-dir[set the root path for extensions]:root path:_directories' + '--list-extensions[list the installed extensions]' + '--show-versions[show versions of installed extensions, when using --list-extension]' + '--install-extension[install an extension]:id or path:_files -g "*.vsix(-.)"' + '--uninstall-extension[uninstall an extension]:id or path:_files -g "*.vsix(-.)"' + '--enable-proposed-api[enables proposed API features for extensions]::extension id: ' + '--verbose[print verbose output (implies --wait)]' + '--log[log level to use]:level [info]:(critical error warn info debug trace off)' + '(-s --status)'{-s,--status}'[print process usage and diagnostics information]' + '(-p --performance)'{-p,--performance}'[start with the "Developer: Startup Performance" command enabled]' + '--prof-startup[run CPU profiler during startup]' + '(--disable-extension --disable-extensions)--disable-extensions[disable all installed extensions]' + \*'--disable-extension[disable an extension]:extension id: ' + '--inspect-extensions[allow debugging and profiling of extensions]' + '--inspect-brk-extensions[allow debugging and profiling of extensions with the extension host being paused after start]' + '--disable-gpu[disable GPU hardware acceleration]' + '--upload-logs[upload logs from current session to a secure endpoint]:confirm:(iConfirmLogsUpload)' + '--max-memory=[max memory size for a window (in Mbytes)]:size (Mbytes)' + '*:file or directory:_files' +) + +_arguments -s -S $arguments diff --git a/resources/linux/code.desktop b/resources/linux/code.desktop index 929171b0c639..8df4caec628c 100644 --- a/resources/linux/code.desktop +++ b/resources/linux/code.desktop @@ -5,7 +5,7 @@ GenericName=Text Editor Exec=/usr/share/@@NAME@@/@@NAME@@ --unity-launch %F Icon=@@ICON@@ Type=Application -StartupNotify=true +StartupNotify=false StartupWMClass=@@NAME_SHORT@@ Categories=Utility;TextEditor;Development;IDE; MimeType=text/plain;inode/directory; diff --git a/resources/linux/debian/control.template b/resources/linux/debian/control.template index 7d39cbbb6d3d..7e03df417079 100644 --- a/resources/linux/debian/control.template +++ b/resources/linux/debian/control.template @@ -1,7 +1,7 @@ Package: @@NAME@@ Version: @@VERSION@@ Section: devel -Depends: libnotify4, libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libgconf-2-4, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libunwind8 +Depends: libnotify4, libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libxss1 Priority: optional Architecture: @@ARCHITECTURE@@ Maintainer: Microsoft Corporation diff --git a/resources/linux/rpm/code.spec.template b/resources/linux/rpm/code.spec.template index cf594670da75..0e1e582b235c 100644 --- a/resources/linux/rpm/code.spec.template +++ b/resources/linux/rpm/code.spec.template @@ -18,9 +18,13 @@ Visual Studio Code is a new choice of tool that combines the simplicity of a cod mkdir -p %{buildroot}/usr/share/@@NAME@@ mkdir -p %{buildroot}/usr/share/applications mkdir -p %{buildroot}/usr/share/pixmaps +#mkdir -p %{buildroot}/usr/share/bash-completion/completions +#mkdir -p %{buildroot}/usr/share/zsh/site-functions cp -r usr/share/@@NAME@@/* %{buildroot}/usr/share/@@NAME@@ cp -r usr/share/applications/@@NAME@@.desktop %{buildroot}/usr/share/applications cp -r usr/share/pixmaps/@@NAME@@.png %{buildroot}/usr/share/pixmaps +#cp usr/share/bash-completion/completions/code %{buildroot}/usr/share/bash-completion/completions/code +#cp usr/share/zsh/site-functions/_code %{buildroot}/usr/share/zsh/site-functions/_code %post # Remove the legacy bin command if this is the stable build @@ -52,3 +56,5 @@ fi /usr/share/@@NAME@@/ /usr/share/applications/@@NAME@@.desktop /usr/share/pixmaps/@@NAME@@.png +#/usr/share/bash-completion/completions/code +#/usr/share/zsh/site-functions/_code diff --git a/resources/linux/rpm/dependencies.json b/resources/linux/rpm/dependencies.json index c2ae8b8fe313..d0b64c4fa684 100644 --- a/resources/linux/rpm/dependencies.json +++ b/resources/linux/rpm/dependencies.json @@ -27,7 +27,6 @@ "libX11.so.6()(64bit)", "libXss.so.1()(64bit)", "libXtst.so.6()(64bit)", - "libgconf-2.so.4()(64bit)", "libgmodule-2.0.so.0()(64bit)", "librt.so.1()(64bit)", "libglib-2.0.so.0()(64bit)", @@ -107,7 +106,6 @@ "libgcc_s.so.1", "libgcc_s.so.1(GCC_4.0.0)", "libgcc_s.so.1(GLIBC_2.0)", - "libgconf-2.so.4", "libgdk-x11-2.0.so.0", "libgdk_pixbuf-2.0.so.0", "libgio-2.0.so.0", diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index e8a5d48fdf11..6c1c9b8eacab 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -15,7 +15,7 @@ parts: source: . stage-packages: - libasound2 - - libgconf2-4 + - libc++1 - libnotify4 - libnspr4 - libnss3 diff --git a/scripts/code.bat b/scripts/code.bat index 6789e54fdda4..8c058365dca7 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -36,9 +36,6 @@ set ELECTRON_ENABLE_STACK_DUMPING=1 :: Launch Code -:: Use the following to get v8 tracing: -:: %CODE% --js-flags="--trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces" . %* - %CODE% . %* goto end diff --git a/scripts/code.sh b/scripts/code.sh index 26332faea6cc..8035059f65cf 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -50,7 +50,4 @@ function code() { exec "$CODE" . "$@" } -# Use the following to get v8 tracing: -# code --js-flags="--trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces" "$@" - code "$@" diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index b6e649bc3436..4bf48cb0c47f 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -51,9 +51,8 @@ exports.load = function (modulePaths, resultCallback, options) { // Enable ASAR support bootstrap.enableASARSupport(path.join(configuration.appRoot, 'node_modules')); - // disable pinch zoom & apply zoom level early to avoid glitches + // Apply zoom level early to avoid glitches const zoomLevel = configuration.zoomLevel; - webFrame.setVisualZoomLevelLimits(1, 1); if (typeof zoomLevel === 'number' && zoomLevel !== 0) { webFrame.setZoomLevel(zoomLevel); } @@ -233,7 +232,7 @@ function registerDeveloperKeybindings() { return function () { if (listener) { window.removeEventListener('keydown', listener); - listener = void 0; + listener = undefined; } }; } diff --git a/src/bootstrap.js b/src/bootstrap.js index f279f8b62fe3..ff6752de505b 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -69,7 +69,15 @@ exports.uriFromPath = function (_path) { pathName = '/' + pathName; } - return encodeURI('file://' + pathName).replace(/#/g, '%23'); + /** @type {string} */ + let uri; + if (process.platform === 'win32' && pathName.startsWith('//')) { // specially handle Windows UNC paths + uri = encodeURI('file:' + pathName); + } else { + uri = encodeURI('file://' + pathName); + } + + return uri.replace(/#/g, '%23'); }; //#endregion diff --git a/src/main.js b/src/main.js index f6e53e54dbd4..e73736670b62 100644 --- a/src/main.js +++ b/src/main.js @@ -28,20 +28,18 @@ bootstrap.enableASARSupport(); const args = parseCLIArgs(); const userDataPath = getUserDataPath(args); -// TODO@Ben global storage migration needs to happen very early before app.on("ready") -// We copy the DB instead of moving it to ensure we are not running into locking issues -if (process.env['VSCODE_TEST_STORAGE_MIGRATION']) { - try { - const globalStorageHome = path.join(userDataPath, 'User', 'globalStorage', 'temp.vscdb'); - const localStorageHome = path.join(userDataPath, 'Local Storage'); - const localStorageDB = path.join(localStorageHome, 'file__0.localstorage'); - const localStorageDBBackup = path.join(localStorageHome, 'file__0.localstorage.vscmig'); - if (!fs.existsSync(globalStorageHome) && fs.existsSync(localStorageDB)) { - fs.copyFileSync(localStorageDB, localStorageDBBackup); - } - } catch (error) { - console.error(error); +// global storage migration needs to happen very early before app.on("ready") +// TODO@Ben remove after a while +try { + const globalStorageHome = path.join(userDataPath, 'User', 'globalStorage', 'state.vscdb'); + const localStorageHome = path.join(userDataPath, 'Local Storage'); + const localStorageDB = path.join(localStorageHome, 'file__0.localstorage'); + const localStorageDBBackup = path.join(localStorageHome, 'file__0.vscmig'); + if (!fs.existsSync(globalStorageHome) && fs.existsSync(localStorageDB)) { + fs.renameSync(localStorageDB, localStorageDBBackup); } +} catch (error) { + console.error(error); } app.setPath('userData', userDataPath); @@ -108,7 +106,10 @@ function onReady() { process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir || ''; // Load main in AMD - require('./bootstrap-amd').load('vs/code/electron-main/main'); + perf.mark('willLoadMainBundle'); + require('./bootstrap-amd').load('vs/code/electron-main/main', () => { + perf.mark('didLoadMainBundle'); + }); }; // We recevied a valid nlsConfig from a user defined locale @@ -149,9 +150,6 @@ function onReady() { */ function configureCommandlineSwitches(cliArgs, nodeCachedDataDir) { - // TODO@Ben Electron 2.0.x: prevent localStorage migration from SQLite to LevelDB due to issues - app.commandLine.appendSwitch('disable-mojo-local-storage'); - // Force pre-Chrome-60 color profile handling (for https://github.com/Microsoft/vscode/issues/51791) app.commandLine.appendSwitch('disable-features', 'ColorCorrectRendering'); @@ -168,10 +166,13 @@ function configureCommandlineSwitches(cliArgs, nodeCachedDataDir) { * @returns {string} */ function resolveJSFlags(cliArgs, ...jsFlags) { + + // Add any existing JS flags we already got from the command line if (cliArgs['js-flags']) { jsFlags.push(cliArgs['js-flags']); } + // Support max-memory flag if (cliArgs['max-memory'] && !/max_old_space_size=(\d+)/g.exec(cliArgs['js-flags'])) { jsFlags.push(`--max_old_space_size=${cliArgs['max-memory']}`); } @@ -269,7 +270,8 @@ function getNodeCachedDir() { } jsFlags() { - return this.value ? '--nolazy' : undefined; + // return this.value ? '--nolazy' : undefined; + return undefined; } ensureExists() { @@ -415,7 +417,7 @@ function rimraf(location) { } }, err => { if (err.code === 'ENOENT') { - return void 0; + return undefined; } throw err; }); @@ -435,20 +437,16 @@ function getUserDefinedLocale() { } const localeConfig = path.join(userDataPath, 'User', 'locale.json'); - return exists(localeConfig).then((result) => { - if (result) { - return bootstrap.readFile(localeConfig).then((content) => { - content = stripComments(content); - try { - const value = JSON.parse(content).locale; - return value && typeof value === 'string' ? value.toLowerCase() : undefined; - } catch (e) { - return undefined; - } - }); - } else { + return bootstrap.readFile(localeConfig).then((content) => { + content = stripComments(content); + try { + const value = JSON.parse(content).locale; + return value && typeof value === 'string' ? value.toLowerCase() : undefined; + } catch (e) { return undefined; } + }, () => { + return undefined; }); } diff --git a/src/sql/base/browser/ui/editableDropdown/actions.ts b/src/sql/base/browser/ui/editableDropdown/actions.ts index 212f5fd0f089..698e9f30aab8 100644 --- a/src/sql/base/browser/ui/editableDropdown/actions.ts +++ b/src/sql/base/browser/ui/editableDropdown/actions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; export class ToggleDropdownAction extends Action { private static readonly ID = 'dropdownAction.toggle'; @@ -14,8 +13,8 @@ export class ToggleDropdownAction extends Action { super(ToggleDropdownAction.ID, label, ToggleDropdownAction.ICON); } - public run(): TPromise { + public run(): Promise { this._fn(); - return TPromise.as(true); + return Promise.resolve(true); } } diff --git a/src/sql/base/browser/ui/editableDropdown/dropdownTree.ts b/src/sql/base/browser/ui/editableDropdown/dropdownTree.ts index 18439759765b..34aae529bb32 100644 --- a/src/sql/base/browser/ui/editableDropdown/dropdownTree.ts +++ b/src/sql/base/browser/ui/editableDropdown/dropdownTree.ts @@ -5,7 +5,6 @@ import * as tree from 'vs/base/parts/tree/browser/tree'; import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults'; -import { Promise, TPromise } from 'vs/base/common/winjs.base'; import { generateUuid } from 'vs/base/common/uuid'; import * as DOM from 'vs/base/browser/dom'; import { Event, Emitter } from 'vs/base/common/event'; @@ -74,17 +73,17 @@ export class DropdownDataSource implements tree.IDataSource { public getChildren(tree: tree.ITree, element: Resource | DropdownModel): Promise { if (element instanceof DropdownModel) { - return TPromise.as(this.options); + return Promise.resolve(this.options); } else { - return TPromise.as(undefined); + return Promise.resolve(undefined); } } public getParent(tree: tree.ITree, element: Resource | DropdownModel): Promise { if (element instanceof DropdownModel) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } else { - return TPromise.as(new DropdownModel()); + return Promise.resolve(new DropdownModel()); } } } diff --git a/src/sql/base/browser/ui/listBox/listBox.ts b/src/sql/base/browser/ui/listBox/listBox.ts index 72fad0fd1eae..3527364f3fdd 100644 --- a/src/sql/base/browser/ui/listBox/listBox.ts +++ b/src/sql/base/browser/ui/listBox/listBox.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { SelectBox, ISelectBoxStyles } from 'vs/base/browser/ui/selectBox/selectBox'; +import { SelectBox, ISelectBoxStyles, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { Color } from 'vs/base/common/color'; import { IMessage, MessageType, defaultOpts } from 'vs/base/browser/ui/inputbox/inputBox'; import * as dom from 'vs/base/browser/dom'; @@ -51,7 +51,7 @@ export class ListBox extends SelectBox { private isValid: boolean; constructor( - options: string[], + options: ISelectOptionItem[], contextViewProvider: IContextViewProvider, private _clipboardService: IClipboardService) { diff --git a/src/sql/base/browser/ui/panel/tabActions.ts b/src/sql/base/browser/ui/panel/tabActions.ts index 6b461290fa14..3a595d24929b 100644 --- a/src/sql/base/browser/ui/panel/tabActions.ts +++ b/src/sql/base/browser/ui/panel/tabActions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; export class CloseTabAction extends Action { private static readonly ID = 'closeTab'; @@ -18,12 +17,12 @@ export class CloseTabAction extends Action { super(CloseTabAction.ID, CloseTabAction.LABEL, CloseTabAction.ICON); } - run(): TPromise { + run(): Promise { try { this.closeFn.apply(this.context); - return TPromise.as(true); + return Promise.resolve(true); } catch (e) { - return TPromise.as(false); + return Promise.resolve(false); } } } \ No newline at end of file diff --git a/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts b/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts index 8ed6bedc9c2d..0da49a58f3be 100644 --- a/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts +++ b/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts @@ -9,7 +9,7 @@ import 'vs/css!./scrollableSplitview'; import { HeightMap, IView as HeightIView, IViewItem as HeightIViewItem } from './heightMap'; import { IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { mapEvent, Emitter, Event, debounceEvent } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import * as types from 'vs/base/common/types'; import * as dom from 'vs/base/browser/dom'; import { clamp } from 'vs/base/common/numbers'; @@ -186,7 +186,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { this.el = document.createElement('div'); this.scrollable = new ScrollableElement(this.el, { vertical: options.verticalScrollbarVisibility }); - debounceEvent(this.scrollable.onScroll, (l, e) => e, types.isNumber(this.options.scrollDebounce) ? this.options.scrollDebounce : 25)(e => { + Event.debounce(this.scrollable.onScroll, (l, e) => e, types.isNumber(this.options.scrollDebounce) ? this.options.scrollDebounce : 25)(e => { this.render(e.scrollTop, e.height); this._onScroll.fire(e.scrollTop); }); @@ -284,11 +284,11 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { ? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey } as ISashEvent) : (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX, alt: e.altKey } as ISashEvent); - const onStart = mapEvent(sash.onDidStart, sashEventMapper); + const onStart = Event.map(sash.onDidStart, sashEventMapper); const onStartDisposable = onStart(this.onSashStart, this); - const onChange = mapEvent(sash.onDidChange, sashEventMapper); + const onChange = Event.map(sash.onDidChange, sashEventMapper); const onChangeDisposable = onChange(this.onSashChange, this); - const onEnd = mapEvent(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); + const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); const onEndDisposable = onEnd(this.onSashEnd, this); const onDidResetDisposable = sash.onDidReset(() => this._onDidSashReset.fire(firstIndex(this.sashItems, item => item.sash === sash))); @@ -379,11 +379,11 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { ? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey } as ISashEvent) : (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX, alt: e.altKey } as ISashEvent); - const onStart = mapEvent(sash.onDidStart, sashEventMapper); + const onStart = Event.map(sash.onDidStart, sashEventMapper); const onStartDisposable = onStart(this.onSashStart, this); - const onChange = mapEvent(sash.onDidChange, sashEventMapper); + const onChange = Event.map(sash.onDidChange, sashEventMapper); const onChangeDisposable = onChange(this.onSashChange, this); - const onEnd = mapEvent(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); + const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); const onEndDisposable = onEnd(this.onSashEnd, this); const onDidResetDisposable = sash.onDidReset(() => this._onDidSashReset.fire(firstIndex(this.sashItems, item => item.sash === sash))); diff --git a/src/sql/base/browser/ui/selectBox/selectBox.ts b/src/sql/base/browser/ui/selectBox/selectBox.ts index df6dbab90de8..288e138f5bf6 100644 --- a/src/sql/base/browser/ui/selectBox/selectBox.ts +++ b/src/sql/base/browser/ui/selectBox/selectBox.ts @@ -6,7 +6,7 @@ 'use strict'; import 'vs/css!./media/selectBox'; -import { SelectBox as vsSelectBox, ISelectBoxStyles as vsISelectBoxStyles, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox'; +import { SelectBox as vsSelectBox, ISelectBoxStyles as vsISelectBoxStyles, ISelectBoxOptions, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { Color } from 'vs/base/common/color'; import { IContextViewProvider, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import * as dom from 'vs/base/browser/dom'; @@ -50,7 +50,7 @@ export class SelectBox extends vsSelectBox { private element: HTMLElement; constructor(options: string[], selectedOption: string, contextViewProvider: IContextViewProvider, container?: HTMLElement, selectBoxOptions?: ISelectBoxOptions) { - super(options, 0, contextViewProvider, undefined, selectBoxOptions); + super(options.map(option => { return { text: option }; }), 0, contextViewProvider, undefined, selectBoxOptions); this._optionsDictionary = new Array(); for (var i = 0; i < options.length; i++) { this._optionsDictionary[options[i]] = i; @@ -114,13 +114,19 @@ export class SelectBox extends vsSelectBox { } } - public setOptions(options: string[], selected?: number, disabled?: number): void { + public setOptions(options: string[] | ISelectOptionItem[], selected?: number): void { + let stringOptions: string[]; + if (options.length > 0 && typeof options[0] !== 'string') { + stringOptions = (options as ISelectOptionItem[]).map(option => option.text); + } else { + stringOptions = options as string[]; + } this._optionsDictionary = []; - for (var i = 0; i < options.length; i++) { - this._optionsDictionary[options[i]] = i; + for (var i = 0; i < stringOptions.length; i++) { + this._optionsDictionary[stringOptions[i]] = i; } - this._dialogOptions = options; - super.setOptions(options, selected, disabled); + this._dialogOptions = stringOptions; + super.setOptions(stringOptions.map(option => { return { text: option }; }), selected); } public get value(): string { diff --git a/src/sql/base/browser/ui/table/tableDataView.ts b/src/sql/base/browser/ui/table/tableDataView.ts index 56ce33abd559..cf5bd2d631a0 100644 --- a/src/sql/base/browser/ui/table/tableDataView.ts +++ b/src/sql/base/browser/ui/table/tableDataView.ts @@ -6,7 +6,6 @@ import { Observable } from 'rxjs/Observable'; import { Observer } from 'rxjs/Observer'; import { Event, Emitter } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as types from 'vs/base/common/types'; import { compare as stringCompare } from 'vs/base/common/strings'; @@ -153,7 +152,7 @@ export class TableDataView implements IDisposableData find(exp: string, maxMatches: number = 0): Thenable { if (!this._findFn) { - return TPromise.wrapError(new Error('no find function provided')); + return Promise.reject(new Error('no find function provided')); } this._findArray = new Array(); this._findIndex = 0; @@ -180,7 +179,7 @@ export class TableDataView implements IDisposableData return this._findArray[this._findIndex]; }); } else { - return TPromise.wrapError(new Error('no expression')); + return Promise.reject(new Error('no expression')); } } @@ -198,9 +197,9 @@ export class TableDataView implements IDisposableData } else { ++this._findIndex; } - return TPromise.as(this._findArray[this._findIndex]); + return Promise.resolve(this._findArray[this._findIndex]); } else { - return TPromise.wrapError(new Error('no search running')); + return Promise.reject(new Error('no search running')); } } @@ -211,17 +210,17 @@ export class TableDataView implements IDisposableData } else { --this._findIndex; } - return TPromise.as(this._findArray[this._findIndex]); + return Promise.resolve(this._findArray[this._findIndex]); } else { - return TPromise.wrapError(new Error('no search running')); + return Promise.reject(new Error('no search running')); } } get currentFindPosition(): Thenable { if (this._findArray && this._findArray.length !== 0) { - return TPromise.as(this._findArray[this._findIndex]); + return Promise.resolve(this._findArray[this._findIndex]); } else { - return TPromise.wrapError(new Error('no search running')); + return Promise.reject(new Error('no search running')); } } diff --git a/src/sql/base/browser/ui/taskbar/actionbar.ts b/src/sql/base/browser/ui/taskbar/actionbar.ts index a3466b3f2a40..546869b59b04 100644 --- a/src/sql/base/browser/ui/taskbar/actionbar.ts +++ b/src/sql/base/browser/ui/taskbar/actionbar.ts @@ -7,7 +7,6 @@ import 'vs/css!vs/base/browser/ui/actionbar/actionbar'; -import { Promise } from 'vs/base/common/winjs.base'; import { Builder, $ } from 'sql/base/browser/builder'; import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions'; import { EventEmitter } from 'sql/base/common/eventEmitter'; @@ -363,7 +362,7 @@ export class ActionBar extends ActionRunner implements IActionRunner { //this.emit('cancel'); } - public run(action: IAction, context?: any): Promise { + public run(action: IAction, context?: any): Promise { return this._actionRunner.run(action, context); } diff --git a/src/sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem.ts b/src/sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem.ts index 5da8878341dc..40eba3a042ea 100644 --- a/src/sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem.ts +++ b/src/sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem.ts @@ -10,7 +10,6 @@ import { Action, IAction } from 'vs/base/common/actions'; import { IDisposable } from 'vs/base/common/lifecycle'; import { $, append } from 'vs/base/browser/dom'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { localize } from 'vs/nls'; import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; @@ -68,7 +67,7 @@ export class ManageLinkedAccountAction extends Action { super(id, label); } - public run(): TPromise { - return new TPromise(() => this._accountManagementService.openAccountListDialog()); + public run(): Promise { + return new Promise(() => this._accountManagementService.openAccountListDialog()); } } diff --git a/src/sql/parts/accountManagement/common/accountActions.ts b/src/sql/parts/accountManagement/common/accountActions.ts index f1cbc4316793..ae374db36115 100644 --- a/src/sql/parts/accountManagement/common/accountActions.ts +++ b/src/sql/parts/accountManagement/common/accountActions.ts @@ -8,7 +8,6 @@ import * as azdata from 'azdata'; import { Event, Emitter } from 'vs/base/common/event'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import { error } from 'sql/base/common/log'; @@ -47,13 +46,13 @@ export class AddAccountAction extends Action { this._addAccountStartEmitter = new Emitter(); } - public run(): TPromise { + public run(): Promise { let self = this; // Fire the event that we've started adding accounts this._addAccountStartEmitter.fire(); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { self._accountManagementService.addAccount(self._providerId) .then( () => { @@ -87,7 +86,7 @@ export class RemoveAccountAction extends Action { super(RemoveAccountAction.ID, RemoveAccountAction.LABEL, 'remove-account-action icon remove'); } - public run(): TPromise { + public run(): Promise { let self = this; // Ask for Confirm @@ -100,9 +99,9 @@ export class RemoveAccountAction extends Action { return this._dialogService.confirm(confirm).then(result => { if (!result) { - return TPromise.as(false); + return Promise.resolve(false); } else { - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { self._accountManagementService.removeAccount(self._account.key) .then( (result) => { resolve(result); }, @@ -135,9 +134,9 @@ export class ApplyFilterAction extends Action { super(id, label, 'apply-filters-action icon filter'); } - public run(): TPromise { + public run(): Promise { // Todo: apply filter to the account - return TPromise.as(true); + return Promise.resolve(true); } } @@ -154,9 +153,9 @@ export class RefreshAccountAction extends Action { ) { super(RefreshAccountAction.ID, RefreshAccountAction.LABEL, 'refresh-account-action icon refresh'); } - public run(): TPromise { + public run(): Promise { let self = this; - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { if (self.account) { self._accountManagementService.refreshAccount(self.account) .then( diff --git a/src/sql/parts/accountManagement/common/accountManagement.contribution.ts b/src/sql/parts/accountManagement/common/accountManagement.contribution.ts index fbca55198c10..1b812b59bd26 100644 --- a/src/sql/parts/accountManagement/common/accountManagement.contribution.ts +++ b/src/sql/parts/accountManagement/common/accountManagement.contribution.ts @@ -75,7 +75,7 @@ export const accountsContribution: IJSONSchema = { ] }; -ExtensionsRegistry.registerExtensionPoint('account-type', [], accountsContribution).setHandler(extensions => { +ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'account-type', jsonSchema: accountsContribution }).setHandler(extensions => { function handleCommand(account: IAccountContrib, extension: IExtensionPointUser) { let { icon, id } = account; diff --git a/src/sql/parts/admin/security/createLoginEditor.ts b/src/sql/parts/admin/security/createLoginEditor.ts index 2cdf267f1849..d53759183252 100644 --- a/src/sql/parts/admin/security/createLoginEditor.ts +++ b/src/sql/parts/admin/security/createLoginEditor.ts @@ -5,7 +5,6 @@ import 'vs/css!sql/parts/query/editor/media/queryEditor'; import * as DOM from 'vs/base/browser/dom'; -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -58,9 +57,9 @@ export class CreateLoginEditor extends BaseEditor { public layout(dimension: DOM.Dimension): void { } - public setInput(input: CreateLoginInput, options: EditorOptions): Thenable { + public setInput(input: CreateLoginInput, options: EditorOptions): Promise { if (this.input instanceof CreateLoginInput && this.input.matches(input)) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } if (!input.hasInitialized) { diff --git a/src/sql/parts/admin/security/createLoginInput.ts b/src/sql/parts/admin/security/createLoginInput.ts index 7d6c6c470b9f..1102e225cd4d 100644 --- a/src/sql/parts/admin/security/createLoginInput.ts +++ b/src/sql/parts/admin/security/createLoginInput.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; @@ -43,7 +42,7 @@ export class CreateLoginInput extends EditorInput { return this._connection; } - public resolve(refresh?: boolean): TPromise { + public resolve(refresh?: boolean): Promise { return undefined; } diff --git a/src/sql/parts/connection/common/connectionActions.ts b/src/sql/parts/connection/common/connectionActions.ts index 6cac61dc2ac0..378abfc8b954 100644 --- a/src/sql/parts/connection/common/connectionActions.ts +++ b/src/sql/parts/connection/common/connectionActions.ts @@ -5,7 +5,6 @@ import nls = require('vs/nls'); import { Action } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter } from 'vs/base/common/event'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; @@ -50,7 +49,7 @@ export class ClearRecentConnectionsAction extends Action { this._useConfirmationMessage = value; } - public run(): TPromise { + public run(): Promise { if (this._useConfirmationMessage) { return this.promptConfirmationMessage().then(result => { if (result.confirmed) { @@ -75,9 +74,9 @@ export class ClearRecentConnectionsAction extends Action { } } - private promptQuickOpenService(): TPromise { + private promptQuickOpenService(): Promise { const self = this; - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { let choices: { key, value }[] = [ { key: nls.localize('connectionAction.yes', 'Yes'), value: true }, { key: nls.localize('connectionAction.no', 'No'), value: false } @@ -90,7 +89,7 @@ export class ClearRecentConnectionsAction extends Action { }); } - private promptConfirmationMessage(): TPromise { + private promptConfirmationMessage(): Promise { let confirm: IConfirmation = { message: nls.localize('clearRecentConnectionMessage', 'Are you sure you want to delete all the connections from the list?'), primaryButton: nls.localize('connectionDialog.yes', 'Yes'), @@ -98,7 +97,7 @@ export class ClearRecentConnectionsAction extends Action { type: 'question' }; - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { this._dialogService.confirm(confirm).then((confirmed) => { resolve(confirmed); }); @@ -126,8 +125,8 @@ export class ClearSingleRecentConnectionAction extends Action { this.enabled = true; } - public run(): TPromise { - return new TPromise((resolve, reject) => { + public run(): Promise { + return new Promise((resolve, reject) => { resolve(this._connectionManagementService.clearRecentConnection(this._connectionProfile)); this._onRecentConnectionRemoved.fire(); }); @@ -155,8 +154,8 @@ export class GetCurrentConnectionStringAction extends Action { this.enabled = true; } - public run(): TPromise { - return new TPromise((resolve, reject) => { + public run(): Promise { + return new Promise((resolve, reject) => { let activeInput = this._editorService.activeEditor; if (activeInput && (activeInput instanceof QueryInput || activeInput instanceof EditDataInput || activeInput instanceof DashboardInput) && this._connectionManagementService.isConnected(activeInput.uri)) { diff --git a/src/sql/parts/connection/connectionDialog/recentConnectionTreeController.ts b/src/sql/parts/connection/connectionDialog/recentConnectionTreeController.ts index d6aa63de8d2d..583a51acc214 100644 --- a/src/sql/parts/connection/connectionDialog/recentConnectionTreeController.ts +++ b/src/sql/parts/connection/connectionDialog/recentConnectionTreeController.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { DefaultController, ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITree } from 'vs/base/parts/tree/browser/tree'; diff --git a/src/sql/parts/dashboard/common/actions.ts b/src/sql/parts/dashboard/common/actions.ts index 2fe8d7a39af0..2af82e98a876 100644 --- a/src/sql/parts/dashboard/common/actions.ts +++ b/src/sql/parts/dashboard/common/actions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Action, IAction } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -30,13 +29,13 @@ export class EditDashboardAction extends Action { super(EditDashboardAction.ID, EditDashboardAction.EDITLABEL, EditDashboardAction.ICON); } - run(): TPromise { + run(): Promise { try { this.editFn.apply(this.context); this.toggleLabel(); - return TPromise.as(true); + return Promise.resolve(true); } catch (e) { - return TPromise.as(false); + return Promise.resolve(false); } } @@ -64,12 +63,12 @@ export class RefreshWidgetAction extends Action { super(RefreshWidgetAction.ID, RefreshWidgetAction.LABEL, RefreshWidgetAction.ICON); } - run(): TPromise { + run(): Promise { try { this.refreshFn.apply(this.context); - return TPromise.as(true); + return Promise.resolve(true); } catch (e) { - return TPromise.as(false); + return Promise.resolve(false); } } } @@ -111,9 +110,9 @@ export class DeleteWidgetAction extends Action { super(DeleteWidgetAction.ID, DeleteWidgetAction.LABEL, DeleteWidgetAction.ICON); } - run(): TPromise { + run(): Promise { this.angularEventService.sendAngularEvent(this._uri, AngularEventType.DELETE_WIDGET, { id: this._widgetId }); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -144,11 +143,11 @@ export class PinUnpinTabAction extends Action { } } - public run(): TPromise { + public run(): Promise { this._isPinned = !this._isPinned; this.updatePinStatus(); this.angularEventService.sendAngularEvent(this._uri, AngularEventType.PINUNPIN_TAB, { tabId: this._tabId, isPinned: this._isPinned }); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -170,9 +169,9 @@ export class AddFeatureTabAction extends Action { this._disposables.push(toDisposableSubscription(this._angularEventService.onAngularEvent(this._uri, (event) => this.handleDashboardEvent(event)))); } - run(): TPromise { + run(): Promise { this._newDashboardTabService.showDialog(this._dashboardTabs, this._openedTabs, this._uri); - return TPromise.as(true); + return Promise.resolve(true); } dispose() { @@ -219,10 +218,10 @@ export class CollapseWidgetAction extends Action { ); } - run(): TPromise { + run(): Promise { this._toggleState(); this._angularEventService.sendAngularEvent(this._uri, AngularEventType.COLLAPSE_WIDGET, this._widgetUuid); - return TPromise.as(true); + return Promise.resolve(true); } private _toggleState(): void { diff --git a/src/sql/parts/dashboard/common/dashboardTab.contribution.ts b/src/sql/parts/dashboard/common/dashboardTab.contribution.ts index 148886ed3256..ada6426844f9 100644 --- a/src/sql/parts/dashboard/common/dashboardTab.contribution.ts +++ b/src/sql/parts/dashboard/common/dashboardTab.contribution.ts @@ -76,7 +76,7 @@ const tabContributionSchema: IJSONSchema = { ] }; -ExtensionsRegistry.registerExtensionPoint('dashboard.tabs', [], tabContributionSchema).setHandler(extensions => { +ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'dashboard.tabs', jsonSchema: tabContributionSchema }).setHandler(extensions => { function handleCommand(tab: IDashboardTabContrib, extension: IExtensionPointUser) { let { description, container, provider, title, when, id, alwaysShow, isHomeTab } = tab; diff --git a/src/sql/parts/dashboard/containers/dashboardContainer.contribution.ts b/src/sql/parts/dashboard/containers/dashboardContainer.contribution.ts index 6bed9817a860..00d290a0d3d8 100644 --- a/src/sql/parts/dashboard/containers/dashboardContainer.contribution.ts +++ b/src/sql/parts/dashboard/containers/dashboardContainer.contribution.ts @@ -55,7 +55,7 @@ const containerContributionSchema: IJSONSchema = { ] }; -ExtensionsRegistry.registerExtensionPoint('dashboard.containers', [], containerContributionSchema).setHandler(extensions => { +ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'dashboard.containers', jsonSchema: containerContributionSchema }).setHandler(extensions => { function handleCommand(dashboardContainer: IDashboardContainerContrib, extension: IExtensionPointUser) { let { id, container } = dashboardContainer; diff --git a/src/sql/parts/dashboard/dashboardEditor.ts b/src/sql/parts/dashboard/dashboardEditor.ts index 17463a494401..3ac1c3e25397 100644 --- a/src/sql/parts/dashboard/dashboardEditor.ts +++ b/src/sql/parts/dashboard/dashboardEditor.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -77,9 +76,9 @@ export class DashboardEditor extends BaseEditor { this._dashboardService.layout(dimension); } - public setInput(input: DashboardInput, options: EditorOptions): TPromise { + public setInput(input: DashboardInput, options: EditorOptions): Promise { if (this.input && this.input.matches(input)) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } const parentElement = this.getContainer(); @@ -93,10 +92,10 @@ export class DashboardEditor extends BaseEditor { container.style.height = '100%'; this._dashboardContainer = DOM.append(parentElement, container); this.input.container = this._dashboardContainer; - return TPromise.wrap(input.initializedPromise.then(() => this.bootstrapAngular(input))); + return Promise.resolve(input.initializedPromise.then(() => this.bootstrapAngular(input))); } else { this._dashboardContainer = DOM.append(parentElement, this.input.container); - return TPromise.wrap(null); + return Promise.resolve(null); } } diff --git a/src/sql/parts/dashboard/dashboardInput.ts b/src/sql/parts/dashboard/dashboardInput.ts index 562d0a52eb64..62ffe5c80c68 100644 --- a/src/sql/parts/dashboard/dashboardInput.ts +++ b/src/sql/parts/dashboard/dashboardInput.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -140,7 +139,7 @@ export class DashboardInput extends EditorInput { return this._connectionService.getConnectionProfile(this._uri); } - public resolve(refresh?: boolean): TPromise { + public resolve(refresh?: boolean): Promise { return undefined; } diff --git a/src/sql/parts/dashboard/widgets/insights/actions.ts b/src/sql/parts/dashboard/widgets/insights/actions.ts index 4bca439475bb..54c8e6303d7b 100644 --- a/src/sql/parts/dashboard/widgets/insights/actions.ts +++ b/src/sql/parts/dashboard/widgets/insights/actions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; import { RunQueryOnConnectionMode, IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; @@ -27,8 +26,8 @@ export class RunInsightQueryAction extends Action { super(id, label); } - public run(context: InsightActionContext): TPromise { - return new TPromise((resolve, reject) => { + public run(context: InsightActionContext): Promise { + return new Promise((resolve, reject) => { TaskUtilities.newQuery( context.profile, this._connectionManagementService, diff --git a/src/sql/parts/dashboard/widgets/insights/insightsWidget.component.ts b/src/sql/parts/dashboard/widgets/insights/insightsWidget.component.ts index b03850e348fa..f0f0b9974924 100644 --- a/src/sql/parts/dashboard/widgets/insights/insightsWidget.component.ts +++ b/src/sql/parts/dashboard/widgets/insights/insightsWidget.component.ts @@ -29,7 +29,6 @@ import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/ import { IntervalTimer, createCancelablePromise } from 'vs/base/common/async'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { TPromise } from 'vs/base/common/winjs.base'; import { toDisposable } from 'vs/base/common/lifecycle'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -93,7 +92,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget, this._updateChild(result); this.setupInterval(); } else { - this.queryObv = Observable.fromPromise(TPromise.as(result)); + this.queryObv = Observable.fromPromise(Promise.resolve(result)); } }, error => { @@ -104,7 +103,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget, if (this._init) { this.showError(error); } else { - this.queryObv = Observable.fromPromise(TPromise.as(error)); + this.queryObv = Observable.fromPromise(Promise.resolve(error)); } } ).then(() => this._cd.detectChanges()); @@ -211,8 +210,8 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget, return `insights.${this.insightConfig.cacheId}.${this.dashboardService.connectionManagementService.connectionInfo.connectionProfile.getOptionsKey()}`; } - private _runQuery(): TPromise { - return TPromise.wrap(this.dashboardService.queryManagementService.runQueryAndReturn(this.insightConfig.query as string).then( + private _runQuery(): Promise { + return Promise.resolve(this.dashboardService.queryManagementService.runQueryAndReturn(this.insightConfig.query as string).then( result => { return this._storeResult(result); }, diff --git a/src/sql/parts/dashboard/widgets/insights/insightsWidget.contribution.ts b/src/sql/parts/dashboard/widgets/insights/insightsWidget.contribution.ts index 5812c9d29832..c64cf0ac500e 100644 --- a/src/sql/parts/dashboard/widgets/insights/insightsWidget.contribution.ts +++ b/src/sql/parts/dashboard/widgets/insights/insightsWidget.contribution.ts @@ -17,7 +17,7 @@ const insightRegistry = Registry.as(InsightExtensions.InsightC registerDashboardWidget('insights-widget', '', insightsSchema); -ExtensionsRegistry.registerExtensionPoint('dashboard.insights', [], insightsContribution).setHandler(extensions => { +ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'dashboard.insights', jsonSchema: insightsContribution }).setHandler(extensions => { function handleCommand(insight: IInsightTypeContrib, extension: IExtensionPointUser) { diff --git a/src/sql/parts/dataExplorer/common/dataExplorerExtensionPoint.ts b/src/sql/parts/dataExplorer/common/dataExplorerExtensionPoint.ts index 11ea872e69d2..ed7c4fbb402b 100644 --- a/src/sql/parts/dataExplorer/common/dataExplorerExtensionPoint.ts +++ b/src/sql/parts/dataExplorer/common/dataExplorerExtensionPoint.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { forEach } from 'vs/base/common/collections'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions, ICustomViewDescriptor, ViewsRegistry } from 'vs/workbench/common/views'; +import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions, ViewsRegistry, ITreeViewDescriptor } from 'vs/workbench/common/views'; import { IExtensionPoint, ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -15,7 +15,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { CustomTreeViewPanel } from 'vs/workbench/browser/parts/views/customView'; import { coalesce } from 'vs/base/common/arrays'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { viewsContainersExtensionPoint } from 'vs/workbench/api/browser/viewsContainersExtensionPoint'; +import { viewsContainersExtensionPoint } from 'vs/workbench/api/browser/viewsExtensionPoint'; import { CustomTreeView } from 'sql/workbench/browser/parts/views/customView'; @@ -71,7 +71,7 @@ const dataExplorerContribution: IJSONSchema = { }; -const dataExplorerExtensionPoint: IExtensionPoint<{ [loc: string]: IUserFriendlyViewDescriptor[] }> = ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: IUserFriendlyViewDescriptor[] }>('dataExplorer', [viewsContainersExtensionPoint], dataExplorerContribution); +const dataExplorerExtensionPoint: IExtensionPoint<{ [loc: string]: IUserFriendlyViewDescriptor[] }> = ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: IUserFriendlyViewDescriptor[] }>({ extensionPoint: 'dataExplorer', deps: [viewsContainersExtensionPoint], jsonSchema: dataExplorerContribution }); class DataExplorerContainerExtensionHandler implements IWorkbenchContribution { @@ -115,11 +115,10 @@ class DataExplorerContainerExtensionHandler implements IWorkbenchContribution { return null; } - const viewDescriptor = { + const viewDescriptor = { id: item.id, name: item.name, ctor: CustomTreeViewPanel, - container, when: ContextKeyExpr.deserialize(item.when), canToggleVisibility: true, collapsed: this.showCollapsed(container), @@ -129,7 +128,7 @@ class DataExplorerContainerExtensionHandler implements IWorkbenchContribution { viewIds.push(viewDescriptor.id); return viewDescriptor; })); - ViewsRegistry.registerViews(viewDescriptors); + ViewsRegistry.registerViews(viewDescriptors, container); }); } }); diff --git a/src/sql/parts/dataExplorer/viewlet/dataExplorerViewlet.ts b/src/sql/parts/dataExplorer/viewlet/dataExplorerViewlet.ts index 01d69fe8523e..374dad05219f 100644 --- a/src/sql/parts/dataExplorer/viewlet/dataExplorerViewlet.ts +++ b/src/sql/parts/dataExplorer/viewlet/dataExplorerViewlet.ts @@ -6,7 +6,6 @@ 'use strict'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IAction } from 'vs/base/common/actions'; @@ -40,14 +39,13 @@ export class DataExplorerViewletViewsContribution implements IWorkbenchContribut private registerViews(): void { let viewDescriptors = []; viewDescriptors.push(this.createObjectExplorerViewDescriptor()); - ViewsRegistry.registerViews(viewDescriptors); + ViewsRegistry.registerViews(viewDescriptors, VIEW_CONTAINER); } private createObjectExplorerViewDescriptor(): IViewDescriptor { return { id: 'dataExplorer.servers', name: localize('dataExplorer.servers', "Servers"), - container: VIEW_CONTAINER, ctor: ConnectionViewletPanel, weight: 100, canToggleVisibility: true, @@ -117,7 +115,7 @@ export class DataExplorerViewlet extends ViewContainerViewlet { protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] { const addedViews = super.onDidAddViews(added); - TPromise.join(addedViews); + Promise.all(addedViews); return addedViews; } diff --git a/src/sql/parts/editData/common/editDataInput.ts b/src/sql/parts/editData/common/editDataInput.ts index 80b4a5f06c1b..023ac5d3fd81 100644 --- a/src/sql/parts/editData/common/editDataInput.ts +++ b/src/sql/parts/editData/common/editDataInput.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput, EditorModel, ConfirmResult, EncodingMode } from 'vs/workbench/common/editor'; import { IConnectionManagementService, IConnectableInput, INewConnectionParams } from 'sql/platform/connection/common/connectionManagement'; import { IQueryModelService } from 'sql/platform/query/common/queryModel'; @@ -112,10 +111,10 @@ export class EditDataInput extends EditorInput implements IConnectableInput { public get setup(): boolean { return this._setup; } public get rowLimit(): number { return this._rowLimit; } public get objectType(): string { return this._objectType; } - public showResultsEditor(): void { this._showResultsEditor.fire(); } + public showResultsEditor(): void { this._showResultsEditor.fire(undefined); } public isDirty(): boolean { return false; } - public save(): TPromise { return TPromise.as(false); } - public confirmSave(): TPromise { return TPromise.wrap(ConfirmResult.DONT_SAVE); } + public save(): Promise { return Promise.resolve(false); } + public confirmSave(): Promise { return Promise.resolve(ConfirmResult.DONT_SAVE); } public getTypeId(): string { return EditDataInput.ID; } public setBootstrappedTrue(): void { this._hasBootstrapped = true; } public getResource(): URI { return this._uri; } @@ -233,7 +232,7 @@ export class EditDataInput extends EditorInput implements IConnectableInput { public get onDidModelChangeContent(): Event { return this._sql.onDidModelChangeContent; } public get onDidModelChangeEncoding(): Event { return this._sql.onDidModelChangeEncoding; } - public resolve(refresh?: boolean): TPromise { return this._sql.resolve(); } + public resolve(refresh?: boolean): Promise { return this._sql.resolve(); } public getEncoding(): string { return this._sql.getEncoding(); } public suggestFileName(): string { return this._sql.suggestFileName(); } public getName(): string { return this._sql.getName(); } diff --git a/src/sql/parts/editData/common/editDataResultsInput.ts b/src/sql/parts/editData/common/editDataResultsInput.ts index 817fb6400df5..8adef4839aa4 100644 --- a/src/sql/parts/editData/common/editDataResultsInput.ts +++ b/src/sql/parts/editData/common/editDataResultsInput.ts @@ -6,7 +6,6 @@ 'use strict'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput } from 'vs/workbench/common/editor'; import { Emitter } from 'vs/base/common/event'; @@ -47,8 +46,8 @@ export class EditDataResultsInput extends EditorInput { return false; } - resolve(refresh?: boolean): TPromise { - return TPromise.as(null); + resolve(refresh?: boolean): Promise { + return Promise.resolve(null); } supportsSplitEditor(): boolean { diff --git a/src/sql/parts/editData/editor/editDataEditor.ts b/src/sql/parts/editData/editor/editDataEditor.ts index 577f20431b3d..c3f56a58b6da 100644 --- a/src/sql/parts/editData/editor/editDataEditor.ts +++ b/src/sql/parts/editData/editor/editDataEditor.ts @@ -5,7 +5,6 @@ import 'vs/css!sql/parts/query/editor/media/queryEditor'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as strings from 'vs/base/common/strings'; import * as DOM from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; @@ -221,7 +220,7 @@ export class EditDataEditor extends BaseEditor { /** * Sets the input data for this editor. */ - public setInput(newInput: EditDataInput, options?: EditorOptions): Thenable { + public setInput(newInput: EditDataInput, options?: EditorOptions): Promise { let oldInput = this.input; if (!newInput.setup) { this._initialized = false; @@ -259,16 +258,16 @@ export class EditDataEditor extends BaseEditor { } // PRIVATE METHODS //////////////////////////////////////////////////////////// - private _createEditor(editorInput: EditorInput, container: HTMLElement): TPromise { + private _createEditor(editorInput: EditorInput, container: HTMLElement): Promise { const descriptor = this._editorDescriptorService.getEditor(editorInput); if (!descriptor) { - return TPromise.wrapError(new Error(strings.format('Can not find a registered editor for the input {0}', editorInput))); + return Promise.reject(new Error(strings.format('Can not find a registered editor for the input {0}', editorInput))); } let editor = descriptor.instantiate(this._instantiationService); editor.create(container); editor.setVisible(this.isVisible(), editor.group); - return TPromise.as(editor); + return Promise.resolve(editor); } /** @@ -518,7 +517,7 @@ export class EditDataEditor extends BaseEditor { /** * Sets input for the results editor after it has been created. */ - private _onResultsEditorCreated(resultsEditor: EditDataResultsEditor, resultsInput: EditDataResultsInput, options: EditorOptions): TPromise { + private _onResultsEditorCreated(resultsEditor: EditDataResultsEditor, resultsInput: EditDataResultsInput, options: EditorOptions): Promise { this._resultsEditor = resultsEditor; return this._resultsEditor.setInput(resultsInput, options); } @@ -546,22 +545,22 @@ export class EditDataEditor extends BaseEditor { * - Opened for the first time * - Opened with a new EditDataInput */ - private _setNewInput(newInput: EditDataInput, options?: EditorOptions): TPromise { + private _setNewInput(newInput: EditDataInput, options?: EditorOptions): Promise { // Promises that will ensure proper ordering of editor creation logic - let createEditors: () => TPromise; - let onEditorsCreated: (result) => TPromise; + let createEditors: () => Promise; + let onEditorsCreated: (result) => Promise; // If both editors exist, create joined promises - one for each editor if (this._isResultsEditorVisible()) { createEditors = () => { - return TPromise.join([ + return Promise.all([ this._createEditor(newInput.results, this._resultsEditorContainer), this._createEditor(newInput.sql, this._sqlEditorContainer) ]); }; onEditorsCreated = (result: IEditor[]) => { - return TPromise.join([ + return Promise.all([ this._onResultsEditorCreated(result[0], newInput.results, options), this._onSqlEditorCreated(result[1], newInput.sql, options) ]); @@ -573,16 +572,16 @@ export class EditDataEditor extends BaseEditor { return this._createEditor(newInput.sql, this._sqlEditorContainer); }; onEditorsCreated = (result: TextResourceEditor) => { - return TPromise.join([ + return Promise.all([ this._onSqlEditorCreated(result, newInput.sql, options) ]); }; } // Create a promise to re render the layout after the editor creation logic - let doLayout: () => TPromise = () => { + let doLayout: () => Promise = () => { this._doLayout(); - return TPromise.as(undefined); + return Promise.resolve(undefined); }; // Run all three steps synchronously @@ -632,7 +631,7 @@ export class EditDataEditor extends BaseEditor { * Handles setting input for this editor. If this new input does not match the old input (e.g. a new file * has been opened with the same editor, or we are opening the editor for the first time). */ - private _updateInput(oldInput: EditDataInput, newInput: EditDataInput, options?: EditorOptions): TPromise { + private _updateInput(oldInput: EditDataInput, newInput: EditDataInput, options?: EditorOptions): Promise { if (this._sqlEditor) { this._sqlEditor.clearInput(); } diff --git a/src/sql/parts/editData/editor/editDataResultsEditor.ts b/src/sql/parts/editData/editor/editDataResultsEditor.ts index 9c4f4e0f96cd..f670f0ada586 100644 --- a/src/sql/parts/editData/editor/editDataResultsEditor.ts +++ b/src/sql/parts/editData/editor/editDataResultsEditor.ts @@ -7,7 +7,6 @@ import * as DOM from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Builder } from 'sql/base/browser/builder'; import { EditorOptions } from 'vs/workbench/common/editor'; -import { TPromise } from 'vs/base/common/winjs.base'; import { getZoomLevel } from 'vs/base/browser/browser'; import { Configuration } from 'vs/editor/browser/config/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -65,13 +64,13 @@ export class EditDataResultsEditor extends BaseEditor { public layout(dimension: DOM.Dimension): void { } - public setInput(input: EditDataResultsInput, options: EditorOptions): TPromise { + public setInput(input: EditDataResultsInput, options: EditorOptions): Promise { super.setInput(input, options, CancellationToken.None); this._applySettings(); if (!input.hasBootstrapped) { this._bootstrapAngular(); } - return TPromise.wrap(null); + return Promise.resolve(null); } private _applySettings() { diff --git a/src/sql/parts/editData/execution/editDataActions.ts b/src/sql/parts/editData/execution/editDataActions.ts index ccda9de0a2f9..13538e1d37a1 100644 --- a/src/sql/parts/editData/execution/editDataActions.ts +++ b/src/sql/parts/editData/execution/editDataActions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Action, IActionItem, IActionRunner } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IQueryModelService } from 'sql/platform/query/common/queryModel'; import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; @@ -37,7 +36,7 @@ export abstract class EditDataAction extends Action { /** * This method is executed when the button is clicked. */ - public abstract run(): TPromise; + public abstract run(): Promise; protected setClass(enabledClass: string): void { this._classes = []; @@ -75,7 +74,7 @@ export class RefreshTableAction extends EditDataAction { this.label = nls.localize('editData.run', 'Run'); } - public run(): TPromise { + public run(): Promise { if (this.isConnected(this.editor)) { let input = this.editor.editDataInput; @@ -97,7 +96,7 @@ export class RefreshTableAction extends EditDataAction { }); }); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -118,10 +117,10 @@ export class StopRefreshTableAction extends EditDataAction { this.label = nls.localize('editData.stop', 'Stop'); } - public run(): TPromise { + public run(): Promise { let input = this.editor.editDataInput; this._queryModelService.disposeEdit(input.uri); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -142,9 +141,9 @@ export class ChangeMaxRowsAction extends EditDataAction { this.class = ChangeMaxRowsAction.EnabledClass; } - public run(): TPromise { + public run(): Promise { - return TPromise.as(null); + return Promise.resolve(null); } } @@ -261,9 +260,9 @@ export class ShowQueryPaneAction extends EditDataAction { } } - public run(): TPromise { + public run(): Promise { this.editor.toggleQueryPane(); this.updateLabel(this.editor.queryPaneEnabled()); - return TPromise.as(null); + return Promise.resolve(null); } } \ No newline at end of file diff --git a/src/sql/parts/extensions/sqlExtensionsHelper.ts b/src/sql/parts/extensions/sqlExtensionsHelper.ts index ed230c9560f6..439516ee8f13 100644 --- a/src/sql/parts/extensions/sqlExtensionsHelper.ts +++ b/src/sql/parts/extensions/sqlExtensionsHelper.ts @@ -8,12 +8,11 @@ import 'vs/css!vs/workbench/parts/extensions/electron-browser/media/extensionEditor'; -import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { Color } from 'vs/base/common/color'; -import { append, $, addClass, removeClass, finalHandler, join, toggleClass } from 'vs/base/browser/dom'; +import { append, $ } from 'vs/base/browser/dom'; import { IInsightTypeContrib } from 'sql/parts/dashboard/widgets/insights/interfaces'; import { IDashboardTabContrib } from 'sql/parts/dashboard/common/dashboardTab.contribution'; import { localize } from 'vs/nls'; +import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; class ContributionReader { constructor(private manifest: IExtensionManifest) { } diff --git a/src/sql/parts/grid/views/editData/editDataGridActions.ts b/src/sql/parts/grid/views/editData/editDataGridActions.ts index 5a1ba3d89a58..9486a87f398b 100644 --- a/src/sql/parts/grid/views/editData/editDataGridActions.ts +++ b/src/sql/parts/grid/views/editData/editDataGridActions.ts @@ -9,7 +9,6 @@ import { IGridInfo } from 'sql/parts/grid/common/interfaces'; import { DataService } from 'sql/parts/grid/services/dataService'; import { GridActionProvider } from 'sql/parts/grid/views/gridActions'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction, Action } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -48,9 +47,9 @@ export class DeleteRowAction extends Action { super(id, label); } - public run(gridInfo: IGridInfo): TPromise { + public run(gridInfo: IGridInfo): Promise { this.callback(gridInfo.rowIndex); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -66,8 +65,8 @@ export class RevertRowAction extends Action { super(id, label); } - public run(gridInfo: IGridInfo): TPromise { + public run(gridInfo: IGridInfo): Promise { this.callback(); - return TPromise.as(true); + return Promise.resolve(true); } } diff --git a/src/sql/parts/grid/views/gridActions.ts b/src/sql/parts/grid/views/gridActions.ts index f7cd9bb232a0..7371a2722ba3 100644 --- a/src/sql/parts/grid/views/gridActions.ts +++ b/src/sql/parts/grid/views/gridActions.ts @@ -9,7 +9,6 @@ import { IGridInfo, SaveFormat } from 'sql/parts/grid/common/interfaces'; import { DataService } from 'sql/parts/grid/services/dataService'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction, Action } from 'vs/base/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -88,14 +87,14 @@ export class SaveResultAction extends Action { super(id, label); } - public run(gridInfo: IGridInfo): TPromise { + public run(gridInfo: IGridInfo): Promise { this.dataService.sendSaveRequest({ batchIndex: gridInfo.batchIndex, resultSetNumber: gridInfo.resultSetNumber, selection: gridInfo.selection, format: this.format }); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -115,9 +114,9 @@ export class CopyResultAction extends Action { super(id, label); } - public run(gridInfo: IGridInfo): TPromise { + public run(gridInfo: IGridInfo): Promise { this.dataService.copyResults(gridInfo.selection, gridInfo.batchIndex, gridInfo.resultSetNumber, this.copyHeader); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -133,9 +132,9 @@ export class SelectAllGridAction extends Action { super(id, label); } - public run(gridInfo: IGridInfo): TPromise { + public run(gridInfo: IGridInfo): Promise { this.selectAllCallback(gridInfo.gridIndex); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -151,9 +150,9 @@ export class SelectAllMessagesAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Promise { this.selectAllCallback(); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -169,8 +168,8 @@ export class CopyMessagesAction extends Action { super(id, label); } - public run(selectedRange: Selection): TPromise { + public run(selectedRange: Selection): Promise { this.clipboardService.writeText(selectedRange.toString()); - return TPromise.as(true); + return Promise.resolve(true); } } diff --git a/src/sql/parts/jobManagement/views/alertsView.component.ts b/src/sql/parts/jobManagement/views/alertsView.component.ts index c195b9fac760..10fdea7698b9 100644 --- a/src/sql/parts/jobManagement/views/alertsView.component.ts +++ b/src/sql/parts/jobManagement/views/alertsView.component.ts @@ -26,7 +26,6 @@ import { CommonServiceInterface } from 'sql/services/common/commonServiceInterfa import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IAction } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService'; diff --git a/src/sql/parts/jobManagement/views/jobHistoryTree.ts b/src/sql/parts/jobManagement/views/jobHistoryTree.ts index 67765acf556d..3fdbe0df06fd 100644 --- a/src/sql/parts/jobManagement/views/jobHistoryTree.ts +++ b/src/sql/parts/jobManagement/views/jobHistoryTree.ts @@ -9,7 +9,6 @@ import { } from 'sql/workbench/common/actions'; import * as tree from 'vs/base/parts/tree/browser/tree'; import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults'; -import { Promise, TPromise } from 'vs/base/common/winjs.base'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { generateUuid } from 'vs/base/common/uuid'; import * as DOM from 'vs/base/browser/dom'; @@ -78,19 +77,19 @@ export class JobHistoryDataSource implements tree.IDataSource { } } - public getChildren(tree: tree.ITree, element: JobHistoryRow | JobHistoryModel): Promise { + public getChildren(tree: tree.ITree, element: JobHistoryRow | JobHistoryModel): Promise { if (element instanceof JobHistoryModel) { - return TPromise.as(this._data); + return Promise.resolve(this._data); } else { - return TPromise.as(undefined); + return Promise.resolve(undefined); } } - public getParent(tree: tree.ITree, element: JobHistoryRow | JobHistoryModel): Promise { + public getParent(tree: tree.ITree, element: JobHistoryRow | JobHistoryModel): Promise { if (element instanceof JobHistoryModel) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } else { - return TPromise.as(new JobHistoryModel()); + return Promise.resolve(new JobHistoryModel()); } } diff --git a/src/sql/parts/jobManagement/views/jobStepsViewTree.ts b/src/sql/parts/jobManagement/views/jobStepsViewTree.ts index 0344d58e24bd..34df0e7dcca9 100644 --- a/src/sql/parts/jobManagement/views/jobStepsViewTree.ts +++ b/src/sql/parts/jobManagement/views/jobStepsViewTree.ts @@ -7,7 +7,6 @@ import * as DOM from 'vs/base/browser/dom'; import * as tree from 'vs/base/parts/tree/browser/tree'; import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults'; -import { Promise, TPromise } from 'vs/base/common/winjs.base'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { generateUuid } from 'vs/base/common/uuid'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -71,19 +70,19 @@ export class JobStepsViewDataSource implements tree.IDataSource { } } - public getChildren(tree: tree.ITree, element: JobStepsViewRow | JobStepsViewModel): Promise { + public getChildren(tree: tree.ITree, element: JobStepsViewRow | JobStepsViewModel): Promise { if (element instanceof JobStepsViewModel) { - return TPromise.as(this._data); + return Promise.resolve(this._data); } else { - return TPromise.as(undefined); + return Promise.resolve(undefined); } } - public getParent(tree: tree.ITree, element: JobStepsViewRow | JobStepsViewModel): Promise { + public getParent(tree: tree.ITree, element: JobStepsViewRow | JobStepsViewModel): Promise { if (element instanceof JobStepsViewModel) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } else { - return TPromise.as(new JobStepsViewModel()); + return Promise.resolve(new JobStepsViewModel()); } } diff --git a/src/sql/parts/jobManagement/views/jobsView.component.ts b/src/sql/parts/jobManagement/views/jobsView.component.ts index 5004701015ae..34500096cdda 100644 --- a/src/sql/parts/jobManagement/views/jobsView.component.ts +++ b/src/sql/parts/jobManagement/views/jobsView.component.ts @@ -30,7 +30,6 @@ import { CommonServiceInterface } from 'sql/services/common/commonServiceInterfa import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService'; diff --git a/src/sql/parts/jobManagement/views/operatorsView.component.ts b/src/sql/parts/jobManagement/views/operatorsView.component.ts index fd3569618e2f..8f752007ba6c 100644 --- a/src/sql/parts/jobManagement/views/operatorsView.component.ts +++ b/src/sql/parts/jobManagement/views/operatorsView.component.ts @@ -26,7 +26,6 @@ import { TabChild } from 'sql/base/browser/ui/panel/tab.component'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService'; diff --git a/src/sql/parts/jobManagement/views/proxiesView.component.ts b/src/sql/parts/jobManagement/views/proxiesView.component.ts index 063484616dcf..7d6521b217dd 100644 --- a/src/sql/parts/jobManagement/views/proxiesView.component.ts +++ b/src/sql/parts/jobManagement/views/proxiesView.component.ts @@ -25,7 +25,6 @@ import { TabChild } from 'sql/base/browser/ui/panel/tab.component'; import { JobManagementView } from 'sql/parts/jobManagement/views/jobManagementView'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction } from 'vs/base/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/sql/parts/modelComponents/listbox.component.ts b/src/sql/parts/modelComponents/listbox.component.ts index b8f390a62a00..8df135485486 100644 --- a/src/sql/parts/modelComponents/listbox.component.ts +++ b/src/sql/parts/modelComponents/listbox.component.ts @@ -81,7 +81,7 @@ export default class ListBoxComponent extends ComponentBase implements IComponen public setProperties(properties: { [key: string]: any; }): void { super.setProperties(properties); - this._input.setOptions(this.values, this.selectedRow); + this._input.setOptions(this.values.map(value => { return { text: value }; }), this.selectedRow); this.validate(); } diff --git a/src/sql/parts/modelComponents/modelEditor/modelViewEditor.ts b/src/sql/parts/modelComponents/modelEditor/modelViewEditor.ts index e95b2c9c126c..1abfe81919be 100644 --- a/src/sql/parts/modelComponents/modelEditor/modelViewEditor.ts +++ b/src/sql/parts/modelComponents/modelEditor/modelViewEditor.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./modelViewEditor'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; diff --git a/src/sql/parts/modelComponents/modelEditor/modelViewInput.ts b/src/sql/parts/modelComponents/modelEditor/modelViewInput.ts index 59e3f0fbb8d1..f9cc7c15b7a7 100644 --- a/src/sql/parts/modelComponents/modelEditor/modelViewInput.ts +++ b/src/sql/parts/modelComponents/modelEditor/modelViewInput.ts @@ -5,7 +5,6 @@ import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { EditorInput, EditorModel, ConfirmResult } from 'vs/workbench/common/editor'; import * as DOM from 'vs/base/browser/dom'; @@ -40,11 +39,11 @@ export class ModelViewInputModel extends EditorModel { this._onDidChangeDirty.fire(); } - save(): TPromise { + save(): Promise { if (this.saveHandler) { - return TPromise.wrap(this.saveHandler(this.handle)); + return Promise.resolve(this.saveHandler(this.handle)); } - return TPromise.wrap(true); + return Promise.resolve(true); } } export class ModelViewInput extends EditorInput { @@ -79,7 +78,7 @@ export class ModelViewInput extends EditorInput { return 'ModelViewEditorInput'; } - public resolve(refresh?: boolean): TPromise { + public resolve(refresh?: boolean): Promise { return undefined; } @@ -130,18 +129,18 @@ export class ModelViewInput extends EditorInput { /** * Subclasses should bring up a proper dialog for the user if the editor is dirty and return the result. */ - confirmSave(): TPromise { + confirmSave(): Promise { // TODO #2530 support save on close / confirm save. This is significantly more work // as we need to either integrate with textFileService (seems like this isn't viable) // or register our own complimentary service that handles the lifecycle operations such // as close all, auto save etc. - return TPromise.wrap(ConfirmResult.DONT_SAVE); + return Promise.resolve(ConfirmResult.DONT_SAVE); } /** * Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation. */ - save(): TPromise { + save(): Promise { return this._model.save(); } diff --git a/src/sql/parts/modelComponents/queryTextEditor.ts b/src/sql/parts/modelComponents/queryTextEditor.ts index 2ff3d5718d05..99abcc793067 100644 --- a/src/sql/parts/modelComponents/queryTextEditor.ts +++ b/src/sql/parts/modelComponents/queryTextEditor.ts @@ -91,7 +91,7 @@ export class QueryTextEditor extends BaseTextEditor { return options; } - setInput(input: UntitledEditorInput, options: EditorOptions): Thenable { + setInput(input: UntitledEditorInput, options: EditorOptions): Promise { return super.setInput(input, options, CancellationToken.None) .then(() => this.input.resolve() .then(editorModel => editorModel.load()) diff --git a/src/sql/parts/modelComponents/tree/treeDataSource.ts b/src/sql/parts/modelComponents/tree/treeDataSource.ts index 34373bf5c43e..a158aa38872b 100644 --- a/src/sql/parts/modelComponents/tree/treeDataSource.ts +++ b/src/sql/parts/modelComponents/tree/treeDataSource.ts @@ -6,7 +6,6 @@ 'use strict'; import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IModelViewTreeViewDataProvider, ITreeComponentItem } from 'sql/workbench/common/views'; import { TreeItemCollapsibleState } from 'vs/workbench/common/views'; @@ -41,7 +40,7 @@ export class TreeComponentDataSource implements IDataSource { /** * Returns the element's children as an array in a promise. */ - public getChildren(tree: ITree, node: ITreeComponentItem): TPromise { + public getChildren(tree: ITree, node: ITreeComponentItem): Promise { if (this._dataProvider) { if (node && node.handle === '0') { return this._dataProvider.getChildren(undefined); @@ -49,11 +48,11 @@ export class TreeComponentDataSource implements IDataSource { return this._dataProvider.getChildren(node); } } - return TPromise.as([]); + return Promise.resolve([]); } - public getParent(tree: ITree, node: any): TPromise { - return TPromise.as(null); + public getParent(tree: ITree, node: any): Promise { + return Promise.resolve(null); } public shouldAutoexpand(tree: ITree, node: ITreeComponentItem): boolean { diff --git a/src/sql/parts/modelComponents/tree/treeViewDataProvider.ts b/src/sql/parts/modelComponents/tree/treeViewDataProvider.ts index 1947a5455ebc..b2ca842123e3 100644 --- a/src/sql/parts/modelComponents/tree/treeViewDataProvider.ts +++ b/src/sql/parts/modelComponents/tree/treeViewDataProvider.ts @@ -9,7 +9,6 @@ import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; import { IModelViewTreeViewDataProvider, ITreeComponentItem } from 'sql/workbench/common/views'; import { INotificationService } from 'vs/platform/notification/common/notification'; import * as vsTreeView from 'vs/workbench/api/electron-browser/mainThreadTreeViews'; -import { TPromise } from 'vs/base/common/winjs.base'; export class TreeViewDataProvider extends vsTreeView.TreeViewDataProvider implements IModelViewTreeViewDataProvider { constructor(handle: number, treeViewId: string, diff --git a/src/sql/parts/notebook/cellViews/code.component.ts b/src/sql/parts/notebook/cellViews/code.component.ts index c55c94735d77..4ee862c97620 100644 --- a/src/sql/parts/notebook/cellViews/code.component.ts +++ b/src/sql/parts/notebook/cellViews/code.component.ts @@ -27,7 +27,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { Emitter, debounceEvent } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { CellTypes } from 'sql/parts/notebook/models/contracts'; import { OVERRIDE_EDITOR_THEMING_SETTING } from 'sql/workbench/services/notebook/common/notebookService'; import * as notebookUtils from 'sql/parts/notebook/notebookUtils'; @@ -107,7 +107,7 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange ) { super(); this._cellToggleMoreActions = this._instantiationService.createInstance(CellToggleMoreActions); - this._register(debounceEvent(this._layoutEmitter.event, (l, e) => e, 250, /*leading=*/false) + this._register(Event.debounce(this._layoutEmitter.event, (l, e) => e, 250, /*leading=*/false) (() => this.layout())); // Handle disconnect on removal of the cell, if it was the active cell this._register({ dispose: () => this.updateConnectionState(false) }); diff --git a/src/sql/parts/notebook/cellViews/codeActions.ts b/src/sql/parts/notebook/cellViews/codeActions.ts index b93f874c927d..9ec94c6fdf93 100644 --- a/src/sql/parts/notebook/cellViews/codeActions.ts +++ b/src/sql/parts/notebook/cellViews/codeActions.ts @@ -6,7 +6,6 @@ import { nb } from 'azdata'; import { Action } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as types from 'vs/base/common/types'; @@ -54,11 +53,11 @@ export abstract class CellActionBase extends Action { return true; } - public run(context: CellContext): TPromise { + public run(context: CellContext): Promise { if (hasModelAndCell(context, this.notificationService)) { - return TPromise.wrap(this.doRun(context).then(() => true)); + return this.doRun(context).then(() => true); } - return TPromise.as(true); + return Promise.resolve(true); } abstract doRun(context: CellContext): Promise; @@ -80,8 +79,8 @@ export class RunCellAction extends MultiStateAction { this.ensureContextIsUpdated(context); } - public run(context?: CellContext): TPromise { - return TPromise.wrap(this.doRun(context).then(() => true)); + public run(context?: CellContext): Promise { + return this.doRun(context).then(() => true); } public async doRun(context: CellContext): Promise { diff --git a/src/sql/parts/notebook/notebookActions.ts b/src/sql/parts/notebook/notebookActions.ts index 65441f97fe22..95839ff0a353 100644 --- a/src/sql/parts/notebook/notebookActions.ts +++ b/src/sql/parts/notebook/notebookActions.ts @@ -6,7 +6,6 @@ import * as azdata from 'azdata'; import { Action } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { INotificationService, Severity, INotificationActions } from 'vs/platform/notification/common/notification'; @@ -42,8 +41,8 @@ export class AddCellAction extends Action { ) { super(id, label, cssClass); } - public run(context: NotebookComponent): TPromise { - return new TPromise((resolve, reject) => { + public run(context: NotebookComponent): Promise { + return new Promise((resolve, reject) => { try { context.addCell(this.cellType); resolve(true); @@ -194,9 +193,9 @@ export class TrustedAction extends ToggleableAction { this.toggle(value); } - public run(context: NotebookComponent): TPromise { + public run(context: NotebookComponent): Promise { let self = this; - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { try { if (self.trusted) { const actions: INotificationActions = { primary: [] }; diff --git a/src/sql/parts/notebook/notebookEditor.ts b/src/sql/parts/notebook/notebookEditor.ts index 051de26f6467..c1898c83fecb 100644 --- a/src/sql/parts/notebook/notebookEditor.ts +++ b/src/sql/parts/notebook/notebookEditor.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; @@ -59,9 +58,9 @@ export class NotebookEditor extends BaseEditor { } } - public setInput(input: NotebookInput, options: EditorOptions): TPromise { + public setInput(input: NotebookInput, options: EditorOptions): Promise { if (this.input && this.input.matches(input)) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } const parentElement = this.getContainer(); @@ -75,11 +74,11 @@ export class NotebookEditor extends BaseEditor { container.style.height = '100%'; this._notebookContainer = DOM.append(parentElement, container); input.container = this._notebookContainer; - return TPromise.wrap(this.bootstrapAngular(input)); + return Promise.resolve(this.bootstrapAngular(input)); } else { this._notebookContainer = DOM.append(parentElement, input.container); input.doChangeLayout(); - return TPromise.wrap(null); + return Promise.resolve(null); } } diff --git a/src/sql/parts/notebook/notebookInput.ts b/src/sql/parts/notebook/notebookInput.ts index 200ef01205db..52f83b2c33e2 100644 --- a/src/sql/parts/notebook/notebookInput.ts +++ b/src/sql/parts/notebook/notebookInput.ts @@ -6,7 +6,6 @@ 'use strict'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { EditorInput, EditorModel, ConfirmResult } from 'vs/workbench/common/editor'; import { Emitter, Event } from 'vs/base/common/event'; @@ -33,7 +32,6 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten export type ModeViewSaveHandler = (handle: number) => Thenable; - export class NotebookEditorModel extends EditorModel { private dirty: boolean; private readonly _onDidChangeDirty: Emitter = this._register(new Emitter()); @@ -77,7 +75,7 @@ export class NotebookEditorModel extends EditorModel { this._onDidChangeDirty.fire(); } - public confirmSave(): TPromise { + public confirmSave(): Promise { return this.textFileService.confirmSave([this.notebookUri]); } @@ -85,10 +83,10 @@ export class NotebookEditorModel extends EditorModel { * UntitledEditor uses TextFileService to save data from UntitledEditorInput * Titled editor uses TextFileEditorModel to save existing notebook */ - save(options: ISaveOptions): TPromise { + save(options: ISaveOptions): Promise { if (this.textEditorModel instanceof TextFileEditorModel) { this.textEditorModel.save(options); - return TPromise.as(true); + return Promise.resolve(true); } else { return this.textFileService.save(this.notebookUri, options); @@ -163,11 +161,11 @@ export class NotebookInput extends EditorInput { return this._textInput; } - public confirmSave(): TPromise { + public confirmSave(): Promise { return this._model.confirmSave(); } - public revert(): TPromise { + public revert(): Promise { return this._textInput.revert(); } @@ -216,7 +214,7 @@ export class NotebookInput extends EditorInput { return this._standardKernels; } - public save(): TPromise { + public save(): Promise { let options: ISaveOptions = { force: false }; return this._model.save(options); } @@ -256,9 +254,9 @@ export class NotebookInput extends EditorInput { return this.resource; } - async resolve(): TPromise { + async resolve(): Promise { if (this._model && this._model.isModelCreated()) { - return TPromise.as(this._model); + return Promise.resolve(this._model); } else { let textOrUntitledEditorModel: UntitledEditorModel | IEditorModel; if (this.resource.scheme === Schemas.untitled) { diff --git a/src/sql/parts/objectExplorer/common/registeredServer.contribution.ts b/src/sql/parts/objectExplorer/common/registeredServer.contribution.ts index 208529e57e29..cc1a54fb5861 100644 --- a/src/sql/parts/objectExplorer/common/registeredServer.contribution.ts +++ b/src/sql/parts/objectExplorer/common/registeredServer.contribution.ts @@ -6,7 +6,7 @@ import 'vs/css!sql/media/actionBarLabel'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -89,4 +89,13 @@ if (process.env.NODE_ENV !== 'development') { } } }); -} \ No newline at end of file +} + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: localize({ key: 'miViewRegisteredServers', comment: ['&& denotes a mnemonic'] }, "&&Servers") + }, + order: 1 +}); \ No newline at end of file diff --git a/src/sql/parts/objectExplorer/viewlet/connectionTreeAction.ts b/src/sql/parts/objectExplorer/viewlet/connectionTreeAction.ts index 203722773f5b..27a33b166144 100644 --- a/src/sql/parts/objectExplorer/viewlet/connectionTreeAction.ts +++ b/src/sql/parts/objectExplorer/viewlet/connectionTreeAction.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; @@ -43,7 +42,7 @@ export class RefreshAction extends Action { super(id, label); this._tree = tree; } - public run(): TPromise { + public run(): Promise { var treeNode: TreeNode; if (this.element instanceof ConnectionProfile) { let connection: ConnectionProfile = this.element; @@ -60,23 +59,23 @@ export class RefreshAction extends Action { } if (treeNode) { - this._tree.collapse(this.element).then(() => { - this._objectExplorerService.refreshTreeNode(treeNode.getSession(), treeNode).then(() => { + return this._tree.collapse(this.element).then(() => { + return this._objectExplorerService.refreshTreeNode(treeNode.getSession(), treeNode).then(() => { - this._tree.refresh(this.element).then(() => { - this._tree.expand(this.element); + return this._tree.refresh(this.element).then(() => { + return this._tree.expand(this.element); }, refreshError => { - return TPromise.as(true); + return Promise.resolve(true); }); }, error => { this.showError(error); - return TPromise.as(true); + return Promise.resolve(true); }); }, collapseError => { - return TPromise.as(true); + return Promise.resolve(true); }); } - return TPromise.as(true); + return Promise.resolve(true); } private showError(errorMessage: string) { @@ -101,8 +100,8 @@ export class DisconnectConnectionAction extends Action { super(id, label); } - run(actionContext: ObjectExplorerActionsContext): TPromise { - return new TPromise((resolve, reject) => { + run(actionContext: ObjectExplorerActionsContext): Promise { + return new Promise((resolve, reject) => { if (!this._connectionProfile) { resolve(true); } @@ -143,7 +142,7 @@ export class AddServerAction extends Action { this.class = 'add-server-action'; } - public run(element: ConnectionProfileGroup): TPromise { + public run(element: ConnectionProfileGroup): Promise { let connection: IConnectionProfile = element === undefined ? undefined : { connectionName: undefined, serverName: undefined, @@ -162,7 +161,7 @@ export class AddServerAction extends Action { id: element.id }; this._connectionManagementService.showConnectionDialog(undefined, connection); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -182,9 +181,9 @@ export class AddServerGroupAction extends Action { this.class = 'add-server-group-action'; } - public run(): TPromise { + public run(): Promise { this._connectionManagementService.showCreateServerGroupDialog(); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -205,9 +204,9 @@ export class EditServerGroupAction extends Action { this.class = 'edit-server-group-action'; } - public run(): TPromise { + public run(): Promise { this._connectionManagementService.showEditServerGroupDialog(this._group); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -241,10 +240,10 @@ export class ActiveConnectionsFilterAction extends Action { this.class = ActiveConnectionsFilterAction.enabledClass; } - public run(): TPromise { + public run(): Promise { if (!this.view) { // return without doing anything - return TPromise.as(true); + return Promise.resolve(true); } if (this.class === ActiveConnectionsFilterAction.enabledClass) { // show active connections in the tree @@ -257,7 +256,7 @@ export class ActiveConnectionsFilterAction extends Action { this.isSet = false; this.label = ActiveConnectionsFilterAction.LABEL; } - return TPromise.as(true); + return Promise.resolve(true); } } @@ -289,10 +288,10 @@ export class RecentConnectionsFilterAction extends Action { this._isSet = false; } - public run(): TPromise { + public run(): Promise { if (!this.view) { // return without doing anything - return TPromise.as(true); + return Promise.resolve(true); } if (this.class === RecentConnectionsFilterAction.enabledClass) { // show recent connections in the tree @@ -303,7 +302,7 @@ export class RecentConnectionsFilterAction extends Action { this.view.refreshTree(); this.isSet = false; } - return TPromise.as(true); + return Promise.resolve(true); } } @@ -331,13 +330,13 @@ export class NewQueryAction extends Action { this.class = 'extension-action update'; } - public run(actionContext: ObjectExplorerActionsContext): TPromise { + public run(actionContext: ObjectExplorerActionsContext): Promise { if (actionContext instanceof ObjectExplorerActionsContext) { this._connectionProfile = new ConnectionProfile(this._capabilitiesService, actionContext.connectionProfile); } TaskUtilities.newQuery(this._connectionProfile, this.connectionManagementService, this.queryEditorService, this._objectExplorerService, this._workbenchEditorService); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -369,13 +368,13 @@ export class DeleteConnectionAction extends Action { } } - public run(): TPromise { + public run(): Promise { if (this.element instanceof ConnectionProfile) { this._connectionManagementService.deleteConnection(this.element); } else if (this.element instanceof ConnectionProfileGroup) { this._connectionManagementService.deleteConnectionGroup(this.element); } - return TPromise.as(true); + return Promise.resolve(true); } } @@ -397,8 +396,8 @@ export class ClearSearchAction extends Action { this.enabled = false; } - public run(): TPromise { + public run(): Promise { this._viewlet.clearSearch(); - return TPromise.as(true); + return Promise.resolve(true); } } diff --git a/src/sql/parts/objectExplorer/viewlet/dragAndDropController.ts b/src/sql/parts/objectExplorer/viewlet/dragAndDropController.ts index 9a5487efbfb7..c8fe9e74ae67 100644 --- a/src/sql/parts/objectExplorer/viewlet/dragAndDropController.ts +++ b/src/sql/parts/objectExplorer/viewlet/dragAndDropController.ts @@ -8,10 +8,11 @@ import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectio import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; -import { ITree, IDragAndDrop, IDragAndDropData, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree'; +import { ITree, IDragAndDrop, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree'; import * as Constants from 'sql/platform/connection/common/constants'; import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; /** * Implements drag and drop for the server tree diff --git a/src/sql/parts/objectExplorer/viewlet/objectExplorerActions.ts b/src/sql/parts/objectExplorer/viewlet/objectExplorerActions.ts index 6f6bbf16e723..2ed83525171c 100644 --- a/src/sql/parts/objectExplorer/viewlet/objectExplorerActions.ts +++ b/src/sql/parts/objectExplorer/viewlet/objectExplorerActions.ts @@ -5,7 +5,6 @@ 'use strict'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { ExecuteCommandAction } from 'vs/platform/actions/common/actions'; @@ -38,7 +37,7 @@ export class ObjectExplorerActionsContext implements azdata.ObjectExplorerContex public isConnectionNode: boolean = false; } -async function getTreeNode(context: ObjectExplorerActionsContext, objectExplorerService: IObjectExplorerService): TPromise { +async function getTreeNode(context: ObjectExplorerActionsContext, objectExplorerService: IObjectExplorerService): Promise { if (context.isConnectionNode) { return Promise.resolve(undefined); } @@ -100,11 +99,11 @@ export class ManageConnectionAction extends Action { super(id, label); } - run(actionContext: ObjectExplorerActionsContext): TPromise { + run(actionContext: ObjectExplorerActionsContext): Promise { this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); this._treeSelectionHandler.onTreeActionStateChange(true); let self = this; - let promise = new TPromise((resolve, reject) => { + let promise = new Promise((resolve, reject) => { self.doManage(actionContext).then((success) => { self.done(); resolve(success); @@ -116,7 +115,7 @@ export class ManageConnectionAction extends Action { return promise; } - private async doManage(actionContext: ObjectExplorerActionsContext): TPromise { + private async doManage(actionContext: ObjectExplorerActionsContext): Promise { let treeNode: TreeNode = undefined; let connectionProfile: IConnectionProfile = undefined; if (actionContext instanceof ObjectExplorerActionsContext) { diff --git a/src/sql/parts/objectExplorer/viewlet/recentConnectionDataSource.ts b/src/sql/parts/objectExplorer/viewlet/recentConnectionDataSource.ts index 6be9cb262fe5..a566a4093b89 100644 --- a/src/sql/parts/objectExplorer/viewlet/recentConnectionDataSource.ts +++ b/src/sql/parts/objectExplorer/viewlet/recentConnectionDataSource.ts @@ -7,7 +7,6 @@ import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; -import { TPromise } from 'vs/base/common/winjs.base'; /** * Implements the DataSource(that returns a parent/children of an element) for the recent connection tree @@ -43,26 +42,26 @@ export class RecentConnectionDataSource implements IDataSource { /** * Returns the element's children as an array in a promise. */ - public getChildren(tree: ITree, element: any): TPromise { + public getChildren(tree: ITree, element: any): Promise { if (element instanceof ConnectionProfile) { - return TPromise.as(null); + return Promise.resolve(null); } else if (element instanceof ConnectionProfileGroup) { - return TPromise.as((element).getChildren()); + return Promise.resolve((element).getChildren()); } else { - return TPromise.as(null); + return Promise.resolve(null); } } /** * Returns the element's parent in a promise. */ - public getParent(tree: ITree, element: any): TPromise { + public getParent(tree: ITree, element: any): Promise { if (element instanceof ConnectionProfile) { - return TPromise.as((element).getParent()); + return Promise.resolve((element).getParent()); } else if (element instanceof ConnectionProfileGroup) { - return TPromise.as((element).getParent()); + return Promise.resolve((element).getParent()); } else { - return TPromise.as(null); + return Promise.resolve(null); } } } \ No newline at end of file diff --git a/src/sql/parts/objectExplorer/viewlet/serverTreeActionProvider.ts b/src/sql/parts/objectExplorer/viewlet/serverTreeActionProvider.ts index 6ef0ce8179e9..2b1ce5cac689 100644 --- a/src/sql/parts/objectExplorer/viewlet/serverTreeActionProvider.ts +++ b/src/sql/parts/objectExplorer/viewlet/serverTreeActionProvider.ts @@ -4,8 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { ContributableActionProvider } from 'vs/workbench/browser/actions'; import { IAction } from 'vs/base/common/actions'; diff --git a/src/sql/parts/objectExplorer/viewlet/serverTreeDataSource.ts b/src/sql/parts/objectExplorer/viewlet/serverTreeDataSource.ts index 125332f64cff..770f964b4c4d 100644 --- a/src/sql/parts/objectExplorer/viewlet/serverTreeDataSource.ts +++ b/src/sql/parts/objectExplorer/viewlet/serverTreeDataSource.ts @@ -9,7 +9,6 @@ import { ConnectionProfile } from 'sql/platform/connection/common/connectionProf import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; import { TreeNode, TreeItemCollapsibleState } from 'sql/parts/objectExplorer/common/treeNode'; import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService'; -import { TPromise } from 'vs/base/common/winjs.base'; import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import Severity from 'vs/base/common/severity'; @@ -58,8 +57,8 @@ export class ServerTreeDataSource implements IDataSource { /** * Returns the element's children as an array in a promise. */ - public getChildren(tree: ITree, element: any): TPromise { - return new TPromise((resolve) => { + public getChildren(tree: ITree, element: any): Promise { + return new Promise((resolve) => { if (element instanceof ConnectionProfile) { TreeUpdateUtils.getObjectExplorerNode(element, this._connectionManagementService, this._objectExplorerService).then(nodes => { resolve(nodes); @@ -93,15 +92,15 @@ export class ServerTreeDataSource implements IDataSource { /** * Returns the element's parent in a promise. */ - public getParent(tree: ITree, element: any): TPromise { + public getParent(tree: ITree, element: any): Promise { if (element instanceof ConnectionProfile) { - return TPromise.as(element.getParent()); + return Promise.resolve(element.getParent()); } else if (element instanceof ConnectionProfileGroup) { - return TPromise.as(element.getParent()); + return Promise.resolve(element.getParent()); } else if (element instanceof TreeNode) { - return TPromise.as(TreeUpdateUtils.getObjectExplorerParent(element, this._connectionManagementService)); + return Promise.resolve(TreeUpdateUtils.getObjectExplorerParent(element, this._connectionManagementService)); } else { - return TPromise.as(null); + return Promise.resolve(null); } } diff --git a/src/sql/parts/objectExplorer/viewlet/serverTreeView.ts b/src/sql/parts/objectExplorer/viewlet/serverTreeView.ts index 453e54f2f27f..692fcc51b682 100644 --- a/src/sql/parts/objectExplorer/viewlet/serverTreeView.ts +++ b/src/sql/parts/objectExplorer/viewlet/serverTreeView.ts @@ -172,7 +172,7 @@ export class ServerTreeView { return uri && uri.startsWith(ConnectionUtils.uriPrefixes.default) && !isBackupRestoreUri; } - private handleAddConnectionProfile(newProfile: IConnectionProfile) { + private async handleAddConnectionProfile(newProfile: IConnectionProfile): Promise { if (newProfile) { let groups = this._connectionManagementService.getConnectionGroups(); let profile = ConnectionUtils.findProfileInGroup(newProfile, groups); @@ -191,7 +191,7 @@ export class ServerTreeView { if (newProfile && currentSelectedElement && !newProfileIsSelected) { this._tree.clearSelection(); } - this.refreshTree(); + await this.refreshTree(); if (newProfile && !newProfileIsSelected) { this._tree.reveal(newProfile); this._tree.select(newProfile); @@ -256,10 +256,10 @@ export class ServerTreeView { return Promise.resolve(); } - public refreshTree(): void { + public refreshTree(): Promise { this.messages.hide(); this.clearOtherActions(); - TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService); + return TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService); } public refreshElement(element: any): Thenable { diff --git a/src/sql/parts/objectExplorer/viewlet/treeUpdateUtils.ts b/src/sql/parts/objectExplorer/viewlet/treeUpdateUtils.ts index 9fe97bd0ce29..279bed9aa8b0 100644 --- a/src/sql/parts/objectExplorer/viewlet/treeUpdateUtils.ts +++ b/src/sql/parts/objectExplorer/viewlet/treeUpdateUtils.ts @@ -10,7 +10,6 @@ import { ConnectionProfile } from 'sql/platform/connection/common/connectionProf import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService'; import { NodeType } from 'sql/parts/objectExplorer/common/nodeType'; -import { TPromise } from 'vs/base/common/winjs.base'; import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode'; import errors = require('vs/base/common/errors'); import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; @@ -22,7 +21,7 @@ export class TreeUpdateUtils { /** * Set input for the tree. */ - public static structuralTreeUpdate(tree: ITree, viewKey: string, connectionManagementService: IConnectionManagementService, providers?: string[]): Thenable { + public static structuralTreeUpdate(tree: ITree, viewKey: string, connectionManagementService: IConnectionManagementService, providers?: string[]): Promise { let selectedElement: any; let targetsToExpand: any[]; if (tree) { @@ -59,7 +58,7 @@ export class TreeUpdateUtils { /** * Set input for the registered servers tree. */ - public static registeredServerUpdate(tree: ITree, connectionManagementService: IConnectionManagementService, elementToSelect?: any): void { + public static registeredServerUpdate(tree: ITree, connectionManagementService: IConnectionManagementService, elementToSelect?: any): Promise { let selectedElement: any = elementToSelect; let targetsToExpand: any[]; @@ -82,7 +81,7 @@ export class TreeUpdateUtils { let treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService); if (treeInput) { if (treeInput !== tree.getInput()) { - tree.setInput(treeInput).then(() => { + return tree.setInput(treeInput).then(() => { // Make sure to expand all folders that where expanded in the previous session if (targetsToExpand) { tree.expandAll(targetsToExpand); @@ -94,6 +93,7 @@ export class TreeUpdateUtils { }, errors.onUnexpectedError); } } + return Promise.resolve(); } public static getTreeInput(connectionManagementService: IConnectionManagementService, providers?: string[]): ConnectionProfileGroup { @@ -117,8 +117,8 @@ export class TreeUpdateUtils { connection: IConnectionProfile, options: IConnectionCompletionOptions, connectionManagementService: IConnectionManagementService, - tree: ITree): TPromise { - return new TPromise((resolve, reject) => { + tree: ITree): Promise { + return new Promise((resolve, reject) => { if (!connectionManagementService.isProfileConnected(connection)) { // don't try to reconnect if currently connecting if (connectionManagementService.isProfileConnecting(connection)) { @@ -176,8 +176,8 @@ export class TreeUpdateUtils { * @param objectExplorerService Object explorer service instance */ public static connectAndCreateOeSession(connection: IConnectionProfile, options: IConnectionCompletionOptions, - connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, tree: ITree): TPromise { - return new TPromise((resolve, reject) => { + connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, tree: ITree): Promise { + return new Promise((resolve, reject) => { TreeUpdateUtils.connectIfNotConnected(connection, options, connectionManagementService, tree).then(connectedConnection => { if (connectedConnection) { // append group ID and original display name to build unique OE session ID @@ -205,8 +205,8 @@ export class TreeUpdateUtils { }); } - public static getObjectExplorerNode(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService): TPromise { - return new TPromise((resolve, reject) => { + public static getObjectExplorerNode(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService): Promise { + return new Promise((resolve, reject) => { if (connection.isDisconnecting) { resolve([]); } else { diff --git a/src/sql/parts/profiler/contrib/profilerActions.contribution.ts b/src/sql/parts/profiler/contrib/profilerActions.contribution.ts index 6c419cd6743b..1727caa9005c 100644 --- a/src/sql/parts/profiler/contrib/profilerActions.contribution.ts +++ b/src/sql/parts/profiler/contrib/profilerActions.contribution.ts @@ -9,7 +9,6 @@ import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiati import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; import { IProfilerService } from 'sql/workbench/services/profiler/common/interfaces'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -60,7 +59,7 @@ CommandsRegistry.registerCommand({ if (connectionProfile && connectionProfile.providerName === mssqlProviderName) { let profilerInput = instantiationService.createInstance(ProfilerInput, connectionProfile); - editorService.openEditor(profilerInput, { pinned: true }, ACTIVE_GROUP).then(() => TPromise.as(true)); + editorService.openEditor(profilerInput, { pinned: true }, ACTIVE_GROUP).then(() => Promise.resolve(true)); } }); } @@ -96,6 +95,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ return profilerService.startSession(profilerInput.id, profilerInput.sessionName); } } - return TPromise.as(false); + return Promise.resolve(false); } }); diff --git a/src/sql/parts/profiler/contrib/profilerActions.ts b/src/sql/parts/profiler/contrib/profilerActions.ts index fcd1d899e461..c48fb8ee5b93 100644 --- a/src/sql/parts/profiler/contrib/profilerActions.ts +++ b/src/sql/parts/profiler/contrib/profilerActions.ts @@ -13,7 +13,6 @@ import { ConnectionProfile } from 'sql/platform/connection/common/connectionProf import { IConnectionManagementService, IConnectionCompletionOptions } from 'sql/platform/connection/common/connectionManagement'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import { IEditorAction } from 'vs/editor/common/editorCommon'; @@ -38,17 +37,17 @@ export class ProfilerConnect extends Action { super(id, label, 'connect'); } - public run(input: ProfilerInput): TPromise { + public run(input: ProfilerInput): Promise { this.enabled = false; if (!this._connected) { - return TPromise.wrap(this._profilerService.connectSession(input.id).then(() => { + return Promise.resolve(this._profilerService.connectSession(input.id).then(() => { this.enabled = true; this.connected = true; input.state.change({ isConnected: true, isRunning: false, isPaused: false, isStopped: true }); return true; })); } else { - return TPromise.wrap(this._profilerService.disconnectSession(input.id).then(() => { + return Promise.resolve(this._profilerService.disconnectSession(input.id).then(() => { this.enabled = true; this.connected = false; input.state.change({ isConnected: false, isRunning: false, isPaused: false, isStopped: false }); @@ -79,9 +78,9 @@ export class ProfilerStart extends Action { super(id, label, 'sql start'); } - public run(input: ProfilerInput): TPromise { + public run(input: ProfilerInput): Promise { input.data.clear(); - return TPromise.wrap(this._profilerService.startSession(input.id, input.sessionName)); + return Promise.resolve(this._profilerService.startSession(input.id, input.sessionName)); } } @@ -98,8 +97,8 @@ export class ProfilerCreate extends Action { super(id, label, 'add'); } - public run(input: ProfilerInput): TPromise { - return TPromise.wrap(this._profilerService.launchCreateSessionDialog(input).then(() => { + public run(input: ProfilerInput): Promise { + return Promise.resolve(this._profilerService.launchCreateSessionDialog(input).then(() => { return true; })); } @@ -123,8 +122,8 @@ export class ProfilerPause extends Action { super(id, label, ProfilerPause.PauseCssClass); } - public run(input: ProfilerInput): TPromise { - return TPromise.wrap(this._profilerService.pauseSession(input.id).then(() => { + public run(input: ProfilerInput): Promise { + return Promise.resolve(this._profilerService.pauseSession(input.id).then(() => { this.paused = !this._paused; input.state.change({ isPaused: this.paused, isStopped: false, isRunning: !this.paused }); return true; @@ -153,8 +152,8 @@ export class ProfilerStop extends Action { super(id, label, 'sql stop'); } - public run(input: ProfilerInput): TPromise { - return TPromise.wrap(this._profilerService.stopSession(input.id)); + public run(input: ProfilerInput): Promise { + return Promise.resolve(this._profilerService.stopSession(input.id)); } } @@ -166,9 +165,9 @@ export class ProfilerClear extends Action { super(id, label, 'clear-results'); } - run(input: ProfilerInput): TPromise { + run(input: ProfilerInput): Promise { input.data.clear(); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -184,12 +183,12 @@ export class ProfilerAutoScroll extends Action { super(id, label, ProfilerAutoScroll.CheckedCssClass); } - run(input: ProfilerInput): TPromise { + run(input: ProfilerInput): Promise { this.checked = !this.checked; this._setLabel(this.checked ? ProfilerAutoScroll.AutoScrollOnText : ProfilerAutoScroll.AutoScrollOffText); this._setClass(this.checked ? ProfilerAutoScroll.CheckedCssClass : ''); input.state.change({ autoscroll: this.checked }); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -203,10 +202,10 @@ export class ProfilerCollapsablePanelAction extends Action { super(id, label, 'minimize-panel-action'); } - public run(input: ProfilerInput): TPromise { + public run(input: ProfilerInput): Promise { this.collapsed = !this._collapsed; input.state.change({ isPanelCollapsed: this._collapsed }); - return TPromise.as(true); + return Promise.resolve(true); } set collapsed(val: boolean) { @@ -226,8 +225,8 @@ export class ProfilerEditColumns extends Action { super(id, label); } - public run(input: ProfilerInput): TPromise { - return TPromise.wrap(this._profilerService.launchColumnEditor(input)).then(() => true); + public run(input: ProfilerInput): Promise { + return Promise.resolve(this._profilerService.launchColumnEditor(input)).then(() => true); } } @@ -281,7 +280,7 @@ export class NewProfilerAction extends Task { }); } - public runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise { + public runTask(accessor: ServicesAccessor, profile: IConnectionProfile): Promise { let profilerInput = accessor.get(IInstantiationService).createInstance(ProfilerInput, profile); return accessor.get(IEditorService).openEditor(profilerInput, { pinned: true }, ACTIVE_GROUP).then(() => { let options: IConnectionCompletionOptions = { @@ -293,7 +292,7 @@ export class NewProfilerAction extends Task { }; accessor.get(IConnectionManagementService).connect(this._connectionProfile, profilerInput.id, options); - return TPromise.as(void 0); + return Promise.resolve(void 0); }); } } @@ -309,9 +308,9 @@ export class ProfilerFilterSession extends Action { super(id, label, 'filterLabel'); } - public run(input: ProfilerInput): TPromise { + public run(input: ProfilerInput): Promise { this._profilerService.launchFilterSessionDialog(input); - return TPromise.wrap(true); + return Promise.resolve(true); } } @@ -325,8 +324,8 @@ export class ProfilerClearSessionFilter extends Action { super(id, label, 'clear-filter'); } - public run(input: ProfilerInput): TPromise { + public run(input: ProfilerInput): Promise { input.clearFilter(); - return TPromise.wrap(true); + return Promise.resolve(true); } } diff --git a/src/sql/parts/profiler/dialog/profilerColumnEditorDialog.ts b/src/sql/parts/profiler/dialog/profilerColumnEditorDialog.ts index 58e4d96f6e7d..18f8cccab54d 100644 --- a/src/sql/parts/profiler/dialog/profilerColumnEditorDialog.ts +++ b/src/sql/parts/profiler/dialog/profilerColumnEditorDialog.ts @@ -20,7 +20,6 @@ import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import * as DOM from 'vs/base/browser/dom'; import { IDataSource, ITree, IRenderer } from 'vs/base/parts/tree/browser/tree'; -import { TPromise } from 'vs/base/common/winjs.base'; import { attachListStyler } from 'vs/platform/theme/common/styler'; import { Event, Emitter } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -272,25 +271,25 @@ class TreeDataSource implements IDataSource { } } - getChildren(tree: ITree, element: any): TPromise> { + getChildren(tree: ITree, element: any): Promise> { if (element instanceof EventItem) { - return TPromise.as(element.getChildren()); + return Promise.resolve(element.getChildren()); } else if (element instanceof SessionItem) { - return TPromise.as(element.getChildren()); + return Promise.resolve(element.getChildren()); } else { - return TPromise.as(null); + return Promise.resolve(null); } } - getParent(tree: ITree, element: any): TPromise { + getParent(tree: ITree, element: any): Promise { if (element instanceof ColumnItem) { - return TPromise.as(element.parent); + return Promise.resolve(element.parent); } else if (element instanceof EventItem) { - return TPromise.as(element.parent); + return Promise.resolve(element.parent); } else if (element instanceof ColumnSortedColumnItem) { - return TPromise.as(element.parent); + return Promise.resolve(element.parent); } else { - return TPromise.as(null); + return Promise.resolve(null); } } @@ -304,8 +303,8 @@ export class ProfilerColumnEditorDialog extends Modal { private _selectBox: SelectBox; private _selectedValue: number = 0; private readonly _options = [ - nls.localize('eventSort', "Sort by event"), - nls.localize('nameColumn', "Sort by column") + { text: nls.localize('eventSort', "Sort by event") }, + { text: nls.localize('nameColumn', "Sort by column") } ]; private _tree: Tree; private _input: ProfilerInput; diff --git a/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts b/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts index a62a6b47df5f..c6a2b61b73f8 100644 --- a/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts +++ b/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts @@ -12,7 +12,6 @@ import { IProfilerStateChangedEvent } from 'sql/parts/profiler/editor/profilerSt import { FindWidget, ITableController, IConfigurationChangedEvent, ACTION_IDS } from './profilerFindWidget'; import { ProfilerFindNext, ProfilerFindPrevious } from 'sql/parts/profiler/contrib/profilerActions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -106,7 +105,7 @@ export class ProfilerTableEditor extends BaseEditor implements IProfilerControll ); } - public setInput(input: ProfilerInput): TPromise { + public setInput(input: ProfilerInput): Promise { this._showStatusBarItem = true; this._input = input; @@ -153,7 +152,7 @@ export class ProfilerTableEditor extends BaseEditor implements IProfilerControll this._input.onDispose(() => { this._disposeStatusbarItem(); }); - return TPromise.as(null); + return Promise.resolve(null); } public toggleSearch(): void { diff --git a/src/sql/parts/profiler/editor/profilerEditor.ts b/src/sql/parts/profiler/editor/profilerEditor.ts index 9fc4ea2d65af..1d44efe26af4 100644 --- a/src/sql/parts/profiler/editor/profilerEditor.ts +++ b/src/sql/parts/profiler/editor/profilerEditor.ts @@ -41,7 +41,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IView, SplitView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; import * as DOM from 'vs/base/browser/dom'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorOptions } from 'vs/workbench/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkbenchThemeService, VS_DARK_THEME, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; @@ -92,7 +91,7 @@ class BasicView implements IView { this.previousSize = this.size; this._minimumSize = this.options.headersize; this._maximumSize = this.options.headersize; - this._onDidChange.fire(); + this._onDidChange.fire(undefined); } else { this._maximumSize = this._defaultMaximumSize; this._minimumSize = this._defaultMinimumSize; @@ -407,7 +406,7 @@ export class ProfilerEditor extends BaseEditor { return this._input as ProfilerInput; } - public setInput(input: ProfilerInput, options?: EditorOptions): Thenable { + public setInput(input: ProfilerInput, options?: EditorOptions): Promise { let savedViewState = this._savedTableViewStates.get(input); this._profilerEditorContextKey.set(true); @@ -415,7 +414,7 @@ export class ProfilerEditor extends BaseEditor { if (savedViewState) { this._profilerTableEditor.restoreViewState(savedViewState); } - return TPromise.as(null); + return Promise.resolve(null); } return super.setInput(input, options, CancellationToken.None).then(() => { diff --git a/src/sql/parts/profiler/editor/profilerInput.ts b/src/sql/parts/profiler/editor/profilerInput.ts index ec7a3abffd67..5c15560958bf 100644 --- a/src/sql/parts/profiler/editor/profilerInput.ts +++ b/src/sql/parts/profiler/editor/profilerInput.ts @@ -11,7 +11,6 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import * as azdata from 'azdata'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput, ConfirmResult } from 'vs/workbench/common/editor'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -124,7 +123,7 @@ export class ProfilerInput extends EditorInput implements IProfilerSession { return ProfilerInput.ID; } - public resolve(refresh?: boolean): TPromise { + public resolve(refresh?: boolean): Promise { return undefined; } @@ -284,7 +283,7 @@ export class ProfilerInput extends EditorInput implements IProfilerSession { this.data.clearFilter(); } - confirmSave(): TPromise { + confirmSave(): Promise { if (this.state.isRunning || this.state.isPaused) { return this._dialogService.show(Severity.Warning, nls.localize('confirmStopProfilerSession', "Would you like to stop the running XEvent session?"), @@ -303,7 +302,7 @@ export class ProfilerInput extends EditorInput implements IProfilerSession { } }); } else { - return TPromise.wrap(ConfirmResult.DONT_SAVE); + return Promise.resolve(ConfirmResult.DONT_SAVE); } } diff --git a/src/sql/parts/profiler/editor/profilerResourceEditor.ts b/src/sql/parts/profiler/editor/profilerResourceEditor.ts index 347dc6915b15..e9770122114f 100644 --- a/src/sql/parts/profiler/editor/profilerResourceEditor.ts +++ b/src/sql/parts/profiler/editor/profilerResourceEditor.ts @@ -7,7 +7,6 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import * as nls from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import * as editorCommon from 'vs/editor/common/editorCommon'; @@ -17,12 +16,9 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; -import { IModeService } from 'vs/editor/common/services/modeService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { EditorOptions } from 'vs/workbench/common/editor'; -import { IEditorContributionCtor } from 'vs/editor/browser/editorExtensions'; -import { FoldingController } from 'vs/editor/contrib/folding/folding'; import { StandaloneCodeEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -83,7 +79,7 @@ export class ProfilerResourceEditor extends BaseTextEditor { return options; } - setInput(input: UntitledEditorInput, options: EditorOptions): Thenable { + setInput(input: UntitledEditorInput, options: EditorOptions): Promise { return super.setInput(input, options, CancellationToken.None) .then(() => this.input.resolve() .then(editorModel => editorModel.load()) diff --git a/src/sql/parts/query/common/flavorStatus.ts b/src/sql/parts/query/common/flavorStatus.ts index a3599e8a14da..bad50bd84aa6 100644 --- a/src/sql/parts/query/common/flavorStatus.ts +++ b/src/sql/parts/query/common/flavorStatus.ts @@ -13,7 +13,6 @@ import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorG import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Action } from 'vs/base/common/actions'; import errors = require('vs/base/common/errors'); -import { TPromise } from 'vs/base/common/winjs.base'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import nls = require('vs/nls'); @@ -180,7 +179,7 @@ export class ChangeFlavorAction extends Action { super(actionId, actionLabel); } - public run(): TPromise { + public run(): Promise { let activeEditor = this._editorService.activeControl; let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); if (this._connectionManagementService.isConnected(currentUri)) { @@ -213,12 +212,12 @@ export class ChangeFlavorAction extends Action { }); } - private _showMessage(sev: Severity, message: string): TPromise { + private _showMessage(sev: Severity, message: string): Promise { this._notificationService.notify({ severity: sev, message: message }); - return TPromise.as(undefined); + return Promise.resolve(undefined); } } diff --git a/src/sql/parts/query/common/queryInput.ts b/src/sql/parts/query/common/queryInput.ts index bc6c2392752a..6b25bfa961f9 100644 --- a/src/sql/parts/query/common/queryInput.ts +++ b/src/sql/parts/query/common/queryInput.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; @@ -148,7 +147,7 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec public getDescription(): string { return this._description; } public supportsSplitEditor(): boolean { return false; } public getModeId(): string { return QueryInput.SCHEMA; } - public revert(): TPromise { return this._sql.revert(); } + public revert(): Promise { return this._sql.revert(); } public matches(otherInput: any): boolean { if (otherInput instanceof QueryInput) { @@ -161,10 +160,10 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec // Forwarding resource functions to the inline sql file editor public get onDidModelChangeContent(): Event { return this._sql.onDidModelChangeContent; } public get onDidModelChangeEncoding(): Event { return this._sql.onDidModelChangeEncoding; } - public resolve(refresh?: boolean): TPromise { return this._sql.resolve(); } - public save(): TPromise { return this._sql.save(); } + public resolve(refresh?: boolean): Promise { return this._sql.resolve(); } + public save(): Promise { return this._sql.save(); } public isDirty(): boolean { return this._sql.isDirty(); } - public confirmSave(): TPromise { return this._sql.confirmSave(); } + public confirmSave(): Promise { return this._sql.confirmSave(); } public getResource(): URI { return this._sql.getResource(); } public getEncoding(): string { return this._sql.getEncoding(); } public suggestFileName(): string { return this._sql.suggestFileName(); } diff --git a/src/sql/parts/query/common/queryResultsInput.ts b/src/sql/parts/query/common/queryResultsInput.ts index f22689bede53..3608c5e5e36e 100644 --- a/src/sql/parts/query/common/queryResultsInput.ts +++ b/src/sql/parts/query/common/queryResultsInput.ts @@ -6,7 +6,6 @@ 'use strict'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput } from 'vs/workbench/common/editor'; import { Emitter } from 'vs/base/common/event'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -95,8 +94,8 @@ export class QueryResultsInput extends EditorInput { return false; } - resolve(refresh?: boolean): TPromise { - return TPromise.as(null); + resolve(refresh?: boolean): Promise { + return Promise.resolve(null); } supportsSplitEditor(): boolean { diff --git a/src/sql/parts/query/editor/actions.ts b/src/sql/parts/query/editor/actions.ts index 4283de509dbe..ac3633f7fc1c 100644 --- a/src/sql/parts/query/editor/actions.ts +++ b/src/sql/parts/query/editor/actions.ts @@ -8,7 +8,6 @@ import { Action } from 'vs/base/common/actions'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -74,14 +73,14 @@ export class SaveResultAction extends Action { super(id, label, icon); } - public run(context: IGridActionContext): TPromise { + public run(context: IGridActionContext): Promise { if (this.accountForNumberColumn) { context.runner.serializeResults(context.batchId, context.resultId, this.format, mapForNumberColumn(context.selection)); } else { context.runner.serializeResults(context.batchId, context.resultId, this.format, context.selection); } - return TPromise.as(true); + return Promise.resolve(true); } } @@ -101,7 +100,7 @@ export class CopyResultAction extends Action { super(id, label); } - public run(context: IGridActionContext): TPromise { + public run(context: IGridActionContext): Promise { if (this.accountForNumberColumn) { context.runner.copyResults( mapForNumberColumn(context.selection), @@ -109,7 +108,7 @@ export class CopyResultAction extends Action { } else { context.runner.copyResults(context.selection, context.batchId, context.resultId, this.copyHeader); } - return TPromise.as(true); + return Promise.resolve(true); } } @@ -121,9 +120,9 @@ export class SelectAllGridAction extends Action { super(SelectAllGridAction.ID, SelectAllGridAction.LABEL); } - public run(context: IGridActionContext): TPromise { + public run(context: IGridActionContext): Promise { context.selectionModel.setSelectedRanges([new Slick.Range(0, 0, context.table.getData().getLength() - 1, context.table.columns.length - 1)]); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -138,9 +137,9 @@ export class CopyMessagesAction extends Action { super(CopyMessagesAction.ID, CopyMessagesAction.LABEL); } - public run(context: IMessagesActionContext): TPromise { + public run(context: IMessagesActionContext): Promise { this.clipboardService.writeText(context.selection.toString()); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -156,7 +155,7 @@ export class CopyAllMessagesAction extends Action { super(CopyAllMessagesAction.ID, CopyAllMessagesAction.LABEL); } - public run(): TPromise { + public run(): Promise { let text = ''; const navigator = this.tree.getNavigator(); // skip first navigator element - the root node @@ -168,7 +167,7 @@ export class CopyAllMessagesAction extends Action { } this.clipboardService.writeText(removeAnsiEscapeCodes(text)); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -181,9 +180,9 @@ export class MaximizeTableAction extends Action { super(MaximizeTableAction.ID, MaximizeTableAction.LABEL, MaximizeTableAction.ICON); } - public run(context: IGridActionContext): TPromise { + public run(context: IGridActionContext): Promise { context.tableState.maximized = true; - return TPromise.as(true); + return Promise.resolve(true); } } @@ -196,9 +195,9 @@ export class RestoreTableAction extends Action { super(RestoreTableAction.ID, RestoreTableAction.LABEL, RestoreTableAction.ICON); } - public run(context: IGridActionContext): TPromise { + public run(context: IGridActionContext): Promise { context.tableState.maximized = false; - return TPromise.as(true); + return Promise.resolve(true); } } @@ -211,13 +210,13 @@ export class ChartDataAction extends Action { super(ChartDataAction.ID, ChartDataAction.LABEL, ChartDataAction.ICON); } - public run(context: IGridActionContext): TPromise { + public run(context: IGridActionContext): Promise { let activeEditor = this.editorService.activeControl; if (activeEditor instanceof QueryEditor) { activeEditor.resultsEditor.chart({ batchId: context.batchId, resultId: context.resultId }); - return TPromise.as(true); + return Promise.resolve(true); } else { - return TPromise.as(false); + return Promise.resolve(false); } } } @@ -232,13 +231,13 @@ export class ShowQueryPlanAction extends Action { super(ShowQueryPlanAction.ID, ShowQueryPlanAction.LABEL); } - public run(xml: string): TPromise { + public run(xml: string): Promise { let activeEditor = this.editorService.activeControl; if (activeEditor instanceof QueryEditor) { activeEditor.resultsEditor.showQueryPlan(xml); - return TPromise.as(true); + return Promise.resolve(true); } else { - return TPromise.as(false); + return Promise.resolve(false); } } } diff --git a/src/sql/parts/query/editor/charting/actions.ts b/src/sql/parts/query/editor/charting/actions.ts index 5f54d38b67a2..34968ede462c 100644 --- a/src/sql/parts/query/editor/charting/actions.ts +++ b/src/sql/parts/query/editor/charting/actions.ts @@ -14,7 +14,6 @@ import { resolveCurrentDirectory, getRootPath } from 'sql/platform/node/pathUtil import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { join, normalize } from 'vs/base/common/paths'; import { writeFile } from 'vs/base/node/pfs'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; @@ -42,11 +41,11 @@ export class CreateInsightAction extends Action { super(CreateInsightAction.ID, CreateInsightAction.LABEL, CreateInsightAction.ICON); } - public run(context: IChartActionContext): TPromise { + public run(context: IChartActionContext): Promise { let uriString: string = this.getActiveUriString(); if (!uriString) { this.showError(localize('createInsightNoEditor', 'Cannot create insight as the active editor is not a SQL Editor')); - return TPromise.as(false); + return Promise.resolve(false); } let uri: URI = URI.parse(uriString); @@ -118,18 +117,18 @@ export class CopyAction extends Action { super(CopyAction.ID, CopyAction.LABEL, CopyAction.ICON); } - public run(context: IChartActionContext): TPromise { + public run(context: IChartActionContext): Promise { if (context.insight instanceof Graph) { let data = context.insight.getCanvasData(); if (!data) { this.showError(localize('chartNotFound', 'Could not find chart to save')); - return TPromise.as(false); + return Promise.resolve(false); } this.clipboardService.writeImageDataUrl(data); - return TPromise.as(true); + return Promise.resolve(true); } - return TPromise.as(false); + return Promise.resolve(false); } private showError(errorMsg: string) { @@ -155,7 +154,7 @@ export class SaveImageAction extends Action { super(SaveImageAction.ID, SaveImageAction.LABEL, SaveImageAction.ICON); } - public run(context: IChartActionContext): TPromise { + public run(context: IChartActionContext): Promise { if (context.insight instanceof Graph) { return this.promptForFilepath().then(filePath => { let data = (context.insight).getCanvasData(); @@ -181,7 +180,7 @@ export class SaveImageAction extends Action { return true; }); } - return TPromise.as(false); + return Promise.resolve(false); } private decodeBase64Image(data: string): Buffer { @@ -189,7 +188,7 @@ export class SaveImageAction extends Action { return Buffer.from(matches[2], 'base64'); } - private promptForFilepath(): TPromise { + private promptForFilepath(): Promise { let filepathPlaceHolder = resolveCurrentDirectory(this.getActiveUriString(), getRootPath(this.workspaceContextService)); filepathPlaceHolder = join(filepathPlaceHolder, 'chart.png'); return this.windowService.showSaveDialog({ diff --git a/src/sql/parts/query/editor/gridPanel.ts b/src/sql/parts/query/editor/gridPanel.ts index 3b6dc078669a..c164d022fccb 100644 --- a/src/sql/parts/query/editor/gridPanel.ts +++ b/src/sql/parts/query/editor/gridPanel.ts @@ -38,7 +38,6 @@ import { range } from 'vs/base/common/arrays'; import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { generateUuid } from 'vs/base/common/uuid'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Separator, ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { isInDOM } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -467,7 +466,7 @@ class GridTable extends Disposable implements IView { 50, index => this.placeholdGenerator(index), 0, - () => TPromise.as([]) + () => Promise.resolve([]) ); this.dataProvider.dataRows = collection; this.table.updateRowCount(); @@ -486,7 +485,7 @@ class GridTable extends Disposable implements IView { 50, index => this.placeholdGenerator(index), 0, - () => TPromise.as([]) + () => Promise.resolve([]) ); collection.setCollectionChangedCallback((startIndex, count) => { this.renderGridDataRowsRange(startIndex, count); @@ -652,7 +651,7 @@ class GridTable extends Disposable implements IView { this.dataProvider.length = resultSet.rowCount; this.table.updateRowCount(); } - this._onDidChange.fire(); + this._onDidChange.fire(undefined); } private generateContext(cell?: Slick.Cell): IGridActionContext { diff --git a/src/sql/parts/query/editor/messagePanel.ts b/src/sql/parts/query/editor/messagePanel.ts index 4f001caa86ea..6e2017431ad2 100644 --- a/src/sql/parts/query/editor/messagePanel.ts +++ b/src/sql/parts/query/editor/messagePanel.ts @@ -14,7 +14,6 @@ import { IResultMessage, ISelectionData } from 'azdata'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree'; -import { TPromise } from 'vs/base/common/winjs.base'; import { generateUuid } from 'vs/base/common/uuid'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -281,20 +280,20 @@ class MessageDataSource implements IDataSource { return element instanceof Model; } - getChildren(tree: ITree, element: any): TPromise { + getChildren(tree: ITree, element: any): Promise<(IMessagePanelMessage | IMessagePanelBatchMessage)[]> { if (element instanceof Model) { let messages = element.messages; if (element.totalExecuteMessage) { messages = messages.concat(element.totalExecuteMessage); } - return TPromise.as(messages); + return Promise.resolve(messages); } else { - return TPromise.as(undefined); + return Promise.resolve(undefined); } } - getParent(tree: ITree, element: any): TPromise { - return TPromise.as(null); + getParent(tree: ITree, element: any): Promise { + return Promise.resolve(null); } } diff --git a/src/sql/parts/query/editor/queryEditor.ts b/src/sql/parts/query/editor/queryEditor.ts index ccba286d5cca..3ca68a624063 100644 --- a/src/sql/parts/query/editor/queryEditor.ts +++ b/src/sql/parts/query/editor/queryEditor.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!sql/parts/query/editor/media/queryEditor'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as strings from 'vs/base/common/strings'; import * as DOM from 'vs/base/browser/dom'; import * as types from 'vs/base/common/types'; @@ -160,11 +159,11 @@ export class QueryEditor extends BaseEditor { /** * Sets the input data for this editor. */ - public setInput(newInput: QueryInput, options?: EditorOptions): Thenable { + public setInput(newInput: QueryInput, options?: EditorOptions): Promise { const oldInput = this.input; if (newInput.matches(oldInput)) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } // Make sure all event callbacks will be sent to this QueryEditor in the case that this QueryInput was moved from @@ -538,7 +537,7 @@ export class QueryEditor extends BaseEditor { /** * Handles setting input for this editor. */ - private _updateInput(oldInput: QueryInput, newInput: QueryInput, options?: EditorOptions): TPromise { + private _updateInput(oldInput: QueryInput, newInput: QueryInput, options?: EditorOptions): Promise { if (this._sqlEditor) { this._sqlEditor.clearInput(); } @@ -576,22 +575,22 @@ export class QueryEditor extends BaseEditor { * This will create only the SQL editor if the results editor does not yet exist for the * given QueryInput. */ - private _setNewInput(newInput: QueryInput, options?: EditorOptions): TPromise { + private _setNewInput(newInput: QueryInput, options?: EditorOptions): Promise { // Promises that will ensure proper ordering of editor creation logic - let createEditors: () => TPromise; - let onEditorsCreated: (result) => TPromise; + let createEditors: () => Promise; + let onEditorsCreated: (result) => Promise; // If both editors exist, create joined promises - one for each editor if (this._isResultsEditorVisible()) { createEditors = () => { - return TPromise.join([ + return Promise.all([ this._createEditor(newInput.results, this._resultsEditorContainer, this.group), this._createEditor(newInput.sql, this._sqlEditorContainer, this.group) ]); }; onEditorsCreated = (result: IEditor[]) => { - return TPromise.join([ + return Promise.all([ this._onResultsEditorCreated(result[0], newInput.results, options), this._onSqlEditorCreated(result[1], newInput.sql, options) ]); @@ -603,16 +602,16 @@ export class QueryEditor extends BaseEditor { return this._createEditor(newInput.sql, this._sqlEditorContainer, this.group); }; onEditorsCreated = (result: TextResourceEditor) => { - return TPromise.join([ + return Promise.all([ this._onSqlEditorCreated(result, newInput.sql, options) ]); }; } // Create a promise to re render the layout after the editor creation logic - let doLayout: () => TPromise = () => { + let doLayout: () => Promise = () => { this._doLayout(); - return TPromise.as(undefined); + return Promise.resolve(undefined); }; // Run all three steps synchronously @@ -632,16 +631,16 @@ export class QueryEditor extends BaseEditor { /** * Create a single editor based on the type of the given EditorInput. */ - private _createEditor(editorInput: EditorInput, container: HTMLElement, group: IEditorGroup): TPromise { + private _createEditor(editorInput: EditorInput, container: HTMLElement, group: IEditorGroup): Promise { const descriptor = this._editorDescriptorService.getEditor(editorInput); if (!descriptor) { - return TPromise.wrapError(new Error(strings.format('Can not find a registered editor for the input {0}', editorInput))); + return Promise.reject(new Error(strings.format('Can not find a registered editor for the input {0}', editorInput))); } let editor = descriptor.instantiate(this._instantiationService); editor.create(container); editor.setVisible(this.isVisible(), group); - return TPromise.as(editor); + return Promise.resolve(editor); } /** @@ -655,7 +654,7 @@ export class QueryEditor extends BaseEditor { /** * Sets input for the results editor after it has been created. */ - private _onResultsEditorCreated(resultsEditor: QueryResultsEditor, resultsInput: QueryResultsInput, options: EditorOptions): TPromise { + private _onResultsEditorCreated(resultsEditor: QueryResultsEditor, resultsInput: QueryResultsInput, options: EditorOptions): Promise { this._resultsEditor = resultsEditor; return this._resultsEditor.setInput(resultsInput, options); } diff --git a/src/sql/parts/query/editor/queryResultsEditor.ts b/src/sql/parts/query/editor/queryResultsEditor.ts index e0a667a24df1..cc1b127e085d 100644 --- a/src/sql/parts/query/editor/queryResultsEditor.ts +++ b/src/sql/parts/query/editor/queryResultsEditor.ts @@ -5,7 +5,6 @@ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorOptions } from 'vs/workbench/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -148,10 +147,10 @@ export class QueryResultsEditor extends BaseEditor { this.resultsView.layout(dimension); } - setInput(input: QueryResultsInput, options: EditorOptions): TPromise { + setInput(input: QueryResultsInput, options: EditorOptions): Promise { super.setInput(input, options, CancellationToken.None); this.resultsView.input = input; - return TPromise.wrap(null); + return Promise.resolve(null); } clearInput() { diff --git a/src/sql/parts/query/editor/queryResultsView.ts b/src/sql/parts/query/editor/queryResultsView.ts index 6ca78e392dc8..04538ace20cc 100644 --- a/src/sql/parts/query/editor/queryResultsView.ts +++ b/src/sql/parts/query/editor/queryResultsView.ts @@ -18,7 +18,7 @@ import * as nls from 'vs/nls'; import { PanelViewlet } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import * as DOM from 'vs/base/browser/dom'; -import { once, anyEvent } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; class ResultsView extends Disposable implements IPanelView { @@ -42,7 +42,7 @@ class ResultsView extends Disposable implements IPanelView { this.panelViewlet.addPanels([ { panel: this.messagePanel, size: this.messagePanel.minimumSize, index: 1 } ]); - anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(e => { + Event.any(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(e => { let size = this.gridPanel.maximumBodySize; if (size < 1 && this.gridPanel.isVisible()) { this.gridPanel.setVisible(false); @@ -62,7 +62,7 @@ class ResultsView extends Disposable implements IPanelView { this.panelViewlet.addPanels([{ panel: this.gridPanel, index: 0, size: panelSize }]); } }); - let resizeList = anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(() => { + let resizeList = Event.any(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(() => { let panelSize: number; if (this.state && this.state.gridPanelSize) { panelSize = this.state.gridPanelSize; @@ -78,7 +78,7 @@ class ResultsView extends Disposable implements IPanelView { this.panelViewlet.resizePanel(this.gridPanel, panelSize); }); // once the user changes the sash we should stop trying to resize the grid - once(this.panelViewlet.onDidSashChange)(e => { + Event.once(this.panelViewlet.onDidSashChange)(e => { this.needsGridResize = false; resizeList.dispose(); }); diff --git a/src/sql/parts/query/execution/keyboardQueryActions.ts b/src/sql/parts/query/execution/keyboardQueryActions.ts index 228f443d1a8a..525d5f542b0e 100644 --- a/src/sql/parts/query/execution/keyboardQueryActions.ts +++ b/src/sql/parts/query/execution/keyboardQueryActions.ts @@ -7,7 +7,6 @@ import nls = require('vs/nls'); import { Action } from 'vs/base/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import * as azdata from 'azdata'; @@ -71,13 +70,13 @@ export class FocusOnCurrentQueryKeyboardAction extends Action { this.enabled = true; } - public run(): TPromise { + public run(): Promise { let editor = this._editorService.activeControl; if (editor && editor instanceof QueryEditor) { let queryEditor: QueryEditor = editor; queryEditor.focus(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -98,13 +97,13 @@ export class RunQueryKeyboardAction extends Action { this.enabled = true; } - public run(): TPromise { + public run(): Promise { let editor = this._editorService.activeControl; if (editor && (editor instanceof QueryEditor || editor instanceof EditDataEditor)) { let queryEditor: QueryEditor | EditDataEditor = editor; queryEditor.runQuery(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -124,13 +123,13 @@ export class RunCurrentQueryKeyboardAction extends Action { this.enabled = true; } - public run(): TPromise { + public run(): Promise { let editor = this._editorService.activeControl; if (editor && editor instanceof QueryEditor) { let queryEditor: QueryEditor = editor; queryEditor.runCurrentQuery(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -147,13 +146,13 @@ export class RunCurrentQueryWithActualPlanKeyboardAction extends Action { this.enabled = true; } - public run(): TPromise { + public run(): Promise { let editor = this._editorService.activeControl; if (editor && editor instanceof QueryEditor) { let queryEditor: QueryEditor = editor; queryEditor.runCurrentQueryWithActualPlan(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -174,13 +173,13 @@ export class CancelQueryKeyboardAction extends Action { this.enabled = true; } - public run(): TPromise { + public run(): Promise { let editor = this._editorService.activeControl; if (editor && (editor instanceof QueryEditor || editor instanceof EditDataEditor)) { let queryEditor: QueryEditor | EditDataEditor = editor; queryEditor.cancelQuery(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -200,13 +199,13 @@ export class RefreshIntellisenseKeyboardAction extends Action { this.enabled = true; } - public run(): TPromise { + public run(): Promise { let editor = this._editorService.activeControl; if (editor && editor instanceof QueryEditor) { let queryEditor: QueryEditor = editor; queryEditor.rebuildIntelliSenseCache(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -227,13 +226,13 @@ export class ToggleQueryResultsKeyboardAction extends Action { this.enabled = true; } - public run(): TPromise { + public run(): Promise { let editor = this._editorService.activeControl; if (editor && editor instanceof QueryEditor) { let queryEditor: QueryEditor = editor; queryEditor.toggleResultsEditorVisibility(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -253,12 +252,12 @@ export class RunQueryShortcutAction extends Action { super(RunQueryShortcutAction.ID); } - public run(index: number): TPromise { - let promise: Thenable = TPromise.as(null); + public run(index: number): Promise { + let promise: Thenable = Promise.resolve(null); runActionOnActiveQueryEditor(this._editorService, (editor) => { promise = this.runQueryShortcut(editor, index); }); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { promise.then(success => resolve(null), err => resolve(null)); }); } @@ -280,7 +279,7 @@ export class RunQueryShortcutAction extends Action { let shortcutText = this.getShortcutText(shortcutIndex); if (!shortcutText.trim()) { // no point going further - return TPromise.as(null); + return Promise.resolve(null); } // if the selection isn't empty then execute the selection @@ -294,7 +293,7 @@ export class RunQueryShortcutAction extends Action { return null; }); } else { - return TPromise.as(null); + return Promise.resolve(null); } } @@ -336,9 +335,9 @@ export class RunQueryShortcutAction extends Action { return parameterText; }); } - return TPromise.as(parameterText); + return Promise.resolve(parameterText); } - return TPromise.as(''); + return Promise.resolve(''); } private isProcWithSingleArgument(result: azdata.SimpleExecuteResult): number { @@ -410,7 +409,7 @@ export class ParseSyntaxAction extends Action { this.enabled = true; } - public run(): TPromise { + public run(): Promise { let editor = this._editorService.activeControl; if (editor && editor instanceof QueryEditor) { let queryEditor: QueryEditor = editor; @@ -442,7 +441,7 @@ export class ParseSyntaxAction extends Action { } - return TPromise.as(null); + return Promise.resolve(null); } /** diff --git a/src/sql/parts/query/execution/queryActions.ts b/src/sql/parts/query/execution/queryActions.ts index 8ac0758290ae..15fbce728844 100644 --- a/src/sql/parts/query/execution/queryActions.ts +++ b/src/sql/parts/query/execution/queryActions.ts @@ -10,7 +10,6 @@ import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown'; import { Action, IActionItem, IActionRunner } from 'vs/base/common/actions'; import { EventEmitter } from 'sql/base/common/eventEmitter'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachEditableDropdownStyler, attachSelectBoxStyler } from 'sql/platform/theme/common/styler'; @@ -52,7 +51,7 @@ export abstract class QueryTaskbarAction extends Action { /** * This method is executed when the button is clicked. */ - public abstract run(): TPromise; + public abstract run(): Promise; protected updateCssClass(enabledClass: string): void { // set the class, useful on change of label or icon @@ -115,7 +114,7 @@ export class RunQueryAction extends QueryTaskbarAction { this.label = nls.localize('runQueryLabel', 'Run'); } - public run(): TPromise { + public run(): Promise { if (!this.editor.isSelectionEmpty()) { if (this.isConnected(this.editor)) { // If we are already connected, run the query @@ -126,10 +125,10 @@ export class RunQueryAction extends QueryTaskbarAction { this.connectEditor(this.editor, RunQueryOnConnectionMode.executeQuery, this.editor.getSelection()); } } - return TPromise.as(null); + return Promise.resolve(null); } - public runCurrent(): TPromise { + public runCurrent(): Promise { if (!this.editor.isSelectionEmpty()) { if (this.isConnected(this.editor)) { // If we are already connected, run the query @@ -140,7 +139,7 @@ export class RunQueryAction extends QueryTaskbarAction { this.connectEditor(this.editor, RunQueryOnConnectionMode.executeCurrentQuery, this.editor.getSelection(false)); } } - return TPromise.as(null); + return Promise.resolve(null); } public runQuery(editor: QueryEditor, runCurrentStatement: boolean = false) { @@ -186,11 +185,11 @@ export class CancelQueryAction extends QueryTaskbarAction { this.label = nls.localize('cancelQueryLabel', 'Cancel'); } - public run(): TPromise { + public run(): Promise { if (this.isConnected(this.editor)) { this._queryModelService.cancelQuery(this.editor.currentQueryInput.uri); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -211,7 +210,7 @@ export class EstimatedQueryPlanAction extends QueryTaskbarAction { this.label = nls.localize('estimatedQueryPlan', 'Explain'); } - public run(): TPromise { + public run(): Promise { if (!this.editor.isSelectionEmpty()) { if (this.isConnected(this.editor)) { // If we are already connected, run the query @@ -222,7 +221,7 @@ export class EstimatedQueryPlanAction extends QueryTaskbarAction { this.connectEditor(this.editor, RunQueryOnConnectionMode.estimatedQueryPlan, this.editor.getSelection()); } } - return TPromise.as(null); + return Promise.resolve(null); } public runQuery(editor: QueryEditor) { @@ -251,7 +250,7 @@ export class ActualQueryPlanAction extends QueryTaskbarAction { this.label = nls.localize('actualQueryPlan', "Actual"); } - public run(): TPromise { + public run(): Promise { if (!this.editor.isSelectionEmpty()) { if (this.isConnected(this.editor)) { // If we are already connected, run the query @@ -262,7 +261,7 @@ export class ActualQueryPlanAction extends QueryTaskbarAction { this.connectEditor(this.editor, RunQueryOnConnectionMode.actualQueryPlan, this.editor.getSelection()); } } - return TPromise.as(null); + return Promise.resolve(null); } public runQuery(editor: QueryEditor) { @@ -298,11 +297,11 @@ export class DisconnectDatabaseAction extends QueryTaskbarAction { this.label = nls.localize('disconnectDatabaseLabel', 'Disconnect'); } - public run(): TPromise { + public run(): Promise { // Call disconnectEditor regardless of the connection state and let the ConnectionManagementService // determine if we need to disconnect, cancel an in-progress conneciton, or do nothing this._connectionManagementService.disconnectEditor(this.editor.currentQueryInput); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -336,9 +335,9 @@ export class ConnectDatabaseAction extends QueryTaskbarAction { this.label = label; } - public run(): TPromise { + public run(): Promise { this.connectEditor(this.editor); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -391,7 +390,7 @@ export class ToggleConnectDatabaseAction extends QueryTaskbarAction { } } - public run(): TPromise { + public run(): Promise { if (this.connected) { // Call disconnectEditor regardless of the connection state and let the ConnectionManagementService // determine if we need to disconnect, cancel an in-progress connection, or do nothing @@ -399,7 +398,7 @@ export class ToggleConnectDatabaseAction extends QueryTaskbarAction { } else { this.connectEditor(this.editor); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -420,8 +419,8 @@ export class ListDatabasesAction extends QueryTaskbarAction { this.class = ListDatabasesAction.EnabledClass; } - public run(): TPromise { - return TPromise.as(null); + public run(): Promise { + return Promise.resolve(null); } } diff --git a/src/sql/parts/queryPlan/queryPlanEditor.ts b/src/sql/parts/queryPlan/queryPlanEditor.ts index e29476b7a35f..a21711db8a09 100644 --- a/src/sql/parts/queryPlan/queryPlanEditor.ts +++ b/src/sql/parts/queryPlan/queryPlanEditor.ts @@ -5,7 +5,6 @@ import 'vs/css!sql/parts/query/editor/media/queryEditor'; import * as DOM from 'vs/base/browser/dom'; -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -72,9 +71,9 @@ export class QueryPlanEditor extends BaseEditor { public layout(dimension: DOM.Dimension): void { } - public setInput(input: QueryPlanInput, options: EditorOptions): Thenable { + public setInput(input: QueryPlanInput, options: EditorOptions): Promise { if (this.input instanceof QueryPlanInput && this.input.matches(input)) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } if (!input.hasInitialized) { diff --git a/src/sql/parts/queryPlan/queryPlanInput.ts b/src/sql/parts/queryPlan/queryPlanInput.ts index 0ee498de1253..4e7493d0f6d7 100644 --- a/src/sql/parts/queryPlan/queryPlanInput.ts +++ b/src/sql/parts/queryPlan/queryPlanInput.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; @@ -49,7 +48,7 @@ export class QueryPlanInput extends EditorInput { return undefined; } - public resolve(refresh?: boolean): TPromise { + public resolve(refresh?: boolean): Promise { return undefined; } diff --git a/src/sql/parts/taskHistory/common/taskHistory.contribution.ts b/src/sql/parts/taskHistory/common/taskHistory.contribution.ts index 731d23d54137..8b18c5eca34c 100644 --- a/src/sql/parts/taskHistory/common/taskHistory.contribution.ts +++ b/src/sql/parts/taskHistory/common/taskHistory.contribution.ts @@ -7,7 +7,7 @@ import nls = require('vs/nls'); import 'vs/css!sql/media/actionBarLabel'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -125,4 +125,13 @@ configurationRegistry.registerConfiguration({ 'type': 'array' } } +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: localize({ key: 'miViewTasks', comment: ['&& denotes a mnemonic'] }, "&&Tasks") + }, + order: 2 }); \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/taskAction.ts b/src/sql/parts/taskHistory/viewlet/taskAction.ts index 366bcd255e98..76206db9b216 100644 --- a/src/sql/parts/taskHistory/viewlet/taskAction.ts +++ b/src/sql/parts/taskHistory/viewlet/taskAction.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import { ITaskService } from 'sql/platform/taskHistory/common/taskService'; import { TaskNode } from 'sql/parts/taskHistory/common/taskNode'; @@ -24,7 +23,7 @@ export class CancelAction extends Action { ) { super(id, label); } - public run(element: TaskNode): TPromise { + public run(element: TaskNode): Promise { if (element instanceof TaskNode) { this._taskService.cancelTask(element.providerName, element.id).then((result) => { if (!result) { @@ -33,10 +32,10 @@ export class CancelAction extends Action { } }, error => { this.showError(error); - return TPromise.as(true); + return Promise.resolve(true); }); } - return TPromise.as(true); + return Promise.resolve(true); } private showError(errorMessage: string) { @@ -58,12 +57,12 @@ export class ScriptAction extends Action { super(id, label); } - public run(element: TaskNode): TPromise { + public run(element: TaskNode): Promise { if (element instanceof TaskNode) { if (element.script && element.script !== '') { this._queryEditorService.newSqlEditor(element.script); } } - return TPromise.as(true); + return Promise.resolve(true); } } \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts b/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts index 338497171960..e2a97e62fddc 100644 --- a/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts +++ b/src/sql/parts/taskHistory/viewlet/taskHistoryActionProvider.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { ContributableActionProvider } from 'vs/workbench/browser/actions'; import { IAction } from 'vs/base/common/actions'; diff --git a/src/sql/parts/taskHistory/viewlet/taskHistoryDataSource.ts b/src/sql/parts/taskHistory/viewlet/taskHistoryDataSource.ts index e18a53575756..5911fb9ee05f 100644 --- a/src/sql/parts/taskHistory/viewlet/taskHistoryDataSource.ts +++ b/src/sql/parts/taskHistory/viewlet/taskHistoryDataSource.ts @@ -7,7 +7,6 @@ import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; import { TaskNode } from 'sql/parts/taskHistory/common/taskNode'; -import { TPromise } from 'vs/base/common/winjs.base'; /** * Implements the DataSource(that returns a parent/children of an element) for the task history @@ -39,17 +38,17 @@ export class TaskHistoryDataSource implements IDataSource { /** * Returns the element's children as an array in a promise. */ - public getChildren(tree: ITree, element: any): TPromise { + public getChildren(tree: ITree, element: any): Promise { if (element instanceof TaskNode) { - return TPromise.as((element).children); + return Promise.resolve((element).children); } - return TPromise.as(null); + return Promise.resolve(null); } /** * Returns the element's parent in a promise. */ - public getParent(tree: ITree, element: any): TPromise { - return TPromise.as(null); + public getParent(tree: ITree, element: any): Promise { + return Promise.resolve(null); } } \ No newline at end of file diff --git a/src/sql/parts/taskHistory/viewlet/taskHistoryViewlet.ts b/src/sql/parts/taskHistory/viewlet/taskHistoryViewlet.ts index 24f859b5474c..f3b2803a9b47 100644 --- a/src/sql/parts/taskHistory/viewlet/taskHistoryViewlet.ts +++ b/src/sql/parts/taskHistory/viewlet/taskHistoryViewlet.ts @@ -6,7 +6,6 @@ 'use strict'; import 'vs/css!sql/media/icons/common-icons'; import 'vs/css!./media/taskHistoryViewlet'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Viewlet } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { toggleClass, Dimension } from 'vs/base/browser/dom'; @@ -53,13 +52,13 @@ export class TaskHistoryViewlet extends Viewlet { }); } - public create(parent: HTMLElement): TPromise { + public create(parent: HTMLElement): Promise { super.create(parent); this._root = parent; this._taskHistoryView = this._instantiationService.createInstance(TaskHistoryView); this._taskHistoryView.renderBody(parent); - return TPromise.as(null); + return Promise.resolve(null); } public setVisible(visible: boolean): void { diff --git a/src/sql/parts/tasks/dialog/taskDialogEditor.ts b/src/sql/parts/tasks/dialog/taskDialogEditor.ts index 30ea6853f9e1..79c44abacc09 100644 --- a/src/sql/parts/tasks/dialog/taskDialogEditor.ts +++ b/src/sql/parts/tasks/dialog/taskDialogEditor.ts @@ -5,7 +5,6 @@ import 'vs/css!sql/parts/query/editor/media/queryEditor'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Dimension } from 'vs/base/browser/dom'; import { EditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; @@ -53,9 +52,9 @@ export class TaskDialogEditor extends BaseEditor { public layout(dimension: Dimension): void { } - public setInput(input: TaskDialogInput, options: EditorOptions): Thenable { + public setInput(input: TaskDialogInput, options: EditorOptions): Promise { if (this.input instanceof TaskDialogInput && this.input.matches(input)) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } if (!input.hasInitialized) { diff --git a/src/sql/parts/tasks/dialog/taskDialogInput.ts b/src/sql/parts/tasks/dialog/taskDialogInput.ts index 4d6ca2b40828..72da8c2ac15c 100644 --- a/src/sql/parts/tasks/dialog/taskDialogInput.ts +++ b/src/sql/parts/tasks/dialog/taskDialogInput.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; @@ -43,7 +42,7 @@ export class TaskDialogInput extends EditorInput { return this._connection; } - public resolve(refresh?: boolean): TPromise { + public resolve(refresh?: boolean): Promise { return undefined; } diff --git a/src/sql/platform/capabilities/common/capabilitiesService.ts b/src/sql/platform/capabilities/common/capabilitiesService.ts index 6b25f9baae7a..0e2a557e6d29 100644 --- a/src/sql/platform/capabilities/common/capabilitiesService.ts +++ b/src/sql/platform/capabilities/common/capabilitiesService.ts @@ -21,7 +21,6 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { Registry } from 'vs/platform/registry/common/platform'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; export const SERVICE_ID = 'capabilitiesService'; export const HOST_NAME = 'azdata'; @@ -135,9 +134,8 @@ export class CapabilitiesService extends Disposable implements ICapabilitiesServ this._register(extentionManagementService.onDidUninstallExtension(({ identifier }) => { const connectionProvider = 'connectionProvider'; - let extensionid = getIdFromLocalExtensionId(identifier.id); extensionService.getExtensions().then(i => { - let extension = i.find(c => c.id === extensionid); + let extension = i.find(c => c.identifier.value.toLowerCase() === identifier.id.toLowerCase()); if (extension && extension.contributes && extension.contributes[connectionProvider] && extension.contributes[connectionProvider].providerId) { diff --git a/src/sql/platform/connection/common/connectionManagementService.ts b/src/sql/platform/connection/common/connectionManagementService.ts index aeb93b83bf81..555d6a2e3cb4 100644 --- a/src/sql/platform/connection/common/connectionManagementService.ts +++ b/src/sql/platform/connection/common/connectionManagementService.ts @@ -651,7 +651,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.AddServerGroup); return new Promise((resolve, reject) => { this._connectionStore.saveProfileGroup(profile).then(groupId => { - this._onAddConnectionProfile.fire(); + this._onAddConnectionProfile.fire(undefined); resolve(groupId); }).catch(err => { reject(err); @@ -1220,7 +1220,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti return new Promise((resolve, reject) => { this._connectionStore.editGroup(group).then(groupId => { this.refreshEditorTitles(); - this._onAddConnectionProfile.fire(); + this._onAddConnectionProfile.fire(undefined); resolve(null); }).catch(err => { reject(err); diff --git a/src/sql/platform/dacfx/common/dacFxService.ts b/src/sql/platform/dacfx/common/dacFxService.ts index e84d402fb191..ace6b445a5c4 100644 --- a/src/sql/platform/dacfx/common/dacFxService.ts +++ b/src/sql/platform/dacfx/common/dacFxService.ts @@ -8,7 +8,6 @@ import * as azdata from 'azdata'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; -import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; export const SERVICE_ID = 'dacFxService'; @@ -79,13 +78,13 @@ export class DacFxService implements IDacFxService { let providerId: string = this._connectionService.getProviderIdFromUri(uri); if (!providerId) { - return TPromise.wrapError(new Error(localize('providerIdNotValidError', "Connection is required in order to interact with DacFxService"))); + return Promise.reject(new Error(localize('providerIdNotValidError', "Connection is required in order to interact with DacFxService"))); } let handler = this._providers[providerId]; if (handler) { return action(handler); } else { - return TPromise.wrapError(new Error(localize('noHandlerRegistered', "No Handler Registered"))); + return Promise.reject(new Error(localize('noHandlerRegistered', "No Handler Registered"))); } } } \ No newline at end of file diff --git a/src/sql/platform/dashboard/common/dashboardRegistry.ts b/src/sql/platform/dashboard/common/dashboardRegistry.ts index 16b76ce80e06..4cfb6c2a5881 100644 --- a/src/sql/platform/dashboard/common/dashboardRegistry.ts +++ b/src/sql/platform/dashboard/common/dashboardRegistry.ts @@ -164,7 +164,7 @@ const dashboardContrib: IJSONSchema = { } }; -ExtensionsRegistry.registerExtensionPoint('dashboard', [], dashboardContrib).setHandler(extensions => { +ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'dashboard', jsonSchema: dashboardContrib }).setHandler(extensions => { function handleCommand(contrib: ProviderProperties, extension: IExtensionPointUser) { dashboardRegistry.registerDashboardProvider(contrib.provider, contrib); diff --git a/src/sql/platform/jobManagement/common/jobActions.ts b/src/sql/platform/jobManagement/common/jobActions.ts index b336a7ad810f..c05fe7328754 100644 --- a/src/sql/platform/jobManagement/common/jobActions.ts +++ b/src/sql/platform/jobManagement/common/jobActions.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import * as azdata from 'azdata'; @@ -47,8 +46,8 @@ export class JobsRefreshAction extends Action { super(JobsRefreshAction.ID, JobsRefreshAction.LABEL, 'refreshIcon'); } - public run(context: IJobActionInfo): TPromise { - return new TPromise((resolve, reject) => { + public run(context: IJobActionInfo): Promise { + return new Promise((resolve, reject) => { if (context) { context.jobHistoryComponent.refreshJobs(); resolve(true); @@ -68,8 +67,8 @@ export class NewJobAction extends Action { super(NewJobAction.ID, NewJobAction.LABEL, 'newStepIcon'); } - public run(context: JobsViewComponent): TPromise { - return new TPromise((resolve, reject) => { + public run(context: JobsViewComponent): Promise { + return new Promise((resolve, reject) => { try { context.openCreateJobDialog(); resolve(true); @@ -94,12 +93,12 @@ export class RunJobAction extends Action { super(RunJobAction.ID, RunJobAction.LABEL, 'start'); } - public run(context: IJobActionInfo): TPromise { + public run(context: IJobActionInfo): Promise { let jobName = context.targetObject.name; let ownerUri = context.ownerUri; let refreshAction = this.instantationService.createInstance(JobsRefreshAction); this.telemetryService.publicLog(TelemetryKeys.RunAgentJob); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(result => { if (result.success) { var startMsg = nls.localize('jobSuccessfullyStarted', ': The job was successfully started.'); @@ -129,12 +128,12 @@ export class StopJobAction extends Action { super(StopJobAction.ID, StopJobAction.LABEL, 'stop'); } - public run(context: IJobActionInfo): TPromise { + public run(context: IJobActionInfo): Promise { let jobName = context.targetObject.name; let ownerUri = context.ownerUri; let refreshAction = this.instantationService.createInstance(JobsRefreshAction); this.telemetryService.publicLog(TelemetryKeys.StopAgentJob); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(result => { if (result.success) { refreshAction.run(context); @@ -160,12 +159,12 @@ export class EditJobAction extends Action { super(EditJobAction.ID, EditJobAction.LABEL, 'edit'); } - public run(actionInfo: IJobActionInfo): TPromise { + public run(actionInfo: IJobActionInfo): Promise { this._commandService.executeCommand( 'agent.openJobDialog', actionInfo.ownerUri, actionInfo.targetObject); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -182,7 +181,7 @@ export class DeleteJobAction extends Action { super(DeleteJobAction.ID, DeleteJobAction.LABEL); } - public run(actionInfo: IJobActionInfo): TPromise { + public run(actionInfo: IJobActionInfo): Promise { let self = this; let job = actionInfo.targetObject as azdata.AgentJobInfo; self._notificationService.prompt( @@ -208,7 +207,7 @@ export class DeleteJobAction extends Action { run: () => { } }] ); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -224,11 +223,11 @@ export class NewStepAction extends Action { super(NewStepAction.ID, NewStepAction.LABEL, 'newStepIcon'); } - public run(context: JobHistoryComponent): TPromise { + public run(context: JobHistoryComponent): Promise { let ownerUri = context.ownerUri; let server = context.serverName; let jobInfo = context.agentJobInfo; - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { resolve(this._commandService.executeCommand('agent.openNewStepDialog', ownerUri, server, jobInfo, null)); }); } @@ -248,7 +247,7 @@ export class DeleteStepAction extends Action { super(DeleteStepAction.ID, DeleteStepAction.LABEL); } - public run(actionInfo: IJobActionInfo): TPromise { + public run(actionInfo: IJobActionInfo): Promise { let self = this; let step = actionInfo.targetObject as azdata.AgentJobStepInfo; let refreshAction = this.instantationService.createInstance(JobsRefreshAction); @@ -276,7 +275,7 @@ export class DeleteStepAction extends Action { run: () => { } }] ); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -292,8 +291,8 @@ export class NewAlertAction extends Action { super(NewAlertAction.ID, NewAlertAction.LABEL, 'newStepIcon'); } - public run(context: AlertsViewComponent): TPromise { - return new TPromise((resolve, reject) => { + public run(context: AlertsViewComponent): Promise { + return new Promise((resolve, reject) => { try { context.openCreateAlertDialog(); resolve(true); @@ -314,12 +313,12 @@ export class EditAlertAction extends Action { super(EditAlertAction.ID, EditAlertAction.LABEL); } - public run(actionInfo: IJobActionInfo): TPromise { + public run(actionInfo: IJobActionInfo): Promise { this._commandService.executeCommand( 'agent.openAlertDialog', actionInfo.ownerUri, actionInfo.targetObject); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -337,7 +336,7 @@ export class DeleteAlertAction extends Action { super(DeleteAlertAction.ID, DeleteAlertAction.LABEL); } - public run(actionInfo: IJobActionInfo): TPromise { + public run(actionInfo: IJobActionInfo): Promise { let self = this; let alert = actionInfo.targetObject as azdata.AgentAlertInfo; self._notificationService.prompt( @@ -363,7 +362,7 @@ export class DeleteAlertAction extends Action { run: () => { } }] ); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -378,8 +377,8 @@ export class NewOperatorAction extends Action { super(NewOperatorAction.ID, NewOperatorAction.LABEL, 'newStepIcon'); } - public run(context: OperatorsViewComponent): TPromise { - return new TPromise((resolve, reject) => { + public run(context: OperatorsViewComponent): Promise { + return new Promise((resolve, reject) => { try { context.openCreateOperatorDialog(); resolve(true); @@ -400,12 +399,12 @@ export class EditOperatorAction extends Action { super(EditOperatorAction.ID, EditOperatorAction.LABEL); } - public run(actionInfo: IJobActionInfo): TPromise { + public run(actionInfo: IJobActionInfo): Promise { this._commandService.executeCommand( 'agent.openOperatorDialog', actionInfo.ownerUri, actionInfo.targetObject); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -422,7 +421,7 @@ export class DeleteOperatorAction extends Action { super(DeleteOperatorAction.ID, DeleteOperatorAction.LABEL); } - public run(actionInfo: IJobActionInfo): TPromise { + public run(actionInfo: IJobActionInfo): Promise { const self = this; let operator = actionInfo.targetObject as azdata.AgentOperatorInfo; self._notificationService.prompt( @@ -448,7 +447,7 @@ export class DeleteOperatorAction extends Action { run: () => { } }] ); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -464,8 +463,8 @@ export class NewProxyAction extends Action { super(NewProxyAction.ID, NewProxyAction.LABEL, 'newStepIcon'); } - public run(context: ProxiesViewComponent): TPromise { - return new TPromise((resolve, reject) => { + public run(context: ProxiesViewComponent): Promise { + return new Promise((resolve, reject) => { try { context.openCreateProxyDialog(); resolve(true); @@ -486,12 +485,12 @@ export class EditProxyAction extends Action { super(EditProxyAction.ID, EditProxyAction.LABEL); } - public run(actionInfo: IJobActionInfo): TPromise { + public run(actionInfo: IJobActionInfo): Promise { this._commandService.executeCommand( 'agent.openProxyDialog', actionInfo.ownerUri, actionInfo.targetObject); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -508,7 +507,7 @@ export class DeleteProxyAction extends Action { super(DeleteProxyAction.ID, DeleteProxyAction.LABEL); } - public run(actionInfo: IJobActionInfo): TPromise { + public run(actionInfo: IJobActionInfo): Promise { let self = this; let proxy = actionInfo.targetObject as azdata.AgentProxyInfo; self._notificationService.prompt( @@ -534,6 +533,6 @@ export class DeleteProxyAction extends Action { run: () => { } }] ); - return TPromise.as(true); + return Promise.resolve(true); } } \ No newline at end of file diff --git a/src/sql/platform/jobManagement/common/jobManagementService.ts b/src/sql/platform/jobManagement/common/jobManagementService.ts index 3bb7f42fe33f..e703ac253342 100644 --- a/src/sql/platform/jobManagement/common/jobManagementService.ts +++ b/src/sql/platform/jobManagement/common/jobManagementService.ts @@ -7,7 +7,6 @@ import { localize } from 'vs/nls'; import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { Event, Emitter } from 'vs/base/common/event'; @@ -115,13 +114,13 @@ export class JobManagementService implements IJobManagementService { let providerId: string = this._connectionService.getProviderIdFromUri(uri); if (!providerId) { - return TPromise.wrapError(new Error(localize('providerIdNotValidError', "Connection is required in order to interact with JobManagementService"))); + return Promise.reject(new Error(localize('providerIdNotValidError', "Connection is required in order to interact with JobManagementService"))); } let handler = this._providers[providerId]; if (handler) { return action(handler); } else { - return TPromise.wrapError(new Error(localize('noHandlerRegistered', "No Handler Registered"))); + return Promise.reject(new Error(localize('noHandlerRegistered', "No Handler Registered"))); } } diff --git a/src/sql/platform/query/common/queryManagement.ts b/src/sql/platform/query/common/queryManagement.ts index 13ce869f60f4..93b4b90d55c4 100644 --- a/src/sql/platform/query/common/queryManagement.ts +++ b/src/sql/platform/query/common/queryManagement.ts @@ -8,7 +8,6 @@ import { IConnectionManagementService } from 'sql/platform/connection/common/con import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as TelemetryKeys from 'sql/common/telemetryKeys'; import * as TelemetryUtils from 'sql/common/telemetryUtilities'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -179,13 +178,13 @@ export class QueryManagementService implements IQueryManagementService { let providerId: string = this._connectionService.getProviderIdFromUri(uri); if (!providerId) { - return TPromise.wrapError(new Error('Connection is required in order to interact with queries')); + return Promise.reject(new Error('Connection is required in order to interact with queries')); } let handler = this._requestHandlers.get(providerId); if (handler) { return action(handler); } else { - return TPromise.wrapError(new Error('No Handler Registered')); + return Promise.reject(new Error('No Handler Registered')); } } diff --git a/src/sql/platform/query/common/queryModelService.ts b/src/sql/platform/query/common/queryModelService.ts index f5650e0085c9..40c4673dc8ee 100644 --- a/src/sql/platform/query/common/queryModelService.ts +++ b/src/sql/platform/query/common/queryModelService.ts @@ -23,7 +23,6 @@ import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar'; import * as platform from 'vs/platform/registry/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; 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 types from 'vs/base/common/types'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -460,7 +459,7 @@ export class QueryModelService implements IQueryModelService { if (queryRunner) { return queryRunner.disposeEdit(ownerUri); } - return TPromise.as(null); + return Promise.resolve(null); } public updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable { @@ -475,7 +474,7 @@ export class QueryModelService implements IQueryModelService { return Promise.reject(error); }); } - return TPromise.as(null); + return Promise.resolve(null); } public commitEdit(ownerUri): Thenable { @@ -490,7 +489,7 @@ export class QueryModelService implements IQueryModelService { return Promise.reject(error); }); } - return TPromise.as(null); + return Promise.resolve(null); } public createRow(ownerUri: string): Thenable { @@ -499,7 +498,7 @@ export class QueryModelService implements IQueryModelService { if (queryRunner) { return queryRunner.createRow(ownerUri); } - return TPromise.as(null); + return Promise.resolve(null); } public deleteRow(ownerUri: string, rowId: number): Thenable { @@ -508,7 +507,7 @@ export class QueryModelService implements IQueryModelService { if (queryRunner) { return queryRunner.deleteRow(ownerUri, rowId); } - return TPromise.as(null); + return Promise.resolve(null); } public revertCell(ownerUri: string, rowId: number, columnId: number): Thenable { @@ -517,7 +516,7 @@ export class QueryModelService implements IQueryModelService { if (queryRunner) { return queryRunner.revertCell(ownerUri, rowId, columnId); } - return TPromise.as(null); + return Promise.resolve(null); } public revertRow(ownerUri: string, rowId: number): Thenable { @@ -526,7 +525,7 @@ export class QueryModelService implements IQueryModelService { if (queryRunner) { return queryRunner.revertRow(ownerUri, rowId); } - return TPromise.as(null); + return Promise.resolve(null); } public getQueryRunner(ownerUri): QueryRunner { diff --git a/src/sql/platform/query/common/queryRunner.ts b/src/sql/platform/query/common/queryRunner.ts index 9d00fe2afd75..9986f1e24ee7 100644 --- a/src/sql/platform/query/common/queryRunner.ts +++ b/src/sql/platform/query/common/queryRunner.ts @@ -24,7 +24,6 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { Emitter, Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResultSerializer } from 'sql/platform/node/resultSerializer'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { URI } from 'vs/base/common/uri'; @@ -187,7 +186,7 @@ export default class QueryRunner extends Disposable { private doRunQuery(input: azdata.ISelectionData, runCurrentStatement: boolean, runOptions?: azdata.ExecutionPlanOptions): Thenable; private doRunQuery(input, runCurrentStatement: boolean, runOptions?: azdata.ExecutionPlanOptions): Thenable { if (this.isExecuting) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } this._planXml = new Deferred(); this._batchSets = []; diff --git a/src/sql/platform/restore/common/restoreService.ts b/src/sql/platform/restore/common/restoreService.ts index 76f394eed3d2..a8edce50708b 100644 --- a/src/sql/platform/restore/common/restoreService.ts +++ b/src/sql/platform/restore/common/restoreService.ts @@ -5,7 +5,6 @@ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import * as azdata from 'azdata'; @@ -47,5 +46,5 @@ export interface IRestoreService { export const IRestoreDialogController = createDecorator('restoreDialogService'); export interface IRestoreDialogController { _serviceBrand: any; - showDialog(connection: IConnectionProfile): TPromise; + showDialog(connection: IConnectionProfile): Promise; } diff --git a/src/sql/platform/restore/common/restoreServiceImpl.ts b/src/sql/platform/restore/common/restoreServiceImpl.ts index 8e11ca4a49e7..ec2936bbee9e 100644 --- a/src/sql/platform/restore/common/restoreServiceImpl.ts +++ b/src/sql/platform/restore/common/restoreServiceImpl.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import * as types from 'vs/base/common/types'; @@ -290,8 +289,8 @@ export class RestoreDialogController implements IRestoreDialogController { }); } - public showDialog(connection: IConnectionProfile): TPromise { - return new TPromise((resolve, reject) => { + public showDialog(connection: IConnectionProfile): Promise { + return new Promise((resolve, reject) => { let result: void; this._ownerUri = this._connectionService.getConnectionUri(connection) diff --git a/src/sql/platform/serverGroup/common/serverGroupController.ts b/src/sql/platform/serverGroup/common/serverGroupController.ts index e60ac5b54a3e..c8cfbb7a0728 100644 --- a/src/sql/platform/serverGroup/common/serverGroupController.ts +++ b/src/sql/platform/serverGroup/common/serverGroupController.ts @@ -6,7 +6,6 @@ 'use strict'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; @@ -18,6 +17,6 @@ export interface IServerGroupDialogCallbacks { export const IServerGroupController = createDecorator('serverGroupController'); export interface IServerGroupController { _serviceBrand: any; - showCreateGroupDialog(connectionManagementService: IConnectionManagementService, callbacks?: IServerGroupDialogCallbacks): TPromise; - showEditGroupDialog(connectionManagementService: IConnectionManagementService, group: ConnectionProfileGroup): TPromise; + showCreateGroupDialog(connectionManagementService: IConnectionManagementService, callbacks?: IServerGroupDialogCallbacks): Promise; + showEditGroupDialog(connectionManagementService: IConnectionManagementService, group: ConnectionProfileGroup): Promise; } diff --git a/src/sql/platform/taskHistory/common/taskService.ts b/src/sql/platform/taskHistory/common/taskService.ts index f668cf60064d..4881eb7c7811 100644 --- a/src/sql/platform/taskHistory/common/taskService.ts +++ b/src/sql/platform/taskHistory/common/taskService.ts @@ -12,7 +12,6 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { localize } from 'vs/nls'; import Severity from 'vs/base/common/severity'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; @@ -125,7 +124,7 @@ export class TaskService implements ITaskService { } private cancelAllTasks(): Thenable { - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { let promises = this._taskQueue.children.map(task => { if (task.status === TaskStatus.InProgress || task.status === TaskStatus.NotStarted) { return this.cancelTask(task.providerName, task.id); @@ -151,14 +150,14 @@ export class TaskService implements ITaskService { this._onAddNewTask.fire(task); } - public beforeShutdown(): TPromise { + public beforeShutdown(): Promise { const message = localize('InProgressWarning', '1 or more tasks are in progress. Are you sure you want to quit?'); const options = [ localize('taskService.yes', "Yes"), localize('taskService.no', "No") ]; - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { let numOfInprogressTasks = this.getNumberOfInProgressTasks(); if (numOfInprogressTasks > 0) { this.dialogService.show(Severity.Warning, message, options).then(choice => { diff --git a/src/sql/platform/tasks/common/tasks.ts b/src/sql/platform/tasks/common/tasks.ts index c1b4bd7918cf..afe6e20fd5c9 100644 --- a/src/sql/platform/tasks/common/tasks.ts +++ b/src/sql/platform/tasks/common/tasks.ts @@ -5,7 +5,6 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import * as types from 'vs/base/common/types'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ILocalizedString, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -65,7 +64,7 @@ export abstract class Task { return TaskRegistry.registerTask(this.toITask()); } - public abstract runTask(accessor: ServicesAccessor, profile: IConnectionProfile, args: any): void | TPromise; + public abstract runTask(accessor: ServicesAccessor, profile: IConnectionProfile, args: any): void | Promise; } export interface ITaskHandlerDescription { diff --git a/src/sql/services/common/commonServiceInterface.service.ts b/src/sql/services/common/commonServiceInterface.service.ts index 75c6f29e4444..ec75ebf28b4b 100644 --- a/src/sql/services/common/commonServiceInterface.service.ts +++ b/src/sql/services/common/commonServiceInterface.service.ts @@ -50,12 +50,12 @@ export class SingleConnectionManagementService { private _contextKey: ConnectionContextKey ) { } - public changeDatabase(name: string): Thenable { - return this._connectionService.changeDatabase(this._uri, name).then(e => { + public changeDatabase(name: string): Promise { + return Promise.resolve(this._connectionService.changeDatabase(this._uri, name).then(e => { // we need to update our context this._contextKey.set(this.connectionInfo.connectionProfile); return e; - }); + })); } public get connectionInfo(): ConnectionManagementInfo { diff --git a/src/sql/workbench/api/electron-browser/mainThreadTasks.ts b/src/sql/workbench/api/electron-browser/mainThreadTasks.ts index 91f662b6683d..e0e597f654f7 100644 --- a/src/sql/workbench/api/electron-browser/mainThreadTasks.ts +++ b/src/sql/workbench/api/electron-browser/mainThreadTasks.ts @@ -6,7 +6,6 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { TaskRegistry, ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; import { @@ -42,7 +41,7 @@ export class MainThreadTasks implements MainThreadTasksShape { this._generateCommandsDocumentationRegistration.dispose(); } - $registerTask(id: string): TPromise { + $registerTask(id: string): Promise { this._disposables.set( id, TaskRegistry.registerTask(id, (accessor, profile: IConnectionProfile, ...args) => { @@ -55,7 +54,7 @@ export class MainThreadTasks implements MainThreadTasksShape { return undefined; } - $unregisterTask(id: string): TPromise { + $unregisterTask(id: string): Promise { if (this._disposables.has(id)) { this._disposables.get(id).dispose(); this._disposables.delete(id); diff --git a/src/sql/workbench/api/node/extHostAccountManagement.ts b/src/sql/workbench/api/node/extHostAccountManagement.ts index 08065fcbc844..832efb91a05a 100644 --- a/src/sql/workbench/api/node/extHostAccountManagement.ts +++ b/src/sql/workbench/api/node/extHostAccountManagement.ts @@ -6,7 +6,6 @@ 'use strict'; import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Disposable } from 'vs/workbench/api/node/extHostTypes'; import { ExtHostAccountManagementShape, @@ -111,7 +110,7 @@ export class ExtHostAccountManagement extends ExtHostAccountManagementShape { } public $accountsChanged(handle: number, accounts: azdata.Account[]): Thenable { - return this._onDidChangeAccounts.fire({ accounts: accounts }); + return Promise.resolve(this._onDidChangeAccounts.fire({ accounts: accounts })); } public $registerAccountProvider(providerMetadata: azdata.AccountProviderMetadata, provider: azdata.AccountProvider): Disposable { @@ -158,7 +157,7 @@ export class ExtHostAccountManagement extends ExtHostAccountManagementShape { private _withProvider(handle: number, callback: (provider: azdata.AccountProvider) => Thenable): Thenable { let provider = this._providers[handle]; if (provider === undefined) { - return TPromise.wrapError(new Error(`Provider ${handle} not found.`)); + return Promise.reject(new Error(`Provider ${handle} not found.`)); } return callback(provider.provider); } diff --git a/src/sql/workbench/api/node/extHostCredentialManagement.ts b/src/sql/workbench/api/node/extHostCredentialManagement.ts index 659ff95716df..974c4371a4e3 100644 --- a/src/sql/workbench/api/node/extHostCredentialManagement.ts +++ b/src/sql/workbench/api/node/extHostCredentialManagement.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import { SqlMainContext, MainThreadCredentialManagementShape, ExtHostCredentialManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import * as vscode from 'vscode'; @@ -71,7 +70,7 @@ export class ExtHostCredentialManagement extends ExtHostCredentialManagementShap let self = this; if (!namespaceId) { - return TPromise.wrapError(new Error('A namespace must be provided when retrieving a credential provider')); + return Promise.reject(new Error('A namespace must be provided when retrieving a credential provider')); } // When the registration promise has finished successfully, @@ -139,7 +138,7 @@ export class ExtHostCredentialManagement extends ExtHostCredentialManagementShap private _withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => Thenable): Thenable { let adapter = this._adapter[handle]; if (!(adapter instanceof ctor)) { - return TPromise.wrapError(new Error('no adapter found')); + return Promise.reject(new Error('no adapter found')); } return callback(adapter); } diff --git a/src/sql/workbench/api/node/extHostDashboardWebview.ts b/src/sql/workbench/api/node/extHostDashboardWebview.ts index 0005c06fb28d..a15e076c2556 100644 --- a/src/sql/workbench/api/node/extHostDashboardWebview.ts +++ b/src/sql/workbench/api/node/extHostDashboardWebview.ts @@ -77,7 +77,7 @@ export class ExtHostDashboardWebviews implements ExtHostDashboardWebviewsShape { $onClosed(handle: number): void { const webview = this._webviews.get(handle); - webview.onClosedEmitter.fire(); + webview.onClosedEmitter.fire(undefined); this._webviews.delete(handle); } diff --git a/src/sql/workbench/api/node/extHostDataProtocol.ts b/src/sql/workbench/api/node/extHostDataProtocol.ts index 5c116d7f4819..81897a5ddaa4 100644 --- a/src/sql/workbench/api/node/extHostDataProtocol.ts +++ b/src/sql/workbench/api/node/extHostDataProtocol.ts @@ -11,7 +11,6 @@ import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import { Disposable } from 'vs/workbench/api/node/extHostTypes'; import { SqlMainContext, MainThreadDataProtocolShape, ExtHostDataProtocolShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { DataProviderType } from 'sql/workbench/api/common/sqlExtHostTypes'; -import { TPromise } from 'vs/base/common/winjs.base'; export class ExtHostDataProtocol extends ExtHostDataProtocolShape { @@ -203,7 +202,7 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { if (provider.buildConnectionInfo) { return provider.buildConnectionInfo(connectionString); } else { - return TPromise.as(undefined); + return Promise.resolve(undefined); } } diff --git a/src/sql/workbench/api/node/extHostModalDialog.ts b/src/sql/workbench/api/node/extHostModalDialog.ts index 625b45a0faa8..c4d5e1a69ec5 100644 --- a/src/sql/workbench/api/node/extHostModalDialog.ts +++ b/src/sql/workbench/api/node/extHostModalDialog.ts @@ -117,6 +117,6 @@ export class ExtHostModalDialogs implements ExtHostModalDialogsShape { $onClosed(handle: number): void { const webview = this._webviews.get(handle); - webview.onClosedEmitter.fire(); + webview.onClosedEmitter.fire(undefined); } } diff --git a/src/sql/workbench/api/node/extHostModelView.ts b/src/sql/workbench/api/node/extHostModelView.ts index 697ea7e90bce..b0412782b286 100644 --- a/src/sql/workbench/api/node/extHostModelView.ts +++ b/src/sql/workbench/api/node/extHostModelView.ts @@ -1364,7 +1364,7 @@ export class ExtHostModelView implements ExtHostModelViewShape { $onClosed(handle: number): void { const view = this._modelViews.get(handle); - view.onClosedEmitter.fire(); + view.onClosedEmitter.fire(undefined); this._modelViews.delete(handle); } diff --git a/src/sql/workbench/api/node/extHostModelViewTree.ts b/src/sql/workbench/api/node/extHostModelViewTree.ts index 2fc95981463e..effa95086cd7 100644 --- a/src/sql/workbench/api/node/extHostModelViewTree.ts +++ b/src/sql/workbench/api/node/extHostModelViewTree.ts @@ -6,7 +6,6 @@ import { localize } from 'vs/nls'; import * as vscode from 'vscode'; -import { TPromise } from 'vs/base/common/winjs.base'; import { SqlMainContext, ExtHostModelViewTreeViewsShape, MainThreadModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { ITreeComponentItem } from 'sql/workbench/common/views'; import { CommandsConverter } from 'vs/workbench/api/node/extHostCommands'; @@ -43,11 +42,11 @@ export class ExtHostModelViewTreeViews implements ExtHostModelViewTreeViewsShape }; } - $getChildren(treeViewId: string, treeItemHandle?: string): TPromise { + $getChildren(treeViewId: string, treeItemHandle?: string): Promise { 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); } diff --git a/src/sql/workbench/api/node/extHostNotebook.ts b/src/sql/workbench/api/node/extHostNotebook.ts index 0b9d2750b589..bf76909b9be7 100644 --- a/src/sql/workbench/api/node/extHostNotebook.ts +++ b/src/sql/workbench/api/node/extHostNotebook.ts @@ -7,7 +7,6 @@ import * as azdata from 'azdata'; import * as vscode from 'vscode'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import { Disposable } from 'vs/workbench/api/node/extHostTypes'; import { localize } from 'vs/nls'; @@ -258,20 +257,20 @@ export class ExtHostNotebook implements ExtHostNotebookShape { return ExtHostNotebook._handlePool++; } - private _withProvider(handle: number, callback: (provider: azdata.nb.NotebookProvider) => R | PromiseLike): TPromise { + private _withProvider(handle: number, callback: (provider: azdata.nb.NotebookProvider) => R | PromiseLike): Promise { let provider = this._adapters.get(handle) as azdata.nb.NotebookProvider; if (provider === undefined) { - return TPromise.wrapError(new Error(localize('errNoProvider', 'no notebook provider found'))); + return Promise.reject(new Error(localize('errNoProvider', 'no notebook provider found'))); } - return TPromise.wrap(callback(provider)); + return Promise.resolve(callback(provider)); } - private _withNotebookManager(handle: number, callback: (manager: NotebookManagerAdapter) => R | PromiseLike): TPromise { + private _withNotebookManager(handle: number, callback: (manager: NotebookManagerAdapter) => R | PromiseLike): Promise { let manager = this._adapters.get(handle) as NotebookManagerAdapter; if (manager === undefined) { - return TPromise.wrapError(new Error(localize('errNoManager', 'No Manager found'))); + return Promise.reject(new Error(localize('errNoManager', 'No Manager found'))); } - return TPromise.wrap(this.callbackWithErrorWrap(callback, manager)); + return this.callbackWithErrorWrap(callback, manager); } private async callbackWithErrorWrap(callback: (manager: NotebookManagerAdapter) => R | PromiseLike, manager: NotebookManagerAdapter): Promise { @@ -283,31 +282,31 @@ export class ExtHostNotebook implements ExtHostNotebookShape { } } - private _withServerManager(handle: number, callback: (manager: azdata.nb.ServerManager) => R | PromiseLike): TPromise { + private _withServerManager(handle: number, callback: (manager: azdata.nb.ServerManager) => R | PromiseLike): Promise { return this._withNotebookManager(handle, (notebookManager) => { let serverManager = notebookManager.serverManager; if (!serverManager) { - return TPromise.wrapError(new Error(localize('noServerManager', 'Notebook Manager for notebook {0} does not have a server manager. Cannot perform operations on it', notebookManager.uriString))); + return Promise.reject(new Error(localize('noServerManager', 'Notebook Manager for notebook {0} does not have a server manager. Cannot perform operations on it', notebookManager.uriString))); } return callback(serverManager); }); } - private _withContentManager(handle: number, callback: (manager: azdata.nb.ContentManager) => R | PromiseLike): TPromise { + private _withContentManager(handle: number, callback: (manager: azdata.nb.ContentManager) => R | PromiseLike): Promise { return this._withNotebookManager(handle, (notebookManager) => { let contentManager = notebookManager.contentManager; if (!contentManager) { - return TPromise.wrapError(new Error(localize('noContentManager', 'Notebook Manager for notebook {0} does not have a content manager. Cannot perform operations on it', notebookManager.uriString))); + return Promise.reject(new Error(localize('noContentManager', 'Notebook Manager for notebook {0} does not have a content manager. Cannot perform operations on it', notebookManager.uriString))); } return callback(contentManager); }); } - private _withSessionManager(handle: number, callback: (manager: azdata.nb.SessionManager) => R | PromiseLike): TPromise { + private _withSessionManager(handle: number, callback: (manager: azdata.nb.SessionManager) => R | PromiseLike): Promise { return this._withNotebookManager(handle, (notebookManager) => { let sessionManager = notebookManager.sessionManager; if (!sessionManager) { - return TPromise.wrapError(new Error(localize('noSessionManager', 'Notebook Manager for notebook {0} does not have a session manager. Cannot perform operations on it', notebookManager.uriString))); + return Promise.reject(new Error(localize('noSessionManager', 'Notebook Manager for notebook {0} does not have a session manager. Cannot perform operations on it', notebookManager.uriString))); } return callback(sessionManager); }); diff --git a/src/sql/workbench/api/node/extHostNotebookDocumentData.ts b/src/sql/workbench/api/node/extHostNotebookDocumentData.ts index 60ecf4753267..83289da39902 100644 --- a/src/sql/workbench/api/node/extHostNotebookDocumentData.ts +++ b/src/sql/workbench/api/node/extHostNotebookDocumentData.ts @@ -10,7 +10,6 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ok } from 'vs/base/common/assert'; import { Schemas } from 'vs/base/common/network'; -import { TPromise } from 'vs/base/common/winjs.base'; import { MainThreadNotebookDocumentsAndEditorsShape, INotebookModelChangedData } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { CellRange } from 'sql/workbench/api/common/sqlExtHostTypes'; @@ -60,7 +59,7 @@ export class ExtHostNotebookDocumentData implements IDisposable { private _save(): Thenable { if (this._isDisposed) { - return TPromise.wrapError(new Error('Document has been closed')); + return Promise.reject(new Error('Document has been closed')); } return this._proxy.$trySaveDocument(this._uri); diff --git a/src/sql/workbench/api/node/extHostNotebookEditor.ts b/src/sql/workbench/api/node/extHostNotebookEditor.ts index 4413ceec4f42..dc64f45186ab 100644 --- a/src/sql/workbench/api/node/extHostNotebookEditor.ts +++ b/src/sql/workbench/api/node/extHostNotebookEditor.ts @@ -10,7 +10,6 @@ import * as vscode from 'vscode'; import { ok } from 'vs/base/common/assert'; import { IDisposable } from 'vs/base/common/lifecycle'; import { readonly } from 'vs/base/common/errors'; -import { TPromise } from 'vs/base/common/winjs.base'; import { MainThreadNotebookDocumentsAndEditorsShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { ExtHostNotebookDocumentData } from 'sql/workbench/api/node/extHostNotebookDocumentData'; @@ -159,19 +158,19 @@ export class ExtHostNotebookEditor implements azdata.nb.NotebookEditor, IDisposa public edit(callback: (editBuilder: azdata.nb.NotebookEditorEdit) => void, options?: { undoStopBefore: boolean; undoStopAfter: boolean; }): Thenable { if (this._disposed) { - return TPromise.wrapError(new Error('NotebookEditor#edit not possible on closed editors')); + return Promise.reject(new Error('NotebookEditor#edit not possible on closed editors')); } let edit = new NotebookEditorEdit(this._documentData.document, options); callback(edit); return this._applyEdit(edit); } - private _applyEdit(editBuilder: NotebookEditorEdit): TPromise { + private _applyEdit(editBuilder: NotebookEditorEdit): Promise { let editData = editBuilder.finalize(); // return when there is nothing to do if (editData.edits.length === 0) { - return TPromise.wrap(true); + return Promise.resolve(true); } // check that the edits are not overlapping (i.e. illegal) @@ -192,9 +191,7 @@ export class ExtHostNotebookEditor implements azdata.nb.NotebookEditor, IDisposa if (nextRangeStart < rangeEnd) { // overlapping ranges - return TPromise.wrapError( - new Error('Overlapping ranges are not allowed!') - ); + return Promise.reject(new Error('Overlapping ranges are not allowed!')); } } diff --git a/src/sql/workbench/api/node/extHostResourceProvider.ts b/src/sql/workbench/api/node/extHostResourceProvider.ts index 4251b246b065..f265ba5e771b 100644 --- a/src/sql/workbench/api/node/extHostResourceProvider.ts +++ b/src/sql/workbench/api/node/extHostResourceProvider.ts @@ -6,7 +6,6 @@ 'use strict'; import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import { Disposable } from 'vs/workbench/api/node/extHostTypes'; import { @@ -79,7 +78,7 @@ export class ExtHostResourceProvider extends ExtHostResourceProviderShape { private _withProvider(handle: number, callback: (provider: azdata.ResourceProvider) => Thenable): Thenable { let provider = this._providers[handle]; if (provider === undefined) { - return TPromise.wrapError(new Error(`Provider ${handle} not found.`)); + return Promise.reject(new Error(`Provider ${handle} not found.`)); } return callback(provider.provider); } diff --git a/src/sql/workbench/api/node/extHostSerializationProvider.ts b/src/sql/workbench/api/node/extHostSerializationProvider.ts index b71af6a9858c..d44b3033caed 100644 --- a/src/sql/workbench/api/node/extHostSerializationProvider.ts +++ b/src/sql/workbench/api/node/extHostSerializationProvider.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import { SqlMainContext, MainThreadSerializationProviderShape, ExtHostSerializationProviderShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import * as vscode from 'vscode'; @@ -47,7 +46,7 @@ export class ExtHostSerializationProvider extends ExtHostSerializationProviderSh private _withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => Thenable): Thenable { let adapter = this._adapter[handle]; if (!(adapter instanceof ctor)) { - return TPromise.wrapError(new Error('no adapter found')); + return Promise.reject(new Error('no adapter found')); } return callback(adapter); } diff --git a/src/sql/workbench/api/node/extHostTasks.ts b/src/sql/workbench/api/node/extHostTasks.ts index 65b63909cf83..1d043fb0aaa2 100644 --- a/src/sql/workbench/api/node/extHostTasks.ts +++ b/src/sql/workbench/api/node/extHostTasks.ts @@ -8,7 +8,6 @@ import { validateConstraint } from 'vs/base/common/types'; import { ILogService } from 'vs/platform/log/common/log'; import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import * as extHostTypes from 'vs/workbench/api/node/extHostTypes'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as azdata from 'azdata'; @@ -56,7 +55,7 @@ export class ExtHostTasks implements ExtHostTasksShape { $executeContributedTask(id: string, ...args: any[]): Thenable { let command = this._tasks.get(id); if (!command) { - return TPromise.wrapError(new Error(`Contributed task '${id}' does not exist.`)); + return Promise.reject(new Error(`Contributed task '${id}' does not exist.`)); } let { callback, thisArg, description } = command; @@ -66,14 +65,14 @@ export class ExtHostTasks implements ExtHostTasksShape { try { validateConstraint(args[i], description.args[i].constraint); } catch (err) { - return TPromise.wrapError(new Error(`Running the contributed task:'${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`)); + return Promise.reject(new Error(`Running the contributed task:'${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`)); } } } try { let result = callback.apply(thisArg, args); - return TPromise.as(result); + return Promise.resolve(result); } catch (err) { // console.log(err); // try { @@ -81,11 +80,11 @@ export class ExtHostTasks implements ExtHostTasksShape { // } catch (err) { // // // } - return TPromise.wrapError(new Error(`Running the contributed task:'${id}' failed.`)); + return Promise.reject(new Error(`Running the contributed task:'${id}' failed.`)); } } - $getContributedTaskHandlerDescriptions(): TPromise<{ [id: string]: any; }> { + $getContributedTaskHandlerDescriptions(): Promise<{ [id: string]: any; }> { throw new Error('Method not implemented.'); } } diff --git a/src/sql/workbench/api/node/mainThreadAccountManagement.ts b/src/sql/workbench/api/node/mainThreadAccountManagement.ts index d00ae7b9d04a..b97f76e02b8b 100644 --- a/src/sql/workbench/api/node/mainThreadAccountManagement.ts +++ b/src/sql/workbench/api/node/mainThreadAccountManagement.ts @@ -5,7 +5,6 @@ 'use strict'; import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAccountManagementService } from 'sql/platform/accountManagement/common/interfaces'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { @@ -92,12 +91,12 @@ export class MainThreadAccountManagement implements MainThreadAccountManagementS this._accountManagementService.registerProvider(providerMetadata, accountProvider); this._providerMetadata[handle] = providerMetadata; - return TPromise.as(null); + return Promise.resolve(null); } public $unregisterAccountProvider(handle: number): Thenable { this._accountManagementService.unregisterProvider(this._providerMetadata[handle]); - return TPromise.as(null); + return Promise.resolve(null); } public dispose(): void { diff --git a/src/sql/workbench/api/node/mainThreadCredentialManagement.ts b/src/sql/workbench/api/node/mainThreadCredentialManagement.ts index 32eb236f254b..6e8ee9085c90 100644 --- a/src/sql/workbench/api/node/mainThreadCredentialManagement.ts +++ b/src/sql/workbench/api/node/mainThreadCredentialManagement.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { SqlExtHostContext, ExtHostCredentialManagementShape, @@ -37,7 +36,7 @@ export class MainThreadCredentialManagement implements MainThreadCredentialManag this._toDispose = dispose(this._toDispose); } - public $registerCredentialProvider(handle: number): TPromise { + public $registerCredentialProvider(handle: number): Promise { let self = this; this._registrations[handle] = this.credentialService.addEventListener(handle, { @@ -55,7 +54,7 @@ export class MainThreadCredentialManagement implements MainThreadCredentialManag return undefined; } - public $unregisterCredentialProvider(handle: number): TPromise { + public $unregisterCredentialProvider(handle: number): Promise { let registration = this._registrations[handle]; if (registration) { registration.dispose(); diff --git a/src/sql/workbench/api/node/mainThreadDataProtocol.ts b/src/sql/workbench/api/node/mainThreadDataProtocol.ts index 2785dd33d2f5..3ffefce1a119 100644 --- a/src/sql/workbench/api/node/mainThreadDataProtocol.ts +++ b/src/sql/workbench/api/node/mainThreadDataProtocol.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { SqlExtHostContext, ExtHostDataProtocolShape, @@ -71,7 +70,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { this._toDispose = dispose(this._toDispose); } - public $registerConnectionProvider(providerId: string, handle: number): TPromise { + public $registerConnectionProvider(providerId: string, handle: number): Promise { const self = this; this._connectionManagementService.registerProvider(providerId, { connect(connectionUri: string, connectionInfo: azdata.ConnectionInfo): Thenable { @@ -103,7 +102,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerQueryProvider(providerId: string, handle: number): TPromise { + public $registerQueryProvider(providerId: string, handle: number): Promise { const self = this; this._queryManagementService.addQueryRequestHandler(providerId, { cancelQuery(ownerUri: string): Thenable { @@ -174,7 +173,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerBackupProvider(providerId: string, handle: number): TPromise { + public $registerBackupProvider(providerId: string, handle: number): Promise { const self = this; this._backupService.registerProvider(providerId, { backup(connectionUri: string, backupInfo: { [key: string]: any }, taskExecutionMode: azdata.TaskExecutionMode): Thenable { @@ -188,7 +187,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerRestoreProvider(providerId: string, handle: number): TPromise { + public $registerRestoreProvider(providerId: string, handle: number): Promise { const self = this; this._restoreService.registerProvider(providerId, { getRestorePlan(connectionUri: string, restoreInfo: azdata.RestoreInfo): Thenable { @@ -208,7 +207,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerMetadataProvider(providerId: string, handle: number): TPromise { + public $registerMetadataProvider(providerId: string, handle: number): Promise { const self = this; this._metadataService.registerProvider(providerId, { getMetadata(connectionUri: string): Thenable { @@ -228,7 +227,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerObjectExplorerProvider(providerId: string, handle: number): TPromise { + public $registerObjectExplorerProvider(providerId: string, handle: number): Promise { const self = this; this._objectExplorerService.registerProvider(providerId, { providerId: providerId, @@ -252,7 +251,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerObjectExplorerNodeProvider(providerId: string, supportedProviderId: string, group: string, handle: number): TPromise { + public $registerObjectExplorerNodeProvider(providerId: string, supportedProviderId: string, group: string, handle: number): Promise { const self = this; this._objectExplorerService.registerNodeProvider( { supportedProviderId: supportedProviderId, @@ -278,7 +277,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerTaskServicesProvider(providerId: string, handle: number): TPromise { + public $registerTaskServicesProvider(providerId: string, handle: number): Promise { const self = this; this._taskService.registerProvider(providerId, { getAllTasks(listTasksParams: azdata.ListTasksParams): Thenable { @@ -292,7 +291,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerScriptingProvider(providerId: string, handle: number): TPromise { + public $registerScriptingProvider(providerId: string, handle: number): Promise { const self = this; this._scriptingService.registerProvider(providerId, { scriptAsOperation(connectionUri: string, operation: azdata.ScriptOperation, metadata: azdata.ObjectMetadata, paramDetails: azdata.ScriptingParamDetails): Thenable { @@ -303,7 +302,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerFileBrowserProvider(providerId: string, handle: number): TPromise { + public $registerFileBrowserProvider(providerId: string, handle: number): Promise { const self = this; this._fileBrowserService.registerProvider(providerId, { openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable { @@ -323,7 +322,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerProfilerProvider(providerId: string, handle: number): TPromise { + public $registerProfilerProvider(providerId: string, handle: number): Promise { const self = this; this._profilerService.registerProvider(providerId, { createSession(sessionId: string, createStatement: string, template: azdata.ProfilerSessionTemplate): Thenable { @@ -342,7 +341,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return self._proxy.$getXEventSessions(handle, sessionId); }, connectSession(sessionId: string): Thenable { - return TPromise.as(true); + return Promise.resolve(true); }, disconnectSession(sessionId: string): Thenable { return self._proxy.$disconnectSession(handle, sessionId); @@ -352,7 +351,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerAdminServicesProvider(providerId: string, handle: number): TPromise { + public $registerAdminServicesProvider(providerId: string, handle: number): Promise { const self = this; this._adminService.registerProvider(providerId, { createDatabase(connectionUri: string, database: azdata.DatabaseInfo): Thenable { @@ -372,7 +371,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerAgentServicesProvider(providerId: string, handle: number): TPromise { + public $registerAgentServicesProvider(providerId: string, handle: number): Promise { const self = this; this._jobManagementService.registerProvider(providerId, { providerId: providerId, @@ -417,7 +416,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerCapabilitiesServiceProvider(providerId: string, handle: number): TPromise { + public $registerCapabilitiesServiceProvider(providerId: string, handle: number): Promise { const self = this; this._capabilitiesService.registerProvider({ getServerCapabilities(client: azdata.DataProtocolClientCapabilities): Thenable { @@ -428,7 +427,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { return undefined; } - public $registerDacFxServicesProvider(providerId: string, handle: number): TPromise { + public $registerDacFxServicesProvider(providerId: string, handle: number): Promise { const self = this; this._dacFxService.registerProvider(providerId, { exportBacpac(databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable { @@ -549,7 +548,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { this._jobManagementService.fireOnDidChange(); } - public $unregisterProvider(handle: number): TPromise { + public $unregisterProvider(handle: number): Promise { let capabilitiesRegistration = this._capabilitiesRegistrations[handle]; if (capabilitiesRegistration) { capabilitiesRegistration.dispose(); diff --git a/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts b/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts index 8532c4c4fc3f..7443b0a2b576 100644 --- a/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts +++ b/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts @@ -24,7 +24,6 @@ import { } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { NotebookInput } from 'sql/parts/notebook/notebookInput'; import { INotebookService, INotebookEditor, IProviderInfo } from 'sql/workbench/services/notebook/common/notebookService'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes'; import { disposed } from 'vs/base/common/errors'; import { ICellModel, NotebookContentChange, INotebookModel } from 'sql/parts/notebook/models/modelInterfaces'; @@ -323,16 +322,16 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements } } - $tryShowNotebookDocument(resource: UriComponents, options: INotebookShowOptions): TPromise { - return TPromise.wrap(this.doOpenEditor(resource, options)); + $tryShowNotebookDocument(resource: UriComponents, options: INotebookShowOptions): Promise { + return Promise.resolve(this.doOpenEditor(resource, options)); } - $tryApplyEdits(id: string, modelVersionId: number, edits: ISingleNotebookEditOperation[], opts: IUndoStopOptions): TPromise { + $tryApplyEdits(id: string, modelVersionId: number, edits: ISingleNotebookEditOperation[], opts: IUndoStopOptions): Promise { let editor = this.getEditor(id); if (!editor) { - return TPromise.wrapError(disposed(`TextEditor(${id})`)); + return Promise.reject(disposed(`TextEditor(${id})`)); } - return TPromise.as(editor.applyEdits(modelVersionId, edits, opts)); + return Promise.resolve(editor.applyEdits(modelVersionId, edits, opts)); } $runCell(id: string, cellUri: UriComponents): Promise { diff --git a/src/sql/workbench/api/node/mainThreadResourceProvider.ts b/src/sql/workbench/api/node/mainThreadResourceProvider.ts index 7d8d85b545dc..e0385e09ff53 100644 --- a/src/sql/workbench/api/node/mainThreadResourceProvider.ts +++ b/src/sql/workbench/api/node/mainThreadResourceProvider.ts @@ -5,7 +5,6 @@ 'use strict'; import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IResourceProviderService } from 'sql/workbench/services/resourceProvider/common/resourceProviderService'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { @@ -50,12 +49,12 @@ export class MainThreadResourceProvider implements MainThreadResourceProviderSha this._resourceProviderService.registerProvider(providerMetadata.id, resourceProvider); this._providerMetadata[handle] = providerMetadata; - return TPromise.as(null); + return Promise.resolve(null); } public $unregisterResourceProvider(handle: number): Thenable { this._resourceProviderService.unregisterProvider(this._providerMetadata[handle].id); - return TPromise.as(null); + return Promise.resolve(null); } public dispose(): void { diff --git a/src/sql/workbench/api/node/mainThreadSerializationProvider.ts b/src/sql/workbench/api/node/mainThreadSerializationProvider.ts index 1117fb43b61f..4d421a92b0b8 100644 --- a/src/sql/workbench/api/node/mainThreadSerializationProvider.ts +++ b/src/sql/workbench/api/node/mainThreadSerializationProvider.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { SqlExtHostContext, ExtHostSerializationProviderShape, @@ -38,7 +37,7 @@ export class MainThreadSerializationProvider implements MainThreadSerializationP this._toDispose = dispose(this._toDispose); } - public $registerSerializationProvider(handle: number): TPromise { + public $registerSerializationProvider(handle: number): Promise { let self = this; this._registrations[handle] = this.serializationService.addEventListener(handle, { @@ -50,7 +49,7 @@ export class MainThreadSerializationProvider implements MainThreadSerializationP return undefined; } - public $unregisterSerializationProvider(handle: number): TPromise { + public $unregisterSerializationProvider(handle: number): Promise { let registration = this._registrations[handle]; if (registration) { registration.dispose(); diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index 23b42b8ec41c..1365d0b8ca10 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -5,7 +5,6 @@ 'use strict'; import * as extHostApi from 'vs/workbench/api/node/extHost.api.impl'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IInitData, IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; @@ -23,7 +22,7 @@ import { ExtHostSerializationProvider } from 'sql/workbench/api/node/extHostSeri import { ExtHostResourceProvider } from 'sql/workbench/api/node/extHostResourceProvider'; import * as sqlExtHostTypes from 'sql/workbench/api/common/sqlExtHostTypes'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/node/extHostConfiguration'; import { ExtHostModalDialogs } from 'sql/workbench/api/node/extHostModalDialog'; import { ExtHostTasks } from 'sql/workbench/api/node/extHostTasks'; import { ExtHostDashboardWebviews } from 'sql/workbench/api/node/extHostDashboardWebview'; @@ -41,10 +40,11 @@ import { ExtHostNotebookDocumentsAndEditors } from 'sql/workbench/api/node/extHo import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { ExtHostExtensionManagement } from 'sql/workbench/api/node/extHostExtensionManagement'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { TernarySearchTree } from 'vs/base/common/map'; export interface ISqlExtensionApiFactory { - vsCodeFactory(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry): typeof vscode; + vsCodeFactory(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; sqlopsFactory(extension: IExtensionDescription): typeof sqlops; azdataFactory(extension: IExtensionDescription): typeof azdata; } @@ -993,11 +993,11 @@ export function createApiFactory( }; } -export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: ISqlExtensionApiFactory, extensionRegistry: ExtensionDescriptionRegistry): TPromise { - return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie, extensionRegistry)); +export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: ISqlExtensionApiFactory, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): Promise { + return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie, extensionRegistry, configProvider)); } -function defineAPI(factory: ISqlExtensionApiFactory, extensionPaths: TernarySearchTree, extensionRegistry: ExtensionDescriptionRegistry): void { +function defineAPI(factory: ISqlExtensionApiFactory, extensionPaths: TernarySearchTree, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): void { type ApiImpl = typeof vscode | typeof azdata | typeof sqlops; // each extension is meant to get its own api implementation @@ -1018,10 +1018,10 @@ function defineAPI(factory: ISqlExtensionApiFactory, extensionPaths: TernarySear // get extension id from filename and api for extension const ext = extensionPaths.findSubstr(URI.file(parent.filename).fsPath); if (ext) { - let apiImpl = apiMap.get(ext.id); + let apiImpl = apiMap.get(ext.identifier.value); if (!apiImpl) { apiImpl = createApi(ext); - apiMap.set(ext.id, apiImpl); + apiMap.set(ext.identifier.value, apiImpl); } return apiImpl; } @@ -1041,14 +1041,14 @@ function defineAPI(factory: ISqlExtensionApiFactory, extensionPaths: TernarySear // TODO look into de-duplicating this code node_module._load = function load(request, parent, isMain) { if (request === 'vscode') { - return getModuleFactory(extApiImpl, (ext) => factory.vsCodeFactory(ext, extensionRegistry), + return getModuleFactory(extApiImpl, (ext) => factory.vsCodeFactory(ext, extensionRegistry, configProvider), defaultApiImpl, (impl) => defaultApiImpl = impl, parent); } else if (request === 'azdata') { return getModuleFactory(azDataExtApiImpl, (ext) => factory.azdataFactory(ext), - defaultDataApiImpl, + defaultAzDataApiImpl, (impl) => defaultAzDataApiImpl = impl, parent); } else if (request === 'sqlops') { @@ -1066,7 +1066,7 @@ function defineAPI(factory: ISqlExtensionApiFactory, extensionPaths: TernarySear const nullExtensionDescription: IExtensionDescription = { - id: 'nullExtensionDescription', + identifier: new ExtensionIdentifier('nullExtensionDescription'), name: 'Null Extension Description', publisher: 'vscode', activationEvents: undefined, diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index 7340ecd4834b..64f95984bdca 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -11,7 +11,6 @@ import { } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as azdata from 'azdata'; @@ -510,22 +509,22 @@ export interface MainThreadResourceProviderShape extends IDisposable { } export interface MainThreadDataProtocolShape extends IDisposable { - $registerConnectionProvider(providerId: string, handle: number): TPromise; - $registerBackupProvider(providerId: string, handle: number): TPromise; - $registerRestoreProvider(providerId: string, handle: number): TPromise; - $registerScriptingProvider(providerId: string, handle: number): TPromise; - $registerQueryProvider(providerId: string, handle: number): TPromise; - $registerProfilerProvider(providerId: string, handle: number): TPromise; - $registerObjectExplorerProvider(providerId: string, handle: number): TPromise; - $registerObjectExplorerNodeProvider(providerId: string, supportedProviderId: string, group: string, handle: number): TPromise; - $registerMetadataProvider(providerId: string, handle: number): TPromise; - $registerTaskServicesProvider(providerId: string, handle: number): TPromise; - $registerFileBrowserProvider(providerId: string, handle: number): TPromise; - $registerCapabilitiesServiceProvider(providerId: string, handle: number): TPromise; - $registerAdminServicesProvider(providerId: string, handle: number): TPromise; - $registerAgentServicesProvider(providerId: string, handle: number): TPromise; - $registerDacFxServicesProvider(providerId: string, handle: number): TPromise; - $unregisterProvider(handle: number): TPromise; + $registerConnectionProvider(providerId: string, handle: number): Promise; + $registerBackupProvider(providerId: string, handle: number): Promise; + $registerRestoreProvider(providerId: string, handle: number): Promise; + $registerScriptingProvider(providerId: string, handle: number): Promise; + $registerQueryProvider(providerId: string, handle: number): Promise; + $registerProfilerProvider(providerId: string, handle: number): Promise; + $registerObjectExplorerProvider(providerId: string, handle: number): Promise; + $registerObjectExplorerNodeProvider(providerId: string, supportedProviderId: string, group: string, handle: number): Promise; + $registerMetadataProvider(providerId: string, handle: number): Promise; + $registerTaskServicesProvider(providerId: string, handle: number): Promise; + $registerFileBrowserProvider(providerId: string, handle: number): Promise; + $registerCapabilitiesServiceProvider(providerId: string, handle: number): Promise; + $registerAdminServicesProvider(providerId: string, handle: number): Promise; + $registerAgentServicesProvider(providerId: string, handle: number): Promise; + $registerDacFxServicesProvider(providerId: string, handle: number): Promise; + $unregisterProvider(handle: number): Promise; $onConnectionComplete(handle: number, connectionInfoSummary: azdata.ConnectionInfoSummary): void; $onIntelliSenseCacheComplete(handle: number, connectionUri: string): void; $onConnectionChangeNotification(handle: number, changedConnInfo: azdata.ChangedConnectionInfo): void; @@ -568,13 +567,13 @@ export interface MainThreadConnectionManagementShape extends IDisposable { } export interface MainThreadCredentialManagementShape extends IDisposable { - $registerCredentialProvider(handle: number): TPromise; - $unregisterCredentialProvider(handle: number): TPromise; + $registerCredentialProvider(handle: number): Promise; + $unregisterCredentialProvider(handle: number): Promise; } export interface MainThreadSerializationProviderShape extends IDisposable { - $registerSerializationProvider(handle: number): TPromise; - $unregisterSerializationProvider(handle: number): TPromise; + $registerSerializationProvider(handle: number): Promise; + $unregisterSerializationProvider(handle: number): Promise; } function ni() { return new Error('Not implemented'); } @@ -650,12 +649,12 @@ export interface ExtHostModalDialogsShape { export interface ExtHostTasksShape { $executeContributedTask(id: string, ...args: any[]): Thenable; - $getContributedTaskHandlerDescriptions(): TPromise<{ [id: string]: string | ITaskHandlerDescription }>; + $getContributedTaskHandlerDescriptions(): Promise<{ [id: string]: string | ITaskHandlerDescription }>; } export interface MainThreadTasksShape extends IDisposable { - $registerTask(id: string): TPromise; - $unregisterTask(id: string): TPromise; + $registerTask(id: string): Promise; + $unregisterTask(id: string): Promise; } export interface ExtHostDashboardWebviewsShape { @@ -680,7 +679,7 @@ export interface ExtHostModelViewShape { } export interface ExtHostModelViewTreeViewsShape { - $getChildren(treeViewId: string, treeItemHandle?: string): TPromise; + $getChildren(treeViewId: string, treeItemHandle?: string): Promise; $createTreeView(handle: number, componentId: string, options: { treeDataProvider: vscode.TreeDataProvider }, extension: IExtensionDescription): azdata.TreeComponentView; $onNodeCheckedChanged(treeViewId: string, treeItemHandle?: string, checked?: boolean): void; $onNodeSelected(treeViewId: string, nodes: string[]): void; @@ -861,9 +860,9 @@ export interface ExtHostNotebookDocumentsAndEditorsShape { export interface MainThreadNotebookDocumentsAndEditorsShape extends IDisposable { $trySaveDocument(uri: UriComponents): Thenable; - $tryShowNotebookDocument(resource: UriComponents, options: INotebookShowOptions): TPromise; - $tryApplyEdits(id: string, modelVersionId: number, edits: ISingleNotebookEditOperation[], opts: IUndoStopOptions): TPromise; - $runCell(id: string, cellUri: UriComponents): TPromise; + $tryShowNotebookDocument(resource: UriComponents, options: INotebookShowOptions): Promise; + $tryApplyEdits(id: string, modelVersionId: number, edits: ISingleNotebookEditOperation[], opts: IUndoStopOptions): Promise; + $runCell(id: string, cellUri: UriComponents): Promise; } export interface ExtHostExtensionManagementShape { diff --git a/src/sql/workbench/browser/parts/views/customView.ts b/src/sql/workbench/browser/parts/views/customView.ts index 9b372c747a9a..d012a5d2b2ab 100644 --- a/src/sql/workbench/browser/parts/views/customView.ts +++ b/src/sql/workbench/browser/parts/views/customView.ts @@ -414,7 +414,7 @@ export class CustomTreeView extends Disposable implements ITreeView { return Promise.resolve(null); } - expand(itemOrItems: ITreeItem | ITreeItem[]): Thenable { + expand(itemOrItems: ITreeItem | ITreeItem[]): Promise { if (this.tree) { itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems]; return this.tree.expandAll(itemOrItems); @@ -443,7 +443,7 @@ export class CustomTreeView extends Disposable implements ITreeView { } } - reveal(item: ITreeItem): Thenable { + reveal(item: ITreeItem): Promise { if (this.tree) { return this.tree.reveal(item); } @@ -620,8 +620,8 @@ class TreeRenderer implements IRenderer { const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); 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')); + DOM.addClass(resourceLabel.element.element, 'custom-view-tree-node-item-resourceLabel'); + const actionsContainer = DOM.append(resourceLabel.element.element, DOM.$('.actions')); const actionBar = new ActionBar(actionsContainer, { actionItemProvider: this.actionItemProvider, actionRunner: new MultipleSelectionActionRunner(() => tree.getSelection()) @@ -646,9 +646,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, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); + templateData.resourceLabel.element.setResource({ name: label, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, fileDecorations: fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); } else { - templateData.resourceLabel.setLabel({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); + templateData.resourceLabel.element.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); } templateData.icon.style.backgroundImage = iconUrl ? `url('${iconUrl.toString(true)}')` : ''; @@ -795,7 +795,7 @@ class MultipleSelectionActionRunner extends ActionRunner { super(); } - runAction(action: IAction, context: any): Thenable { + runAction(action: IAction, context: any): Promise { if (action instanceof MenuItemAction) { const selection = this.getSelectedResources(); const filteredSelection = selection.filter(s => s !== context); diff --git a/src/sql/workbench/common/workspaceActions.ts b/src/sql/workbench/common/workspaceActions.ts index 945f4985d872..0a401aea401f 100644 --- a/src/sql/workbench/common/workspaceActions.ts +++ b/src/sql/workbench/common/workspaceActions.ts @@ -1,4 +1,3 @@ -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import { IWindowsService } from 'vs/platform/windows/common/windows'; @@ -8,7 +7,7 @@ export class ShowFileInFolderAction extends Action { super('showItemInFolder.action.id', label); } - run(): TPromise { + run(): Promise { return this.windowsService.showItemInFolder(this.path); } } diff --git a/src/sql/workbench/electron-browser/menus.ts b/src/sql/workbench/electron-browser/menus.ts deleted file mode 100644 index d1ef71c4bc9d..000000000000 --- a/src/sql/workbench/electron-browser/menus.ts +++ /dev/null @@ -1,1464 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import * as nls from 'vs/nls'; -import { isMacintosh, isLinux, isWindows, language } from 'vs/base/common/platform'; -import * as arrays from 'vs/base/common/arrays'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { app, shell, Menu, MenuItem, BrowserWindow, ipcMain } from 'electron'; -import { OpenContext, IRunActionInWindowRequest, IWindowsService } from 'vs/platform/windows/common/windows'; -import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { AutoSaveConfiguration } from 'vs/platform/files/common/files'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IUpdateService, StateType } from 'vs/platform/update/common/update'; -import product from 'vs/platform/node/product'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { mnemonicMenuLabel as baseMnemonicLabel, unmnemonicLabel } from 'vs/base/common/labels'; -import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows'; -import { IHistoryMainService } from 'vs/platform/history/common/history'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { URI } from 'vs/base/common/uri'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { ConfigWatcher } from 'vs/base/node/config'; -import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; -import { Emitter, Event, once } from 'vs/base/common/event'; -import { IStateService } from 'vs/platform/state/common/state'; -import { ILogService } from 'vs/platform/log/common/log'; - -interface IMenuItemClickHandler { - inDevTools: (contents: Electron.WebContents) => void; - inNoWindow: () => void; -} - -const telemetryFrom = 'menu'; - -export class CodeMenu { - - private static readonly MAX_MENU_RECENT_ENTRIES = 10; - - private keys = [ - 'files.autoSave', - 'editor.multiCursorModifier', - 'workbench.sideBar.location', - 'workbench.statusBar.visible', - 'workbench.activityBar.visible', - 'window.enableMenuBarMnemonics', - 'window.nativeTabs' - ]; - - private isQuitting: boolean; - private appMenuInstalled: boolean; - - private menuUpdater: RunOnceScheduler; - - private keybindingsResolver: KeybindingsResolver; - - private closeFolder: Electron.MenuItem; - private closeWorkspace: Electron.MenuItem; - - private nativeTabMenuItems: Electron.MenuItem[]; - - constructor( - @IUpdateService private updateService: IUpdateService, - @IInstantiationService instantiationService: IInstantiationService, - @IConfigurationService private configurationService: IConfigurationService, - @IWindowsMainService private windowsMainService: IWindowsMainService, - @IWindowsService private windowsService: IWindowsService, - @IEnvironmentService private environmentService: IEnvironmentService, - @ITelemetryService private telemetryService: ITelemetryService, - @IHistoryMainService private historyMainService: IHistoryMainService, - @ILabelService private labelService: ILabelService - ) { - this.nativeTabMenuItems = []; - - this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0); - this.keybindingsResolver = instantiationService.createInstance(KeybindingsResolver); - - this.install(); - - this.registerListeners(); - } - - private registerListeners(): void { - - // Keep flag when app quits - app.on('will-quit', () => { - this.isQuitting = true; - }); - - // Listen to some events from window service to update menu - this.historyMainService.onRecentlyOpenedChange(() => this.updateMenu()); - this.windowsMainService.onWindowsCountChanged(e => this.onWindowsCountChanged(e)); - this.windowsMainService.onActiveWindowChanged(() => this.updateWorkspaceMenuItems()); - this.windowsMainService.onWindowReady(() => this.updateWorkspaceMenuItems()); - this.windowsMainService.onWindowClose(() => this.updateWorkspaceMenuItems()); - - // Update when auto save config changes - this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e)); - - // Listen to update service - this.updateService.onStateChange(() => this.updateMenu()); - - // Listen to keybindings change - this.keybindingsResolver.onKeybindingsChanged(() => this.updateMenu()); - } - - private onConfigurationUpdated(event: IConfigurationChangeEvent): void { - if (this.keys.some(key => event.affectsConfiguration(key))) { - this.updateMenu(); - } - } - - private get currentAutoSaveSetting(): string { - return this.configurationService.getValue('files.autoSave'); - } - - private get currentMultiCursorModifierSetting(): string { - return this.configurationService.getValue('editor.multiCursorModifier'); - } - - private get currentSidebarLocation(): string { - return this.configurationService.getValue('workbench.sideBar.location') || 'left'; - } - - private get currentStatusbarVisible(): boolean { - let statusbarVisible = this.configurationService.getValue('workbench.statusBar.visible'); - if (typeof statusbarVisible !== 'boolean') { - statusbarVisible = true; - } - return statusbarVisible; - } - - private get currentActivityBarVisible(): boolean { - let activityBarVisible = this.configurationService.getValue('workbench.activityBar.visible'); - if (typeof activityBarVisible !== 'boolean') { - activityBarVisible = true; - } - return activityBarVisible; - } - - private get currentEnableMenuBarMnemonics(): boolean { - let enableMenuBarMnemonics = this.configurationService.getValue('window.enableMenuBarMnemonics'); - if (typeof enableMenuBarMnemonics !== 'boolean') { - enableMenuBarMnemonics = true; - } - return enableMenuBarMnemonics; - } - - private get currentEnableNativeTabs(): boolean { - let enableNativeTabs = this.configurationService.getValue('window.nativeTabs'); - if (typeof enableNativeTabs !== 'boolean') { - enableNativeTabs = false; - } - return enableNativeTabs; - } - - private updateMenu(): void { - this.menuUpdater.schedule(); // buffer multiple attempts to update the menu - } - - private doUpdateMenu(): void { - - // Due to limitations in Electron, it is not possible to update menu items dynamically. The suggested - // workaround from Electron is to set the application menu again. - // See also https://github.com/electron/electron/issues/846 - // - // Run delayed to prevent updating menu while it is open - if (!this.isQuitting) { - setTimeout(() => { - if (!this.isQuitting) { - this.install(); - } - }, 10 /* delay this because there is an issue with updating a menu when it is open */); - } - } - - private onWindowsCountChanged(e: IWindowsCountChangedEvent): void { - if (!isMacintosh) { - return; - } - - // Update menu if window count goes from N > 0 or 0 > N to update menu item enablement - if ((e.oldCount === 0 && e.newCount > 0) || (e.oldCount > 0 && e.newCount === 0)) { - this.updateMenu(); - } - - // Update specific items that are dependent on window count - else if (this.currentEnableNativeTabs) { - this.nativeTabMenuItems.forEach(item => { - if (item) { - item.enabled = e.newCount > 1; - } - }); - } - } - - private updateWorkspaceMenuItems(): void { - const window = this.windowsMainService.getLastActiveWindow(); - const isInWorkspaceContext = window && !!window.openedWorkspace; - const isInFolderContext = window && !!window.openedFolderUri; - - this.closeWorkspace.visible = isInWorkspaceContext; - this.closeFolder.visible = !isInWorkspaceContext; - this.closeFolder.enabled = isInFolderContext || isLinux /* https://github.com/Microsoft/vscode/issues/36431 */; - } - - private install(): void { - - // Menus - const menubar = new Menu(); - - // Mac: Application - let macApplicationMenuItem: Electron.MenuItem; - if (isMacintosh) { - const applicationMenu = new Menu(); - // {{ SQL CARBON EDIT}} - macApplicationMenuItem = new MenuItem({ label: product.nameLong, submenu: applicationMenu }); - this.setMacApplicationMenu(applicationMenu); - } - - // File - const fileMenu = new Menu(); - const fileMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mFile', comment: ['&& denotes a mnemonic'] }, "&&File")), submenu: fileMenu }); - this.setFileMenu(fileMenu); - - // Edit - const editMenu = new Menu(); - const editMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mEdit', comment: ['&& denotes a mnemonic'] }, "&&Edit")), submenu: editMenu }); - this.setEditMenu(editMenu); - - // {{SQL CARBON EDIT}} - // Selection - // const selectionMenu = new Menu(); - // const selectionMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection")), submenu: selectionMenu }); - // this.setSelectionMenu(selectionMenu); - - // View - const viewMenu = new Menu(); - const viewMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mView', comment: ['&& denotes a mnemonic'] }, "&&View")), submenu: viewMenu }); - this.setViewMenu(viewMenu); - - // {{SQL CARBON EDIT}} - // Goto - // const gotoMenu = new Menu(); - // const gotoMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go")), submenu: gotoMenu }); - // this.setGotoMenu(gotoMenu); - - // {{SQL CARBON EDIT}} - // Debug - // const debugMenu = new Menu(); - // const debugMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug")), submenu: debugMenu }); - // this.setDebugMenu(debugMenu); - - // Mac: Window - let macWindowMenuItem: Electron.MenuItem; - if (isMacintosh) { - const windowMenu = new Menu(); - macWindowMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize('mWindow', "Window")), submenu: windowMenu, role: 'window' }); - this.setMacWindowMenu(windowMenu); - } - - // Help - const helpMenu = new Menu(); - const helpMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help")), submenu: helpMenu, role: 'help' }); - this.setHelpMenu(helpMenu); - - // {{SQL CARBON EDIT}} - // Tasks - // const taskMenu = new Menu(); - // const taskMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTask', comment: ['&& denotes a mnemonic'] }, "&&Tasks")), submenu: taskMenu }); - // this.setTaskMenu(taskMenu); - - // Menu Structure - if (macApplicationMenuItem) { - menubar.append(macApplicationMenuItem); - } - - menubar.append(fileMenuItem); - menubar.append(editMenuItem); - // {{SQL CARBON EDIT}} - //menubar.append(selectionMenuItem); - menubar.append(viewMenuItem); - // {{SQL CARBON EDIT}} - //menubar.append(gotoMenuItem); - // menubar.append(debugMenuItem); - // menubar.append(taskMenuItem); - - if (macWindowMenuItem) { - menubar.append(macWindowMenuItem); - } - - menubar.append(helpMenuItem); - - Menu.setApplicationMenu(menubar); - - // Dock Menu - if (isMacintosh && !this.appMenuInstalled) { - this.appMenuInstalled = true; - - const dockMenu = new Menu(); - dockMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsMainService.openNewWindow(OpenContext.DOCK) })); - - app.dock.setMenu(dockMenu); - } - } - - private setMacApplicationMenu(macApplicationMenu: Electron.Menu): void { - const about = new MenuItem({ label: nls.localize('mAbout', "About {0}", product.nameLong), role: 'about' }); - const checkForUpdates = this.getUpdateMenuItems(); - const preferences = this.getPreferencesMenu(); - const servicesMenu = new Menu(); - const services = new MenuItem({ label: nls.localize('mServices', "Services"), role: 'services', submenu: servicesMenu }); - const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' }); - const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideothers', accelerator: 'Command+Alt+H' }); - const showAll = new MenuItem({ label: nls.localize('mShowAll', "Show All"), role: 'unhide' }); - const quit = new MenuItem(this.likeAction('workbench.action.quit', { - label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => { - if (this.windowsMainService.getWindowCount() === 0 || !!BrowserWindow.getFocusedWindow()) { - this.windowsMainService.quit(); // fix for https://github.com/Microsoft/vscode/issues/39191 - } - } - })); - - const actions = [about]; - actions.push(...checkForUpdates); - actions.push(...[ - __separator__(), - preferences, - __separator__(), - services, - __separator__(), - hide, - hideOthers, - showAll, - __separator__(), - quit - ]); - - actions.forEach(i => macApplicationMenu.append(i)); - } - - private setFileMenu(fileMenu: Electron.Menu): void { - const hasNoWindows = (this.windowsMainService.getWindowCount() === 0); - - // {{SQL CARBON EDIT}} - let newFile: Electron.MenuItem; - if (hasNoWindows) { - newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: this.mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New Query")), click: () => this.windowsMainService.openNewWindow(OpenContext.MENU) })); - } else { - newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New Query"), 'workbench.action.files.newUntitledFile'); - } - - // {{SQL CARBON EDIT}} - let newNotebook: Electron.MenuItem; - if (hasNoWindows) { - newNotebook = new MenuItem(this.likeAction('notebook.command.new', { label: this.mnemonicLabel(nls.localize({ key: 'miNewNotebook', comment: ['&& denotes a mnemonic'] }, "&&New Notebook")), click: () => this.windowsMainService.openNewWindow(OpenContext.MENU) })); - } else { - newNotebook = this.createMenuItem(nls.localize({ key: 'miNewNotebook', comment: ['&& denotes a mnemonic'] }, "&&New Notebook"), 'notebook.command.new'); - } - - let open: Electron.MenuItem; - if (hasNoWindows) { - open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsMainService.pickFileFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); - } else { - open = this.createMenuItem(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open..."), ['workbench.action.files.openFileFolder', 'workbench.action.files.openFileFolderInNewWindow']); - } - - let openWorkspace: Electron.MenuItem; - if (hasNoWindows) { - openWorkspace = new MenuItem(this.likeAction('workbench.action.openWorkspace', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...")), click: (menuItem, win, event) => this.windowsMainService.pickWorkspaceAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); - } else { - openWorkspace = this.createMenuItem(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace..."), ['workbench.action.openWorkspace', 'workbench.action.openWorkspaceInNewWindow']); - } - - let openFolder: Electron.MenuItem; - if (hasNoWindows) { - openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsMainService.pickFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); - } else { - openFolder = this.createMenuItem(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder..."), ['workbench.action.files.openFolder', 'workbench.action.files.openFolderInNewWindow']); - } - - let openFile: Electron.MenuItem; - if (hasNoWindows) { - openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: (menuItem, win, event) => this.windowsMainService.pickFileAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); - } else { - openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), ['workbench.action.files.openFile', 'workbench.action.files.openFileInNewWindow']); - } - - const openRecentMenu = new Menu(); - this.setOpenRecentMenu(openRecentMenu); - const openRecent = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent")), submenu: openRecentMenu, enabled: openRecentMenu.items.length > 0 }); - - const saveWorkspaceAs = this.createMenuItem(nls.localize('miSaveWorkspaceAs', "Save Workspace As..."), 'workbench.action.saveWorkspaceAs'); - const addFolder = this.createMenuItem(nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace..."), 'workbench.action.addRootFolder'); - - const saveFile = this.createMenuItem(nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save"), 'workbench.action.files.save'); - const saveFileAs = this.createMenuItem(nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As..."), 'workbench.action.files.saveAs'); - const saveAllFiles = this.createMenuItem(nls.localize({ key: 'miSaveAll', comment: ['&& denotes a mnemonic'] }, "Save A&&ll"), 'workbench.action.files.saveAll'); - - const autoSaveEnabled = [AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE].some(s => this.currentAutoSaveSetting === s); - - const autoSave = this.createMenuItem(this.mnemonicLabel(nls.localize('miAutoSave', "Auto Save")), 'workbench.action.toggleAutoSave', this.windowsMainService.getWindowCount() > 0, autoSaveEnabled); - - // {{SQL CARBON EDIT}} - const installVsixExtension = this.createMenuItem(nls.localize({ key: 'miinstallVsix', comment: ['&& denotes a mnemonic'] }, "Install Extension from VSIX Package"), 'workbench.extensions.action.installVSIX'); - - const preferences = this.getPreferencesMenu(); - - const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsMainService.openNewWindow(OpenContext.MENU) })); - const revertFile = this.createMenuItem(nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), 'workbench.action.files.revert'); - const closeWindow = new MenuItem(this.likeAction('workbench.action.closeWindow', { label: this.mnemonicLabel(nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window")), click: () => this.windowsMainService.getLastActiveWindow().win.close(), enabled: this.windowsMainService.getWindowCount() > 0 })); - - this.closeWorkspace = this.createMenuItem(nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace"), 'workbench.action.closeFolder'); - this.closeFolder = this.createMenuItem(nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), 'workbench.action.closeFolder'); - - const closeEditor = this.createMenuItem(nls.localize({ key: 'miCloseEditor', comment: ['&& denotes a mnemonic'] }, "&&Close Editor"), 'workbench.action.closeActiveEditor'); - - const exit = new MenuItem(this.likeAction('workbench.action.quit', { label: this.mnemonicLabel(nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit")), click: () => this.windowsMainService.quit() })); - - this.updateWorkspaceMenuItems(); - - arrays.coalesce([ - newFile, - // {{SQL CARBON EDIT}} - newNotebook, - newWindow, - __separator__(), - isMacintosh ? open : null, - !isMacintosh ? openFile : null, - !isMacintosh ? openFolder : null, - openWorkspace, - openRecent, - __separator__(), - addFolder, - saveWorkspaceAs, - __separator__(), - saveFile, - saveFileAs, - saveAllFiles, - __separator__(), - autoSave, - __separator__(), - // {{SQL CARBON EDIT}} - installVsixExtension, - __separator__(), - !isMacintosh ? preferences : null, - !isMacintosh ? __separator__() : null, - revertFile, - closeEditor, - this.closeWorkspace, - this.closeFolder, - closeWindow, - !isMacintosh ? __separator__() : null, - !isMacintosh ? exit : null - ]).forEach(item => fileMenu.append(item)); - } - - private getPreferencesMenu(): Electron.MenuItem { - const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openSettings'); - const kebindingSettings = this.createMenuItem(nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts"), 'workbench.action.openGlobalKeybindings'); - // {{SQL CARBON EDIT}} - // const keymapExtensions = this.createMenuItem(nls.localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions"), 'workbench.extensions.action.showRecommendedKeymapExtensions'); - // const snippetsSettings = this.createMenuItem(nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets"), 'workbench.action.openSnippets'); - const colorThemeSelection = this.createMenuItem(nls.localize({ key: 'miSelectColorTheme', comment: ['&& denotes a mnemonic'] }, "&&Color Theme"), 'workbench.action.selectTheme'); - const iconThemeSelection = this.createMenuItem(nls.localize({ key: 'miSelectIconTheme', comment: ['&& denotes a mnemonic'] }, "File &&Icon Theme"), 'workbench.action.selectIconTheme'); - - const preferencesMenu = new Menu(); - preferencesMenu.append(settings); - preferencesMenu.append(__separator__()); - preferencesMenu.append(kebindingSettings); - // {{SQL CARBON EDIT}} - // preferencesMenu.append(keymapExtensions); - // preferencesMenu.append(__separator__()); - // preferencesMenu.append(snippetsSettings); - preferencesMenu.append(__separator__()); - preferencesMenu.append(colorThemeSelection); - preferencesMenu.append(iconThemeSelection); - - return new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu }); - } - - private setOpenRecentMenu(openRecentMenu: Electron.Menu): void { - openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), 'workbench.action.reopenClosedEditor')); - - const { workspaces, files } = this.historyMainService.getRecentlyOpened(); - - // Workspaces - if (workspaces.length > 0) { - openRecentMenu.append(__separator__()); - - for (let i = 0; i < CodeMenu.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) { - openRecentMenu.append(this.createOpenRecentMenuItem(workspaces[i], 'openRecentWorkspace', false)); - } - } - - // Files - if (files.length > 0) { - openRecentMenu.append(__separator__()); - - for (let i = 0; i < CodeMenu.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) { - openRecentMenu.append(this.createOpenRecentMenuItem(files[i], 'openRecentFile', true)); - } - } - - if (workspaces.length || files.length) { - openRecentMenu.append(__separator__()); - openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More..."), 'workbench.action.openRecent')); - openRecentMenu.append(__separator__()); - openRecentMenu.append(new MenuItem(this.likeAction('workbench.action.clearRecentFiles', { label: this.mnemonicLabel(nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened")), click: () => this.historyMainService.clearRecentlyOpened() }))); - } - } - - private createOpenRecentMenuItem(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, commandId: string, isFile: boolean): Electron.MenuItem { - let label: string; - let uri: URI; - if (isSingleFolderWorkspaceIdentifier(workspace)) { - label = unmnemonicLabel(this.labelService.getWorkspaceLabel(workspace, { verbose: true })); - uri = workspace; - } else if (isWorkspaceIdentifier(workspace)) { - label = this.labelService.getWorkspaceLabel(workspace, { verbose: true }); - uri = URI.file(workspace.configPath); - } else { - uri = URI.file(workspace); - label = unmnemonicLabel(this.labelService.getUriLabel(uri)); - } - - return new MenuItem(this.likeAction(commandId, { - label, - click: (menuItem, win, event) => { - const openInNewWindow = this.isOptionClick(event); - const success = this.windowsMainService.open({ - context: OpenContext.MENU, - cli: this.environmentService.args, - urisToOpen: [uri], - forceNewWindow: openInNewWindow, - forceOpenWorkspaceAsFile: isFile - }).length > 0; - - if (!success) { - this.historyMainService.removeFromRecentlyOpened([workspace]); - } - } - }, false)); - } - - private isOptionClick(event: Electron.Event): boolean { - return event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey))); - } - - private createRoleMenuItem(label: string, commandId: string, role: string): Electron.MenuItem { - const options: Electron.MenuItemConstructorOptions = { - label: this.mnemonicLabel(label), - role, - enabled: true - }; - - return new MenuItem(this.withKeybinding(commandId, options)); - } - - private setEditMenu(winLinuxEditMenu: Electron.Menu): void { - let undo: Electron.MenuItem; - let redo: Electron.MenuItem; - let cut: Electron.MenuItem; - let copy: Electron.MenuItem; - let paste: Electron.MenuItem; - // {{SQL CARBON EDIT}} - let selectAll: Electron.MenuItem; - - if (isMacintosh) { - undo = this.createContextAwareMenuItem(nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), 'undo', { - inDevTools: devTools => devTools.undo(), - inNoWindow: () => Menu.sendActionToFirstResponder('undo:') - }); - redo = this.createContextAwareMenuItem(nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), 'redo', { - inDevTools: devTools => devTools.redo(), - inNoWindow: () => Menu.sendActionToFirstResponder('redo:') - }); - cut = this.createRoleMenuItem(nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), 'editor.action.clipboardCutAction', 'cut'); - copy = this.createRoleMenuItem(nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), 'editor.action.clipboardCopyAction', 'copy'); - paste = this.createRoleMenuItem(nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), 'editor.action.clipboardPasteAction', 'paste'); - // {{SQL CARBON EDIT}} - selectAll = this.createContextAwareMenuItem(nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), 'editor.action.selectAll', { - inDevTools: devTools => devTools.selectAll(), - inNoWindow: () => Menu.sendActionToFirstResponder('selectAll:') - }); - } else { - undo = this.createMenuItem(nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), 'undo'); - redo = this.createMenuItem(nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), 'redo'); - cut = this.createMenuItem(nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), 'editor.action.clipboardCutAction'); - copy = this.createMenuItem(nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), 'editor.action.clipboardCopyAction'); - paste = this.createMenuItem(nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), 'editor.action.clipboardPasteAction'); - // {{SQL CARBON EDIT}} - selectAll = this.createMenuItem(nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), 'editor.action.selectAll'); - } - - const find = this.createMenuItem(nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"), 'actions.find'); - const replace = this.createMenuItem(nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace"), 'editor.action.startFindReplaceAction'); - const findInFiles = this.createMenuItem(nls.localize({ key: 'miFindInFiles', comment: ['&& denotes a mnemonic'] }, "Find &&in Files"), 'workbench.action.findInFiles'); - const replaceInFiles = this.createMenuItem(nls.localize({ key: 'miReplaceInFiles', comment: ['&& denotes a mnemonic'] }, "Replace &&in Files"), 'workbench.action.replaceInFiles'); - - // {{SQL CARBON EDIT}} - // const emmetExpandAbbreviation = this.createMenuItem(nls.localize({ key: 'miEmmetExpandAbbreviation', comment: ['&& denotes a mnemonic'] }, "Emmet: E&&xpand Abbreviation"), 'editor.emmet.action.expandAbbreviation'); - // const showEmmetCommands = this.createMenuItem(nls.localize({ key: 'miShowEmmetCommands', comment: ['&& denotes a mnemonic'] }, "E&&mmet..."), 'workbench.action.showEmmetCommands'); - // const toggleLineComment = this.createMenuItem(nls.localize({ key: 'miToggleLineComment', comment: ['&& denotes a mnemonic'] }, "&&Toggle Line Comment"), 'editor.action.commentLine'); - // const toggleBlockComment = this.createMenuItem(nls.localize({ key: 'miToggleBlockComment', comment: ['&& denotes a mnemonic'] }, "Toggle &&Block Comment"), 'editor.action.blockComment'); - - [ - undo, - redo, - __separator__(), - cut, - copy, - paste, - __separator__(), - find, - replace, - __separator__(), - findInFiles, - replaceInFiles, - // {{SQL CARBON EDIT}} - selectAll, - // {{SQL CARBON EDIT}} - // __separator__(), - // toggleLineComment, - // toggleBlockComment, - // emmetExpandAbbreviation, - // showEmmetCommands - ].forEach(item => winLinuxEditMenu.append(item)); - } - - // {{SQL CARBON EDIT}} - /* - private setSelectionMenu(winLinuxEditMenu: Electron.Menu): void { - let multiCursorModifierLabel: string; - if (this.currentMultiCursorModifierSetting === 'ctrlCmd') { - multiCursorModifierLabel = nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor"); // The default has been overwritten - } else { - multiCursorModifierLabel = ( - isMacintosh - ? nls.localize('miMultiCursorCmd', "Switch to Cmd+Click for Multi-Cursor") - : nls.localize('miMultiCursorCtrl', "Switch to Ctrl+Click for Multi-Cursor") - ); - } - - const multicursorModifier = this.createMenuItem(multiCursorModifierLabel, 'workbench.action.toggleMultiCursorModifier'); - const insertCursorAbove = this.createMenuItem(nls.localize({ key: 'miInsertCursorAbove', comment: ['&& denotes a mnemonic'] }, "&&Add Cursor Above"), 'editor.action.insertCursorAbove'); - const insertCursorBelow = this.createMenuItem(nls.localize({ key: 'miInsertCursorBelow', comment: ['&& denotes a mnemonic'] }, "A&&dd Cursor Below"), 'editor.action.insertCursorBelow'); - const insertCursorAtEndOfEachLineSelected = this.createMenuItem(nls.localize({ key: 'miInsertCursorAtEndOfEachLineSelected', comment: ['&& denotes a mnemonic'] }, "Add C&&ursors to Line Ends"), 'editor.action.insertCursorAtEndOfEachLineSelected'); - const addSelectionToNextFindMatch = this.createMenuItem(nls.localize({ key: 'miAddSelectionToNextFindMatch', comment: ['&& denotes a mnemonic'] }, "Add &&Next Occurrence"), 'editor.action.addSelectionToNextFindMatch'); - const addSelectionToPreviousFindMatch = this.createMenuItem(nls.localize({ key: 'miAddSelectionToPreviousFindMatch', comment: ['&& denotes a mnemonic'] }, "Add P&&revious Occurrence"), 'editor.action.addSelectionToPreviousFindMatch'); - const selectHighlights = this.createMenuItem(nls.localize({ key: 'miSelectHighlights', comment: ['&& denotes a mnemonic'] }, "Select All &&Occurrences"), 'editor.action.selectHighlights'); - - const copyLinesUp = this.createMenuItem(nls.localize({ key: 'miCopyLinesUp', comment: ['&& denotes a mnemonic'] }, "&&Copy Line Up"), 'editor.action.copyLinesUpAction'); - const copyLinesDown = this.createMenuItem(nls.localize({ key: 'miCopyLinesDown', comment: ['&& denotes a mnemonic'] }, "Co&&py Line Down"), 'editor.action.copyLinesDownAction'); - const moveLinesUp = this.createMenuItem(nls.localize({ key: 'miMoveLinesUp', comment: ['&& denotes a mnemonic'] }, "Mo&&ve Line Up"), 'editor.action.moveLinesUpAction'); - const moveLinesDown = this.createMenuItem(nls.localize({ key: 'miMoveLinesDown', comment: ['&& denotes a mnemonic'] }, "Move &&Line Down"), 'editor.action.moveLinesDownAction'); - - let selectAll: Electron.MenuItem; - if (isMacintosh) { - selectAll = this.createContextAwareMenuItem(nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), 'editor.action.selectAll', { - inDevTools: devTools => devTools.selectAll(), - inNoWindow: () => Menu.sendActionToFirstResponder('selectAll:') - }); - } else { - selectAll = this.createMenuItem(nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), 'editor.action.selectAll'); - } - const smartSelectGrow = this.createMenuItem(nls.localize({ key: 'miSmartSelectGrow', comment: ['&& denotes a mnemonic'] }, "&&Expand Selection"), 'editor.action.smartSelect.grow'); - const smartSelectshrink = this.createMenuItem(nls.localize({ key: 'miSmartSelectShrink', comment: ['&& denotes a mnemonic'] }, "&&Shrink Selection"), 'editor.action.smartSelect.shrink'); - - [ - selectAll, - smartSelectGrow, - smartSelectshrink, - __separator__(), - copyLinesUp, - copyLinesDown, - moveLinesUp, - moveLinesDown, - __separator__(), - multicursorModifier, - insertCursorAbove, - insertCursorBelow, - insertCursorAtEndOfEachLineSelected, - addSelectionToNextFindMatch, - addSelectionToPreviousFindMatch, - selectHighlights, - ].forEach(item => winLinuxEditMenu.append(item)); - } - // {{SQL CARBON EDIT}} - */ - - private setViewMenu(viewMenu: Electron.Menu): void { - const commands = this.createMenuItem(nls.localize({ key: 'miCommandPalette', comment: ['&& denotes a mnemonic'] }, "&&Command Palette..."), 'workbench.action.showCommands'); - const openView = this.createMenuItem(nls.localize({ key: 'miOpenView', comment: ['&& denotes a mnemonic'] }, "&&Open View..."), 'workbench.action.openView'); - - // {{SQL CARBON EDIT}} - const servers = this.createMenuItem(nls.localize({ key: 'miViewRegisteredServers', comment: ['&& denotes a mnemonic'] }, "&&Servers"), 'workbench.view.connections'); - const tasks = this.createMenuItem(nls.localize({ key: 'miViewTasks', comment: ['&& denotes a mnemonic'] }, "&&Tasks"), 'workbench.view.taskHistory'); - const explorer = this.createMenuItem(nls.localize({ key: 'miViewExplorer', comment: ['&& denotes a mnemonic'] }, "&&Explorer"), 'workbench.view.explorer'); - const search = this.createMenuItem(nls.localize({ key: 'miViewSearch', comment: ['&& denotes a mnemonic'] }, "&&Search"), 'workbench.view.search'); - const scm = this.createMenuItem(nls.localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM"), 'workbench.view.scm'); - // {{SQL CARBON EDIT}} - // const debug = this.createMenuItem(nls.localize({ key: 'miViewDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug"), 'workbench.view.debug'); - const extensions = this.createMenuItem(nls.localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions"), 'workbench.view.extensions'); - - // Panels - const output = this.createMenuItem(nls.localize({ key: 'miToggleOutput', comment: ['&& denotes a mnemonic'] }, "&&Output"), 'workbench.action.output.toggleOutput'); - // {{SQL CARBON EDIT}} - // const debugConsole = this.createMenuItem(nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console"), 'workbench.debug.action.toggleRepl'); - const terminal = this.createMenuItem(nls.localize({ key: 'miToggleTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal"), 'workbench.action.terminal.toggleTerminal'); - const problems = this.createMenuItem(nls.localize({ key: 'miMarker', comment: ['&& denotes a mnemonic'] }, "&&Problems"), 'workbench.actions.view.problems'); - - // Appearance - - const appearanceMenu = new Menu(); - - const fullscreen = new MenuItem(this.withKeybinding('workbench.action.toggleFullScreen', { label: this.mnemonicLabel(nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen")), click: () => this.windowsMainService.getLastActiveWindow().toggleFullScreen(), enabled: this.windowsMainService.getWindowCount() > 0 })); - const toggleZenMode = this.createMenuItem(nls.localize('miToggleZenMode', "Toggle Zen Mode"), 'workbench.action.toggleZenMode'); - const toggleCenteredLayout = this.createMenuItem(nls.localize('miToggleCenteredLayout', "Toggle Centered Layout"), 'workbench.action.toggleCenteredLayout'); - const toggleMenuBar = this.createMenuItem(nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar"), 'workbench.action.toggleMenuBar'); - - const toggleSidebar = this.createMenuItem(nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar"), 'workbench.action.toggleSidebarVisibility'); - - let moveSideBarLabel: string; - if (this.currentSidebarLocation !== 'right') { - moveSideBarLabel = nls.localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right"); - } else { - moveSideBarLabel = nls.localize({ key: 'miMoveSidebarLeft', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left"); - } - - const moveSidebar = this.createMenuItem(moveSideBarLabel, 'workbench.action.toggleSidebarPosition'); - const togglePanel = this.createMenuItem(nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel"), 'workbench.action.togglePanel'); - - let statusBarLabel: string; - if (this.currentStatusbarVisible) { - statusBarLabel = nls.localize({ key: 'miHideStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Hide Status Bar"); - } else { - statusBarLabel = nls.localize({ key: 'miShowStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Show Status Bar"); - } - const toggleStatusbar = this.createMenuItem(statusBarLabel, 'workbench.action.toggleStatusbarVisibility'); - - let activityBarLabel: string; - if (this.currentActivityBarVisible) { - activityBarLabel = nls.localize({ key: 'miHideActivityBar', comment: ['&& denotes a mnemonic'] }, "Hide &&Activity Bar"); - } else { - activityBarLabel = nls.localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar"); - } - const toggleActivtyBar = this.createMenuItem(activityBarLabel, 'workbench.action.toggleActivityBarVisibility'); - - const zoomIn = this.createMenuItem(nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In"), 'workbench.action.zoomIn'); - const zoomOut = this.createMenuItem(nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "Zoom O&&ut"), 'workbench.action.zoomOut'); - const resetZoom = this.createMenuItem(nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom"), 'workbench.action.zoomReset'); - // {{SQL CARBON EDIT}} - arrays.coalesce([ - fullscreen, - toggleZenMode, - toggleCenteredLayout, - isWindows || isLinux ? toggleMenuBar : void 0, - __separator__(), - moveSidebar, - toggleSidebar, - togglePanel, - toggleStatusbar, - toggleActivtyBar, - __separator__(), - zoomIn, - zoomOut, - resetZoom - ]).forEach(item => appearanceMenu.append(item)); - - const appearance = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance")), submenu: appearanceMenu }); - - // Editor Layout - - const editorLayoutMenu = new Menu(); - - const splitEditorUp = this.createMenuItem(nls.localize({ key: 'miSplitEditorUp', comment: ['&& denotes a mnemonic'] }, "Split &&Up"), 'workbench.action.splitEditorUp'); - const splitEditorDown = this.createMenuItem(nls.localize({ key: 'miSplitEditorDown', comment: ['&& denotes a mnemonic'] }, "Split &&Down"), 'workbench.action.splitEditorDown'); - const splitEditorLeft = this.createMenuItem(nls.localize({ key: 'miSplitEditorLeft', comment: ['&& denotes a mnemonic'] }, "Split &&Left"), 'workbench.action.splitEditorLeft'); - const splitEditorRight = this.createMenuItem(nls.localize({ key: 'miSplitEditorRight', comment: ['&& denotes a mnemonic'] }, "Split &&Right"), 'workbench.action.splitEditorRight'); - - const singleColumnEditorLayout = this.createMenuItem(nls.localize({ key: 'miSingleColumnEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Single"), 'workbench.action.editorLayoutSingle'); - const twoColumnsEditorLayout = this.createMenuItem(nls.localize({ key: 'miTwoColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Two Columns"), 'workbench.action.editorLayoutTwoColumns'); - const threeColumnsEditorLayout = this.createMenuItem(nls.localize({ key: 'miThreeColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&hree Columns"), 'workbench.action.editorLayoutThreeColumns'); - const twoRowsEditorLayout = this.createMenuItem(nls.localize({ key: 'miTwoRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&wo Rows"), 'workbench.action.editorLayoutTwoRows'); - const threeRowsEditorLayout = this.createMenuItem(nls.localize({ key: 'miThreeRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "Three &&Rows"), 'workbench.action.editorLayoutThreeRows'); - const twoByTwoGridEditorLayout = this.createMenuItem(nls.localize({ key: 'miTwoByTwoGridEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Grid (2x2)"), 'workbench.action.editorLayoutTwoByTwoGrid'); - const twoRowsRightEditorLayout = this.createMenuItem(nls.localize({ key: 'miTwoRowsRightEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two R&&ows Right"), 'workbench.action.editorLayoutTwoRowsRight'); - const twoColumnsBottomEditorLayout = this.createMenuItem(nls.localize({ key: 'miTwoColumnsBottomEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two &&Columns Bottom"), 'workbench.action.editorLayoutTwoColumnsBottom'); - - const toggleEditorLayout = this.createMenuItem(nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Flip &&Layout"), 'workbench.action.toggleEditorGroupLayout'); - - [ - splitEditorUp, - splitEditorDown, - splitEditorLeft, - splitEditorRight, - __separator__(), - singleColumnEditorLayout, - twoColumnsEditorLayout, - threeColumnsEditorLayout, - twoRowsEditorLayout, - threeRowsEditorLayout, - twoByTwoGridEditorLayout, - twoRowsRightEditorLayout, - twoColumnsBottomEditorLayout, - __separator__(), - toggleEditorLayout - ].forEach(item => editorLayoutMenu.append(item)); - - const editorLayout = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout")), submenu: editorLayoutMenu }); - - // const toggleWordWrap = this.createMenuItem(nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap"), 'editor.action.toggleWordWrap'); - // const toggleMinimap = this.createMenuItem(nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap"), 'editor.action.toggleMinimap'); - // const toggleRenderWhitespace = this.createMenuItem(nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace"), 'editor.action.toggleRenderWhitespace'); - // const toggleRenderControlCharacters = this.createMenuItem(nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters"), 'editor.action.toggleRenderControlCharacter'); - // const toggleBreadcrumbs = this.createMenuItem(nls.localize({ key: 'miToggleBreadcrumbs', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breadcrumbs"), 'breadcrumbs.toggle'); - - arrays.coalesce([ - commands, - openView, - __separator__(), - appearance, - editorLayout, - __separator__(), - servers, - tasks, - explorer, - search, - scm, - // {{SQL CARBON EDIT}} - // debug, - extensions, - __separator__(), - output, - problems, - // {{SQL CARBON EDIT}} - // debugConsole, - terminal, - //__separator__(), - // {{SQL CARBON EDIT}} - // toggleWordWrap, - // toggleMinimap, - // toggleRenderWhitespace, - // toggleRenderControlCharacters, - // toggleBreadcrumbs - ]).forEach(item => viewMenu.append(item)); - } - - private setGotoMenu(gotoMenu: Electron.Menu): void { - const back = this.createMenuItem(nls.localize({ key: 'miBack', comment: ['&& denotes a mnemonic'] }, "&&Back"), 'workbench.action.navigateBack'); - const forward = this.createMenuItem(nls.localize({ key: 'miForward', comment: ['&& denotes a mnemonic'] }, "&&Forward"), 'workbench.action.navigateForward'); - - const switchEditorMenu = new Menu(); - - const nextEditor = this.createMenuItem(nls.localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor"), 'workbench.action.nextEditor'); - const previousEditor = this.createMenuItem(nls.localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor"), 'workbench.action.previousEditor'); - const nextEditorInGroup = this.createMenuItem(nls.localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group"), 'workbench.action.openNextRecentlyUsedEditorInGroup'); - const previousEditorInGroup = this.createMenuItem(nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group"), 'workbench.action.openPreviousRecentlyUsedEditorInGroup'); - - [ - nextEditor, - previousEditor, - __separator__(), - nextEditorInGroup, - previousEditorInGroup - ].forEach(item => switchEditorMenu.append(item)); - - const switchEditor = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor")), submenu: switchEditorMenu, enabled: true }); - - const switchGroupMenu = new Menu(); - - const focusFirstGroup = this.createMenuItem(nls.localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "Group &&1"), 'workbench.action.focusFirstEditorGroup'); - const focusSecondGroup = this.createMenuItem(nls.localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "Group &&2"), 'workbench.action.focusSecondEditorGroup'); - const focusThirdGroup = this.createMenuItem(nls.localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "Group &&3"), 'workbench.action.focusThirdEditorGroup'); - const focusFourthGroup = this.createMenuItem(nls.localize({ key: 'miFocusFourthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&4"), 'workbench.action.focusFourthEditorGroup'); - const focusFifthGroup = this.createMenuItem(nls.localize({ key: 'miFocusFifthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&5"), 'workbench.action.focusFifthEditorGroup'); - const nextGroup = this.createMenuItem(nls.localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group"), 'workbench.action.focusNextGroup'); - const previousGroup = this.createMenuItem(nls.localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group"), 'workbench.action.focusPreviousGroup'); - - const focusLeftGroup = this.createMenuItem(nls.localize({ key: 'miFocusLeftGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Left"), 'workbench.action.focusLeftGroup'); - const focusRightGroup = this.createMenuItem(nls.localize({ key: 'miFocusRightGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Right"), 'workbench.action.focusRightGroup'); - const focusAboveGroup = this.createMenuItem(nls.localize({ key: 'miFocusAboveGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Above"), 'workbench.action.focusAboveGroup'); - const focusBelowGroup = this.createMenuItem(nls.localize({ key: 'miFocusBelowGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Below"), 'workbench.action.focusBelowGroup'); - - [ - focusFirstGroup, - focusSecondGroup, - focusThirdGroup, - focusFourthGroup, - focusFifthGroup, - __separator__(), - nextGroup, - previousGroup, - __separator__(), - focusAboveGroup, - focusBelowGroup, - focusLeftGroup, - focusRightGroup - ].forEach(item => switchGroupMenu.append(item)); - - const switchGroup = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group")), submenu: switchGroupMenu, enabled: true }); - - const gotoFile = this.createMenuItem(nls.localize({ key: 'miGotoFile', comment: ['&& denotes a mnemonic'] }, "Go to &&File..."), 'workbench.action.quickOpen'); - // {{SQL CARBON EDIT}} - // const gotoSymbolInFile = this.createMenuItem(nls.localize({ key: 'miGotoSymbolInFile', comment: ['&& denotes a mnemonic'] }, "Go to &&Symbol in File..."), 'workbench.action.gotoSymbol'); - // const gotoSymbolInWorkspace = this.createMenuItem(nls.localize({ key: 'miGotoSymbolInWorkspace', comment: ['&& denotes a mnemonic'] }, "Go to Symbol in &&Workspace..."), 'workbench.action.showAllSymbols'); - // const gotoDefinition = this.createMenuItem(nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition"), 'editor.action.goToDeclaration'); - const gotoTypeDefinition = this.createMenuItem(nls.localize({ key: 'miGotoTypeDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Type Definition"), 'editor.action.goToTypeDefinition'); - const goToImplementation = this.createMenuItem(nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementation"), 'editor.action.goToImplementation'); - const gotoLine = this.createMenuItem(nls.localize({ key: 'miGotoLine', comment: ['&& denotes a mnemonic'] }, "Go to &&Line..."), 'workbench.action.gotoLine'); - - [ - back, - forward, - __separator__(), - switchEditor, - switchGroup, - __separator__(), - gotoFile, - // {{SQL CARBON EDIT}} - // gotoSymbolInFile, - // gotoSymbolInWorkspace, - // gotoDefinition, - // gotoTypeDefinition, - // goToImplementation, - gotoLine - ].forEach(item => gotoMenu.append(item)); - } - - private setDebugMenu(debugMenu: Electron.Menu): void { - const start = this.createMenuItem(nls.localize({ key: 'miStartDebugging', comment: ['&& denotes a mnemonic'] }, "&&Start Debugging"), 'workbench.action.debug.start'); - const startWithoutDebugging = this.createMenuItem(nls.localize({ key: 'miStartWithoutDebugging', comment: ['&& denotes a mnemonic'] }, "Start &&Without Debugging"), 'workbench.action.debug.run'); - const stop = this.createMenuItem(nls.localize({ key: 'miStopDebugging', comment: ['&& denotes a mnemonic'] }, "&&Stop Debugging"), 'workbench.action.debug.stop'); - const restart = this.createMenuItem(nls.localize({ key: 'miRestart Debugging', comment: ['&& denotes a mnemonic'] }, "&&Restart Debugging"), 'workbench.action.debug.restart'); - - const openConfigurations = this.createMenuItem(nls.localize({ key: 'miOpenConfigurations', comment: ['&& denotes a mnemonic'] }, "Open &&Configurations"), 'workbench.action.debug.configure'); - const addConfiguration = this.createMenuItem(nls.localize({ key: 'miAddConfiguration', comment: ['&& denotes a mnemonic'] }, "Add Configuration..."), 'debug.addConfiguration'); - - const stepOver = this.createMenuItem(nls.localize({ key: 'miStepOver', comment: ['&& denotes a mnemonic'] }, "Step &&Over"), 'workbench.action.debug.stepOver'); - const stepInto = this.createMenuItem(nls.localize({ key: 'miStepInto', comment: ['&& denotes a mnemonic'] }, "Step &&Into"), 'workbench.action.debug.stepInto'); - const stepOut = this.createMenuItem(nls.localize({ key: 'miStepOut', comment: ['&& denotes a mnemonic'] }, "Step O&&ut"), 'workbench.action.debug.stepOut'); - const continueAction = this.createMenuItem(nls.localize({ key: 'miContinue', comment: ['&& denotes a mnemonic'] }, "&&Continue"), 'workbench.action.debug.continue'); - - const toggleBreakpoint = this.createMenuItem(nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breakpoint"), 'editor.debug.action.toggleBreakpoint'); - const breakpointsMenu = new Menu(); - breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Conditional Breakpoint..."), 'editor.debug.action.conditionalBreakpoint')); - breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miInlineBreakpoint', comment: ['&& denotes a mnemonic'] }, "Inline Breakp&&oint"), 'editor.debug.action.toggleInlineBreakpoint')); - breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint..."), 'workbench.debug.viewlet.action.addFunctionBreakpointAction')); - breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "&&Logpoint..."), 'editor.debug.action.toggleLogPoint')); - const newBreakpoints = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&New Breakpoint")), submenu: breakpointsMenu }); - const enableAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Enable All Breakpoints"), 'workbench.debug.viewlet.action.enableAllBreakpoints'); - const disableAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints"), 'workbench.debug.viewlet.action.disableAllBreakpoints'); - const removeAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints"), 'workbench.debug.viewlet.action.removeAllBreakpoints'); - - const installAdditionalDebuggers = this.createMenuItem(nls.localize({ key: 'miInstallAdditionalDebuggers', comment: ['&& denotes a mnemonic'] }, "&&Install Additional Debuggers..."), 'debug.installAdditionalDebuggers'); - [ - start, - startWithoutDebugging, - stop, - restart, - __separator__(), - openConfigurations, - addConfiguration, - __separator__(), - stepOver, - stepInto, - stepOut, - continueAction, - __separator__(), - toggleBreakpoint, - newBreakpoints, - enableAllBreakpoints, - disableAllBreakpoints, - removeAllBreakpoints, - __separator__(), - installAdditionalDebuggers - ].forEach(item => debugMenu.append(item)); - } - - private setMacWindowMenu(macWindowMenu: Electron.Menu): void { - const minimize = new MenuItem({ label: nls.localize('mMinimize', "Minimize"), role: 'minimize', accelerator: 'Command+M', enabled: this.windowsMainService.getWindowCount() > 0 }); - const zoom = new MenuItem({ label: nls.localize('mZoom', "Zoom"), role: 'zoom', enabled: this.windowsMainService.getWindowCount() > 0 }); - const bringAllToFront = new MenuItem({ label: nls.localize('mBringToFront', "Bring All to Front"), role: 'front', enabled: this.windowsMainService.getWindowCount() > 0 }); - const switchWindow = this.createMenuItem(nls.localize({ key: 'miSwitchWindow', comment: ['&& denotes a mnemonic'] }, "Switch &&Window..."), 'workbench.action.switchWindow'); - - this.nativeTabMenuItems = []; - const nativeTabMenuItems: Electron.MenuItem[] = []; - if (this.currentEnableNativeTabs) { - const hasMultipleWindows = this.windowsMainService.getWindowCount() > 1; - - this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mShowPreviousTab', "Show Previous Tab"), 'workbench.action.showPreviousWindowTab', hasMultipleWindows)); - this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mShowNextTab', "Show Next Tab"), 'workbench.action.showNextWindowTab', hasMultipleWindows)); - this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mMoveTabToNewWindow', "Move Tab to New Window"), 'workbench.action.moveWindowTabToNewWindow', hasMultipleWindows)); - this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mMergeAllWindows', "Merge All Windows"), 'workbench.action.mergeAllWindowTabs', hasMultipleWindows)); - - nativeTabMenuItems.push(__separator__(), ...this.nativeTabMenuItems); - } else { - this.nativeTabMenuItems = []; - } - - [ - minimize, - zoom, - switchWindow, - ...nativeTabMenuItems, - __separator__(), - bringAllToFront - ].forEach(item => macWindowMenu.append(item)); - } - - private toggleDevTools(): void { - const w = this.windowsMainService.getFocusedWindow(); - if (w && w.win) { - const contents = w.win.webContents; - if (isMacintosh && w.hasHiddenTitleBarStyle() && !w.win.isFullScreen() && !contents.isDevToolsOpened()) { - contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647 - } else { - contents.toggleDevTools(); - } - } - } - - private setHelpMenu(helpMenu: Electron.Menu): void { - const toggleDevToolsItem = new MenuItem(this.likeAction('workbench.action.toggleDevTools', { - label: this.mnemonicLabel(nls.localize({ key: 'miToggleDevTools', comment: ['&& denotes a mnemonic'] }, "&&Toggle Developer Tools")), - click: () => this.toggleDevTools(), - enabled: (this.windowsMainService.getWindowCount() > 0) - })); - - const showAccessibilityOptions = new MenuItem(this.likeAction('accessibilityOptions', { - label: this.mnemonicLabel(nls.localize({ key: 'miAccessibilityOptions', comment: ['&& denotes a mnemonic'] }, "Accessibility &&Options")), - accelerator: null, - click: () => { - this.openAccessibilityOptions(); - } - }, false)); - - const openProcessExplorer = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer")), click: () => this.runActionInRenderer('workbench.action.openProcessExplorer') }); - - let reportIssuesItem: Electron.MenuItem = null; - if (product.reportIssueUrl) { - const label = nls.localize({ key: 'miReportIssue', comment: ['&& denotes a mnemonic', 'Translate this to "Report Issue in English" in all languages please!'] }, "Report &&Issue"); - - if (this.windowsMainService.getWindowCount() > 0) { - reportIssuesItem = this.createMenuItem(label, 'workbench.action.openIssueReporter'); - } else { - reportIssuesItem = new MenuItem({ label: this.mnemonicLabel(label), click: () => this.openUrl(product.reportIssueUrl, 'openReportIssues') }); - } - } - - // {{SQL CARBON EDIT}} - // const keyboardShortcutsUrl = isLinux ? product.keyboardShortcutsUrlLinux : isMacintosh ? product.keyboardShortcutsUrlMac : product.keyboardShortcutsUrlWin; - arrays.coalesce([ - // new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showWelcomePage') }), - // {{SQL CARBON EDIT}} - product.gettingStartedUrl ? - this.createMenuItem(nls.localize({ key: 'miGettingStarted', comment: ['&& denotes a mnemonic'] }, "Getting &&Started"), 'update.showGettingStarted') - : null, - product.documentationUrl ? - this.createMenuItem(nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation"), 'workbench.action.openDocumentationUrl') - : null, - // __separator__(), - // keyboardShortcutsUrl ? new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.keybindingsReference') }) : null, - // product.introductoryVideosUrl ? new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.openIntroductoryVideosUrl') }) : null, - // (product.introductoryVideosUrl || keyboardShortcutsUrl) ? __separator__() : null, - // product.twitterUrl ? new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join us on Twitter")), click: () => this.openUrl(product.twitterUrl, 'openTwitterUrl') }) : null, - // product.requestFeatureUrl ? new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests")), click: () => this.openUrl(product.requestFeatureUrl, 'openUserVoiceUrl') }) : null, - reportIssuesItem, - (product.twitterUrl || product.requestFeatureUrl || product.reportIssueUrl) ? __separator__() : null, - product.licenseUrl ? new MenuItem({ - label: this.mnemonicLabel(nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License")), click: () => { - if (language) { - const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; - this.openUrl(`${product.licenseUrl}${queryArgChar}lang=${language}`, 'openLicenseUrl'); - } else { - this.openUrl(product.licenseUrl, 'openLicenseUrl'); - } - } - }) : null, - product.privacyStatementUrl ? new MenuItem({ - label: this.mnemonicLabel(nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "&&Privacy Statement")), click: () => { - if (language) { - const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; - this.openUrl(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`, 'openPrivacyStatement'); - } else { - this.openUrl(product.privacyStatementUrl, 'openPrivacyStatement'); - } - } - }) : null, - product.telemetryOptOutUrl ? new MenuItem({ - label: this.mnemonicLabel(nls.localize({ key: 'mTelemetryOptOut', comment: ['&& denotes a mnemonic'] }, "Telemetry &&Opt-Out")), click: () => { - if (language) { - const queryArgChar = product.telemetryOptOutUrl.indexOf('?') > 0 ? '&' : '?'; - this.openUrl(`${product.telemetryOptOutUrl}${queryArgChar}lang=${language}`, 'openTelemetryOptOut'); - } else { - this.openUrl(product.telemetryOptOutUrl, 'openTelemetryOptOut'); - } - } - }) : null, - (product.licenseUrl || product.privacyStatementUrl) ? __separator__() : null, - toggleDevToolsItem, - openProcessExplorer, - isWindows && product.quality !== 'stable' ? showAccessibilityOptions : null, - ]).forEach(item => helpMenu.append(item)); - - if (!isMacintosh) { - const updateMenuItems = this.getUpdateMenuItems(); - if (updateMenuItems.length) { - helpMenu.append(__separator__()); - updateMenuItems.forEach(i => helpMenu.append(i)); - } - - helpMenu.append(__separator__()); - helpMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About")), click: () => this.windowsService.openAboutDialog() })); - } - } - - private setTaskMenu(taskMenu: Electron.Menu): void { - // {{SQL CARBON EDIT}} - /* - const runTask = this.createMenuItem(nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task..."), 'workbench.action.tasks.runTask'); - const buildTask = this.createMenuItem(nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task..."), 'workbench.action.tasks.build'); - const showTasks = this.createMenuItem(nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks..."), 'workbench.action.tasks.showTasks'); - const restartTask = this.createMenuItem(nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task..."), 'workbench.action.tasks.restartTask'); - const terminateTask = this.createMenuItem(nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task..."), 'workbench.action.tasks.terminate'); - const configureTask = this.createMenuItem(nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks..."), 'workbench.action.tasks.configureTaskRunner'); - const configureBuildTask = this.createMenuItem(nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task..."), 'workbench.action.tasks.configureDefaultBuildTask'); - - [ - //__separator__(), - runTask, - buildTask, - __separator__(), - terminateTask, - restartTask, - showTasks, - __separator__(), - configureTask, - configureBuildTask - ].forEach(item => taskMenu.append(item)); - // {{SQL CARBON EDIT}} - */ - } - - private openAccessibilityOptions(): void { - const win = new BrowserWindow({ - alwaysOnTop: true, - skipTaskbar: true, - resizable: false, - width: 450, - height: 300, - show: true, - title: nls.localize('accessibilityOptionsWindowTitle', "Accessibility Options"), - webPreferences: { - disableBlinkFeatures: 'Auxclick' - } - }); - - win.setMenuBarVisibility(false); - - win.loadURL('chrome://accessibility'); - } - - private getUpdateMenuItems(): Electron.MenuItem[] { - const state = this.updateService.state; - - switch (state.type) { - case StateType.Uninitialized: - return []; - - case StateType.Idle: - return [new MenuItem({ - label: nls.localize('miCheckForUpdates', "Check for Updates..."), click: () => setTimeout(() => { - this.reportMenuActionTelemetry('CheckForUpdate'); - - const focusedWindow = this.windowsMainService.getFocusedWindow(); - const context = focusedWindow ? { windowId: focusedWindow.id } : null; - this.updateService.checkForUpdates(context); - }, 0) - })]; - - case StateType.CheckingForUpdates: - return [new MenuItem({ label: nls.localize('miCheckingForUpdates', "Checking For Updates..."), enabled: false })]; - - case StateType.AvailableForDownload: - return [new MenuItem({ - label: nls.localize('miDownloadUpdate', "Download Available Update"), click: () => { - this.updateService.downloadUpdate(); - } - })]; - - case StateType.Downloading: - return [new MenuItem({ label: nls.localize('miDownloadingUpdate', "Downloading Update..."), enabled: false })]; - - case StateType.Downloaded: - return [new MenuItem({ - label: nls.localize('miInstallUpdate', "Install Update..."), click: () => { - this.reportMenuActionTelemetry('InstallUpdate'); - this.updateService.applyUpdate(); - } - })]; - - case StateType.Updating: - return [new MenuItem({ label: nls.localize('miInstallingUpdate', "Installing Update..."), enabled: false })]; - - case StateType.Ready: - return [new MenuItem({ - label: nls.localize('miRestartToUpdate', "Restart to Update..."), click: () => { - this.reportMenuActionTelemetry('RestartToUpdate'); - this.updateService.quitAndInstall(); - } - })]; - } - } - - private createMenuItem(label: string, commandId: string | string[], enabled?: boolean, checked?: boolean): Electron.MenuItem; - private createMenuItem(label: string, click: () => void, enabled?: boolean, checked?: boolean): Electron.MenuItem; - private createMenuItem(arg1: string, arg2: any, arg3?: boolean, arg4?: boolean): Electron.MenuItem { - const label = this.mnemonicLabel(arg1); - const click: () => void = (typeof arg2 === 'function') ? arg2 : (menuItem: Electron.MenuItem, win: Electron.BrowserWindow, event: Electron.Event) => { - let commandId = arg2; - if (Array.isArray(arg2)) { - commandId = this.isOptionClick(event) ? arg2[1] : arg2[0]; // support alternative action if we got multiple action Ids and the option key was pressed while invoking - } - - this.runActionInRenderer(commandId); - }; - const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsMainService.getWindowCount() > 0; - const checked = typeof arg4 === 'boolean' ? arg4 : false; - - const options: Electron.MenuItemConstructorOptions = { - label, - click, - enabled - }; - - if (checked) { - options['type'] = 'checkbox'; - options['checked'] = checked; - } - - let commandId: string; - if (typeof arg2 === 'string') { - commandId = arg2; - } else if (Array.isArray(arg2)) { - commandId = arg2[0]; - } - - return new MenuItem(this.withKeybinding(commandId, options)); - } - - private createContextAwareMenuItem(label: string, commandId: string, clickHandler: IMenuItemClickHandler): Electron.MenuItem { - return new MenuItem(this.withKeybinding(commandId, { - label: this.mnemonicLabel(label), - enabled: this.windowsMainService.getWindowCount() > 0, - click: () => { - - // No Active Window - const activeWindow = this.windowsMainService.getFocusedWindow(); - if (!activeWindow) { - return clickHandler.inNoWindow(); - } - - // DevTools focused - if (activeWindow.win.webContents.isDevToolsFocused()) { - return clickHandler.inDevTools(activeWindow.win.webContents.devToolsWebContents); - } - - // Finally execute command in Window - this.runActionInRenderer(commandId); - } - })); - } - - private runActionInRenderer(id: string): void { - // We make sure to not run actions when the window has no focus, this helps - // for https://github.com/Microsoft/vscode/issues/25907 and specifically for - // https://github.com/Microsoft/vscode/issues/11928 - const activeWindow = this.windowsMainService.getFocusedWindow(); - if (activeWindow) { - this.windowsMainService.sendToFocused('vscode:runAction', { id, from: 'menu' } as IRunActionInWindowRequest); - } - } - - private withKeybinding(commandId: string, options: Electron.MenuItemConstructorOptions): Electron.MenuItemConstructorOptions { - const binding = this.keybindingsResolver.getKeybinding(commandId); - - // Apply binding if there is one - if (binding && binding.label) { - - // if the binding is native, we can just apply it - if (binding.isNative) { - options.accelerator = binding.label; - } - - // the keybinding is not native so we cannot show it as part of the accelerator of - // the menu item. we fallback to a different strategy so that we always display it - else { - const bindingIndex = options.label.indexOf('['); - if (bindingIndex >= 0) { - options.label = `${options.label.substr(0, bindingIndex)} [${binding.label}]`; - } else { - options.label = `${options.label} [${binding.label}]`; - } - } - } - - // Unset bindings if there is none - else { - options.accelerator = void 0; - } - - return options; - } - - private likeAction(commandId: string, options: Electron.MenuItemConstructorOptions, setAccelerator = !options.accelerator): Electron.MenuItemConstructorOptions { - if (setAccelerator) { - options = this.withKeybinding(commandId, options); - } - - const originalClick = options.click; - options.click = (item, window, event) => { - this.reportMenuActionTelemetry(commandId); - if (originalClick) { - originalClick(item, window, event); - } - }; - - return options; - } - - private openUrl(url: string, id: string): void { - shell.openExternal(url); - this.reportMenuActionTelemetry(id); - } - - private reportMenuActionTelemetry(id: string): void { - /* __GDPR__ - "workbenchActionExecuted" : { - "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('workbenchActionExecuted', { id, from: telemetryFrom }); - } - - private mnemonicLabel(label: string): string { - return baseMnemonicLabel(label, !this.currentEnableMenuBarMnemonics); - } -} - -function __separator__(): Electron.MenuItem { - return new MenuItem({ type: 'separator' }); -} - -export interface IKeybinding { - id: string; - label: string; - isNative: boolean; -} - -export class KeybindingsResolver { - - private static readonly lastKnownKeybindingsMapStorageKey = 'lastKnownKeybindings'; - - private commandIds: Set; - private keybindings: { [commandId: string]: IKeybinding }; - private keybindingsWatcher: ConfigWatcher; - - private _onKeybindingsChanged = new Emitter(); - onKeybindingsChanged: Event = this._onKeybindingsChanged.event; - - constructor( - @IStateService private stateService: IStateService, - @IEnvironmentService environmentService: IEnvironmentService, - @IWindowsMainService private windowsMainService: IWindowsMainService, - @ILogService private logService: ILogService - ) { - this.commandIds = new Set(); - this.keybindings = this.stateService.getItem<{ [id: string]: string; }>(KeybindingsResolver.lastKnownKeybindingsMapStorageKey) || Object.create(null); - this.keybindingsWatcher = new ConfigWatcher(environmentService.appKeybindingsPath, { changeBufferDelay: 100, onError: error => this.logService.error(error), defaultConfig: [] }); - - this.registerListeners(); - } - - private registerListeners(): void { - - // Listen to resolved keybindings from window - ipcMain.on('vscode:keybindingsResolved', (event, rawKeybindings: string) => { - let keybindings: IKeybinding[] = []; - try { - keybindings = JSON.parse(rawKeybindings); - } catch (error) { - // Should not happen - } - - // Fill hash map of resolved keybindings and check for changes - let keybindingsChanged = false; - let keybindingsCount = 0; - const resolvedKeybindings: { [commandId: string]: IKeybinding } = Object.create(null); - keybindings.forEach(keybinding => { - keybindingsCount++; - - resolvedKeybindings[keybinding.id] = keybinding; - - if (!this.keybindings[keybinding.id] || keybinding.label !== this.keybindings[keybinding.id].label) { - keybindingsChanged = true; - } - }); - - // A keybinding might have been unassigned, so we have to account for that too - if (Object.keys(this.keybindings).length !== keybindingsCount) { - keybindingsChanged = true; - } - - if (keybindingsChanged) { - this.keybindings = resolvedKeybindings; - this.stateService.setItem(KeybindingsResolver.lastKnownKeybindingsMapStorageKey, this.keybindings); // keep to restore instantly after restart - - this._onKeybindingsChanged.fire(); - } - }); - - // Resolve keybindings when any first window is loaded - const onceOnWindowReady = once(this.windowsMainService.onWindowReady); - onceOnWindowReady(win => this.resolveKeybindings(win)); - - // Resolve keybindings again when keybindings.json changes - this.keybindingsWatcher.onDidUpdateConfiguration(() => this.resolveKeybindings()); - - // Resolve keybindings when window reloads because an installed extension could have an impact - this.windowsMainService.onWindowReload(() => this.resolveKeybindings()); - } - - private resolveKeybindings(win = this.windowsMainService.getLastActiveWindow()): void { - if (this.commandIds.size && win) { - const commandIds: string[] = []; - this.commandIds.forEach(id => commandIds.push(id)); - win.sendWhenReady('vscode:resolveKeybindings', JSON.stringify(commandIds)); - } - } - - public getKeybinding(commandId: string): IKeybinding { - if (!commandId) { - return void 0; - } - - if (!this.commandIds.has(commandId)) { - this.commandIds.add(commandId); - } - - return this.keybindings[commandId]; - } - - public dispose(): void { - this._onKeybindingsChanged.dispose(); - this.keybindingsWatcher.dispose(); - } -} \ No newline at end of file diff --git a/src/sql/workbench/parts/connection/common/connectionProviderExtension.ts b/src/sql/workbench/parts/connection/common/connectionProviderExtension.ts index 2f5cee602707..b2bbcf429436 100644 --- a/src/sql/workbench/parts/connection/common/connectionProviderExtension.ts +++ b/src/sql/workbench/parts/connection/common/connectionProviderExtension.ts @@ -115,7 +115,7 @@ const ConnectionProviderContrib: IJSONSchema = { required: ['providerId'] }; -ExtensionsRegistry.registerExtensionPoint('connectionProvider', [], ConnectionProviderContrib).setHandler(extensions => { +ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'connectionProvider', jsonSchema: ConnectionProviderContrib }).setHandler(extensions => { function handleCommand(contrib: ConnectionProviderProperties, extension: IExtensionPointUser) { connectionRegistry.registerConnectionProvider(contrib.providerId, contrib); diff --git a/src/sql/workbench/parts/connection/electron-browser/connectionViewlet.ts b/src/sql/workbench/parts/connection/electron-browser/connectionViewlet.ts index 58a6b1be8e71..279f5be87b1d 100644 --- a/src/sql/workbench/parts/connection/electron-browser/connectionViewlet.ts +++ b/src/sql/workbench/parts/connection/electron-browser/connectionViewlet.ts @@ -7,7 +7,6 @@ import 'vs/css!./media/connectionViewlet'; import * as DOM from 'vs/base/browser/dom'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Builder } from 'sql/base/browser/builder'; import { Viewlet } from 'vs/workbench/browser/viewlet'; import { IAction } from 'vs/base/common/actions'; @@ -77,8 +76,8 @@ export class ConnectionViewlet extends Viewlet implements IConnectionsViewlet { }); } - public create(parent: HTMLElement): TPromise { - return new TPromise((resolve) => { + public create(parent: HTMLElement): Promise { + return new Promise((resolve) => { super.create(parent); this._root = parent; let parentBuilder = new Builder(parent); diff --git a/src/sql/workbench/services/admin/common/adminService.ts b/src/sql/workbench/services/admin/common/adminService.ts index ce7e3023e3aa..7119627c7ce2 100644 --- a/src/sql/workbench/services/admin/common/adminService.ts +++ b/src/sql/workbench/services/admin/common/adminService.ts @@ -13,7 +13,6 @@ import { IConnectionManagementService } from 'sql/platform/connection/common/con import { CreateLoginInput } from 'sql/parts/admin/security/createLoginInput'; import { TaskDialogInput } from 'sql/parts/tasks/dialog/taskDialogInput'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; @@ -53,13 +52,13 @@ export class AdminService implements IAdminService { let providerId: string = this._connectionService.getProviderIdFromUri(uri); if (!providerId) { - return TPromise.wrapError(new Error(localize('adminService.providerIdNotValidError', 'Connection is required in order to interact with adminservice'))); + return Promise.reject(new Error(localize('adminService.providerIdNotValidError', 'Connection is required in order to interact with adminservice'))); } let handler = this._providers[providerId]; if (handler) { return action(handler); } else { - return TPromise.wrapError(new Error(localize('adminService.noHandlerRegistered', 'No Handler Registered'))); + return Promise.reject(new Error(localize('adminService.noHandlerRegistered', 'No Handler Registered'))); } } diff --git a/src/sql/workbench/services/backup/browser/backupUiService.ts b/src/sql/workbench/services/backup/browser/backupUiService.ts index 3d7861011f2b..8258d22be527 100644 --- a/src/sql/workbench/services/backup/browser/backupUiService.ts +++ b/src/sql/workbench/services/backup/browser/backupUiService.ts @@ -7,7 +7,6 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as azdata from 'azdata'; @@ -61,7 +60,7 @@ export class BackupUiService implements IBackupUiService { } } - public showBackupDialog(connection: IConnectionProfile): TPromise { + public showBackupDialog(connection: IConnectionProfile): Promise { let self = this; self._connectionUri = ConnectionUtils.generateUri(connection); self._currentProvider = connection.providerName; @@ -81,7 +80,7 @@ export class BackupUiService implements IBackupUiService { } let backupOptions = this.getOptions(this._currentProvider); - return new TPromise((resolve) => { + return new Promise((resolve) => { let uri = this._connectionManagementService.getConnectionUri(connection) + ProviderConnectionInfo.idSeparator + ConnectionUtils.ConnectionUriBackupIdAttributeName diff --git a/src/sql/workbench/services/connection/browser/connectionDialogService.ts b/src/sql/workbench/services/connection/browser/connectionDialogService.ts index bda3a12fea20..21a21998cdea 100644 --- a/src/sql/workbench/services/connection/browser/connectionDialogService.ts +++ b/src/sql/workbench/services/connection/browser/connectionDialogService.ts @@ -22,7 +22,6 @@ import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMess import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import * as platform from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; @@ -311,8 +310,8 @@ export class ConnectionDialogService implements IConnectionDialogService { return newProfile; } - private showDialogWithModel(): TPromise { - return new TPromise((resolve, reject) => { + private showDialogWithModel(): Promise { + return new Promise((resolve, reject) => { this.updateModelServerCapabilities(this._inputModel); this.doShowDialog(this._params); resolve(null); @@ -373,7 +372,7 @@ export class ConnectionDialogService implements IConnectionDialogService { } - private doShowDialog(params: INewConnectionParams): TPromise { + private doShowDialog(params: INewConnectionParams): Promise { if (!this._connectionDialog) { let container = this._partService.getWorkbenchElement().parentElement; this._container = container; @@ -391,7 +390,7 @@ export class ConnectionDialogService implements IConnectionDialogService { } this._connectionDialog.newConnectionParams = params; - return new TPromise(() => { + return new Promise(() => { this._connectionDialog.open(this._connectionManagementService.getRecentConnections(params.providers).length > 0); this.uiController.focusOnOpen(); }); diff --git a/src/sql/workbench/services/connection/browser/connectionDialogWidget.ts b/src/sql/workbench/services/connection/browser/connectionDialogWidget.ts index 55d7d196034c..20ea89d06cf9 100644 --- a/src/sql/workbench/services/connection/browser/connectionDialogWidget.ts +++ b/src/sql/workbench/services/connection/browser/connectionDialogWidget.ts @@ -166,19 +166,19 @@ export class ConnectionDialogWidget extends Modal { } }); - this._panel.onTabChange(c => { + this._panel.onTabChange(async c => { if (c === savedConnectionTabId && this._savedConnectionTree.getContentHeight() === 0) { // Update saved connection tree - TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this._connectionManagementService, this._providers).then(() => { - if (this._savedConnectionTree.getContentHeight() > 0) { - this._noSavedConnectionBuilder.hide(); - this._savedConnectionBuilder.show(); - } else { - this._noSavedConnectionBuilder.show(); - this._savedConnectionBuilder.hide(); - } - this._savedConnectionTree.layout(DOM.getTotalHeight(this._savedConnectionTree.getHTMLElement())); - }); + await TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this._connectionManagementService, this._providers); + + if (this._savedConnectionTree.getContentHeight() > 0) { + this._noSavedConnectionBuilder.hide(); + this._savedConnectionBuilder.show(); + } else { + this._noSavedConnectionBuilder.show(); + this._savedConnectionBuilder.hide(); + } + this._savedConnectionTree.layout(DOM.getTotalHeight(this._savedConnectionTree.getHTMLElement())); } }); @@ -378,7 +378,7 @@ export class ConnectionDialogWidget extends Modal { * Open the flyout dialog * @param recentConnections Are there recent connections that should be shown */ - public open(recentConnections: boolean) { + public async open(recentConnections: boolean): Promise { this._panel.showTab(this._recentConnectionTabId); this.show(); @@ -389,7 +389,7 @@ export class ConnectionDialogWidget extends Modal { this._recentConnectionBuilder.hide(); this._noRecentConnectionBuilder.show(); } - TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService, this._providers); + await TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService, this._providers); // reset saved connection tree this._savedConnectionTree.setInput([]); diff --git a/src/sql/workbench/services/fileBrowser/browser/fileBrowserDataSource.ts b/src/sql/workbench/services/fileBrowser/browser/fileBrowserDataSource.ts index 19e9e47fc8ac..7cbc8b6f47c3 100644 --- a/src/sql/workbench/services/fileBrowser/browser/fileBrowserDataSource.ts +++ b/src/sql/workbench/services/fileBrowser/browser/fileBrowserDataSource.ts @@ -7,7 +7,6 @@ import { IFileBrowserService } from 'sql/platform/fileBrowser/common/interfaces'; import { FileNode } from 'sql/workbench/services/fileBrowser/common/fileNode'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree'; /** @@ -45,8 +44,8 @@ export class FileBrowserDataSource implements IDataSource { /** * Returns the element's children as an array in a promise. */ - public getChildren(tree: ITree, element: any): TPromise { - return new TPromise((resolve) => { + public getChildren(tree: ITree, element: any): Promise { + return new Promise((resolve) => { if (element instanceof FileNode) { var node = element; if (node.children) { @@ -67,7 +66,7 @@ export class FileBrowserDataSource implements IDataSource { /** * Returns the element's parent in a promise. */ - public getParent(tree: ITree, element: any): TPromise { - return TPromise.as(null); + public getParent(tree: ITree, element: any): Promise { + return Promise.resolve(null); } } \ No newline at end of file diff --git a/src/sql/workbench/services/fileBrowser/browser/fileBrowserRenderer.ts b/src/sql/workbench/services/fileBrowser/browser/fileBrowserRenderer.ts index 06cca97a855e..fbc3a54067fa 100644 --- a/src/sql/workbench/services/fileBrowser/browser/fileBrowserRenderer.ts +++ b/src/sql/workbench/services/fileBrowser/browser/fileBrowserRenderer.ts @@ -9,9 +9,9 @@ import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree'; import { FileKind } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { FileLabel } from 'vs/workbench/browser/labels'; import { IFileTemplateData } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; import { toDisposable } from 'vs/base/common/lifecycle'; +import { ResourceLabels, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels'; const EmptyDisposable = toDisposable(() => null); @@ -22,10 +22,12 @@ const EmptyDisposable = toDisposable(() => null); export class FileBrowserRenderer implements IRenderer { public static readonly FILE_HEIGHT = 22; private static readonly FILE_TEMPLATE_ID = 'carbonFileBrowser'; + private resourceLabels: ResourceLabels; constructor( @IInstantiationService private instantiationService: IInstantiationService ) { + this.resourceLabels = this.instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER); } /** @@ -46,8 +48,8 @@ export class FileBrowserRenderer implements IRenderer { * Render template in a dom element based on template id */ public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): IFileTemplateData { + const label = this.resourceLabels.create(container); const elementDisposable = EmptyDisposable; - const label = this.instantiationService.createInstance(FileLabel, container, void 0); return { elementDisposable, label, container }; } diff --git a/src/sql/workbench/services/insights/browser/insightsDialogView.ts b/src/sql/workbench/services/insights/browser/insightsDialogView.ts index c7a5964cffe8..427bde12ae5a 100644 --- a/src/sql/workbench/services/insights/browser/insightsDialogView.ts +++ b/src/sql/workbench/services/insights/browser/insightsDialogView.ts @@ -30,7 +30,6 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import * as nls from 'vs/nls'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IAction } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import * as types from 'vs/base/common/types'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/sql/workbench/services/insights/common/insightDialogActions.ts b/src/sql/workbench/services/insights/common/insightDialogActions.ts index bc5707c274ac..cc1179fbfc40 100644 --- a/src/sql/workbench/services/insights/common/insightDialogActions.ts +++ b/src/sql/workbench/services/insights/common/insightDialogActions.ts @@ -7,7 +7,6 @@ import { IInsightDialogActionContext } from 'sql/workbench/services/insights/com import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; export class CopyInsightDialogSelectionAction extends Action { @@ -21,8 +20,8 @@ export class CopyInsightDialogSelectionAction extends Action { super(id, label); } - public run(event?: IInsightDialogActionContext): TPromise { + public run(event?: IInsightDialogActionContext): Promise { this._clipboardService.writeText(event.cellData); - return TPromise.as(void 0); + return Promise.resolve(void 0); } } diff --git a/src/sql/workbench/services/insights/common/insightsDialogModel.ts b/src/sql/workbench/services/insights/common/insightsDialogModel.ts index 73dcc2bb97b6..a9f56f7b1686 100644 --- a/src/sql/workbench/services/insights/common/insightsDialogModel.ts +++ b/src/sql/workbench/services/insights/common/insightsDialogModel.ts @@ -7,7 +7,7 @@ import { IInsightsDialogModel, ListResource } from 'sql/workbench/services/insig import { IInsightsConfigDetails, IInsightsLabel } from 'sql/parts/dashboard/widgets/insights/interfaces'; import { Conditional } from 'sql/parts/dashboard/common/interfaces'; -import { Event, Emitter, debounceEvent } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; export class InsightsDialogModel implements IInsightsDialogModel { private _rows: string[][]; @@ -16,7 +16,7 @@ export class InsightsDialogModel implements IInsightsDialogModel { private _onDataChangeEmitter: Emitter = new Emitter(); private _onDataChangeEvent: Event = this._onDataChangeEmitter.event; - public onDataChange: Event = debounceEvent(this._onDataChangeEvent, (last, event) => event, 75, false); + public onDataChange: Event = Event.debounce(this._onDataChangeEvent, (last, event) => event, 75, false); public set insight(insight: IInsightsConfigDetails) { this._insight = insight; diff --git a/src/sql/workbench/services/notebook/common/notebookRegistry.ts b/src/sql/workbench/services/notebook/common/notebookRegistry.ts index fa95101e44a3..f92f3bd457d9 100644 --- a/src/sql/workbench/services/notebook/common/notebookRegistry.ts +++ b/src/sql/workbench/services/notebook/common/notebookRegistry.ts @@ -191,7 +191,7 @@ const notebookProviderRegistry = new NotebookProviderRegistry(); platform.Registry.add(Extensions.NotebookProviderContribution, notebookProviderRegistry); -ExtensionsRegistry.registerExtensionPoint(Extensions.NotebookProviderContribution, [], notebookContrib).setHandler(extensions => { +ExtensionsRegistry.registerExtensionPoint({ extensionPoint: Extensions.NotebookProviderContribution, jsonSchema: notebookContrib }).setHandler(extensions => { function handleExtension(contrib: NotebookProviderRegistration, extension: IExtensionPointUser) { notebookProviderRegistry.registerNotebookProvider(contrib); @@ -209,7 +209,7 @@ ExtensionsRegistry.registerExtensionPoint(Extensions.NotebookLanguageMagicContribution, [], languageMagicContrib).setHandler(extensions => { +ExtensionsRegistry.registerExtensionPoint({ extensionPoint: Extensions.NotebookLanguageMagicContribution, jsonSchema: languageMagicContrib }).setHandler(extensions => { function handleExtension(contrib: NotebookLanguageMagicRegistration, extension: IExtensionPointUser) { notebookProviderRegistry.registerNotebookLanguageMagic(contrib); diff --git a/src/sql/workbench/services/notebook/common/notebookServiceImpl.ts b/src/sql/workbench/services/notebook/common/notebookServiceImpl.ts index 152713ffb486..0539f6959af1 100644 --- a/src/sql/workbench/services/notebook/common/notebookServiceImpl.ts +++ b/src/sql/workbench/services/notebook/common/notebookServiceImpl.ts @@ -23,7 +23,6 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionManagementService, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { Deferred } from 'sql/base/common/promise'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -460,9 +459,8 @@ export class NotebookService extends Disposable implements INotebookService { private removeContributedProvidersFromCache(identifier: IExtensionIdentifier, extensionService: IExtensionService) { const notebookProvider = 'notebookProvider'; - let extensionid = getIdFromLocalExtensionId(identifier.id); extensionService.getExtensions().then(i => { - let extension = i.find(c => c.id === extensionid); + let extension = i.find(c => c.identifier.value.toLowerCase() === identifier.id.toLowerCase()); if (extension && extension.contributes && extension.contributes[notebookProvider] && extension.contributes[notebookProvider].providerId) { diff --git a/src/sql/workbench/services/profiler/common/profilerService.ts b/src/sql/workbench/services/profiler/common/profilerService.ts index 593507ea2d1a..c28bef16eb02 100644 --- a/src/sql/workbench/services/profiler/common/profilerService.ts +++ b/src/sql/workbench/services/profiler/common/profilerService.ts @@ -14,7 +14,6 @@ import { ProfilerColumnEditorDialog } from 'sql/parts/profiler/dialog/profilerCo import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -90,7 +89,7 @@ export class ProfilerService implements IProfilerService { this._sessionMap.set(uri, session); this._connectionMap.set(uri, connectionProfile); this._idMap.set(uri, uri); - return TPromise.wrap(uri); + return Promise.resolve(uri); } public onMoreRows(params: azdata.ProfilerSessionEvents): void { @@ -162,13 +161,13 @@ export class ProfilerService implements IProfilerService { let providerId = 'MSSQL'; if (!providerId) { - return TPromise.wrapError(new Error('Connection is required in order to interact with queries')); + return Promise.reject(new Error('Connection is required in order to interact with queries')); } let handler = this._providers.get(providerId); if (handler) { return action(handler); } else { - return TPromise.wrapError(new Error('No Handler Registered')); + return Promise.reject(new Error('No Handler Registered')); } } @@ -228,7 +227,7 @@ export class ProfilerService implements IProfilerService { } this._editColumnDialog.open(input); - return TPromise.as(null); + return Promise.resolve(null); } public launchCreateSessionDialog(input?: ProfilerInput): Thenable { diff --git a/src/sql/workbench/services/serverGroup/browser/serverGroupController.ts b/src/sql/workbench/services/serverGroup/browser/serverGroupController.ts index e2155da5f2d2..beb6ee491d04 100644 --- a/src/sql/workbench/services/serverGroup/browser/serverGroupController.ts +++ b/src/sql/workbench/services/serverGroup/browser/serverGroupController.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import Severity from 'vs/base/common/severity'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -77,7 +76,7 @@ export class ServerGroupController implements IServerGroupController { } - public showCreateGroupDialog(connectionManagementService: IConnectionManagementService, callbacks?: IServerGroupDialogCallbacks): TPromise { + public showCreateGroupDialog(connectionManagementService: IConnectionManagementService, callbacks?: IServerGroupDialogCallbacks): Promise { this._connectionManagementService = connectionManagementService; this._group = null; this._viewModel = new ServerGroupViewModel(undefined, this._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_COLORS_CONFIG]); @@ -85,14 +84,14 @@ export class ServerGroupController implements IServerGroupController { return this.openServerGroupDialog(); } - public showEditGroupDialog(connectionManagementService: IConnectionManagementService, group: ConnectionProfileGroup): TPromise { + public showEditGroupDialog(connectionManagementService: IConnectionManagementService, group: ConnectionProfileGroup): Promise { this._connectionManagementService = connectionManagementService; this._group = group; this._viewModel = new ServerGroupViewModel(group, this._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_COLORS_CONFIG]); return this.openServerGroupDialog(); } - private openServerGroupDialog(): TPromise { + private openServerGroupDialog(): Promise { if (!this._serverGroupDialog) { this._serverGroupDialog = this._instantiationService.createInstance(ServerGroupDialog); this._serverGroupDialog.viewModel = this._viewModel; @@ -105,7 +104,7 @@ export class ServerGroupController implements IServerGroupController { this._serverGroupDialog.viewModel = this._viewModel; } - return new TPromise(() => { + return new Promise(() => { this._serverGroupDialog.open(); }); } diff --git a/src/sql/workbench/services/telemetry/node/fileTelemetryService.ts b/src/sql/workbench/services/telemetry/node/fileTelemetryService.ts index 661193cbda25..f744c049d63f 100644 --- a/src/sql/workbench/services/telemetry/node/fileTelemetryService.ts +++ b/src/sql/workbench/services/telemetry/node/fileTelemetryService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITelemetryService, ITelemetryInfo, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; const fs = require('fs'); @@ -30,11 +29,11 @@ export class FileTelemetryService implements ITelemetryService { fs.appendFileSync(this._outputFile, telemetryData + '\n'); } } - return TPromise.wrap(null); + return Promise.resolve(null); } isOptedIn: true; - getTelemetryInfo(): TPromise { - return TPromise.wrap({ + getTelemetryInfo(): Promise { + return Promise.resolve({ instanceId: 'someValue.instanceId', sessionId: 'someValue.sessionId', machineId: 'someValue.machineId' diff --git a/src/sql/workbench/update/releaseNotes.ts b/src/sql/workbench/update/releaseNotes.ts index fed581b1edcd..76c60b731b66 100644 --- a/src/sql/workbench/update/releaseNotes.ts +++ b/src/sql/workbench/update/releaseNotes.ts @@ -6,7 +6,6 @@ 'use strict'; import nls = require('vs/nls'); -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import pkg from 'vs/platform/node/package'; import product from 'vs/platform/node/product'; @@ -14,6 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { URI } from 'vs/base/common/uri'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { AbstractShowReleaseNotesAction } from 'vs/workbench/parts/update/electron-browser/update'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export class OpenGettingStartedInBrowserAction extends Action { @@ -23,7 +23,7 @@ export class OpenGettingStartedInBrowserAction extends Action { super('update.openGettingStartedGuide', nls.localize('gettingStarted', "Get Started"), null, true); } - run(): TPromise { + run(): Promise { const uri = URI.parse(product.gettingStartedUrl); return this.openerService.open(uri); } @@ -42,3 +42,12 @@ export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesActio super(id, label, pkg.version, instantiationService); } } + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '1_welcome', + command: { + id: ShowCurrentReleaseNotesAction.ID, + title: nls.localize({ key: 'miGettingStarted', comment: ['&& denotes a mnemonic'] }, "Getting &&Started") + }, + order: 1 +}); diff --git a/src/sqltest/common/telemetryUtilities.test.ts b/src/sqltest/common/telemetryUtilities.test.ts index 063f43a13387..813de75f5865 100644 --- a/src/sqltest/common/telemetryUtilities.test.ts +++ b/src/sqltest/common/telemetryUtilities.test.ts @@ -8,7 +8,6 @@ import * as TelemetryUtils from 'sql/common/telemetryUtilities'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryServiceStub } from 'sqltest/stubs/telemetryServiceStub'; import * as TypeMoq from 'typemoq'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as assert from 'assert'; suite('SQL Telemetry Utilities tests', () => { @@ -37,7 +36,7 @@ suite('SQL Telemetry Utilities tests', () => { setup(() => { telemetryService = TypeMoq.Mock.ofType(TelemetryServiceStub, TypeMoq.MockBehavior.Strict); - telemetryService.setup(x => x.publicLog(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(x => TPromise.as(none)); + telemetryService.setup(x => x.publicLog(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(x => Promise.resolve(none)); }); test('addTelemetry should add provider id using the connection', (done) => { diff --git a/src/sqltest/parts/connection/connectionConfig.test.ts b/src/sqltest/parts/connection/connectionConfig.test.ts index b1b1c5590d95..144671e3dca4 100644 --- a/src/sqltest/parts/connection/connectionConfig.test.ts +++ b/src/sqltest/parts/connection/connectionConfig.test.ts @@ -15,7 +15,6 @@ import { WorkspaceConfigurationTestService } from 'sqltest/stubs/workspaceConfig import { IConfigurationValue, ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; import * as Constants from 'sql/platform/connection/common/constants'; import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as assert from 'assert'; import { ProviderFeatures, ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import * as azdata from 'azdata'; diff --git a/src/sqltest/parts/connection/connectionManagementService.test.ts b/src/sqltest/parts/connection/connectionManagementService.test.ts index 6ce45249b497..4efc2230460a 100644 --- a/src/sqltest/parts/connection/connectionManagementService.test.ts +++ b/src/sqltest/parts/connection/connectionManagementService.test.ts @@ -27,7 +27,6 @@ import { ResourceProviderStub } from 'sqltest/stubs/resourceProviderServiceStub' import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import { WorkspaceConfigurationTestService } from 'sqltest/stubs/workspaceConfigurationTestService'; import * as assert from 'assert'; @@ -95,14 +94,14 @@ suite('SQL ConnectionManagementService tests', () => { let root = new ConnectionProfileGroup(ConnectionProfileGroup.RootGroupName, undefined, ConnectionProfileGroup.RootGroupName, undefined, undefined); root.connections = [ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile)]; - connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined)).returns(() => TPromise.as(none)); - connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined)).returns(() => TPromise.as(none)); - connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => TPromise.as(none)); - connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny())).returns(() => TPromise.as(none)); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined)).returns(() => Promise.resolve(none)); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined)).returns(() => Promise.resolve(none)); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(none)); + connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny())).returns(() => Promise.resolve(none)); connectionStore.setup(x => x.addActiveConnection(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve()); connectionStore.setup(x => x.saveProfile(TypeMoq.It.isAny())).returns(() => Promise.resolve(connectionProfile)); - workbenchEditorService.setup(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => TPromise.as(undefined)); + workbenchEditorService.setup(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined)); connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is( c => c.serverName === connectionProfile.serverName))).returns(() => Promise.resolve({ profile: connectionProfile, savedCred: true })); connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is( diff --git a/src/sqltest/parts/connection/connectionTreeActions.test.ts b/src/sqltest/parts/connection/connectionTreeActions.test.ts index 949b68243ad0..19b5ba5acddb 100644 --- a/src/sqltest/parts/connection/connectionTreeActions.test.ts +++ b/src/sqltest/parts/connection/connectionTreeActions.test.ts @@ -5,7 +5,6 @@ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as TypeMoq from 'typemoq'; import * as assert from 'assert'; import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; @@ -27,7 +26,6 @@ import { NodeType } from 'sql/parts/objectExplorer/common/nodeType'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { ServerTreeDataSource } from 'sql/parts/objectExplorer/viewlet/serverTreeDataSource'; import { Builder, $ } from 'sql/base/browser/builder'; -import WinJS = require('vs/base/common/winjs.base'); import { Emitter } from 'vs/base/common/event'; import Severity from 'vs/base/common/severity'; import { ObjectExplorerActionsContext, ManageConnectionAction } from 'sql/parts/objectExplorer/viewlet/objectExplorerActions'; @@ -202,7 +200,7 @@ suite('SQL Connection Tree Action tests', () => { let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { - return new TPromise((resolve) => resolve({})); + return new Promise((resolve) => resolve({})); }); let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService); @@ -219,7 +217,7 @@ suite('SQL Connection Tree Action tests', () => { let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { - return new TPromise((resolve) => resolve({})); + return new Promise((resolve) => resolve({})); }); let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService); @@ -237,7 +235,7 @@ suite('SQL Connection Tree Action tests', () => { let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { - return new TPromise((resolve) => resolve({})); + return new Promise((resolve) => resolve({})); }); let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService); @@ -254,7 +252,7 @@ suite('SQL Connection Tree Action tests', () => { let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { - return new TPromise((resolve) => resolve({})); + return new Promise((resolve) => resolve({})); }); let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService); @@ -401,15 +399,15 @@ suite('SQL Connection Tree Action tests', () => { let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object); objectExplorerService.callBase = true; objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => tablesNode); - objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => TPromise.as([table1Node, table2Node])); + objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([table1Node, table2Node])); let builder: Builder = $().div(); var dataSource = new ServerTreeDataSource(objectExplorerService.object, connectionManagementService.object, undefined); let tree = TypeMoq.Mock.ofType(Tree, TypeMoq.MockBehavior.Loose, builder.getHTMLElement(), { dataSource }); tree.callBase = true; - tree.setup(x => x.refresh(TypeMoq.It.isAny())).returns(() => WinJS.TPromise.as(null)); - tree.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => WinJS.TPromise.as(null)); - tree.setup(x => x.collapse(TypeMoq.It.isAny())).returns(() => WinJS.TPromise.as(null)); + tree.setup(x => x.refresh(TypeMoq.It.isAny())).returns(() => Promise.resolve(null)); + tree.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => Promise.resolve(null)); + tree.setup(x => x.collapse(TypeMoq.It.isAny())).returns(() => Promise.resolve(null)); let connectionAction: RefreshAction = new RefreshAction(RefreshAction.ID, RefreshAction.LABEL, tree.object, @@ -424,7 +422,9 @@ suite('SQL Connection Tree Action tests', () => { objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); tree.verify(x => x.refresh(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); tree.verify(x => x.expand(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); - }).then(() => done(), (err) => done(err)); + }).then(() => done(), (err) => { + done(err); + }); }); test('RefreshConnectionAction - refresh should not be called if connection status is not connect', (done) => { @@ -488,14 +488,14 @@ suite('SQL Connection Tree Action tests', () => { let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object); objectExplorerService.callBase = true; objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => tablesNode); - objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => TPromise.as([table1Node, table2Node])); + objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([table1Node, table2Node])); let builder: Builder = $().div(); var dataSource = new ServerTreeDataSource(objectExplorerService.object, connectionManagementService.object, undefined); let tree = TypeMoq.Mock.ofType(Tree, TypeMoq.MockBehavior.Loose, builder.getHTMLElement(), { dataSource }); tree.callBase = true; - tree.setup(x => x.refresh(TypeMoq.It.isAny())).returns(() => WinJS.TPromise.as(null)); - tree.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => WinJS.TPromise.as(null)); + tree.setup(x => x.refresh(TypeMoq.It.isAny())).returns(() => Promise.resolve(null)); + tree.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => Promise.resolve(null)); let connectionAction: RefreshAction = new RefreshAction(RefreshAction.ID, RefreshAction.LABEL, tree.object, diff --git a/src/sqltest/parts/connection/objectExplorerService.test.ts b/src/sqltest/parts/connection/objectExplorerService.test.ts index acb34b1a91cd..25381b54ff13 100644 --- a/src/sqltest/parts/connection/objectExplorerService.test.ts +++ b/src/sqltest/parts/connection/objectExplorerService.test.ts @@ -12,7 +12,6 @@ import { ObjectExplorerService, NodeExpandInfoWithProviderId } from 'sql/workben import { NodeType } from 'sql/parts/objectExplorer/common/nodeType'; import { TreeNode, TreeItemCollapsibleState } from 'sql/parts/objectExplorer/common/treeNode'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as azdata from 'azdata'; import * as TypeMoq from 'typemoq'; import * as assert from 'assert'; @@ -281,11 +280,11 @@ suite('SQL Object Explorer Service tests', () => { })); sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => { objectExplorerService.onNodeExpanded(objectExplorerExpandInfo); - }).returns(() => TPromise.as(true)); + }).returns(() => Promise.resolve(true)); sqlOEProvider.setup(x => x.refreshNode(TypeMoq.It.isAny())).callback(() => { objectExplorerService.onNodeExpanded(objectExplorerExpandInfoRefresh); - }).returns(() => TPromise.as(true)); - sqlOEProvider.setup(x => x.closeSession(TypeMoq.It.isAny())).returns(() => TPromise.as(objectExplorerCloseSessionResponse)); + }).returns(() => Promise.resolve(true)); + sqlOEProvider.setup(x => x.closeSession(TypeMoq.It.isAny())).returns(() => Promise.resolve(objectExplorerCloseSessionResponse)); objectExplorerService.onUpdateObjectExplorerNodes(args => { if (args && args.errorMessage !== undefined) { @@ -552,7 +551,7 @@ suite('SQL Object Explorer Service tests', () => { objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)).then(childNodes => { sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => { objectExplorerService.onNodeExpanded(tableExpandInfo); - }).returns(() => TPromise.as(true)); + }).returns(() => Promise.resolve(true)); let tableNode = childNodes.find(node => node.nodePath === table1NodePath); objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, tableNode).then(() => { // If I check whether the table is expanded, the answer should be yes @@ -610,7 +609,7 @@ suite('SQL Object Explorer Service tests', () => { objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)).then(childNodes => { sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => { objectExplorerService.onNodeExpanded(tableExpandInfo); - }).returns(() => TPromise.as(true)); + }).returns(() => Promise.resolve(true)); objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, childNodes.find(node => node.nodePath === table1NodePath)).then(() => { // If I check whether the table is expanded, the answer should be yes let tableNode = childNodes.find(node => node.nodePath === table1NodePath); @@ -639,7 +638,7 @@ suite('SQL Object Explorer Service tests', () => { // Set up the OE provider so that the second expand call expands the table sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.is(nodeInfo => nodeInfo.nodePath === table1NodePath))).callback(() => { objectExplorerService.onNodeExpanded(tableExpandInfo); - }).returns(() => TPromise.as(true)); + }).returns(() => Promise.resolve(true)); serverTreeView.setup(x => x.setExpandedState(TypeMoq.It.isAny(), TypeMoq.It.is(state => state === TreeItemCollapsibleState.Expanded))).returns(treeNode => { if (treeNode instanceof ConnectionProfile) { treeNode = objectExplorerService.getObjectExplorerNode(treeNode); @@ -768,7 +767,7 @@ suite('SQL Object Explorer Service tests', () => { // Set up the provider to not respond to the second expand request, simulating a request that takes a long time to complete const nodePath = objectExplorerSession.rootNode.nodePath; - sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.is(x => x.nodePath === nodePath))).callback(() => { }).returns(() => TPromise.as(true)); + sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.is(x => x.nodePath === nodePath))).callback(() => { }).returns(() => Promise.resolve(true)); // If I queue a second expand request (the first completes normally because of the original mock) and then close the session await objectExplorerService.expandNode(providerId, objectExplorerSession, objectExplorerSession.rootNode.nodePath); diff --git a/src/sqltest/parts/query/editor/queryActions.test.ts b/src/sqltest/parts/query/editor/queryActions.test.ts index f422ce8f76b3..9a02e435f008 100644 --- a/src/sqltest/parts/query/editor/queryActions.test.ts +++ b/src/sqltest/parts/query/editor/queryActions.test.ts @@ -5,7 +5,6 @@ 'use strict'; import { Emitter } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ISelectionData } from 'azdata'; @@ -122,7 +121,7 @@ suite('SQL QueryAction Tests', () => { connectionParams = params; countCalledShowDialog++; }) - .returns(() => TPromise.as(none)); + .returns(() => Promise.resolve(none)); // ... Mock "isConnected" in ConnectionManagementService let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object); @@ -227,7 +226,7 @@ suite('SQL QueryAction Tests', () => { showDialogConnectionParams = params; countCalledShowDialog++; }) - .returns(() => TPromise.as(none)); + .returns(() => Promise.resolve(none)); // ... Mock "getSelection" in QueryEditor let queryInput = TypeMoq.Mock.ofType(QueryInput, TypeMoq.MockBehavior.Loose); @@ -385,7 +384,7 @@ suite('SQL QueryAction Tests', () => { connectionParams = params; countCalledShowDialog++; }) - .returns(() => TPromise.as(none)); + .returns(() => Promise.resolve(none)); // ... Mock "isConnected" in ConnectionManagementService let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object); @@ -431,7 +430,7 @@ suite('SQL QueryAction Tests', () => { calledShowDialog++; connectionParams = params; }) - .returns(() => TPromise.as(none)); + .returns(() => Promise.resolve(none)); // ... Mock "isConnected" in ConnectionManagementService let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object); diff --git a/src/sqltest/parts/query/editor/queryEditor.test.ts b/src/sqltest/parts/query/editor/queryEditor.test.ts index bf67fbf9b959..832840b58d10 100644 --- a/src/sqltest/parts/query/editor/queryEditor.test.ts +++ b/src/sqltest/parts/query/editor/queryEditor.test.ts @@ -7,7 +7,6 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IEditorDescriptor } from 'vs/workbench/browser/editor'; -import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; import * as DOM from 'vs/base/browser/dom'; import { Memento } from 'vs/workbench/common/memento'; @@ -86,10 +85,10 @@ suite('SQL QueryEditor Tests', () => { // Mock InstantiationService to give us our mockEditor instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { - return new TPromise((resolve) => resolve(mockEditor)); + return new Promise((resolve) => resolve(mockEditor)); }); instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((input) => { - return new TPromise((resolve) => resolve(new RunQueryAction(undefined, undefined, undefined))); + return new Promise((resolve) => resolve(new RunQueryAction(undefined, undefined, undefined))); }); // Setup hook to capture calls to create the listDatabase action instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => { @@ -339,7 +338,7 @@ suite('SQL QueryEditor Tests', () => { queryActionInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { - return new TPromise((resolve) => resolve(mockEditor)); + return new Promise((resolve) => resolve(mockEditor)); }); queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((input) => { diff --git a/src/sqltest/parts/registeredServer/viewlet/serverTreeView.test.ts b/src/sqltest/parts/registeredServer/viewlet/serverTreeView.test.ts index 1cd2e8c2738b..98c2aae3fa25 100644 --- a/src/sqltest/parts/registeredServer/viewlet/serverTreeView.test.ts +++ b/src/sqltest/parts/registeredServer/viewlet/serverTreeView.test.ts @@ -38,40 +38,41 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => { mockTree = TypeMoq.Mock.ofInstance(tree); (serverTreeView as any)._tree = mockTree.object; mockRefreshTreeMethod = TypeMoq.Mock.ofType(Function); + mockRefreshTreeMethod.setup(x => x()).returns(() => Promise.resolve()); (serverTreeView as any).refreshTree = mockRefreshTreeMethod.object; mockTree.setup(x => x.clearSelection()); mockTree.setup(x => x.select(TypeMoq.It.isAny())); }); - function runAddConnectionProfileHandler(oldSelection, newProfile) { + async function runAddConnectionProfileHandler(oldSelection, newProfile): Promise { mockTree.setup(x => x.getSelection()).returns(() => [oldSelection] || []); - (serverTreeView as any).handleAddConnectionProfile(newProfile); + return (serverTreeView as any).handleAddConnectionProfile(newProfile); } - test('onAddConnectionProfile handler selects the new profile when no profile is already selected', () => { + test('onAddConnectionProfile handler selects the new profile when no profile is already selected', async () => { let newProfile = { id: 'test_connection' }; - runAddConnectionProfileHandler(undefined, newProfile); + await runAddConnectionProfileHandler(undefined, newProfile); mockRefreshTreeMethod.verify(x => x(), TypeMoq.Times.once()); mockTree.verify(x => x.clearSelection(), TypeMoq.Times.never()); mockTree.verify(x => x.select(TypeMoq.It.is(profile => profile === newProfile)), TypeMoq.Times.once()); }); - test('onAddConnectionProfile handler selects the new profile when a different profile is already selected', () => { + test('onAddConnectionProfile handler selects the new profile when a different profile is already selected', async () => { let oldProfile = { id: 'old_connection' }; let newProfile = { id: 'test_connection' }; - runAddConnectionProfileHandler(oldProfile, newProfile); + await runAddConnectionProfileHandler(oldProfile, newProfile); mockRefreshTreeMethod.verify(x => x(), TypeMoq.Times.once()); mockTree.verify(x => x.clearSelection(), TypeMoq.Times.once()); mockTree.verify(x => x.select(TypeMoq.It.is(profile => profile === newProfile)), TypeMoq.Times.once()); }); - test('onAddConnectionProfile handler does not clear the selection when the new profile is already selected', () => { + test('onAddConnectionProfile handler does not clear the selection when the new profile is already selected', async () => { let selectionId = 'test_connection'; let oldProfile = { id: selectionId @@ -79,17 +80,17 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => { let newProfile = { id: selectionId }; - runAddConnectionProfileHandler(oldProfile, newProfile); + await runAddConnectionProfileHandler(oldProfile, newProfile); mockRefreshTreeMethod.verify(x => x(), TypeMoq.Times.once()); mockTree.verify(x => x.clearSelection(), TypeMoq.Times.never()); mockTree.verify(x => x.select(TypeMoq.It.isAny()), TypeMoq.Times.never()); }); - test('onAddConnectionProfile handler does not clear the previously selected profile if there is no new one', () => { + test('onAddConnectionProfile handler does not clear the previously selected profile if there is no new one', async () => { let oldProfile = { id: 'test_connection' }; - runAddConnectionProfileHandler(oldProfile, undefined); + await runAddConnectionProfileHandler(oldProfile, undefined); mockRefreshTreeMethod.verify(x => x(), TypeMoq.Times.once()); mockTree.verify(x => x.clearSelection(), TypeMoq.Times.never()); mockTree.verify(x => x.select(TypeMoq.It.isAny()), TypeMoq.Times.never()); diff --git a/src/sqltest/stubs/accountManagementStubs.ts b/src/sqltest/stubs/accountManagementStubs.ts index 8f069408b8b3..f4cf82e5df41 100644 --- a/src/sqltest/stubs/accountManagementStubs.ts +++ b/src/sqltest/stubs/accountManagementStubs.ts @@ -9,7 +9,6 @@ import * as azdata from 'azdata'; import { Event } from 'vs/base/common/event'; import { IAccountManagementService } from 'sql/platform/accountManagement/common/interfaces'; import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accountManagement/common/eventTypes'; -import { TPromise } from 'vs/base/common/winjs.base'; export class AccountManagementTestService implements IAccountManagementService { _serviceBrand: any; @@ -62,7 +61,7 @@ export class AccountManagementTestService implements IAccountManagementService { return undefined; } - openAccountListDialog(): TPromise { + openAccountListDialog(): Promise { return undefined; } diff --git a/src/sqltest/stubs/connectionDialogTestService.ts b/src/sqltest/stubs/connectionDialogTestService.ts index bb1d03a9a631..494247e65cec 100644 --- a/src/sqltest/stubs/connectionDialogTestService.ts +++ b/src/sqltest/stubs/connectionDialogTestService.ts @@ -5,7 +5,6 @@ 'use strict'; import { INewConnectionParams, IConnectionResult, IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService'; @@ -14,19 +13,19 @@ export class ConnectionDialogTestService implements IConnectionDialogService { public showDialog( connectionManagementService: IConnectionManagementService, - params: INewConnectionParams, model: IConnectionProfile, connectionResult?: IConnectionResult): TPromise { + params: INewConnectionParams, model: IConnectionProfile, connectionResult?: IConnectionResult): Promise { let none: void; - return TPromise.as(none); + return Promise.resolve(none); } public openDialogAndWait( connectionManagementService: IConnectionManagementService, - params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): TPromise { - return TPromise.as(undefined); + params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Promise { + return Promise.resolve(undefined); } public openDialogAndWaitButDontConnect(connectionManagementService: IConnectionManagementService, - params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): TPromise { - return TPromise.as(undefined); + params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Promise { + return Promise.resolve(undefined); } } diff --git a/src/sqltest/stubs/credentialsTestStubs.ts b/src/sqltest/stubs/credentialsTestStubs.ts index b20e1196c261..fe375ff89080 100644 --- a/src/sqltest/stubs/credentialsTestStubs.ts +++ b/src/sqltest/stubs/credentialsTestStubs.ts @@ -6,7 +6,6 @@ 'use strict'; import * as azdata from 'azdata'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CredentialManagementEvents, ICredentialsService } from 'sql/platform/credentials/common/credentialsService'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -20,17 +19,17 @@ export class CredentialsTestProvider implements azdata.CredentialProvider { credentialId: credentialId, password: password }; - return TPromise.as(true); + return Promise.resolve(true); } readCredential(credentialId: string): Thenable { - return TPromise.as(this.storedCredentials[credentialId]); + return Promise.resolve(this.storedCredentials[credentialId]); } deleteCredential(credentialId: string): Thenable { let exists = this.storedCredentials[credentialId] !== undefined; delete this.storedCredentials[credentialId]; - return TPromise.as(exists); + return Promise.resolve(exists); } } diff --git a/src/sqltest/stubs/messageServiceStub.ts b/src/sqltest/stubs/messageServiceStub.ts index cd1c9831d767..a6320f4748a5 100644 --- a/src/sqltest/stubs/messageServiceStub.ts +++ b/src/sqltest/stubs/messageServiceStub.ts @@ -8,7 +8,6 @@ /* import Severity from 'vs/base/common/severity'; import { IConfirmation, IMessageService, IMessageWithAction, IConfirmationResult } from 'vs/platform/message/common/message'; -import { TPromise } from 'vs/base/common/winjs.base'; export class MessageServiceStub implements IMessageService { _serviceBrand: any; @@ -35,7 +34,7 @@ export class MessageServiceStub implements IMessageService { } - confirmWithCheckbox(confirmation: IConfirmation): TPromise { + confirmWithCheckbox(confirmation: IConfirmation): Promise { return undefined; } } diff --git a/src/sqltest/stubs/telemetryServiceStub.ts b/src/sqltest/stubs/telemetryServiceStub.ts index 02ee36c4a8cd..937b44cf68a3 100644 --- a/src/sqltest/stubs/telemetryServiceStub.ts +++ b/src/sqltest/stubs/telemetryServiceStub.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { ITelemetryService, ITelemetryData, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; -import { TPromise } from 'vs/base/common/winjs.base'; // Test stubs for commonly used objects @@ -15,11 +14,11 @@ export class TelemetryServiceStub implements 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): TPromise { + publicLog(eventName: string, data?: ITelemetryData): Promise { return undefined; } - getTelemetryInfo(): TPromise { + getTelemetryInfo(): Promise { return undefined; } diff --git a/src/sqltest/stubs/workbenchEditorTestService.ts b/src/sqltest/stubs/workbenchEditorTestService.ts index be6bd21224fe..9991e14a9f66 100644 --- a/src/sqltest/stubs/workbenchEditorTestService.ts +++ b/src/sqltest/stubs/workbenchEditorTestService.ts @@ -7,7 +7,6 @@ import { IEditorService, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, IResourceEditor, IR import { ServiceIdentifier, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IEditorOptions, IResourceInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IEditor, IEditorInput, IResourceDiffInput, IResourceSideBySideInput, GroupIdentifier, ITextEditor, IUntitledResourceInput, ITextDiffEditor, ITextSideBySideEditor, IEditorInputWithOptions } from 'vs/workbench/common/editor'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event } from 'vs/base/common/event'; import { Position } from 'vs/editor/common/core/position'; import { IEditorGroup, IEditorReplacement } from 'vs/workbench/services/group/common/editorGroupsService'; @@ -46,30 +45,30 @@ export class WorkbenchEditorTestService implements IEditorService { return undefined; } - public openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): TPromise; - public openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): TPromise; - public openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): TPromise; - public openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): TPromise; - public openEditor(input: any, arg2?: any, arg3?: any): TPromise { + public openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + public openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + public openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + public openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + public openEditor(input: any, arg2?: any, arg3?: any): Promise { return undefined; } - public openEditors(editors: IEditorInputWithOptions[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): TPromise>; - public openEditors(editors: IResourceEditor[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): TPromise>; - public openEditors(editors: any[]): TPromise { + public openEditors(editors: IEditorInputWithOptions[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise>; + public openEditors(editors: IResourceEditor[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise>; + public openEditors(editors: any[]): Promise { return undefined; } - public replaceEditors(editors: IResourceEditorReplacement[], group: IEditorGroup | GroupIdentifier): TPromise; - public replaceEditors(editors: IEditorReplacement[], group: IEditorGroup | GroupIdentifier): TPromise; - public replaceEditors(editors: any[], group: any): TPromise { + public replaceEditors(editors: IResourceEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise; + public replaceEditors(editors: IEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise; + public replaceEditors(editors: any[], group: any): Promise { return undefined; } /** * Closes the editor at the provided position. */ - closeEditor(position: Position, input: IEditorInput): TPromise { + closeEditor(position: Position, input: IEditorInput): Promise { return undefined; } @@ -78,14 +77,14 @@ export class WorkbenchEditorTestService implements IEditorService { * will not be closed. The direction can be used in that case to control if all other editors should get closed, * or towards a specific direction. */ - closeEditors(p1?: any, p2?: any, p3?: any): TPromise { + closeEditors(p1?: any, p2?: any, p3?: any): Promise { return undefined; } /** * Closes all editors across all groups. The optional position allows to keep one group alive. */ - closeAllEditors(except?: Position): TPromise { + closeAllEditors(except?: Position): Promise { return undefined; } diff --git a/src/sqltest/utils/testUtils.ts b/src/sqltest/utils/testUtils.ts index e9fbd02a5a8b..31c2f841771f 100644 --- a/src/sqltest/utils/testUtils.ts +++ b/src/sqltest/utils/testUtils.ts @@ -8,15 +8,15 @@ import * as assert from 'assert'; -export async function assertThrowsAsync(fn, regExp?: any): Promise { - let f = () => { - // Empty - }; +export async function assertThrowsAsync(fn, expectedMessage?: string): Promise { + var threw = false; try { await fn(); } catch (e) { - f = () => { throw e; }; - } finally { - assert.throws(f, regExp); + threw = true; + if (expectedMessage) { + assert.strictEqual(e.message, expectedMessage); + } } + assert.equal(threw, true, 'Expected function to throw but it did not'); } diff --git a/src/tsconfig.base.json b/src/tsconfig.base.json index cd513883d3de..9c55727cb7fa 100644 --- a/src/tsconfig.base.json +++ b/src/tsconfig.base.json @@ -9,6 +9,7 @@ "noUnusedLocals": true, "noImplicitThis": true, "alwaysStrict": true, + "strictBindCallApply": true, "baseUrl": ".", "paths": { "vs/*": [ diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 1bf4732a4a09..1303af547f4c 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -19,6 +19,7 @@ "./typings/require-monaco.d.ts", "typings/thenable.d.ts", "typings/es6-promise.d.ts", + "typings/lib.es2018.promise.d.ts", "typings/lib.array-ext.d.ts", "typings/lib.ie11_safe_es6.d.ts", "vs/css.d.ts", @@ -35,4 +36,4 @@ "exclude": [ "node_modules/*" ] -} \ No newline at end of file +} diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 7f4bf7973ec7..0a62155bf7a9 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -6,68 +6,24 @@ }, "include": [ "./typings", - "./sql/base/common/**/*ts", + "./vs/base/browser/**/*.ts", "./vs/base/common/**/*.ts", + "./vs/base/node/**/*.ts", + "./vs/editor/browser/**/*.ts", + "./vs/editor/common/**/*.ts", + "./vs/editor/contrib/codeAction/**/*.ts", + "./vs/editor/contrib/format/**/*.ts", + "./vs/editor/contrib/gotoError/**/*.ts", + "./vs/editor/contrib/inPlaceReplace/**/*.ts", + "./vs/editor/contrib/smartSelect/**/*.ts", + "./vs/editor/contrib/snippet/**/*.ts", + "./vs/editor/contrib/suggest/**/*.ts", + "./vs/editor/test/**/*.ts", + // Begin SQL files + "./sql/base/common/**/*ts", + // End SQL files ], "files": [ - // "./vs/base/browser/browser.ts", - // "./vs/base/browser/dnd.ts", - // "./vs/base/browser/dom.ts", - // "./vs/base/browser/event.ts", - // "./vs/base/browser/fastDomNode.ts", - // "./vs/base/browser/globalMouseMoveMonitor.ts", - // "./vs/base/browser/history.ts", - // "./vs/base/browser/htmlContentRenderer.ts", - // "./vs/base/browser/iframe.ts", - // "./vs/base/browser/keyboardEvent.ts", - // "./vs/base/browser/mouseEvent.ts", - // "./vs/base/browser/touch.ts", - // "./vs/base/browser/ui/actionbar/actionbar.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/checkbox/checkbox.ts", - // "./vs/base/browser/ui/contextview/contextview.ts", - // "./vs/base/browser/ui/countBadge/countBadge.ts", - // "./vs/base/browser/ui/findinput/findInputCheckboxes.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", - // "./vs/base/browser/ui/progressbar/progressbar.ts", - // "./vs/base/browser/ui/sash/sash.ts", - // "./vs/base/browser/ui/scrollbar/abstractScrollbar.ts", - // "./vs/base/browser/ui/scrollbar/horizontalScrollbar.ts", - // "./vs/base/browser/ui/scrollbar/scrollableElement.ts", - // "./vs/base/browser/ui/scrollbar/scrollableElementOptions.ts", - // "./vs/base/browser/ui/scrollbar/scrollbarArrow.ts", - // "./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/selectBox/selectBox.ts", - // "./vs/base/browser/ui/selectBox/selectBoxCustom.ts", - // "./vs/base/browser/ui/selectBox/selectBoxNative.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/asyncDataTree.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/parts/contextmenu/common/contextmenu.ts", // "./vs/base/parts/contextmenu/electron-browser/contextmenu.ts", // "./vs/base/parts/contextmenu/electron-main/contextmenu.ts", @@ -77,207 +33,136 @@ // "./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/ipc.cp.test.ts", + // "./vs/base/parts/ipc/test/node/ipc.net.test.ts", + // "./vs/base/parts/ipc/test/node/ipc.test.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/parts/quickopen/common/quickOpenScorer.ts", + // "./vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts", + // "./vs/base/parts/tree/browser/tree.ts", + // "./vs/base/parts/tree/browser/treeDefaults.ts", + // "./vs/base/parts/tree/browser/treeDnd.ts", + // "./vs/base/parts/tree/browser/treeImpl.ts", + // "./vs/base/parts/tree/browser/treeModel.ts", + // "./vs/base/parts/tree/browser/treeUtils.ts", + // "./vs/base/parts/tree/browser/treeView.ts", + // "./vs/base/parts/tree/browser/treeViewModel.ts", + // "./vs/base/parts/tree/test/browser/treeModel.test.ts", + // "./vs/base/parts/tree/test/browser/treeViewModel.test.ts", + // "./vs/base/test/browser/browser.test.ts", + // "./vs/base/test/browser/comparers.test.ts", + // "./vs/base/test/browser/dom.test.ts", + // "./vs/base/test/browser/highlightedLabel.test.ts", + // "./vs/base/test/browser/htmlContent.test.ts", + // "./vs/base/test/browser/progressBar.test.ts", + // "./vs/base/test/browser/ui/contextview/contextview.test.ts", + // "./vs/base/test/browser/ui/grid/grid.test.ts", + // "./vs/base/test/browser/ui/grid/gridview.test.ts", // "./vs/base/test/browser/ui/grid/util.ts", + // "./vs/base/test/browser/ui/list/listView.test.ts", + // "./vs/base/test/browser/ui/list/rangeMap.test.ts", + // "./vs/base/test/browser/ui/scrollbar/scrollableElement.test.ts", + // "./vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts", + // "./vs/base/test/browser/ui/splitview/splitview.test.ts", + // "./vs/base/test/browser/ui/tree/asyncDataTree.test.ts", + // "./vs/base/test/browser/ui/tree/dataTree.test.ts", + // "./vs/base/test/browser/ui/tree/indexTreeModel.test.ts", + // "./vs/base/test/browser/ui/tree/objectTree.test.ts", + // "./vs/base/test/browser/ui/tree/objectTreeModel.test.ts", + // "./vs/base/test/common/arrays.test.ts", + // "./vs/base/test/common/assert.test.ts", + // "./vs/base/test/common/async.test.ts", + // "./vs/base/test/common/cache.test.ts", + // "./vs/base/test/common/cancellation.test.ts", + // "./vs/base/test/common/charCode.test.ts", + // "./vs/base/test/common/collections.test.ts", + // "./vs/base/test/common/color.test.ts", + // "./vs/base/test/common/decorators.test.ts", + // "./vs/base/test/common/diff/diff.test.ts", + // "./vs/base/test/common/errors.test.ts", + // "./vs/base/test/common/event.test.ts", + // "./vs/base/test/common/filters.perf.test.ts", + // "./vs/base/test/common/filters.test.ts", + // "./vs/base/test/common/hash.test.ts", + // "./vs/base/test/common/history.test.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/keyCodes.test.ts", + // "./vs/base/test/common/labels.test.ts", + // "./vs/base/test/common/lifecycle.test.ts", + // "./vs/base/test/common/linkedList.test.ts", + // "./vs/base/test/common/map.test.ts", + // "./vs/base/test/common/marshalling.test.ts", + // "./vs/base/test/common/mime.test.ts", + // "./vs/base/test/common/objects.test.ts", + // "./vs/base/test/common/octicon.test.ts", + // "./vs/base/test/common/paging.test.ts", // "./vs/base/test/common/paths.test.ts", + // "./vs/base/test/common/resources.test.ts", + // "./vs/base/test/common/scrollable.test.ts", + // "./vs/base/test/common/strings.test.ts", + // "./vs/base/test/common/types.test.ts", + // "./vs/base/test/common/uri.test.ts", // "./vs/base/test/common/utils.ts", + // "./vs/base/test/common/uuid.test.ts", + // "./vs/base/test/node/config.test.ts", + // "./vs/base/test/node/console.test.ts", + // "./vs/base/test/node/decoder.test.ts", + // "./vs/base/test/node/encoding/encoding.test.ts", + // "./vs/base/test/node/extfs/extfs.test.ts", + // "./vs/base/test/node/flow.test.ts", + // "./vs/base/test/node/glob.test.ts", + // "./vs/base/test/node/id.test.ts", + // "./vs/base/test/node/pfs.test.ts", + // "./vs/base/test/node/port.test.ts", // "./vs/base/test/node/processes/fixtures/fork.ts", // "./vs/base/test/node/processes/fixtures/fork_large.ts", + // "./vs/base/test/node/processes/processes.test.ts", + // "./vs/base/test/node/storage/storage.test.ts", + // "./vs/base/test/node/stream/stream.test.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/issueReporterMain.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/issue/test/testReporterModel.test.ts", // "./vs/code/electron-browser/processExplorer/processExplorerMain.ts", // "./vs/code/electron-browser/sharedProcess/contrib/contributions.ts", // "./vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts", + // "./vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts", // "./vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts", // "./vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts", + // "./vs/code/electron-browser/sharedProcess/sharedProcessMain.ts", // "./vs/code/electron-main/auth.ts", // "./vs/code/electron-main/keyboard.ts", // "./vs/code/electron-main/logUploader.ts", // "./vs/code/electron-main/sharedProcess.ts", // "./vs/code/electron-main/theme.ts", // "./vs/code/node/cli.ts", + // "./vs/code/node/cliProcessMain.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", - // "./vs/editor/browser/controller/coreCommands.ts", - // "./vs/editor/browser/controller/mouseHandler.ts", - // "./vs/editor/browser/controller/mouseTarget.ts", - // "./vs/editor/browser/controller/pointerHandler.ts", - // "./vs/editor/browser/controller/textAreaHandler.ts", - // "./vs/editor/browser/controller/textAreaInput.ts", - // "./vs/editor/browser/controller/textAreaState.ts", - // "./vs/editor/browser/core/editorState.ts", - // "./vs/editor/browser/editorBrowser.ts", - // "./vs/editor/browser/editorDom.ts", - // "./vs/editor/browser/editorExtensions", - // "./vs/editor/browser/editorExtensions.ts", - // "./vs/editor/browser/services/abstractCodeEditorService.ts", - // "./vs/editor/browser/services/bulkEditService.ts", - // "./vs/editor/browser/services/codeEditorService.ts", - // "./vs/editor/browser/services/codeEditorServiceImpl.ts", - // "./vs/editor/browser/services/openerService.ts", - // "./vs/editor/browser/view/dynamicViewOverlay.ts", - // "./vs/editor/browser/view/viewController.ts", - // "./vs/editor/browser/view/viewImpl.ts", - // "./vs/editor/browser/view/viewLayer.ts", - // "./vs/editor/browser/view/viewOutgoingEvents.ts", - // "./vs/editor/browser/view/viewOverlays.ts", - // "./vs/editor/browser/view/viewPart.ts", - // "./vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts", - // "./vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts", - // "./vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts", - // "./vs/editor/browser/viewParts/decorations/decorations.ts", - // "./vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts", - // "./vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts", - // "./vs/editor/browser/viewParts/indentGuides/indentGuides.ts", - // "./vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts", - // "./vs/editor/browser/viewParts/lines/rangeUtil.ts", - // "./vs/editor/browser/viewParts/lines/viewLine.ts", - // "./vs/editor/browser/viewParts/lines/viewLines.ts", - // "./vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts", - // "./vs/editor/browser/viewParts/margin/margin.ts", - // "./vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts", - // "./vs/editor/browser/viewParts/minimap/minimap.ts", - // "./vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts", - // "./vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts", - // "./vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts", - // "./vs/editor/browser/viewParts/rulers/rulers.ts", - // "./vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts", - // "./vs/editor/browser/viewParts/selections/selections.ts", - // "./vs/editor/browser/viewParts/viewCursors/viewCursor.ts", - // "./vs/editor/browser/viewParts/viewCursors/viewCursors.ts", - // "./vs/editor/browser/viewParts/viewZones/viewZones.ts", - // "./vs/editor/browser/widget/codeEditorWidget.ts", - // "./vs/editor/browser/widget/diffNavigator.ts", - // "./vs/editor/common/commands/replaceCommand.ts", - // "./vs/editor/common/commands/shiftCommand.ts", - // "./vs/editor/common/commands/surroundSelectionCommand.ts", - // "./vs/editor/common/commands/trimTrailingWhitespaceCommand.ts", - // "./vs/editor/common/config/commonEditorConfig.ts", - // "./vs/editor/common/config/editorOptions.ts", - // "./vs/editor/common/config/editorZoom.ts", - // "./vs/editor/common/config/fontInfo.ts", - // "./vs/editor/common/controller/cursor.ts", - // "./vs/editor/common/controller/cursorCollection.ts", - // "./vs/editor/common/controller/cursorColumnSelection.ts", - // "./vs/editor/common/controller/cursorCommon.ts", - // "./vs/editor/common/controller/cursorDeleteOperations.ts", - // "./vs/editor/common/controller/cursorEvents.ts", - // "./vs/editor/common/controller/cursorMoveCommands.ts", - // "./vs/editor/common/controller/cursorMoveOperations.ts", - // "./vs/editor/common/controller/cursorTypeOperations.ts", - // "./vs/editor/common/controller/cursorWordOperations.ts", - // "./vs/editor/common/controller/oneCursor.ts", - // "./vs/editor/common/controller/wordCharacterClassifier.ts", - // "./vs/editor/common/core/characterClassifier.ts", - // "./vs/editor/common/core/editOperation.ts", - // "./vs/editor/common/core/lineTokens.ts", - // "./vs/editor/common/core/position.ts", - // "./vs/editor/common/core/range.ts", - // "./vs/editor/common/core/rgba.ts", - // "./vs/editor/common/core/selection.ts", - // "./vs/editor/common/core/stringBuilder.ts", - // "./vs/editor/common/core/token.ts", - // "./vs/editor/common/core/uint.ts", - // "./vs/editor/common/diff/diffComputer.ts", - // "./vs/editor/common/editorAction.ts", - // "./vs/editor/common/editorCommon.ts", - // "./vs/editor/common/editorContextKeys.ts", - // "./vs/editor/common/model.ts", - // "./vs/editor/common/model/editStack.ts", - // "./vs/editor/common/model/indentationGuesser.ts", - // "./vs/editor/common/model/intervalTree.ts", - // "./vs/editor/common/model/mirrorTextModel.ts", - // "./vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts", - // "./vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts", - // "./vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts", - // "./vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts", - // "./vs/editor/common/model/textModel.ts", - // "./vs/editor/common/model/textModelEvents.ts", - // "./vs/editor/common/model/textModelSearch.ts", - // "./vs/editor/common/model/textModelTokens.ts", - // "./vs/editor/common/model/wordHelper.ts", - // "./vs/editor/common/modes.ts", - // "./vs/editor/common/modes/abstractMode.ts", - // "./vs/editor/common/modes/languageConfiguration.ts", - // "./vs/editor/common/modes/languageConfigurationRegistry.ts", - // "./vs/editor/common/modes/languageFeatureRegistry.ts", - // "./vs/editor/common/modes/languageSelector.ts", - // "./vs/editor/common/modes/linkComputer.ts", - // "./vs/editor/common/modes/modesRegistry.ts", - // "./vs/editor/common/modes/nullMode.ts", - // "./vs/editor/common/modes/supports.ts", - // "./vs/editor/common/modes/supports/characterPair.ts", - // "./vs/editor/common/modes/supports/electricCharacter.ts", - // "./vs/editor/common/modes/supports/indentRules.ts", - // "./vs/editor/common/modes/supports/inplaceReplaceSupport.ts", - // "./vs/editor/common/modes/supports/onEnter.ts", - // "./vs/editor/common/modes/supports/richEditBrackets.ts", - // "./vs/editor/common/modes/supports/tokenization.ts", - // "./vs/editor/common/modes/textToHtmlTokenizer.ts", - // "./vs/editor/common/modes/tokenizationRegistry.ts", - // "./vs/editor/common/services/editorSimpleWorker.ts", - // "./vs/editor/common/services/editorWorkerService.ts", - // "./vs/editor/common/services/editorWorkerServiceImpl.ts", - // "./vs/editor/common/services/getIconClasses.ts", - // "./vs/editor/common/services/languagesRegistry.ts", - // "./vs/editor/common/services/modeService.ts", - // "./vs/editor/common/services/modeServiceImpl.ts", - // "./vs/editor/common/services/modelService.ts", - // "./vs/editor/common/services/modelServiceImpl.ts", - // "./vs/editor/common/services/resolverService.ts", - // "./vs/editor/common/services/resourceConfiguration.ts", - // "./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", - // "./vs/editor/common/view/renderingContext.ts", - // "./vs/editor/common/view/runtimeMinimapCharRenderer.ts", - // "./vs/editor/common/view/viewContext.ts", - // "./vs/editor/common/view/viewEventDispatcher.ts", - // "./vs/editor/common/view/viewEvents.ts", - // "./vs/editor/common/viewLayout/lineDecorations.ts", - // "./vs/editor/common/viewLayout/linesLayout.ts", - // "./vs/editor/common/viewLayout/viewLayout.ts", - // "./vs/editor/common/viewLayout/viewLineRenderer.ts", - // "./vs/editor/common/viewLayout/viewLinesViewportData.ts", - // "./vs/editor/common/viewLayout/whitespaceComputer.ts", - // "./vs/editor/common/viewModel/characterHardWrappingLineMapper.ts", - // "./vs/editor/common/viewModel/prefixSumComputer.ts", - // "./vs/editor/common/viewModel/splitLinesCollection.ts", - // "./vs/editor/common/viewModel/viewEventHandler.ts", - // "./vs/editor/common/viewModel/viewModel.ts", - // "./vs/editor/common/viewModel/viewModelDecorations.ts", - // "./vs/editor/common/viewModel/viewModelImpl.ts", + // "./vs/code/test/node/argv.test.ts", + // "./vs/code/test/node/windowsFinder.test.ts", // "./vs/editor/contrib/bracketMatching/bracketMatching.ts", + // "./vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts", // "./vs/editor/contrib/caretOperations/caretOperations.ts", // "./vs/editor/contrib/caretOperations/moveCaretCommand.ts", + // "./vs/editor/contrib/caretOperations/test/moveCarretCommand.test.ts", // "./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/codelens/codelensController.ts", + // "./vs/editor/contrib/codelens/codelensWidget.ts", // "./vs/editor/contrib/colorPicker/color.ts", // "./vs/editor/contrib/colorPicker/colorDetector.ts", // "./vs/editor/contrib/colorPicker/colorPickerModel.ts", @@ -285,14 +170,26 @@ // "./vs/editor/contrib/comment/blockCommentCommand.ts", // "./vs/editor/contrib/comment/comment.ts", // "./vs/editor/contrib/comment/lineCommentCommand.ts", + // "./vs/editor/contrib/comment/test/blockCommentCommand.test.ts", + // "./vs/editor/contrib/comment/test/lineCommentCommand.test.ts", + // "./vs/editor/contrib/contextmenu/contextmenu.ts", // "./vs/editor/contrib/cursorUndo/cursorUndo.ts", + // "./vs/editor/contrib/documentSymbols/outlineModel.ts", // "./vs/editor/contrib/dnd/dnd.ts", // "./vs/editor/contrib/dnd/dragAndDropCommand.ts", + // "./vs/editor/contrib/find/findController.ts", // "./vs/editor/contrib/find/findDecorations.ts", // "./vs/editor/contrib/find/findModel.ts", + // "./vs/editor/contrib/find/findOptionsWidget.ts", // "./vs/editor/contrib/find/findState.ts", + // "./vs/editor/contrib/find/findWidget.ts", // "./vs/editor/contrib/find/replaceAllCommand.ts", // "./vs/editor/contrib/find/replacePattern.ts", + // "./vs/editor/contrib/find/simpleFindWidget.ts", + // "./vs/editor/contrib/find/test/find.test.ts", + // "./vs/editor/contrib/find/test/findController.test.ts", + // "./vs/editor/contrib/find/test/findModel.test.ts", + // "./vs/editor/contrib/find/test/replacePattern.test.ts", // "./vs/editor/contrib/folding/folding.ts", // "./vs/editor/contrib/folding/foldingDecorations.ts", // "./vs/editor/contrib/folding/foldingModel.ts", @@ -308,54 +205,68 @@ // "./vs/editor/contrib/folding/test/indentRangeProvider.test.ts", // "./vs/editor/contrib/folding/test/syntaxFold.test.ts", // "./vs/editor/contrib/fontZoom/fontZoom.ts", - // "./vs/editor/contrib/format/format.ts", - // "./vs/editor/contrib/format/formattingEdit.ts", // "./vs/editor/contrib/goToDefinition/clickLinkGesture.ts", // "./vs/editor/contrib/goToDefinition/goToDefinition.ts", - // "./vs/editor/contrib/gotoError/gotoError.ts", - // "./vs/editor/contrib/gotoError/gotoErrorWidget.ts", // "./vs/editor/contrib/hover/getHover.ts", + // "./vs/editor/contrib/hover/hover.ts", // "./vs/editor/contrib/hover/hoverOperation.ts", // "./vs/editor/contrib/hover/hoverWidgets.ts", + // "./vs/editor/contrib/hover/modesContentHover.ts", // "./vs/editor/contrib/hover/modesGlyphHover.ts", - // "./vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts", - // "./vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts", // "./vs/editor/contrib/indentation/indentUtils.ts", // "./vs/editor/contrib/indentation/indentation.ts", + // "./vs/editor/contrib/indentation/test/indentation.test.ts", // "./vs/editor/contrib/linesOperations/copyLinesCommand.ts", // "./vs/editor/contrib/linesOperations/deleteLinesCommand.ts", // "./vs/editor/contrib/linesOperations/linesOperations.ts", // "./vs/editor/contrib/linesOperations/moveLinesCommand.ts", // "./vs/editor/contrib/linesOperations/sortLinesCommand.ts", + // "./vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts", + // "./vs/editor/contrib/linesOperations/test/deleteLinesCommand.test.ts", + // "./vs/editor/contrib/linesOperations/test/linesOperations.test.ts", + // "./vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts", + // "./vs/editor/contrib/linesOperations/test/sortLinesCommand.test.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/multicursor/multicursor.ts", + // "./vs/editor/contrib/multicursor/test/multicursor.test.ts", // "./vs/editor/contrib/parameterHints/parameterHints.ts", + // "./vs/editor/contrib/parameterHints/parameterHintsModel.ts", // "./vs/editor/contrib/parameterHints/parameterHintsWidget.ts", // "./vs/editor/contrib/parameterHints/provideSignatureHelp.ts", + // "./vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts", // "./vs/editor/contrib/quickOpen/quickOpen.ts", + // "./vs/editor/contrib/referenceSearch/peekViewWidget.ts", // "./vs/editor/contrib/referenceSearch/referencesModel.ts", // "./vs/editor/contrib/referenceSearch/referencesTree.ts", + // "./vs/editor/contrib/referenceSearch/referencesWidget.ts", + // "./vs/editor/contrib/referenceSearch/test/referencesModel.test.ts", // "./vs/editor/contrib/rename/rename.ts", // "./vs/editor/contrib/rename/renameInputField.ts", - // "./vs/editor/contrib/snippet/snippetParser.ts", - // "./vs/editor/contrib/snippet/snippetVariables.ts", - // "./vs/editor/contrib/suggest/suggest.ts", - // "./vs/editor/contrib/suggest/wordContextKey.ts", - // "./vs/editor/contrib/suggest/wordDistance.ts", + // "./vs/editor/contrib/suggest/test/suggestMemory.test.ts", // "./vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts", + // "./vs/editor/contrib/tokenization/tokenization.ts", // "./vs/editor/contrib/wordHighlighter/wordHighlighter.ts", + // "./vs/editor/contrib/wordOperations/test/wordOperations.test.ts", // "./vs/editor/contrib/wordOperations/test/wordTestUtils.ts", // "./vs/editor/contrib/wordOperations/wordOperations.ts", + // "./vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts", // "./vs/editor/contrib/wordPartOperations/wordPartOperations.ts", // "./vs/editor/contrib/zoneWidget/zoneWidget.ts", + // "./vs/editor/editor.api.ts", // "./vs/editor/editor.worker.ts", + // "./vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts", // "./vs/editor/standalone/browser/colorizer.ts", // "./vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts", // "./vs/editor/standalone/browser/inspectTokens/inspectTokens.ts", // "./vs/editor/standalone/browser/simpleServices.ts", + // "./vs/editor/standalone/browser/standaloneCodeEditor.ts", // "./vs/editor/standalone/browser/standaloneCodeServiceImpl.ts", + // "./vs/editor/standalone/browser/standaloneEditor.ts", + // "./vs/editor/standalone/browser/standaloneLanguages.ts", + // "./vs/editor/standalone/browser/standaloneServices.ts", // "./vs/editor/standalone/browser/standaloneThemeServiceImpl.ts", // "./vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts", // "./vs/editor/standalone/common/monarch/monarchCommon.ts", @@ -364,12 +275,20 @@ // "./vs/editor/standalone/common/monarch/monarchTypes.ts", // "./vs/editor/standalone/common/standaloneThemeService.ts", // "./vs/editor/standalone/common/themes.ts", + // "./vs/editor/standalone/test/browser/simpleServices.test.ts", + // "./vs/editor/standalone/test/browser/standaloneLanguages.test.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/browser/view/viewLayer.test.ts", // "./vs/editor/test/common/commentMode.ts", + // "./vs/editor/test/common/config/commonEditorConfig.test.ts", + // "./vs/editor/test/common/controller/cursorMoveHelper.test.ts", + // "./vs/editor/test/common/core/characterClassifier.test.ts", + // "./vs/editor/test/common/core/lineTokens.test.ts", + // "./vs/editor/test/common/core/range.test.ts", // "./vs/editor/test/common/core/viewLineToken.ts", // "./vs/editor/test/common/editorTestUtils.ts", // "./vs/editor/test/common/mocks/mockMode.ts", @@ -379,57 +298,93 @@ // "./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/editableTextModelAuto.test.ts", // "./vs/editor/test/common/model/editableTextModelTestUtils.ts", + // "./vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test.ts", // "./vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts", + // "./vs/editor/test/common/model/model.test.ts", + // "./vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts", + // "./vs/editor/test/common/modes/languageConfiguration.test.ts", + // "./vs/editor/test/common/modes/supports/characterPair.test.ts", // "./vs/editor/test/common/modes/supports/javascriptOnEnterRules.ts", + // "./vs/editor/test/common/modes/supports/tokenization.test.ts", // "./vs/editor/test/common/modesTestUtils.ts", + // "./vs/editor/test/common/services/languagesRegistry.test.ts", // "./vs/editor/test/common/view/minimapCharRendererFactory.ts", + // "./vs/editor/test/common/view/overviewZoneManager.test.ts", + // "./vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts", + // "./vs/editor/test/common/viewLayout/lineDecorations.test.ts", + // "./vs/editor/test/common/viewLayout/viewLineRenderer.test.ts", + // "./vs/editor/test/common/viewLayout/whitespaceComputer.test.ts", + // "./vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts", + // "./vs/editor/test/common/viewModel/prefixSumComputer.test.ts", // "./vs/editor/test/common/viewModel/testViewModel.ts", + // "./vs/editor/test/common/viewModel/viewModelDecorations.test.ts", + // "./vs/editor/test/common/viewModel/viewModelImpl.test.ts", // "./vs/monaco.d.ts", // "./vs/nls.d.ts", // "./vs/nls.mock.ts", + // "./vs/platform/actions/browser/menuItemActionItem.ts", // "./vs/platform/actions/common/actions.ts", - // "./vs/platform/actions/common/menu.ts", // "./vs/platform/actions/common/menuService.ts", + // "./vs/platform/actions/test/common/menuService.test.ts", // "./vs/platform/backup/common/backup.ts", // "./vs/platform/backup/electron-main/backupMainService.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/commands/test/commands.test.ts", // "./vs/platform/configuration/common/configuration.ts", // "./vs/platform/configuration/common/configurationModels.ts", // "./vs/platform/configuration/common/configurationRegistry.ts", // "./vs/platform/configuration/node/configuration.ts", + // "./vs/platform/configuration/node/configurationService.ts", + // "./vs/platform/configuration/test/common/configuration.test.ts", + // "./vs/platform/configuration/test/common/configurationModels.test.ts", // "./vs/platform/configuration/test/common/testConfigurationService.ts", + // "./vs/platform/configuration/test/node/configurationService.test.ts", // "./vs/platform/contextkey/browser/contextKeyService.ts", // "./vs/platform/contextkey/common/contextkey.ts", + // "./vs/platform/contextkey/test/common/contextkey.test.ts", + // "./vs/platform/contextview/browser/contextMenuHandler.ts", + // "./vs/platform/contextview/browser/contextMenuService.ts", + // "./vs/platform/contextview/browser/contextView.ts", + // "./vs/platform/contextview/browser/contextViewService.ts", + // "./vs/platform/credentials/test/node/keytar.test.ts", // "./vs/platform/diagnostics/electron-main/diagnosticsService.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/downloadIpc.ts", // "./vs/platform/download/node/downloadService.ts", + // "./vs/platform/driver/electron-browser/driver.ts", // "./vs/platform/driver/electron-main/driver.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", // "./vs/platform/environment/node/environmentService.ts", + // "./vs/platform/environment/test/node/environmentService.test.ts", // "./vs/platform/extensionManagement/common/extensionEnablementService.ts", // "./vs/platform/extensionManagement/common/extensionManagement.ts", // "./vs/platform/extensionManagement/common/extensionManagementUtil.ts", // "./vs/platform/extensionManagement/common/extensionNls.ts", + // "./vs/platform/extensionManagement/node/extensionGalleryService.ts", // "./vs/platform/extensionManagement/node/extensionLifecycle.ts", // "./vs/platform/extensionManagement/node/extensionManagementIpc.ts", + // "./vs/platform/extensionManagement/node/extensionManagementService.ts", // "./vs/platform/extensionManagement/node/extensionManagementUtil.ts", // "./vs/platform/extensionManagement/node/extensionsManifestCache.ts", + // "./vs/platform/extensionManagement/node/multiExtensionManagement.ts", + // "./vs/platform/extensionManagement/test/electron-browser/extensionManagement.test.ts", // "./vs/platform/extensions/common/extensionHost.ts", // "./vs/platform/extensions/common/extensions.ts", // "./vs/platform/extensions/node/extensionValidator.ts", + // "./vs/platform/extensions/test/node/extensionValidator.test.ts", // "./vs/platform/files/common/files.ts", // "./vs/platform/files/node/files.ts", + // "./vs/platform/files/test/files.test.ts", // "./vs/platform/history/common/history.ts", // "./vs/platform/history/electron-main/historyMainService.ts", // "./vs/platform/instantiation/common/descriptors.ts", @@ -439,6 +394,9 @@ // "./vs/platform/instantiation/common/instantiationService.ts", // "./vs/platform/instantiation/common/serviceCollection.ts", // "./vs/platform/instantiation/node/instantiationService.ts", + // "./vs/platform/instantiation/test/common/graph.test.ts", + // "./vs/platform/instantiation/test/common/instantiationService.test.ts", + // "./vs/platform/instantiation/test/common/instantiationServiceMock.ts", // "./vs/platform/integrity/common/integrity.ts", // "./vs/platform/integrity/node/integrityServiceImpl.ts", // "./vs/platform/issue/common/issue.ts", @@ -451,14 +409,18 @@ // "./vs/platform/keybinding/common/keybindingsRegistry.ts", // "./vs/platform/keybinding/common/resolvedKeybindingItem.ts", // "./vs/platform/keybinding/common/usLayoutResolvedKeybinding.ts", + // "./vs/platform/keybinding/test/common/abstractKeybindingService.test.ts", + // "./vs/platform/keybinding/test/common/keybindingLabels.test.ts", + // "./vs/platform/keybinding/test/common/keybindingResolver.test.ts", // "./vs/platform/keybinding/test/common/mockKeybindingService.ts", // "./vs/platform/label/common/label.ts", - // "./vs/platform/label/electron-browser/label.contribution.ts", // "./vs/platform/launch/electron-main/launchService.ts", // "./vs/platform/lifecycle/common/lifecycle.ts", // "./vs/platform/lifecycle/electron-browser/lifecycleService.ts", // "./vs/platform/lifecycle/electron-main/lifecycleMain.ts", + // "./vs/platform/list/browser/listService.ts", // "./vs/platform/localizations/common/localizations.ts", + // "./vs/platform/localizations/node/localizations.ts", // "./vs/platform/localizations/node/localizationsIpc.ts", // "./vs/platform/log/common/bufferLog.ts", // "./vs/platform/log/common/log.ts", @@ -466,6 +428,7 @@ // "./vs/platform/log/node/spdlogService.ts", // "./vs/platform/markers/common/markerService.ts", // "./vs/platform/markers/common/markers.ts", + // "./vs/platform/markers/test/common/markerService.test.ts", // "./vs/platform/menubar/common/menubar.ts", // "./vs/platform/menubar/electron-main/menubar.ts", // "./vs/platform/menubar/electron-main/menubarService.ts", @@ -473,6 +436,7 @@ // "./vs/platform/node/minimalTranslations.ts", // "./vs/platform/node/package.ts", // "./vs/platform/node/product.ts", + // "./vs/platform/node/test/zip.test.ts", // "./vs/platform/node/zip.ts", // "./vs/platform/notification/common/notification.ts", // "./vs/platform/notification/test/common/testNotificationService.ts", @@ -482,6 +446,7 @@ // "./vs/platform/quickOpen/common/quickOpen.ts", // "./vs/platform/quickinput/common/quickInput.ts", // "./vs/platform/registry/common/platform.ts", + // "./vs/platform/registry/test/common/platform.test.ts", // "./vs/platform/remote/common/remoteAuthorityResolver.ts", // "./vs/platform/remote/common/remoteHosts.ts", // "./vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts", @@ -494,20 +459,25 @@ // "./vs/platform/search/common/replace.ts", // "./vs/platform/search/common/search.ts", // "./vs/platform/search/test/common/replace.test.ts", + // "./vs/platform/search/test/common/search.test.ts", // "./vs/platform/state/common/state.ts", // "./vs/platform/state/node/stateService.ts", // "./vs/platform/statusbar/common/statusbar.ts", // "./vs/platform/storage/common/storage.ts", - // "./vs/platform/storage/common/storageLegacyService.ts", - // //"./vs/platform/storage/node/storageService.ts", TODO@Ben bring back when storageMigration is removed + // "./vs/platform/storage/node/storageIpc.ts", + // "./vs/platform/storage/node/storageMainService.ts", + // "./vs/platform/storage/node/storageService.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/appInsightsAppender.ts", // "./vs/platform/telemetry/node/commonProperties.ts", // "./vs/platform/telemetry/node/telemetryIpc.ts", // "./vs/platform/telemetry/node/telemetryNodeUtils.ts", // "./vs/platform/telemetry/node/workbenchCommonProperties.ts", + // "./vs/platform/telemetry/test/electron-browser/appInsightsAppender.test.ts", + // "./vs/platform/telemetry/test/electron-browser/telemetryService.test.ts", // "./vs/platform/theme/common/colorRegistry.ts", // "./vs/platform/theme/common/styler.ts", // "./vs/platform/theme/common/themeService.ts", @@ -517,20 +487,24 @@ // "./vs/platform/update/electron-main/updateService.darwin.ts", // "./vs/platform/update/electron-main/updateService.linux.ts", // "./vs/platform/update/electron-main/updateService.snap.ts", + // "./vs/platform/update/electron-main/updateService.win32.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/browser/contextScopedHistoryWidget.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/electron-main/windowsService.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/workspace/test/common/workspace.test.ts", // "./vs/platform/workspaces/common/workspaces.ts", // "./vs/platform/workspaces/electron-main/workspacesMainService.ts", // "./vs/platform/workspaces/node/workspaces.ts", @@ -538,26 +512,61 @@ // "./vs/vscode.d.ts", // "./vs/vscode.proposed.d.ts", // "./vs/workbench/api/node/extHostExtensionActivator.ts", + // "./vs/workbench/api/node/extHostSearch.fileIndex.ts", // "./vs/workbench/api/node/extHostTypes.ts", + // "./vs/workbench/api/shared/editor.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/actions.ts", + // "./vs/workbench/browser/actions/layoutActions.ts", + // "./vs/workbench/browser/actions/navigationActions.ts", + // "./vs/workbench/browser/actions/workspaceActions.ts", + // "./vs/workbench/browser/actions/workspaceCommands.ts", + // "./vs/workbench/browser/composite.ts", + // "./vs/workbench/browser/editor.ts", + // "./vs/workbench/browser/panel.ts", // "./vs/workbench/browser/part.ts", + // "./vs/workbench/browser/parts/compositePart.ts", + // "./vs/workbench/browser/parts/editor/baseEditor.ts", + // "./vs/workbench/browser/parts/editor/binaryDiffEditor.ts", + // "./vs/workbench/browser/parts/editor/binaryEditor.ts", + // "./vs/workbench/browser/parts/editor/breadcrumbs.ts", + // "./vs/workbench/browser/parts/editor/breadcrumbsModel.ts", + // "./vs/workbench/browser/parts/editor/editor.ts", + // "./vs/workbench/browser/parts/editor/editorControl.ts", // "./vs/workbench/browser/parts/editor/editorWidgets.ts", + // "./vs/workbench/browser/parts/editor/rangeDecorations.ts", + // "./vs/workbench/browser/parts/editor/resourceViewer.ts", + // "./vs/workbench/browser/parts/editor/sideBySideEditor.ts", + // "./vs/workbench/browser/parts/editor/textEditor.ts", + // "./vs/workbench/browser/parts/notifications/notificationsActions.ts", // "./vs/workbench/browser/parts/notifications/notificationsAlerts.ts", + // "./vs/workbench/browser/parts/notifications/notificationsCenter.ts", + // "./vs/workbench/browser/parts/notifications/notificationsCommands.ts", + // "./vs/workbench/browser/parts/notifications/notificationsList.ts", + // "./vs/workbench/browser/parts/notifications/notificationsStatus.ts", + // "./vs/workbench/browser/parts/notifications/notificationsToasts.ts", + // "./vs/workbench/browser/parts/notifications/notificationsViewer.ts", + // "./vs/workbench/browser/parts/quickinput/quickInputBox.ts", + // "./vs/workbench/browser/parts/quickinput/quickInputList.ts", // "./vs/workbench/browser/parts/quickinput/quickInputUtils.ts", // "./vs/workbench/browser/parts/quickopen/quickopen.ts", + // "./vs/workbench/browser/parts/sidebar/sidebarPart.ts", // "./vs/workbench/browser/parts/statusbar/statusbar.ts", + // "./vs/workbench/browser/parts/statusbar/statusbarPart.ts", + // "./vs/workbench/browser/parts/views/panelViewlet.ts", + // "./vs/workbench/browser/parts/views/views.ts", + // "./vs/workbench/browser/parts/views/viewsViewlet.ts", + // "./vs/workbench/browser/viewlet.ts", // "./vs/workbench/common/actions.ts", // "./vs/workbench/common/activity.ts", // "./vs/workbench/common/component.ts", // "./vs/workbench/common/composite.ts", // "./vs/workbench/common/contributions.ts", + // "./vs/workbench/common/editor.ts", + // "./vs/workbench/common/editor/binaryEditorModel.ts", + // "./vs/workbench/common/editor/dataUriEditorInput.ts", + // "./vs/workbench/common/editor/diffEditorModel.ts", + // "./vs/workbench/common/editor/editorGroup.ts", // "./vs/workbench/common/extensionHostProtocol.ts", // "./vs/workbench/common/memento.ts", // "./vs/workbench/common/notifications.ts", @@ -566,13 +575,23 @@ // "./vs/workbench/common/theme.ts", // "./vs/workbench/common/viewlet.ts", // "./vs/workbench/common/views.ts", + // "./vs/workbench/electron-browser/actions/helpActions", + // "./vs/workbench/electron-browser/actions/developerActions", + // "./vs/workbench/electron-browser/actions/windowActions", + // "./vs/workbench/electron-browser/resources.ts", + // "./vs/workbench/electron-browser/window.ts", + // "./vs/workbench/parts/backup/common/backupRestorer.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/inspectKeybindings.ts", // "./vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts", // "./vs/workbench/parts/codeEditor/electron-browser/largeFileOptimizations.ts", // "./vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts", + // "./vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts", + // "./vs/workbench/parts/codeEditor/electron-browser/sleepResumeRepaintMinimap.ts", + // "./vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts", // "./vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts", // "./vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts", // "./vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts", @@ -581,6 +600,10 @@ // "./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/simpleCommentEditor.ts", + // "./vs/workbench/parts/debug/browser/debugANSIHandling.ts", + // "./vs/workbench/parts/debug/browser/linkDetector.ts", + // "./vs/workbench/parts/debug/node/telemetryApp.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", @@ -588,14 +611,34 @@ // "./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/execution.contribution.ts", // "./vs/workbench/parts/execution/electron-browser/terminal.ts", // "./vs/workbench/parts/execution/electron-browser/terminalService.ts", + // "./vs/workbench/parts/execution/test/electron-browser/terminalService.test.ts", + // "./vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts", + // "./vs/workbench/parts/experiments/electron-browser/experiments.contribution.ts", + // "./vs/workbench/parts/experiments/node/experimentService.ts", + // "./vs/workbench/parts/extensions/browser/extensionsViewer.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/common/extensionsInput.ts", + // "./vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts", + // "./vs/workbench/parts/extensions/electron-browser/extensionsActions.ts", // "./vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts", - // // "./vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts", // "./vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts", + // "./vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts", + // "./vs/workbench/parts/extensions/test/common/extensionQuery.test.ts", + // "./vs/workbench/parts/feedback/electron-browser/feedback.contribution.ts", + // "./vs/workbench/parts/feedback/electron-browser/feedback.ts", + // "./vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts", + // "./vs/workbench/parts/files/browser/files.ts", + // "./vs/workbench/parts/files/common/explorerModel.ts", + // "./vs/workbench/parts/files/common/files.ts", + // "./vs/workbench/parts/files/electron-browser/explorerService.ts", + // "./vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts", + // "./vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts", + // "./vs/workbench/parts/localizations/electron-browser/localizationsActions.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", @@ -604,23 +647,50 @@ // "./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/markersPanelActions.ts", // "./vs/workbench/parts/markers/electron-browser/messages.ts", + // "./vs/workbench/parts/markers/test/electron-browser/markersModel.test.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/performance/electron-browser/startupTimings.ts", + // "./vs/workbench/parts/preferences/browser/settingsWidgets.ts", // "./vs/workbench/parts/preferences/common/smartSnippetInserter.ts", + // "./vs/workbench/parts/preferences/test/common/smartSnippetInserter.test.ts", // "./vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts", // "./vs/workbench/parts/scm/common/scm.ts", + // "./vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts", + // "./vs/workbench/parts/scm/electron-browser/scmActivity.ts", + // "./vs/workbench/parts/scm/electron-browser/scmMenus.ts", // "./vs/workbench/parts/scm/electron-browser/scmUtil.ts", + // "./vs/workbench/parts/search/browser/patternInputWidget.ts", + // "./vs/workbench/parts/search/browser/replaceContributions.ts", + // "./vs/workbench/parts/search/browser/replaceService.ts", // "./vs/workbench/parts/search/common/constants.ts", // "./vs/workbench/parts/search/common/queryBuilder.ts", + // "./vs/workbench/parts/search/common/replace.ts", + // "./vs/workbench/parts/search/common/search.ts", + // "./vs/workbench/parts/search/common/searchModel.ts", + // "./vs/workbench/parts/search/test/browser/mockSearchTree.ts", + // "./vs/workbench/parts/search/test/common/searchModel.test.ts", + // "./vs/workbench/parts/search/test/common/searchResult.test.ts", // "./vs/workbench/parts/snippets/electron-browser/configureSnippets.ts", + // "./vs/workbench/parts/snippets/electron-browser/insertSnippet.ts", // "./vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts", // "./vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts", // "./vs/workbench/parts/snippets/electron-browser/snippetsFile.ts", // "./vs/workbench/parts/snippets/electron-browser/snippetsService.ts", + // "./vs/workbench/parts/snippets/electron-browser/tabCompletion.ts", + // "./vs/workbench/parts/snippets/test/electron-browser/snippetFile.test.ts", + // "./vs/workbench/parts/snippets/test/electron-browser/snippetsRegistry.test.ts", + // "./vs/workbench/parts/snippets/test/electron-browser/snippetsRewrite.test.ts", + // "./vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts", + // "./vs/workbench/parts/splash/electron-browser/partsSplash.contribution.ts", + // "./vs/workbench/parts/stats/node/stats.contribution.ts", + // "./vs/workbench/parts/stats/node/workspaceStats.ts", + // "./vs/workbench/parts/stats/test/workspaceStats.test.ts", + // "./vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.ts", // "./vs/workbench/parts/surveys/electron-browser/nps.contribution.ts", // "./vs/workbench/parts/tasks/common/problemCollectors.ts", // "./vs/workbench/parts/tasks/common/problemMatcher.ts", @@ -630,56 +700,103 @@ // "./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/electron-browser/jsonSchema_v1.ts", + // "./vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts", // "./vs/workbench/parts/tasks/electron-browser/runAutomaticTasks.ts", + // "./vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts", + // "./vs/workbench/parts/tasks/node/processRunnerDetector.ts", + // "./vs/workbench/parts/tasks/node/processTaskSystem.ts", + // "./vs/workbench/parts/tasks/node/taskConfiguration.ts", // "./vs/workbench/parts/tasks/node/tasks.ts", + // "./vs/workbench/parts/tasks/test/common/problemMatcher.test.ts", + // "./vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts", + // "./vs/workbench/parts/terminal/browser/terminalFindWidget.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/terminalMenu.ts", // "./vs/workbench/parts/terminal/common/terminalService.ts", + // "./vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts", + // "./vs/workbench/parts/terminal/electron-browser/terminalInstance.ts", + // "./vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts", + // "./vs/workbench/parts/terminal/electron-browser/terminalProcessManager.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/terminal/node/windowsShellHelper.ts", + // "./vs/workbench/parts/terminal/test/electron-browser/terminalColorRegistry.test.ts", + // "./vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts", + // "./vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts", + // "./vs/workbench/parts/terminal/test/node/terminalCommandTracker.test.ts", + // "./vs/workbench/parts/terminal/test/node/terminalEnvironment.test.ts", + // "./vs/workbench/parts/themes/electron-browser/themes.contribution.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.contribution.ts", // "./vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.ts", + // "./vs/workbench/parts/welcome/gettingStarted/electron-browser/telemetryOptOut.ts", + // "./vs/workbench/parts/welcome/gettingStarted/test/common/gettingStarted.test.ts", // "./vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts", + // "./vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts", + // "./vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts", + // "./vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts", + // "./vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts", + // "./vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts", + // "./vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts", + // "./vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts", + // "./vs/workbench/parts/welcome/walkThrough/node/walkThroughInput.ts", // "./vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils.ts", - // "./vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts", + // "./vs/workbench/api/common/menusExtensionPoint.ts", + // "./vs/workbench/api/common/configurationExtensionPoint.ts", // "./vs/workbench/services/activity/common/activity.ts", // "./vs/workbench/services/backup/common/backup.ts", // "./vs/workbench/services/backup/node/backupFileService.ts", + // "./vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts", + // "./vs/workbench/services/codeEditor/browser/codeEditorService.ts", // "./vs/workbench/services/commands/common/commandService.ts", + // "./vs/workbench/services/commands/test/common/commandService.test.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/configuration/node/jsonEditingService.ts", + // "./vs/workbench/services/configuration/test/common/configurationModels.test.ts", // "./vs/workbench/services/configurationResolver/common/configurationResolver.ts", + // "./vs/workbench/services/configurationResolver/common/configurationResolverSchema.ts", // "./vs/workbench/services/configurationResolver/common/configurationResolverUtils.ts", + // "./vs/workbench/services/contextview/electron-browser/contextmenuService.ts", // "./vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts", // "./vs/workbench/services/decorations/browser/decorations.ts", // "./vs/workbench/services/decorations/browser/decorationsService.ts", + // "./vs/workbench/services/decorations/test/browser/decorationsService.test.ts", + // "./vs/workbench/services/dialogs/electron-browser/dialogService.ts", + // "./vs/workbench/services/editor/common/editorService.ts", // "./vs/workbench/services/extensions/common/extensions.ts", // "./vs/workbench/services/extensions/common/extensionsRegistry.ts", // "./vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts", + // "./vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts", // "./vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts", + // "./vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput.ts", // "./vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts", // "./vs/workbench/services/extensions/node/extensionManagementServerService.ts", // "./vs/workbench/services/extensions/node/extensionPoints.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/extensions/test/node/rpcProtocol.test.ts", // "./vs/workbench/services/files/electron-browser/encoding.ts", // "./vs/workbench/services/files/node/watcher/common.ts", + // "./vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts", + // "./vs/workbench/services/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts", // "./vs/workbench/services/files/node/watcher/nsfw/watcher.ts", + // "./vs/workbench/services/files/node/watcher/nsfw/watcherApp.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/chokidarWatcherService.ts", + // "./vs/workbench/services/files/node/watcher/unix/test/chockidarWatcherService.test.ts", // "./vs/workbench/services/files/node/watcher/unix/watcher.ts", // "./vs/workbench/services/files/node/watcher/unix/watcherApp.ts", // "./vs/workbench/services/files/node/watcher/unix/watcherIpc.ts", @@ -687,9 +804,14 @@ // "./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/files/test/electron-browser/watcher.test.ts", + // "./vs/workbench/services/group/common/editorGroupsService.ts", // "./vs/workbench/services/hash/common/hashService.ts", // "./vs/workbench/services/hash/node/hashService.ts", + // "./vs/workbench/services/history/common/history.ts", + // "./vs/workbench/services/history/electron-browser/history.ts", // "./vs/workbench/services/issue/common/issue.ts", + // "./vs/workbench/services/issue/electron-browser/workbenchIssueService.ts", // "./vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts", // "./vs/workbench/services/keybinding/common/keybindingIO.ts", // "./vs/workbench/services/keybinding/common/keyboardMapper.ts", @@ -697,42 +819,72 @@ // "./vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts", // "./vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts", // "./vs/workbench/services/keybinding/electron-browser/keybindingService.ts", + // "./vs/workbench/services/keybinding/test/keybindingIO.test.ts", // "./vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts", + // "./vs/workbench/services/keybinding/test/macLinuxFallbackKeyboardMapper.test.ts", + // "./vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts", + // "./vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts", + // "./vs/workbench/services/label/common/labelService.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/part/common/partService.ts", + // "./vs/workbench/services/preferences/common/keybindingsEditorModel.ts", + // "./vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts", + // "./vs/workbench/services/progress/browser/progressService.ts", + // "./vs/workbench/services/progress/test/progressService.test.ts", // "./vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts", // "./vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts", // "./vs/workbench/services/remote/node/remoteAgentService.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/fileSearch.ts", // "./vs/workbench/services/search/node/fileSearchManager.ts", - // "./vs/workbench/services/search/node/legacy/search.ts", + // "./vs/workbench/services/search/node/rawSearchService.ts", // "./vs/workbench/services/search/node/ripgrepFileSearch.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/searchApp.ts", // "./vs/workbench/services/search/node/searchHistoryService.ts", // "./vs/workbench/services/search/node/searchIpc.ts", // "./vs/workbench/services/search/node/textSearchAdapter.ts", // "./vs/workbench/services/search/node/textSearchManager.ts", + // "./vs/workbench/services/search/test/common/searchHelpers.test.ts", + // "./vs/workbench/services/search/test/node/ripgrepFileSearch.test.ts", // "./vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts", + // "./vs/workbench/services/search/test/node/search.test.ts", + // "./vs/workbench/services/search/test/node/textSearch.integrationTest.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/TMSyntax.ts", // "./vs/workbench/services/textMate/electron-browser/textMateService.ts", + // "./vs/workbench/services/textfile/common/textfiles.ts", // "./vs/workbench/services/textfile/electron-browser/textResourcePropertiesService.ts", // "./vs/workbench/services/themes/common/colorExtensionPoint.ts", // "./vs/workbench/services/themes/common/colorThemeSchema.ts", // "./vs/workbench/services/themes/common/fileIconThemeSchema.ts", // "./vs/workbench/services/themes/common/workbenchThemeService.ts", + // "./vs/workbench/services/themes/electron-browser/colorThemeData.ts", + // "./vs/workbench/services/themes/electron-browser/colorThemeStore.ts", + // "./vs/workbench/services/themes/electron-browser/fileIconThemeData.ts", + // "./vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts", + // "./vs/workbench/services/themes/electron-browser/workbenchThemeService.ts", + // "./vs/workbench/services/themes/electron-browser/themeCompatibility.ts", + // "./vs/workbench/services/timer/electron-browser/timerService.ts", // "./vs/workbench/services/title/common/titleService.ts", + // "./vs/workbench/services/viewlet/browser/viewlet.ts", // "./vs/workbench/services/workspace/common/workspaceEditing.ts", + // "./vs/workbench/test/browser/actionRegistry.test.ts", + // "./vs/workbench/test/browser/viewlet.test.ts", + // "./vs/workbench/test/common/editor/editorOptions.test.ts", + // "./vs/workbench/test/common/notifications.test.ts", + // "./vs/workbench/test/electron-browser/api/extHostTypes.test.ts", // "./vs/workbench/test/electron-browser/api/mock.ts", + // Begin SQL files // "./sql/base/browser/ui/breadcrumb/breadcrumb.component.ts", // pulls in angular which gives extrenous errors // "./sql/base/browser/ui/breadcrumb/interfaces.ts", // pulls in angular which gives extrenous errors "./sql/base/browser/ui/button/button.ts", @@ -746,8 +898,9 @@ // "./sql/base/browser/ui/inputBox/inputBox.component.ts", // pulls in angular which gives extrenous errors // "./sql/base/browser/ui/inputBox/inputBox.ts", // skipping since vscode hasn't checked their inputbox yet // "./sql/base/browser/ui/listBox/listBox.ts", + // End SQL files ], "exclude": [ "./typings/require-monaco.d.ts" ] -} +} \ No newline at end of file diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index 607eeb388c90..8f72a1bc5651 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,5 +1,5 @@ -// Type definitions for Electron 2.0.5 -// Project: http://electron.atom.io/ +// Type definitions for Electron 3.1.2 +// Project: http://electronjs.org/ // Definitions by: The Electron Team // Definitions: https://github.com/electron/electron-typescript-definitions @@ -64,6 +64,7 @@ declare namespace Electron { Menu: typeof Menu; MenuItem: typeof MenuItem; net: Net; + netLog: NetLog; Notification: typeof Notification; powerMonitor: PowerMonitor; powerSaveBlocker: PowerSaveBlocker; @@ -101,6 +102,7 @@ declare namespace Electron { type nativeImage = NativeImage; const nativeImage: typeof NativeImage; const net: Net; + const netLog: NetLog; const powerMonitor: PowerMonitor; const powerSaveBlocker: PowerSaveBlocker; const protocol: Protocol; @@ -470,6 +472,50 @@ declare namespace Electron { once(event: 'ready', listener: (launchInfo: any) => void): this; addListener(event: 'ready', listener: (launchInfo: any) => void): this; removeListener(event: 'ready', listener: (launchInfo: any) => void): this; + /** + * This event will be emitted inside the primary instance of your application when + * a second instance has been executed. argv is an Array of the second instance's + * command line arguments, and workingDirectory is its current working directory. + * Usually applications respond to this by making their primary window focused and + * non-minimized. This event is guaranteed to be emitted after the ready event of + * app gets emitted. + */ + on(event: 'second-instance', listener: (event: Event, + /** + * An array of the second instance's command line arguments + */ + argv: string[], + /** + * The second instance's working directory + */ + workingDirectory: string) => void): this; + once(event: 'second-instance', listener: (event: Event, + /** + * An array of the second instance's command line arguments + */ + argv: string[], + /** + * The second instance's working directory + */ + workingDirectory: string) => void): this; + addListener(event: 'second-instance', listener: (event: Event, + /** + * An array of the second instance's command line arguments + */ + argv: string[], + /** + * The second instance's working directory + */ + workingDirectory: string) => void): this; + removeListener(event: 'second-instance', listener: (event: Event, + /** + * An array of the second instance's command line arguments + */ + argv: string[], + /** + * The second instance's working directory + */ + workingDirectory: string) => void): this; /** * Emitted when a client certificate is requested. The url corresponds to the * navigation entry requesting the client certificate and callback can be called @@ -496,6 +542,13 @@ declare namespace Electron { url: string, certificateList: Certificate[], callback: (certificate?: Certificate) => void) => void): this; + /** + * Emitted when Electron has created a new session. + */ + on(event: 'session-created', listener: (session: Session) => void): this; + once(event: 'session-created', listener: (session: Session) => void): this; + addListener(event: 'session-created', listener: (session: Session) => void): this; + removeListener(event: 'session-created', listener: (session: Session) => void): this; /** * Emitted when Handoff is about to be resumed on another device. If you need to * update the state to be transferred, you should call event.preventDefault() @@ -581,7 +634,7 @@ declare namespace Electron { * event represents the applicationWillFinishLaunching notification of * NSApplication. You would usually set up listeners for the open-file and open-url * events here, and start the crash reporter and auto updater. In most cases, you - * should just do everything in the ready event handler. + * should do everything in the ready event handler. */ on(event: 'will-finish-launching', listener: Function): this; once(event: 'will-finish-launching', listener: Function): this; @@ -656,12 +709,12 @@ declare namespace Electron { * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux * and macOS, icons depend on the application associated with file mime type. */ - getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; + getFileIcon(path: string, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; /** * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux * and macOS, icons depend on the application associated with file mime type. */ - getFileIcon(path: string, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; + getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; getGPUFeatureStatus(): GPUFeatureStatus; getJumpListSettings(): JumpListSettings; /** @@ -688,6 +741,12 @@ declare namespace Electron { */ getPath(name: string): string; getVersion(): string; + /** + * This method returns whether or not this instance of your app is currently + * holding the single instance lock. You can request the lock with + * app.requestSingleInstanceLock() and release with app.releaseSingleInstanceLock() + */ + hasSingleInstanceLock(): boolean; /** * Hides all application windows without minimizing them. */ @@ -717,27 +776,6 @@ declare namespace Electron { isInApplicationsFolder(): boolean; isReady(): boolean; isUnityRunning(): boolean; - /** - * This method makes your application a Single Instance Application - instead of - * allowing multiple instances of your app to run, this will ensure that only a - * single instance of your app is running, and other instances signal this instance - * and exit. callback will be called by the first instance with callback(argv, - * workingDirectory) when a second instance has been executed. argv is an Array of - * the second instance's command line arguments, and workingDirectory is its - * current working directory. Usually applications respond to this by making their - * primary window focused and non-minimized. The callback is guaranteed to be - * executed after the ready event of app gets emitted. This method returns false if - * your process is the primary instance of the application and your app should - * continue loading. And returns true if your process has sent its parameters to - * another instance, and you should immediately quit. On macOS the system enforces - * single instance automatically when users try to open a second instance of your - * app in Finder, and the open-file and open-url events will be emitted for that. - * However when users start your app in command line the system's single instance - * mechanism will be bypassed and you have to use this method to ensure single - * instance. An example of activating the window of primary instance when a second - * instance starts: - */ - makeSingleInstance(callback: (argv: string[], workingDirectory: string) => void): boolean; /** * No confirmation dialog will be presented by default, if you wish to allow the * user to confirm the operation you may do so using the dialog API. NOTE: This @@ -770,15 +808,34 @@ declare namespace Electron { */ relaunch(options?: RelaunchOptions): void; /** - * Releases all locks that were created by makeSingleInstance. This will allow - * multiple instances of the application to once again run side by side. + * Releases all locks that were created by requestSingleInstanceLock. This will + * allow multiple instances of the application to once again run side by side. */ - releaseSingleInstance(): void; + releaseSingleInstanceLock(): void; /** * This method checks if the current executable as the default handler for a * protocol (aka URI scheme). If so, it will remove the app as the default handler. */ removeAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; + /** + * This method makes your application a Single Instance Application - instead of + * allowing multiple instances of your app to run, this will ensure that only a + * single instance of your app is running, and other instances signal this instance + * and exit. The return value of this method indicates whether or not this instance + * of your application successfully obtained the lock. If it failed to obtain the + * lock you can assume that another instance of your application is already running + * with the lock and exit immediately. I.e. This method returns true if your + * process is the primary instance of your application and your app should continue + * loading. It returns false if your process should immediately quit as it has + * sent its parameters to another instance that has already acquired the lock. On + * macOS the system enforces single instance automatically when users try to open a + * second instance of your app in Finder, and the open-file and open-url events + * will be emitted for that. However when users start your app in command line the + * system's single instance mechanism will be bypassed and you have to use this + * method to ensure single instance. An example of activating the window of primary + * instance when a second instance starts: + */ + requestSingleInstanceLock(): boolean; /** * Set the about panel options. This will override the values defined in the app's * .plist file. See the Apple docs for more details. @@ -883,14 +940,32 @@ declare namespace Electron { * userInfo into its current userInfo dictionary. */ updateCurrentActivity(type: string, userInfo: any): void; + whenReady(): Promise; commandLine: CommandLine; dock: Dock; + /** + * A Boolean property that returns true if the app is packaged, false otherwise. + * For many apps, this property can be used to distinguish development and + * production environments. + */ + isPackaged?: boolean; } interface AutoUpdater extends EventEmitter { // Docs: http://electron.atom.io/docs/api/auto-updater + /** + * This event is emitted after a user calls quitAndInstall(). When this API is + * called, the before-quit event is not emitted before all windows are closed. As a + * result you should listen to this event if you wish to perform actions before the + * windows are closed while a process is quitting, as well as listening to + * before-quit. + */ + on(event: 'before-quit-for-update', listener: Function): this; + once(event: 'before-quit-for-update', listener: Function): this; + addListener(event: 'before-quit-for-update', listener: Function): this; + removeListener(event: 'before-quit-for-update', listener: Function): this; /** * Emitted when checking if an update has started. */ @@ -1034,7 +1109,7 @@ declare namespace Electron { * cancel the close. For example: Note: There is a subtle difference between the * behaviors of window.onbeforeunload = handler and * window.addEventListener('beforeunload', handler). It is recommended to always - * set the event.returnValue explicitly, instead of just returning a value, as the + * set the event.returnValue explicitly, instead of only returning a value, as the * former works more consistently within Electron. */ on(event: 'close', listener: (event: Event) => void): this; @@ -1107,7 +1182,7 @@ declare namespace Electron { removeListener(event: 'minimize', listener: Function): this; /** * Emitted when the window is being moved to a new position. Note: On macOS this - * event is just an alias of moved. + * event is an alias of moved. */ on(event: 'move', listener: Function): this; once(event: 'move', listener: Function): this; @@ -1274,7 +1349,7 @@ declare namespace Electron { * emitted. */ static getExtensions(): Extensions; - static getFocusedWindow(): BrowserWindow; + static getFocusedWindow(): BrowserWindow | null; /** * Remove a DevTools extension by name. Note: This API cannot be called before the * ready event of the app module is emitted. @@ -1439,6 +1514,10 @@ declare namespace Electron { * more than one tab in the current window. */ moveTabToNewWindow(): void; + /** + * Moves window to top(z-order) regardless of focus + */ + moveTop(): void; /** * Uses Quick Look to preview a file at a given path. */ @@ -1484,8 +1563,9 @@ declare namespace Electron { * ratio for HD @1920x1080) within the player itself we would call this function * with arguments of 16/9 and [ 40, 50 ]. The second argument doesn't care where * the extra width and height are within the content view--only that they exist. - * Just sum any extra width and height areas you have within the overall content - * view. + * Sum any extra width and height areas you have within the overall content view. + * Calling this function with a value of 0 will remove any previously set aspect + * ratios. */ setAspectRatio(aspectRatio: number, extraSize: Size): void; /** @@ -1602,7 +1682,7 @@ declare namespace Electron { * Sets a 16 x 16 pixel overlay onto the current taskbar icon, usually used to * convey some sort of application status or to passively notify the user. */ - setOverlayIcon(overlay: NativeImage, description: string): void; + setOverlayIcon(overlay: NativeImage | null, description: string): void; /** * Sets parent as current window's parent window, passing null will turn current * window into a top-level window. @@ -1632,6 +1712,14 @@ declare namespace Electron { * Sets whether the window can be manually resized by user. */ setResizable(resizable: boolean): void; + /** + * Setting a window shape determines the area within the window where the system + * permits drawing and user interaction. Outside of the given region, no pixels + * will be drawn and no mouse events will be registered. Mouse events outside of + * the region will not be received by that window, but will fall through to + * whatever is behind the window. + */ + setShape(rects: Rectangle[]): void; /** * Changes the attachment point for sheets on macOS. By default, sheets are * attached just below the window frame, but you may want to display them beneath a @@ -1644,7 +1732,8 @@ declare namespace Electron { */ setSimpleFullScreen(flag: boolean): void; /** - * Resizes the window to width and height. + * Resizes the window to width and height. If width or height are below any set + * minimum size constraints the window will snap to its minimum size. */ setSize(width: number, height: number, animate?: boolean): void; /** @@ -1962,6 +2051,11 @@ declare namespace Electron { */ followRedirect(): void; getHeader(name: string): Header; + /** + * You can use this method in conjunction with POST requests to get the progress of + * a file upload or other data transfer. + */ + getUploadProgress(): UploadProgress; /** * Removes a previously set extra header name. This method can be called only * before first write. Trying to call it after the first write will throw an error. @@ -2085,20 +2179,9 @@ declare namespace Electron { * Start recording on all processes. Recording begins immediately locally and * asynchronously on child processes as soon as they receive the EnableRecording * request. The callback will be called once all child processes have acknowledged - * the startRecording request. categoryFilter is a filter to control what category - * groups should be traced. A filter can have an optional - prefix to exclude - * category groups that contain a matching category. Having both included and - * excluded category patterns in the same list is not supported. Examples: - * traceOptions controls what kind of tracing is enabled, it is a comma-delimited - * list. Possible options are: The first 3 options are trace recording modes and - * hence mutually exclusive. If more than one trace recording modes appear in the - * traceOptions string, the last one takes precedence. If none of the trace - * recording modes are specified, recording mode is record-until-full. The trace - * option will first be reset to the default option (record_mode set to - * record-until-full, enable_sampling and enable_systrace set to false) before - * options parsed from traceOptions are applied on it. - */ - startRecording(options: StartRecordingOptions, callback: Function): void; + * the startRecording request. + */ + startRecording(options: TraceCategoriesAndOptions | TraceConfig, callback: Function): void; /** * Stop monitoring on all processes. Once all child processes have acknowledged the * stopMonitoring request the callback is called. @@ -2437,6 +2520,12 @@ declare namespace Electron { // Docs: http://electron.atom.io/docs/api/structures/desktop-capturer-source + /** + * A unique identifier that will correspond to the id of the matching returned by + * the . On some platforms, this is equivalent to the XX portion of the id field + * above and on others it will differ. It will be an empty string if not available. + */ + display_id: string; /** * The identifier of a window or screen that can be used as a chromeMediaSourceId * constraint when calling [navigator.webkitGetUserMedia]. The format of the @@ -2705,7 +2794,9 @@ declare namespace Electron { * registered shortcut is pressed by the user. When the accelerator is already * taken by other applications, this call will silently fail. This behavior is * intended by operating systems, since they don't want applications to fight for - * global shortcuts. + * global shortcuts. The following accelerators will not be registered successfully + * on macOS 10.14 Mojave unless the app has been authorized as a trusted + * accessibility client: */ register(accelerator: Accelerator, callback: Function): void; /** @@ -2785,26 +2876,42 @@ declare namespace Electron { */ on(event: 'transactions-updated', listener: (event: Event, /** - * Array of transactions. + * Array of objects. */ transactions: Transaction[]) => void): this; once(event: 'transactions-updated', listener: (event: Event, /** - * Array of transactions. + * Array of objects. */ transactions: Transaction[]) => void): this; addListener(event: 'transactions-updated', listener: (event: Event, /** - * Array of transactions. + * Array of objects. */ transactions: Transaction[]) => void): this; removeListener(event: 'transactions-updated', listener: (event: Event, /** - * Array of transactions. + * Array of objects. */ transactions: Transaction[]) => void): this; canMakePayments(): boolean; + /** + * Completes all pending transactions. + */ + finishAllTransactions(): void; + /** + * Completes the pending transactions corresponding to the date. + */ + finishTransactionByDate(date: string): void; + /** + * Retrieves the product descriptions. + */ + getProducts(productIDs: string[], callback: (products: Product[]) => void): void; getReceiptURL(): string; + /** + * You should listen for the transactions-updated event as soon as possible and + * certainly before you call purchaseProduct. + */ purchaseProduct(productID: string, quantity?: number, callback?: (isProductValid: boolean) => void): void; } @@ -2964,9 +3071,9 @@ declare namespace Electron { */ // sendSync(channel: string, ...args: any[]): any; ### VSCODE CHANGE (we do not want to use sendSync) /** - * Sends a message to a window with windowid via channel. + * Sends a message to a window with webContentsId via channel. */ - sendTo(windowId: number, channel: string, ...args: any[]): void; + sendTo(webContentsId: number, channel: string, ...args: any[]): void; /** * Like ipcRenderer.send but the event will be sent to the element in the * host page instead of the main process. @@ -3097,10 +3204,9 @@ declare namespace Electron { removeListener(event: 'menu-will-show', listener: (event: Event) => void): this; constructor(); /** - * Generally, the template is just an array of options for constructing a MenuItem. - * The usage can be referenced above. You can also attach other fields to the - * element of the template and they will become properties of the constructed menu - * items. + * Generally, the template is an array of options for constructing a MenuItem. The + * usage can be referenced above. You can also attach other fields to the element + * of the template and they will become properties of the constructed menu items. */ static buildFromTemplate(template: MenuItemConstructorOptions[]): Menu; /** @@ -3110,9 +3216,9 @@ declare namespace Electron { static getApplicationMenu(): Menu | null; /** * Sends the action to the first responder of application. This is used for - * emulating default macOS menu behaviors. Usually you would just use the role - * property of a MenuItem. See the macOS Cocoa Event Handling Guide for more - * information on macOS' native actions. + * emulating default macOS menu behaviors. Usually you would use the role property + * of a MenuItem. See the macOS Cocoa Event Handling Guide for more information on + * macOS' native actions. */ static sendActionToFirstResponder(action: string): void; /** @@ -3248,6 +3354,29 @@ declare namespace Electron { request(options: any | string): ClientRequest; } + interface NetLog extends EventEmitter { + + // Docs: http://electron.atom.io/docs/api/net-log + + /** + * Starts recording network events to path. + */ + startLogging(path: string): void; + /** + * Stops recording network events. If not called, net logging will automatically + * end when app quits. + */ + stopLogging(callback?: (path: string) => void): void; + /** + * A Boolean property that indicates whether network logs are recorded. + */ + currentlyLogging?: boolean; + /** + * A String property that returns the path to the current log file. + */ + currentlyLoggingPath?: string; + } + class Notification extends EventEmitter { // Docs: http://electron.atom.io/docs/api/notification @@ -3329,11 +3458,11 @@ declare namespace Electron { close(): void; /** * Immediately shows the notification to the user, please note this means unlike - * the HTML5 Notification implementation, simply instantiating a new Notification - * does not immediately show it to the user, you need to call this method before - * the OS will display it. If the notification has been shown before, this method - * will dismiss the previously shown notification and create a new one with - * identical properties. + * the HTML5 Notification implementation, instantiating a new Notification does not + * immediately show it to the user, you need to call this method before the OS will + * display it. If the notification has been shown before, this method will dismiss + * the previously shown notification and create a new one with identical + * properties. */ show(): void; } @@ -3364,6 +3493,13 @@ declare namespace Electron { // Docs: http://electron.atom.io/docs/api/power-monitor + /** + * Emitted when the system is about to lock the screen. + */ + on(event: 'lock-screen', listener: Function): this; + once(event: 'lock-screen', listener: Function): this; + addListener(event: 'lock-screen', listener: Function): this; + removeListener(event: 'lock-screen', listener: Function): this; /** * Emitted when the system changes to AC power. */ @@ -3402,6 +3538,13 @@ declare namespace Electron { once(event: 'suspend', listener: Function): this; addListener(event: 'suspend', listener: Function): this; removeListener(event: 'suspend', listener: Function): this; + /** + * Emitted as soon as the systems screen is unlocked. + */ + on(event: 'unlock-screen', listener: Function): this; + once(event: 'unlock-screen', listener: Function): this; + addListener(event: 'unlock-screen', listener: Function): this; + removeListener(event: 'unlock-screen', listener: Function): this; } interface PowerSaveBlocker extends EventEmitter { @@ -3458,6 +3601,45 @@ declare namespace Electron { type: string; } + interface Product { + + // Docs: http://electron.atom.io/docs/api/structures/product + + /** + * The total size of the content, in bytes. + */ + contentLengths: number[]; + /** + * A string that identifies the version of the content. + */ + contentVersion: string; + /** + * A Boolean value that indicates whether the App Store has downloadable content + * for this product. + */ + downloadable: boolean; + /** + * The locale formatted price of the product. + */ + formattedPrice: string; + /** + * A description of the product. + */ + localizedDescription: string; + /** + * The name of the product. + */ + localizedTitle: string; + /** + * The cost of the product in the local currency. + */ + price: number; + /** + * The string that identifies the product to the Apple App Store. + */ + productIdentifier: string; + } + interface Protocol extends EventEmitter { // Docs: http://electron.atom.io/docs/api/protocol @@ -3590,11 +3772,32 @@ declare namespace Electron { y: number; } + interface Referrer { + + // Docs: http://electron.atom.io/docs/api/structures/referrer + + /** + * Can be default, unsafe-url, no-referrer-when-downgrade, no-referrer, origin, + * strict-origin-when-cross-origin, same-origin or strict-origin. See the for more + * details on the meaning of these values. + */ + policy: ('default' | 'unsafe-url' | 'no-referrer-when-downgrade' | 'no-referrer' | 'origin' | 'strict-origin-when-cross-origin' | 'same-origin' | 'strict-origin'); + /** + * HTTP Referrer URL. + */ + url: string; + } + interface Remote extends MainInterface { // Docs: http://electron.atom.io/docs/api/remote getCurrentWebContents(): WebContents; + /** + * Note: Do not use removeAllListeners on BrowserWindow. Use of this can remove all + * blur listeners, disable click events on touch bar buttons, and other unintended + * consequences. + */ getCurrentWindow(): BrowserWindow; getGlobal(name: string): any; /** @@ -3698,6 +3901,17 @@ declare namespace Electron { oldDisplay: Display) => void): this; removeListener(event: 'display-removed', listener: (event: Event, oldDisplay: Display) => void): this; + /** + * Converts a screen DIP point to a screen physical point. The DPI scale is + * performed relative to the display containing the DIP point. + */ + dipToScreenPoint(point: Point): Point; + /** + * Converts a screen DIP rect to a screen physical rect. The DPI scale is performed + * relative to the display nearest to window. If window is null, scaling will be + * performed to the display nearest to rect. + */ + dipToScreenRect(window: BrowserWindow | null, rect: Rectangle): Rectangle; getAllDisplays(): Display[]; /** * The current absolute position of the mouse pointer. @@ -3705,8 +3919,18 @@ declare namespace Electron { getCursorScreenPoint(): Point; getDisplayMatching(rect: Rectangle): Display; getDisplayNearestPoint(point: Point): Display; - getMenuBarHeight(): number; getPrimaryDisplay(): Display; + /** + * Converts a screen physical point to a screen DIP point. The DPI scale is + * performed relative to the display containing the physical point. + */ + screenToDipPoint(point: Point): Point; + /** + * Converts a screen physical rect to a screen DIP rect. The DPI scale is performed + * relative to the display nearest to window. If window is null, scaling will be + * performed to the display nearest to rect. + */ + screenToDipRect(window: BrowserWindow | null, rect: Rectangle): Rectangle; } interface ScrubberItem { @@ -3869,6 +4093,7 @@ declare namespace Electron { */ setUserAgent(userAgent: string, acceptLanguages?: string): void; cookies: Cookies; + netLog: NetLog; protocol: Protocol; webRequest: WebRequest; } @@ -4051,6 +4276,11 @@ declare namespace Electron { * contains the user information dictionary sent along with the notification. */ postNotification(event: string, userInfo: any): void; + /** + * Posts event as native notifications of macOS. The userInfo is an Object that + * contains the user information dictionary sent along with the notification. + */ + postWorkspaceNotification(event: string, userInfo: any): void; /** * Add the specified defaults to your application's NSUserDefaults. */ @@ -4069,7 +4299,7 @@ declare namespace Electron { * Same as subscribeNotification, but uses NSNotificationCenter for local defaults. * This is necessary for events such as NSUserDefaultsDidChangeNotification. */ - subscribeLocalNotification(event: string, callback: (event: string, userInfo: any) => void): void; + subscribeLocalNotification(event: string, callback: (event: string, userInfo: any) => void): number; /** * Subscribes to native notifications of macOS, callback will be called with * callback(event, userInfo) when the corresponding event happens. The userInfo is @@ -4078,7 +4308,13 @@ declare namespace Electron { * unsubscribe the event. Under the hood this API subscribes to * NSDistributedNotificationCenter, example values of event are: */ - subscribeNotification(event: string, callback: (event: string, userInfo: any) => void): void; + subscribeNotification(event: string, callback: (event: string, userInfo: any) => void): number; + /** + * Same as subscribeNotification, but uses + * NSWorkspace.sharedWorkspace.notificationCenter. This is necessary for events + * such as NSWorkspaceDidActivateApplicationNotification. + */ + subscribeWorkspaceNotification(event: string, callback: (event: string, userInfo: any) => void): void; /** * Same as unsubscribeNotification, but removes the subscriber from * NSNotificationCenter. @@ -4088,6 +4324,11 @@ declare namespace Electron { * Removes the subscriber with id. */ unsubscribeNotification(id: number): void; + /** + * Same as unsubscribeNotification, but removes the subscriber from + * NSWorkspace.sharedWorkspace.notificationCenter. + */ + unsubscribeWorkspaceNotification(id: number): void; } interface Task { @@ -4247,21 +4488,72 @@ declare namespace Electron { static TouchBarSpacer: typeof TouchBarSpacer; } + interface TraceCategoriesAndOptions { + + // Docs: http://electron.atom.io/docs/api/structures/trace-categories-and-options + + /** + * – is a filter to control what category groups should be traced. A filter can + * have an optional prefix to exclude category groups that contain a matching + * category. Having both included and excluded category patterns in the same list + * is not supported. Examples: test_MyTest*, test_MyTest*,test_OtherStuff, + * -excluded_category1,-excluded_category2. + */ + categoryFilter: string; + /** + * Controls what kind of tracing is enabled, it is a comma-delimited sequence of + * the following strings: record-until-full, record-continuously, trace-to-console, + * enable-sampling, enable-systrace, e.g. 'record-until-full,enable-sampling'. The + * first 3 options are trace recording modes and hence mutually exclusive. If more + * than one trace recording modes appear in the traceOptions string, the last one + * takes precedence. If none of the trace recording modes are specified, recording + * mode is record-until-full. The trace option will first be reset to the default + * option (record_mode set to record-until-full, enable_sampling and + * enable_systrace set to false) before options parsed from traceOptions are + * applied on it. + */ + traceOptions: string; + } + + interface TraceConfig { + + // Docs: http://electron.atom.io/docs/api/structures/trace-config + + excluded_categories?: string[]; + included_categories?: string[]; + memory_dump_config?: MemoryDumpConfig; + } + interface Transaction { // Docs: http://electron.atom.io/docs/api/structures/transaction + /** + * The error code if an error occurred while processing the transaction. + */ errorCode: number; + /** + * The error message if an error occurred while processing the transaction. + */ errorMessage: string; + /** + * The identifier of the restored transaction by the App Store. + */ originalTransactionIdentifier: string; payment: Payment; + /** + * The date the transaction was added to the App Store’s payment queue. + */ transactionDate: string; + /** + * A string that uniquely identifies a successful payment transaction. + */ transactionIdentifier: string; /** - * The transaction sate ("purchasing", "purchased", "failed", "restored", or - * "deferred") + * The transaction state, can be purchasing, purchased, failed, restored or + * deferred. */ - transactionState: string; + transactionState: ('purchasing' | 'purchased' | 'failed' | 'restored' | 'deferred'); } class Tray extends EventEmitter { @@ -4531,6 +4823,7 @@ declare namespace Electron { * The bounds of this tray icon as Object. */ getBounds(): Rectangle; + getIgnoreDoubleClickEvents(): boolean; isDestroyed(): boolean; /** * Pops up the context menu of the tray icon. When menu is passed, the menu will be @@ -4541,13 +4834,19 @@ declare namespace Electron { /** * Sets the context menu for this icon. */ - setContextMenu(menu: Menu): void; + setContextMenu(menu: Menu | null): void; /** * Sets when the tray's icon background becomes highlighted (in blue). Note: You * can use highlightMode with a BrowserWindow by toggling between 'never' and * 'always' modes when the window visibility changes. */ setHighlightMode(mode: 'selection' | 'always' | 'never'): void; + /** + * Sets the option to ignore double click events. Ignoring these events allows you + * to detect every individual click of the tray icon. This value is set to false by + * default. + */ + setIgnoreDoubleClickEvents(ignore: boolean): void; /** * Sets the image associated with this tray icon. */ @@ -4555,7 +4854,7 @@ declare namespace Electron { /** * Sets the image associated with this tray icon when pressed on macOS. */ - setPressedImage(image: NativeImage): void; + setPressedImage(image: NativeImage | string): void; /** * Sets the title displayed aside of the tray icon in the status bar (Support ANSI * colors). @@ -4625,32 +4924,6 @@ declare namespace Electron { type: string; } - interface UploadFileSystem { - - // Docs: http://electron.atom.io/docs/api/structures/upload-file-system - - /** - * FileSystem url to read data for upload. - */ - filsSystemURL: string; - /** - * Number of bytes to read from offset. Defaults to 0. - */ - length: number; - /** - * Last Modification time in number of seconds since the UNIX epoch. - */ - modificationTime: number; - /** - * Defaults to 0. - */ - offset: number; - /** - * fileSystem. - */ - type: string; - } - interface UploadRawData { // Docs: http://electron.atom.io/docs/api/structures/upload-raw-data @@ -4737,19 +5010,23 @@ declare namespace Electron { * Emitted when the associated window logs a console message. Will not be emitted * for windows with offscreen rendering enabled. */ - on(event: 'console-message', listener: (level: number, + on(event: 'console-message', listener: (event: Event, + level: number, message: string, line: number, sourceId: string) => void): this; - once(event: 'console-message', listener: (level: number, + once(event: 'console-message', listener: (event: Event, + level: number, message: string, line: number, sourceId: string) => void): this; - addListener(event: 'console-message', listener: (level: number, + addListener(event: 'console-message', listener: (event: Event, + level: number, message: string, line: number, sourceId: string) => void): this; - removeListener(event: 'console-message', listener: (level: number, + removeListener(event: 'console-message', listener: (event: Event, + level: number, message: string, line: number, sourceId: string) => void): this; @@ -4938,22 +5215,30 @@ declare namespace Electron { errorCode: number, errorDescription: string, validatedURL: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; once(event: 'did-fail-load', listener: (event: Event, errorCode: number, errorDescription: string, validatedURL: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; addListener(event: 'did-fail-load', listener: (event: Event, errorCode: number, errorDescription: string, validatedURL: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; removeListener(event: 'did-fail-load', listener: (event: Event, errorCode: number, errorDescription: string, validatedURL: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** * Emitted when the navigation is done, i.e. the spinner of the tab has stopped * spinning, and the onload event was dispatched. @@ -4966,119 +5251,149 @@ declare namespace Electron { * Emitted when a frame has done navigation. */ on(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; once(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; addListener(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; removeListener(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** - * Emitted when a redirect is received while requesting a resource. + * Emitted when any frame navigation is done. This event is not emitted for in-page + * navigations, such as clicking anchor links or updating the window.location.hash. + * Use did-navigate-in-page event for this purpose. */ - on(event: 'did-get-redirect-request', listener: (event: Event, - oldURL: string, - newURL: string, - isMainFrame: boolean, + on(event: 'did-frame-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any) => void): this; - once(event: 'did-get-redirect-request', listener: (event: Event, - oldURL: string, - newURL: string, + /** + * empty for non HTTP navigations, + */ + httpStatusText: string, isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + once(event: 'did-frame-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any) => void): this; - addListener(event: 'did-get-redirect-request', listener: (event: Event, - oldURL: string, - newURL: string, + /** + * empty for non HTTP navigations, + */ + httpStatusText: string, isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + addListener(event: 'did-frame-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any) => void): this; - removeListener(event: 'did-get-redirect-request', listener: (event: Event, - oldURL: string, - newURL: string, + /** + * empty for non HTTP navigations, + */ + httpStatusText: string, isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + removeListener(event: 'did-frame-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any) => void): this; - /** - * Emitted when details regarding a requested resource are available. status - * indicates the socket connection to download the resource. - */ - on(event: 'did-get-response-details', listener: (event: Event, - status: boolean, - newURL: string, - originalURL: string, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any, - resourceType: string) => void): this; - once(event: 'did-get-response-details', listener: (event: Event, - status: boolean, - newURL: string, - originalURL: string, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any, - resourceType: string) => void): this; - addListener(event: 'did-get-response-details', listener: (event: Event, - status: boolean, - newURL: string, - originalURL: string, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any, - resourceType: string) => void): this; - removeListener(event: 'did-get-response-details', listener: (event: Event, - status: boolean, - newURL: string, - originalURL: string, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any, - resourceType: string) => void): this; + /** + * empty for non HTTP navigations, + */ + httpStatusText: string, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** - * Emitted when a navigation is done. This event is not emitted for in-page - * navigations, such as clicking anchor links or updating the window.location.hash. - * Use did-navigate-in-page event for this purpose. + * Emitted when a main frame navigation is done. This event is not emitted for + * in-page navigations, such as clicking anchor links or updating the + * window.location.hash. Use did-navigate-in-page event for this purpose. */ on(event: 'did-navigate', listener: (event: Event, - url: string) => void): this; + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations + */ + httpStatusText: string) => void): this; once(event: 'did-navigate', listener: (event: Event, - url: string) => void): this; + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations + */ + httpStatusText: string) => void): this; addListener(event: 'did-navigate', listener: (event: Event, - url: string) => void): this; + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations + */ + httpStatusText: string) => void): this; removeListener(event: 'did-navigate', listener: (event: Event, - url: string) => void): this; + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations + */ + httpStatusText: string) => void): this; /** - * Emitted when an in-page navigation happened. When in-page navigation happens, - * the page URL changes but does not cause navigation outside of the page. Examples - * of this occurring are when anchor links are clicked or when the DOM hashchange - * event is triggered. + * Emitted when an in-page navigation happened in any frame. When in-page + * navigation happens, the page URL changes but does not cause navigation outside + * of the page. Examples of this occurring are when anchor links are clicked or + * when the DOM hashchange event is triggered. */ on(event: 'did-navigate-in-page', listener: (event: Event, url: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; once(event: 'did-navigate-in-page', listener: (event: Event, url: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; addListener(event: 'did-navigate-in-page', listener: (event: Event, url: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; removeListener(event: 'did-navigate-in-page', listener: (event: Event, url: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** * Corresponds to the points in time when the spinner of the tab started spinning. */ @@ -5086,6 +5401,30 @@ declare namespace Electron { once(event: 'did-start-loading', listener: Function): this; addListener(event: 'did-start-loading', listener: Function): this; removeListener(event: 'did-start-loading', listener: Function): this; + /** + * Emitted when any frame (including main) starts navigating. isInplace will be + * true for in-page navigations. + */ + on(event: 'did-start-navigation', listener: (url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + once(event: 'did-start-navigation', listener: (url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + addListener(event: 'did-start-navigation', listener: (url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + removeListener(event: 'did-start-navigation', listener: (url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** * Corresponds to the points in time when the spinner of the tab stopped spinning. */ @@ -5171,7 +5510,12 @@ declare namespace Electron { * The non-standard features (features not handled by Chromium or Electron) given * to `window.open()`. */ - additionalFeatures: string[]) => void): this; + additionalFeatures: string[], + /** + * The referrer that will be passed to the new window. May or may not result in the + * `Referer` header being sent, depending on the referrer policy. + */ + referrer: Referrer) => void): this; once(event: 'new-window', listener: (event: Event, url: string, frameName: string, @@ -5188,7 +5532,12 @@ declare namespace Electron { * The non-standard features (features not handled by Chromium or Electron) given * to `window.open()`. */ - additionalFeatures: string[]) => void): this; + additionalFeatures: string[], + /** + * The referrer that will be passed to the new window. May or may not result in the + * `Referer` header being sent, depending on the referrer policy. + */ + referrer: Referrer) => void): this; addListener(event: 'new-window', listener: (event: Event, url: string, frameName: string, @@ -5205,7 +5554,12 @@ declare namespace Electron { * The non-standard features (features not handled by Chromium or Electron) given * to `window.open()`. */ - additionalFeatures: string[]) => void): this; + additionalFeatures: string[], + /** + * The referrer that will be passed to the new window. May or may not result in the + * `Referer` header being sent, depending on the referrer policy. + */ + referrer: Referrer) => void): this; removeListener(event: 'new-window', listener: (event: Event, url: string, frameName: string, @@ -5222,7 +5576,12 @@ declare namespace Electron { * The non-standard features (features not handled by Chromium or Electron) given * to `window.open()`. */ - additionalFeatures: string[]) => void): this; + additionalFeatures: string[], + /** + * The referrer that will be passed to the new window. May or may not result in the + * `Referer` header being sent, depending on the referrer policy. + */ + referrer: Referrer) => void): this; /** * Emitted when page receives favicon urls. */ @@ -5289,6 +5648,13 @@ declare namespace Electron { removeListener(event: 'plugin-crashed', listener: (event: Event, name: string, version: string) => void): this; + /** + * Emitted when the unresponsive web page becomes responsive again. + */ + on(event: 'responsive', listener: Function): this; + once(event: 'responsive', listener: Function): this; + addListener(event: 'responsive', listener: Function): this; + removeListener(event: 'responsive', listener: Function): this; /** * Emitted when bluetooth device needs to be selected on call to * navigator.bluetooth.requestDevice. To use navigator.bluetooth api webBluetooth @@ -5328,6 +5694,13 @@ declare namespace Electron { url: string, certificateList: Certificate[], callback: (certificate: Certificate) => void) => void): this; + /** + * Emitted when the web page becomes unresponsive. + */ + on(event: 'unresponsive', listener: Function): this; + once(event: 'unresponsive', listener: Function): this; + addListener(event: 'unresponsive', listener: Function): this; + removeListener(event: 'unresponsive', listener: Function): this; /** * Emitted when mouse moves over a link or the keyboard moves the focus to a link. */ @@ -5424,30 +5797,22 @@ declare namespace Electron { addWorkSpace(path: string): void; /** * Begin subscribing for presentation events and captured frames, the callback will - * be called with callback(frameBuffer, dirtyRect) when there is a presentation - * event. The frameBuffer is a Buffer that contains raw pixel data. On most - * machines, the pixel data is effectively stored in 32bit BGRA format, but the - * actual representation depends on the endianness of the processor (most modern - * processors are little-endian, on machines with big-endian processors the data is - * in 32bit ARGB format). The dirtyRect is an object with x, y, width, height - * properties that describes which part of the page was repainted. If onlyDirty is - * set to true, frameBuffer will only contain the repainted area. onlyDirty - * defaults to false. - */ - beginFrameSubscription(callback: (frameBuffer: Buffer, dirtyRect: Rectangle) => void): void; + * be called with callback(image, dirtyRect) when there is a presentation event. + * The image is an instance of NativeImage that stores the captured frame. The + * dirtyRect is an object with x, y, width, height properties that describes which + * part of the page was repainted. If onlyDirty is set to true, image will only + * contain the repainted area. onlyDirty defaults to false. + */ + beginFrameSubscription(callback: (image: NativeImage, dirtyRect: Rectangle) => void): void; /** * Begin subscribing for presentation events and captured frames, the callback will - * be called with callback(frameBuffer, dirtyRect) when there is a presentation - * event. The frameBuffer is a Buffer that contains raw pixel data. On most - * machines, the pixel data is effectively stored in 32bit BGRA format, but the - * actual representation depends on the endianness of the processor (most modern - * processors are little-endian, on machines with big-endian processors the data is - * in 32bit ARGB format). The dirtyRect is an object with x, y, width, height - * properties that describes which part of the page was repainted. If onlyDirty is - * set to true, frameBuffer will only contain the repainted area. onlyDirty - * defaults to false. - */ - beginFrameSubscription(onlyDirty: boolean, callback: (frameBuffer: Buffer, dirtyRect: Rectangle) => void): void; + * be called with callback(image, dirtyRect) when there is a presentation event. + * The image is an instance of NativeImage that stores the captured frame. The + * dirtyRect is an object with x, y, width, height properties that describes which + * part of the page was repainted. If onlyDirty is set to true, image will only + * contain the repainted area. onlyDirty defaults to false. + */ + beginFrameSubscription(onlyDirty: boolean, callback: (image: NativeImage, dirtyRect: Rectangle) => void): void; canGoBack(): boolean; canGoForward(): boolean; canGoToOffset(offset: number): boolean; @@ -5456,13 +5821,13 @@ declare namespace Electron { * called with callback(image). The image is an instance of NativeImage that stores * data of the snapshot. Omitting rect will capture the whole visible page. */ - capturePage(callback: (image: NativeImage) => void): void; + capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; /** * Captures a snapshot of the page within rect. Upon completion callback will be * called with callback(image). The image is an instance of NativeImage that stores * data of the snapshot. Omitting rect will capture the whole visible page. */ - capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + capturePage(callback: (image: NativeImage) => void): void; /** * Clears the navigation history. */ @@ -5528,6 +5893,7 @@ declare namespace Electron { * Get the system printer list. */ getPrinters(): PrinterInfo[]; + getProcessId(): number; getTitle(): string; getURL(): string; getUserAgent(): string; @@ -5714,10 +6080,6 @@ declare namespace Electron { * Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. */ setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Set the size of the page. This is only supported for guest contents. - */ - setSize(options: SizeOptions): void; /** * Overrides the user agent for this web page. */ @@ -5817,6 +6179,9 @@ declare namespace Electron { * Work like executeJavaScript but evaluates scripts in isolated context. */ executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean, callback?: (result: any) => void): void; + findFrameByName(name: string): WebFrame; + findFrameByRoutingId(routingId: number): WebFrame; + getFrameForSelector(selector: string): WebFrame; /** * Returns an object describing usage information of Blink's internal memory * caches. This will generate: @@ -5840,12 +6205,6 @@ declare namespace Electron { * privileged scheme, without bypassing Content Security Policy: */ registerURLSchemeAsPrivileged(scheme: string, options?: RegisterURLSchemeAsPrivilegedOptions): void; - /** - * Registers the scheme as secure scheme. Secure schemes do not trigger mixed - * content warnings. For example, https and data are secure schemes because they - * cannot be corrupted by active network attackers. - */ - registerURLSchemeAsSecure(scheme: string): void; /** * Set the content security policy of the isolated world. */ @@ -5883,6 +6242,39 @@ declare namespace Electron { * limits of 300% and 50% of original size, respectively. */ setZoomLevel(level: number): void; + /** + * A WebFrame representing the first child frame of webFrame, the property would be + * null if webFrame has no children or if first child is not in the current + * renderer process. + */ + firstChild?: WebFrame; + /** + * A WebFrame representing next sibling frame, the property would be null if + * webFrame is the last frame in its parent or if the next sibling is not in the + * current renderer process. + */ + nextSibling?: WebFrame; + /** + * A WebFrame representing the frame which opened webFrame, the property would be + * null if there's no opener or opener is not in the current renderer process. + */ + opener?: WebFrame; + /** + * A WebFrame representing parent frame of webFrame, the property would be null if + * webFrame is top or parent is not in the current renderer process. + */ + parent?: WebFrame; + /** + * An Integer representing the unique frame id in the current renderer process. + * Distinct WebFrame instances that refer to the same underlying frame will have + * the same routingId. + */ + routingId?: number; + /** + * A WebFrame representing top frame in frame hierarchy to which webFrame belongs, + * the property would be null if top frame is not in the current renderer process. + */ + top?: WebFrame; } class WebRequest extends EventEmitter { @@ -5917,14 +6309,14 @@ declare namespace Electron { * connection is made to the server, but before any http data is sent. The callback * has to be called with an response object. */ - onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: Function): void; + onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: (details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void): void; /** * The listener will be called with listener(details, callback) before sending an * HTTP request, once the request headers are available. This may occur after a TCP * connection is made to the server, but before any http data is sent. The callback * has to be called with an response object. */ - onBeforeSendHeaders(listener: Function): void; + onBeforeSendHeaders(listener: (details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void): void; /** * The listener will be called with listener(details) when a request is completed. */ @@ -5946,13 +6338,13 @@ declare namespace Electron { * headers of a request have been received. The callback has to be called with an * response object. */ - onHeadersReceived(filter: OnHeadersReceivedFilter, listener: Function): void; + onHeadersReceived(filter: OnHeadersReceivedFilter, listener: (details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void): void; /** * The listener will be called with listener(details, callback) when HTTP response * headers of a request have been received. The callback has to be called with an * response object. */ - onHeadersReceived(listener: Function): void; + onHeadersReceived(listener: (details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void): void; /** * The listener will be called with listener(details) when first byte of the * response body is received. For HTTP requests, this means that the status line @@ -6029,17 +6421,6 @@ declare namespace Electron { */ addEventListener(event: 'did-stop-loading', listener: (event: Event) => void, useCapture?: boolean): this; removeEventListener(event: 'did-stop-loading', listener: (event: Event) => void): this; - /** - * Fired when details regarding a requested resource is available. status indicates - * socket connection to download the resource. - */ - addEventListener(event: 'did-get-response-details', listener: (event: DidGetResponseDetailsEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-get-response-details', listener: (event: DidGetResponseDetailsEvent) => void): this; - /** - * Fired when a redirect was received while requesting a resource. - */ - addEventListener(event: 'did-get-redirect-request', listener: (event: DidGetRedirectRequestEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-get-redirect-request', listener: (event: DidGetRedirectRequestEvent) => void): this; /** * Fired when document in the given frame is loaded. */ @@ -6118,8 +6499,8 @@ declare namespace Electron { removeEventListener(event: 'close', listener: (event: Event) => void): this; /** * Fired when the guest page has sent an asynchronous message to embedder page. - * With sendToHost method and ipc-message event you can easily communicate between - * guest page and embedder page: + * With sendToHost method and ipc-message event you can communicate between guest + * page and embedder page: */ addEventListener(event: 'ipc-message', listener: (event: IpcMessageEvent) => void, useCapture?: boolean): this; removeEventListener(event: 'ipc-message', listener: (event: IpcMessageEvent) => void): this; @@ -6383,40 +6764,23 @@ declare namespace Electron { * than the minimum values or greater than the maximum. */ autosize?: string; - /** - * A list of strings which specifies the blink features to be enabled separated by - * ,. The full list of supported feature strings can be found in the - * RuntimeEnabledFeatures.json5 file. - */ - blinkfeatures?: string; /** * A list of strings which specifies the blink features to be disabled separated by * ,. The full list of supported feature strings can be found in the * RuntimeEnabledFeatures.json5 file. */ disableblinkfeatures?: string; - /** - * When this attribute is present the webview contents will be prevented from - * resizing when the webview element itself is resized. This can be used in - * combination with webContents.setSize to manually resize the webview contents in - * reaction to a window size change. This can make resizing faster compared to - * relying on the webview element bounds to automatically resize the contents. - */ - disableguestresize?: string; /** * When this attribute is present the guest page will have web security disabled. * Web security is enabled by default. */ // disablewebsecurity?: string; ### VSCODE CHANGE(https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### /** - * A value that links the webview to a specific webContents. When a webview first - * loads a new webContents is created and this attribute is set to its instance - * identifier. Setting this attribute on a new or existing webview connects it to - * the existing webContents that currently renders in a different webview. The - * existing webview will see the destroy event and will then create a new - * webContents when a new url is loaded. + * A list of strings which specifies the blink features to be enabled separated by + * ,. The full list of supported feature strings can be found in the + * RuntimeEnabledFeatures.json5 file. */ - guestinstance?: string; + enableblinkfeatures?: string; /** * Sets the referrer URL for the guest page. */ @@ -6727,7 +7091,9 @@ declare namespace Electron { enableLargerThanScreen?: boolean; /** * Window's background color as a hexadecimal value, like #66CD00 or #FFF or - * #80FFFFFF (alpha is supported). Default is #FFF (white). + * #80FFFFFF (alpha is supported). Default is #FFF (white). If transparent is set + * to true, only values with transparent (#00-------) or opaque (#FF-----) alpha + * values are respected. */ backgroundColor?: string; /** @@ -6827,7 +7193,7 @@ declare namespace Electron { origin?: string; /** * The types of storages to clear, can contain: appcache, cookies, filesystem, - * indexdb, localstorage, shadercache, websql, serviceworkers. + * indexdb, localstorage, shadercache, websql, serviceworkers, cachestorage. */ storages?: string[]; /** @@ -7097,23 +7463,6 @@ declare namespace Electron { isMainFrame: boolean; } - interface DidGetRedirectRequestEvent extends Event { - oldURL: string; - newURL: string; - isMainFrame: boolean; - } - - interface DidGetResponseDetailsEvent extends Event { - status: boolean; - newURL: string; - originalURL: string; - httpResponseCode: number; - requestMethod: string; - referrer: string; - headers: Headers; - resourceType: string; - } - interface DidNavigateEvent extends Event { url: string; } @@ -7284,6 +7633,18 @@ declare namespace Electron { interface Headers { } + interface HeapStatistics { + totalHeapSize: number; + totalHeapSizeExecutable: number; + totalPhysicalSize: number; + totalAvailableSize: number; + usedHeapSize: number; + heapSizeLimit: number; + mallocedMemory: number; + peakMallocedMemory: number; + doesZapGarbage: boolean; + } + interface IgnoreMouseEventsOptions { /** * If true, forwards mouse move messages to Chromium, enabling mouse related events @@ -7413,9 +7774,9 @@ declare namespace Electron { interface LoadURLOptions { /** - * A HTTP Referrer url. + * An HTTP Referrer url. */ - httpReferrer?: string; + httpReferrer?: string | Referrer; /** * A user agent originating the request. */ @@ -7424,10 +7785,7 @@ declare namespace Electron { * Extra headers separated by "\n" */ extraHeaders?: string; - /** - * - - */ - postData?: UploadRawData[] | UploadFile[] | UploadFileSystem[] | UploadBlob[]; + postData?: UploadRawData[] | UploadFile[] | UploadBlob[]; /** * Base url (with trailing path separator) for files to be loaded by the data url. * This is needed only if the specified url is a data url and needs to load other @@ -7477,6 +7835,9 @@ declare namespace Electron { args?: string[]; } + interface MemoryDumpConfig { + } + interface MenuItemConstructorOptions { /** * Will be called with click(menuItem, browserWindow, event) when the menu item is @@ -7695,6 +8056,16 @@ declare namespace Electron { urls: string[]; } + interface OnBeforeSendHeadersDetails { + id: number; + url: string; + method: string; + webContentsId?: number; + resourceType: string; + timestamp: number; + requestHeaders: RequestHeaders; + } + interface OnBeforeSendHeadersFilter { /** * Array of URL patterns that will be used to filter out the requests that do not @@ -7703,6 +8074,14 @@ declare namespace Electron { urls: string[]; } + interface OnBeforeSendHeadersResponse { + cancel?: boolean; + /** + * When provided, request will be made with these headers. + */ + requestHeaders?: RequestHeaders; + } + interface OnCompletedDetails { id: number; url: string; @@ -7746,6 +8125,18 @@ declare namespace Electron { urls: string[]; } + interface OnHeadersReceivedDetails { + id: number; + url: string; + method: string; + webContentsId?: number; + resourceType: string; + timestamp: number; + statusLine: string; + statusCode: number; + responseHeaders: ResponseHeaders; + } + interface OnHeadersReceivedFilter { /** * Array of URL patterns that will be used to filter out the requests that do not @@ -7754,6 +8145,19 @@ declare namespace Electron { urls: string[]; } + interface OnHeadersReceivedResponse { + cancel: boolean; + /** + * When provided, the server is assumed to have responded with these headers. + */ + responseHeaders?: ResponseHeaders; + /** + * Should be provided when overriding responseHeaders to change header status + * otherwise original response header's status will be used. + */ + statusLine?: string; + } + interface OnResponseStartedDetails { id: number; url: string; @@ -7879,7 +8283,13 @@ declare namespace Electron { } interface Payment { + /** + * The identifier of the purchased product. + */ productIdentifier: string; + /** + * The quantity purchased. + */ quantity: number; } @@ -7944,7 +8354,7 @@ declare namespace Electron { * Specify page size of the generated PDF. Can be A3, A4, A5, Legal, Letter, * Tabloid or an Object containing height and width in microns. */ - pageSize?: string; + pageSize?: string | Size; /** * Whether to print CSS backgrounds. */ @@ -8104,6 +8514,7 @@ declare namespace Electron { interface ResourceUsage { images: MemoryUsageDetails; + scripts: MemoryUsageDetails; cssStyleSheets: MemoryUsageDetails; xslStyleSheets: MemoryUsageDetails; fonts: MemoryUsageDetails; @@ -8176,7 +8587,7 @@ declare namespace Electron { /** * true to open the app as hidden. Defaults to false. The user can edit this * setting from the System Preferences so - * app.getLoginItemStatus().wasOpenedAsHidden should be checked when the app is + * app.getLoginItemSettings().wasOpenedAsHidden should be checked when the app is * opened to know the current value. This setting is not available on . */ openAsHidden?: boolean; @@ -8191,29 +8602,6 @@ declare namespace Electron { args?: string[]; } - interface SizeOptions { - /** - * true to make the webview container automatically resize within the bounds - * specified by the attributes normal, min and max. - */ - enableAutoSize?: boolean; - /** - * Normal size of the page. This can be used in combination with the attribute to - * manually resize the webview guest contents. - */ - normal?: Size; - /** - * Minimum size of the page. This can be used in combination with the attribute to - * manually resize the webview guest contents. - */ - min?: Size; - /** - * Maximium size of the page. This can be used in combination with the attribute to - * manually resize the webview guest contents. - */ - max?: Size; - } - interface SourcesOptions { /** * An array of Strings that lists the types of desktop sources to be captured, @@ -8232,11 +8620,6 @@ declare namespace Electron { traceOptions: string; } - interface StartRecordingOptions { - categoryFilter: string; - traceOptions: string; - } - interface SystemMemoryInfo { /** * The total amount of physical memory in Kilobytes available to the system. @@ -8451,6 +8834,27 @@ declare namespace Electron { url: string; } + interface UploadProgress { + /** + * Whether the request is currently active. If this is false no other properties + * will be set + */ + active: boolean; + /** + * Whether the upload has started. If this is false both current and total will be + * set to 0. + */ + started: boolean; + /** + * The number of bytes that have been uploaded so far + */ + current: number; + /** + * The number of bytes that will be uploaded this request + */ + total: number; + } + interface Versions { /** * A String representing Chrome's version string. @@ -8675,7 +9079,7 @@ declare namespace Electron { * A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to * enable. The full list of supported feature strings can be found in the file. */ - blinkFeatures?: string; + enableBlinkFeatures?: string; /** * A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to * disable. The full list of supported feature strings can be found in the file. @@ -8727,7 +9131,9 @@ declare namespace Electron { */ contextIsolation?: boolean; /** - * Whether to use native window.open(). Defaults to false. This option is currently + * Whether to use native window.open(). If set to true, the webPreferences of child + * window will always be the same with parent window, regardless of the parameters + * passed to window.open(). Defaults to false. This option is currently * experimental. */ nativeWindowOpen?: boolean; @@ -8745,7 +9151,22 @@ declare namespace Electron { * of this app. Useful for passing small bits of data down to renderer process * preload scripts. */ - additionArguments?: string[]; + additionalArguments?: string[]; + /** + * Whether to enable browser style consecutive dialog protection. Default is false. + */ + safeDialogs?: boolean; + /** + * The message to display when consecutive dialog protection is triggered. If not + * defined the default message would be used, note that currently the default + * message is in English and not localized. + */ + safeDialogsMessage?: string; + /** + * Whether dragging and dropping a file or link onto the page causes a navigation. + * Default is false. + */ + navigateOnDragDrop?: boolean; } interface DefaultFontFamily { @@ -8774,6 +9195,7 @@ declare namespace Electron { */ fantasy?: string; } + } declare module 'electron' { @@ -8823,6 +9245,11 @@ declare namespace NodeJS { */ crash(): void; getCPUUsage(): Electron.CPUUsage; + /** + * Returns an object with V8 heap statistics. Note that all statistics are reported + * in Kilobytes. + */ + getHeapStatistics(): Electron.HeapStatistics; getIOCounters(): Electron.IOCounters; /** * Returns an object giving memory usage statistics about the current process. Note @@ -8903,4 +9330,4 @@ declare namespace NodeJS { electron: string; chrome: string; } -} \ No newline at end of file +} diff --git a/src/typings/lib.es2018.promise.d.ts b/src/typings/lib.es2018.promise.d.ts new file mode 100644 index 000000000000..9f7b2d38cb26 --- /dev/null +++ b/src/typings/lib.es2018.promise.d.ts @@ -0,0 +1,27 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + +/** + * Represents the completion of an asynchronous operation + */ +interface Promise { + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): Promise; +} diff --git a/src/typings/node-pty.d.ts b/src/typings/node-pty.d.ts index bf333cf377e5..772443146f79 100644 --- a/src/typings/node-pty.d.ts +++ b/src/typings/node-pty.d.ts @@ -25,6 +25,11 @@ declare module 'node-pty' { uid?: number; gid?: number; encoding?: string; + /** + * Whether to use the experimental ConPTY system on Windows. This setting will be ignored on + * non-Windows. + */ + experimentalUseConpty?: boolean; } /** diff --git a/src/typings/v8-inspect-profiler.d.ts b/src/typings/v8-inspect-profiler.d.ts index c60190b3fe32..59d7f81cfa06 100644 --- a/src/typings/v8-inspect-profiler.d.ts +++ b/src/typings/v8-inspect-profiler.d.ts @@ -32,16 +32,24 @@ declare module 'v8-inspect-profiler' { } export interface Target { - description: string, - devtoolsFrontendUrl: string, - id: string, - title: string, - type: string, - url: string, - webSocketDebuggerUrl: string + description: string; + devtoolsFrontendUrl: string; + id: string; + title: string; + type: string; + url: string; + webSocketDebuggerUrl: string; } - export function startProfiling(options: { port: number, tries?: number, retyWait?: number, target?: (targets: Target[]) => Target }): PromiseLike; + export interface StartOptions { + port: number; + tries?: number; + retyWait?: number; + checkForPaused?: boolean; + target?: (targets: Target[]) => Target; + } + + export function startProfiling(options: StartOptions): PromiseLike; export function writeProfile(profile: ProfileResult, name?: string): PromiseLike; export function rewriteAbsolutePaths(profile: ProfileResult, replaceWith?: string): ProfileResult; } diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index 89d0ae3802b9..e425a37c43f1 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -7,6 +7,8 @@ * to be stable and consumed by external programs. */ +/// + declare module 'vscode-xterm' { /** * A string representing text font weight. @@ -39,6 +41,16 @@ declare module 'vscode-xterm' { */ bellStyle?: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/; + /** + * When enabled the cursor will be set to the beginning of the next line + * with every new line. This equivalent to sending '\r\n' for each '\n'. + * Normally the termios settings of the underlying PTY deals with the + * translation of '\n' to '\r\n' and this setting should not be used. If you + * deal with data from a non-PTY related source, this settings might be + * useful. + */ + convertEol?: boolean; + /** * The number of columns in the terminal. */ @@ -96,9 +108,10 @@ declare module 'vscode-xterm' { * - 'TypedArray': The new experimental implementation based on TypedArrays that is expected to * significantly boost performance and memory consumption. Use at your own risk. * - * This option will be removed in the future. + * @deprecated This option will be removed in the future. */ experimentalBufferLineImpl?: 'JsArray' | 'TypedArray'; + /** * The font size used to render text. */ @@ -144,19 +157,12 @@ declare module 'vscode-xterm' { macOptionClickForcesSelection?: boolean; /** - * (EXPERIMENTAL) The type of renderer to use, this allows using the - * fallback DOM renderer when canvas is too slow for the environment. The - * following features do not work when the DOM renderer is used: + * The type of renderer to use, this allows using the fallback DOM renderer + * when canvas is too slow for the environment. The following features do + * not work when the DOM renderer is used: * - * - Links - * - Line height * - Letter spacing * - Cursor blink - * - Cursor style - * - * This option is marked as experiemental because it will eventually be - * moved to an addon. You can only set this option in the constructor (not - * setOption). */ rendererType?: RendererType; @@ -393,13 +399,13 @@ declare module 'vscode-xterm' { * @param type The type of the event. * @param listener The listener. */ - on(type: 'refresh', listener: (data: {start: number, end: number}) => void): void; + on(type: 'refresh', listener: (data: { start: number, end: number }) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ - on(type: 'resize', listener: (data: {cols: number, rows: number}) => void): void; + on(type: 'resize', listener: (data: { cols: number, rows: number }) => void): void; /** * Registers an event listener. * @param type The type of the event. @@ -426,8 +432,21 @@ declare module 'vscode-xterm' { */ off(type: 'blur' | 'focus' | 'linefeed' | 'selection' | 'data' | 'key' | 'keypress' | 'keydown' | 'refresh' | 'resize' | 'scroll' | 'title' | string, listener: (...args: any[]) => void): void; + /** + * Emits an event on the terminal. + * @param type The type of event + * @param data data associated with the event. + * @deprecated This is being removed from the API with no replacement, see + * issue #1505. + */ emit(type: string, data?: any): void; + /** + * Adds an event listener to the Terminal, returning an IDisposable that can + * be used to conveniently remove the event listener. + * @param type The type of event. + * @param handler The event handler. + */ addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable; /** @@ -480,6 +499,44 @@ declare module 'vscode-xterm' { */ deregisterLinkMatcher(matcherId: number): void; + /** + * (EXPERIMENTAL) Registers a character joiner, allowing custom sequences of + * characters to be rendered as a single unit. This is useful in particular + * for rendering ligatures and graphemes, among other things. + * + * Each registered character joiner is called with a string of text + * representing a portion of a line in the terminal that can be rendered as + * a single unit. The joiner must return a sorted array, where each entry is + * itself an array of length two, containing the start (inclusive) and end + * (exclusive) index of a substring of the input that should be rendered as + * a single unit. When multiple joiners are provided, the results of each + * are collected. If there are any overlapping substrings between them, they + * are combined into one larger unit that is drawn together. + * + * All character joiners that are registered get called every time a line is + * rendered in the terminal, so it is essential for the handler function to + * run as quickly as possible to avoid slowdowns when rendering. Similarly, + * joiners should strive to return the smallest possible substrings to + * render together, since they aren't drawn as optimally as individual + * characters. + * + * NOTE: character joiners are only used by the canvas renderer. + * + * @param handler The function that determines character joins. It is called + * with a string of text that is eligible for joining and returns an array + * where each entry is an array containing the start (inclusive) and end + * (exclusive) indexes of ranges that should be rendered as a single unit. + * @return The ID of the new joiner, this can be used to deregister + */ + registerCharacterJoiner(handler: (text: string) => [number, number][]): number; + + /** + * (EXPERIMENTAL) Deregisters the character joiner if one was registered. + * NOTE: character joiners are only used by the canvas renderer. + * @param joinerId The character joiner's ID (returned after register) + */ + deregisterCharacterJoiner(joinerId: number): void; + /** * (EXPERIMENTAL) Adds a marker to the normal buffer and returns it. If the * alt buffer is active, undefined is returned. @@ -687,6 +744,7 @@ declare module 'vscode-xterm' { } } + // Modifications to official .d.ts below declare module 'vscode-xterm' { interface TerminalCore { @@ -717,7 +775,7 @@ declare module 'vscode-xterm' { }; } - interface ISearchOptions { + interface ISearchOptions { /** * Whether the find should be done as a regex. */ diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index 2da223801863..b212faa38a9d 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -14,7 +14,7 @@ class WindowManager { // --- Zoom Level private _zoomLevel: number = 0; private _lastZoomLevelChangeTime: number = 0; - private readonly _onDidChangeZoomLevel: Emitter = new Emitter(); + private readonly _onDidChangeZoomLevel = new Emitter(); public readonly onDidChangeZoomLevel: Event = this._onDidChangeZoomLevel.event; public getZoomLevel(): number { @@ -58,7 +58,7 @@ class WindowManager { // --- Fullscreen private _fullscreen: boolean; - private readonly _onDidChangeFullscreen: Emitter = new Emitter(); + private readonly _onDidChangeFullscreen = new Emitter(); public readonly onDidChangeFullscreen: Event = this._onDidChangeFullscreen.event; public setFullscreen(fullscreen: boolean): void { @@ -75,7 +75,7 @@ class WindowManager { // --- Accessibility private _accessibilitySupport = platform.AccessibilitySupport.Unknown; - private readonly _onDidChangeAccessibilitySupport: Emitter = new Emitter(); + private readonly _onDidChangeAccessibilitySupport = new Emitter(); public readonly onDidChangeAccessibilitySupport: Event = this._onDidChangeAccessibilitySupport.event; public setAccessibilitySupport(accessibilitySupport: platform.AccessibilitySupport): void { @@ -146,7 +146,8 @@ export const isOpera = (userAgent.indexOf('Opera') >= 0); export const isFirefox = (userAgent.indexOf('Firefox') >= 0); export const isWebKit = (userAgent.indexOf('AppleWebKit') >= 0); export const isChrome = (userAgent.indexOf('Chrome') >= 0); -export const isSafari = (userAgent.indexOf('Chrome') === -1) && (userAgent.indexOf('Safari') >= 0); +export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0)); +export const isWebkitWebView = (!isChrome && !isSafari && isWebKit); export const isIPad = (userAgent.indexOf('iPad') >= 0); export const isEdgeWebView = isEdge && (userAgent.indexOf('WebView/') >= 0); diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index 9a421651c2a2..46b2b902077e 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -17,17 +17,17 @@ export interface IContextMenuEvent { } export class ContextSubMenu extends SubmenuAction { - constructor(label: string, public entries: (ContextSubMenu | IAction)[]) { + constructor(label: string, public entries: Array) { super(label, entries, 'contextsubmenu'); } } export interface IContextMenuDelegate { getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number; }; - getActions(): (IAction | ContextSubMenu)[]; - getActionItem?(action: IAction): IActionItem; + getActions(): Array; + getActionItem?(action: IAction): IActionItem | null; getActionsContext?(event?: IContextMenuEvent): any; - getKeyBinding?(action: IAction): ResolvedKeybinding; + getKeyBinding?(action: IAction): ResolvedKeybinding | undefined; getMenuClassName?(): string; onHide?(didCancel: boolean): void; actionRunner?: IActionRunner; diff --git a/src/vs/base/browser/dnd.ts b/src/vs/base/browser/dnd.ts index 1b0860835d6c..e839c7205705 100644 --- a/src/vs/base/browser/dnd.ts +++ b/src/vs/base/browser/dnd.ts @@ -83,4 +83,30 @@ export function applyDragImage(event: DragEvent, label: string, clazz: string): // Removes the element when the DND operation is done setTimeout(() => document.body.removeChild(dragImage), 0); } -} \ No newline at end of file +} + +export interface IDragAndDropData { + update(dataTransfer: DataTransfer): void; + getData(): any; +} + +export class DragAndDropData implements IDragAndDropData { + + constructor(private data: T) { } + + update(): void { + // noop + } + + getData(): T { + return this.data; + } +} + +export interface IStaticDND { + CurrentDragAndDropData: IDragAndDropData | undefined; +} + +export const StaticDND: IStaticDND = { + CurrentDragAndDropData: undefined +}; \ No newline at end of file diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index da907ab5e797..fc677d15bd65 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -11,8 +11,9 @@ import { TimeoutTimer } from 'vs/base/common/async'; import { CharCode } from 'vs/base/common/charCode'; 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 { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; +import { coalesce } from 'vs/base/common/arrays'; export function clearNode(node: HTMLElement): void { while (node.firstChild) { @@ -146,10 +147,10 @@ const _manualClassList = new class implements IDomClassList { toggleClass(node: HTMLElement, className: string, shouldHaveIt?: boolean): void { this._findClassName(node, className); - if (this._lastStart !== -1 && (shouldHaveIt === void 0 || !shouldHaveIt)) { + if (this._lastStart !== -1 && (shouldHaveIt === undefined || !shouldHaveIt)) { this.removeClass(node, className); } - if (this._lastStart === -1 && (shouldHaveIt === void 0 || shouldHaveIt)) { + if (this._lastStart === -1 && (shouldHaveIt === undefined || shouldHaveIt)) { this.addClass(node, className); } } @@ -265,7 +266,7 @@ export let addStandardDisposableListener: IAddStandardDisposableListenerSignatur export function addDisposableNonBubblingMouseOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { return addDisposableListener(node, 'mouseout', (e: MouseEvent) => { // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements - let toElement: Node | null = (e.relatedTarget || e.toElement); + let toElement: Node | null = (e.relatedTarget || e.target); while (toElement && toElement !== node) { toElement = toElement.parentNode; } @@ -994,7 +995,7 @@ export function prepend(parent: HTMLElement, child: T): T { const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/; -export function $(description: string, attrs?: { [key: string]: any; }, ...children: (Node | string)[]): T { +export function $(description: string, attrs?: { [key: string]: any; }, ...children: Array): T { let match = SELECTOR_REGEX.exec(description); if (!match) { @@ -1025,8 +1026,7 @@ export function $(description: string, attrs?: { [key: st } }); - children - .filter(child => !!child) + coalesce(children) .forEach(child => { if (child instanceof Node) { result.appendChild(child); @@ -1157,3 +1157,13 @@ export function windowOpenNoOpener(url: string): void { } } } + +export function animate(fn: () => void): IDisposable { + const step = () => { + fn(); + stepDisposable = scheduleAtNextAnimationFrame(step); + }; + + let stepDisposable = scheduleAtNextAnimationFrame(step); + return toDisposable(() => stepDisposable.dispose()); +} diff --git a/src/vs/base/browser/event.ts b/src/vs/base/browser/event.ts index f969447dd786..021a0be5b20d 100644 --- a/src/vs/base/browser/event.ts +++ b/src/vs/base/browser/event.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter, mapEvent } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; export type EventHandler = HTMLElement | HTMLDocument | Window; @@ -32,7 +32,7 @@ export interface CancellableEvent { } export function stop(event: Event): Event { - return mapEvent(event, e => { + return Event.map(event, e => { e.preventDefault(); e.stopPropagation(); return e; diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index f6bb1d8d6444..f4fd8b538236 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -92,12 +92,12 @@ export class GlobalMouseMoveMonitor extends Disposable { this.onStopCallback = onStopCallback; let windowChain = IframeUtils.getSameOriginWindowChain(); - for (let i = 0; i < windowChain.length; i++) { - this.hooks.push(dom.addDisposableThrottledListener(windowChain[i].window.document, 'mousemove', + for (const element of windowChain) { + this.hooks.push(dom.addDisposableThrottledListener(element.window.document, 'mousemove', (data: R) => this.mouseMoveCallback!(data), (lastEvent: R, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) )); - this.hooks.push(dom.addDisposableListener(windowChain[i].window.document, 'mouseup', (e: MouseEvent) => this.stopMonitoring(true))); + this.hooks.push(dom.addDisposableListener(element.window.document, 'mouseup', (e: MouseEvent) => this.stopMonitoring(true))); } if (IframeUtils.hasDifferentOriginAncestor()) { diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts index 950d451d4da9..2587de3ea28a 100644 --- a/src/vs/base/browser/htmlContentRenderer.ts +++ b/src/vs/base/browser/htmlContentRenderer.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { escape } from 'vs/base/common/strings'; -import { removeMarkdownEscapes, IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; +import { removeMarkdownEscapes, IMarkdownString } from 'vs/base/common/htmlContent'; import * as marked from 'vs/base/common/marked/marked'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -24,7 +24,7 @@ export interface RenderOptions { className?: string; inline?: boolean; actionHandler?: IContentActionHandler; - codeBlockRenderer?: (modeId: string, value: string) => Thenable; + codeBlockRenderer?: (modeId: string, value: string) => Promise; codeBlockRenderCallback?: () => void; } @@ -92,7 +92,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions // signal to code-block render that the // element has been created - let signalInnerHTML: Function; + let signalInnerHTML: () => void; const withInnerHTML = new Promise(c => signalInnerHTML = c); const renderer = new marked.Renderer(); @@ -225,7 +225,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions } const markedOptions: marked.MarkedOptions = { - sanitize: markdown instanceof MarkdownString ? markdown.sanitize : true, + sanitize: true, renderer }; diff --git a/src/vs/base/browser/iframe.ts b/src/vs/base/browser/iframe.ts index d76a83dd5ff2..7d0f5647bb58 100644 --- a/src/vs/base/browser/iframe.ts +++ b/src/vs/base/browser/iframe.ts @@ -111,8 +111,7 @@ export class IframeUtils { let windowChain = this.getSameOriginWindowChain(); - for (let i = 0; i < windowChain.length; i++) { - let windowChainEl = windowChain[i]; + for (const windowChainEl of windowChain) { if (windowChainEl.window === ancestorWindow) { break; diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 0634c966fef3..d50430417af7 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -7,7 +7,7 @@ import 'vs/css!./actionbar'; import * as platform from 'vs/base/common/platform'; import * as nls from 'vs/nls'; import { Disposable, dispose } from 'vs/base/common/lifecycle'; -import { SelectBox, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox'; +import { SelectBox, ISelectOptionItem, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox'; import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, IRunEvent } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import * as types from 'vs/base/common/types'; @@ -60,24 +60,24 @@ export class BaseActionItem extends Disposable implements IActionItem { } private handleActionChangeEvent(event: IActionChangeEvent): void { - if (event.enabled !== void 0) { + if (event.enabled !== undefined) { this.updateEnabled(); } - if (event.checked !== void 0) { + if (event.checked !== undefined) { this.updateChecked(); } - if (event.class !== void 0) { + if (event.class !== undefined) { this.updateClass(); } - if (event.label !== void 0) { + if (event.label !== undefined) { this.updateLabel(); this.updateTooltip(); } - if (event.tooltip !== void 0) { + if (event.tooltip !== undefined) { this.updateTooltip(); } } @@ -228,7 +228,7 @@ export class Separator extends Action { export interface IActionItemOptions extends IBaseActionItemOptions { icon?: boolean; label?: boolean; - keybinding?: string; + keybinding?: string | null; } export class ActionItem extends BaseActionItem { @@ -363,7 +363,7 @@ export interface ActionTrigger { } export interface IActionItemProvider { - (action: IAction): IActionItem; + (action: IAction): IActionItem | null; } export interface IActionBarOptions { @@ -376,7 +376,7 @@ export interface IActionBarOptions { triggerKeys?: ActionTrigger; } -let defaultOptions: IActionBarOptions = { +const defaultOptions: IActionBarOptions = { orientation: ActionsOrientation.HORIZONTAL, context: null, triggerKeys: { @@ -473,7 +473,7 @@ export class ActionBar extends Disposable implements IActionRunner { } this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_DOWN, e => { - let event = new StandardKeyboardEvent(e); + const event = new StandardKeyboardEvent(e); let eventHandled = true; if (event.equals(previousKey)) { @@ -498,7 +498,7 @@ export class ActionBar extends Disposable implements IActionRunner { })); this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_UP, e => { - let event = new StandardKeyboardEvent(e); + const event = new StandardKeyboardEvent(e); // Run action on Enter/Space if (this.isTriggerKeyEvent(event)) { @@ -560,7 +560,7 @@ export class ActionBar extends Disposable implements IActionRunner { private updateFocusedItem(): void { for (let i = 0; i < this.actionsList.children.length; i++) { - let elem = this.actionsList.children[i]; + const elem = this.actionsList.children[i]; if (DOM.isAncestor(document.activeElement, elem)) { this.focusedItem = i; break; @@ -577,11 +577,11 @@ export class ActionBar extends Disposable implements IActionRunner { this.items.forEach(i => i.setActionContext(context)); } - get actionRunner(): IActionRunner | undefined { + get actionRunner(): IActionRunner { return this._actionRunner; } - set actionRunner(actionRunner: IActionRunner | undefined) { + set actionRunner(actionRunner: IActionRunner) { if (actionRunner) { this._actionRunner = actionRunner; this.items.forEach(item => item.actionRunner = actionRunner); @@ -657,8 +657,8 @@ export class ActionBar extends Disposable implements IActionRunner { pull(index: number): void { if (index >= 0 && index < this.items.length) { - this.items.splice(index, 1); this.actionsList.removeChild(this.actionsList.childNodes[index]); + dispose(this.items.splice(index, 1)); } } @@ -679,7 +679,7 @@ export class ActionBar extends Disposable implements IActionRunner { focus(selectFirst?: boolean): void; focus(arg?: any): void { let selectFirst: boolean = false; - let index: number | undefined = void 0; + let index: number | undefined = undefined; if (arg === undefined) { selectFirst = true; } else if (typeof arg === 'number') { @@ -701,12 +701,12 @@ export class ActionBar extends Disposable implements IActionRunner { } } - private focusNext(): void { + protected focusNext(): void { if (typeof this.focusedItem === 'undefined') { this.focusedItem = this.items.length - 1; } - let startIndex = this.focusedItem; + const startIndex = this.focusedItem; let item: IActionItem; do { @@ -721,12 +721,12 @@ export class ActionBar extends Disposable implements IActionRunner { this.updateFocus(); } - private focusPrevious(): void { + protected focusPrevious(): void { if (typeof this.focusedItem === 'undefined') { this.focusedItem = 0; } - let startIndex = this.focusedItem; + const startIndex = this.focusedItem; let item: IActionItem; do { @@ -752,9 +752,8 @@ export class ActionBar extends Disposable implements IActionRunner { } for (let i = 0; i < this.items.length; i++) { - let item = this.items[i]; - - let actionItem = item; + const item = this.items[i]; + const actionItem = item; if (i === this.focusedItem) { if (types.isFunction(actionItem.isEnabled)) { @@ -778,7 +777,7 @@ export class ActionBar extends Disposable implements IActionRunner { } // trigger action - let actionItem = this.items[this.focusedItem]; + const actionItem = this.items[this.focusedItem]; if (actionItem instanceof BaseActionItem) { const context = (actionItem._context === null || actionItem._context === undefined) ? event : actionItem._context; this.run(actionItem._action, context); @@ -793,7 +792,7 @@ export class ActionBar extends Disposable implements IActionRunner { this._onDidCancel.fire(); } - run(action: IAction, context?: any): Thenable { + run(action: IAction, context?: any): Promise { return this._actionRunner.run(action, context); } @@ -810,7 +809,7 @@ export class ActionBar extends Disposable implements IActionRunner { export class SelectActionItem extends BaseActionItem { protected selectBox: SelectBox; - constructor(ctx: any, action: IAction, options: string[], selected: number, contextViewProvider: IContextViewProvider, selectBoxOptions?: ISelectBoxOptions) { + constructor(ctx: any, action: IAction, options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, selectBoxOptions?: ISelectBoxOptions) { super(ctx, action); this.selectBox = new SelectBox(options, selected, contextViewProvider, undefined, selectBoxOptions); @@ -819,8 +818,8 @@ export class SelectActionItem extends BaseActionItem { this.registerListeners(); } - setOptions(options: string[], selected?: number, disabled?: number): void { - this.selectBox.setOptions(options, selected, disabled); + setOptions(options: ISelectOptionItem[], selected?: number): void { + this.selectBox.setOptions(options, selected); } select(index: number): void { diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index ed74ce74ab04..2acd6ffe0258 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -11,7 +11,7 @@ import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { Event as BaseEvent, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Gesture } from 'vs/base/browser/touch'; +import { Gesture, EventType } from 'vs/base/browser/touch'; export interface IButtonOptions extends IButtonStyles { title?: boolean; @@ -65,14 +65,16 @@ export class Button extends Disposable { Gesture.addTarget(this._element); - this._register(DOM.addDisposableListener(this._element, DOM.EventType.CLICK, e => { - if (!this.enabled) { - DOM.EventHelper.stop(e); - return; - } + [DOM.EventType.CLICK, EventType.Tap].forEach(eventType => { + this._register(DOM.addDisposableListener(this._element, eventType, e => { + if (!this.enabled) { + DOM.EventHelper.stop(e); + return; + } - this._onDidClick.fire(e); - })); + this._onDidClick.fire(e); + })); + }); this._register(DOM.addDisposableListener(this._element, DOM.EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e); diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 5df282ab39b8..d51cb04707eb 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -5,7 +5,7 @@ import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; import { $ } from 'vs/base/browser/dom'; -import { Event, mapEvent } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IView } from 'vs/base/browser/ui/grid/gridview'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; @@ -39,8 +39,8 @@ function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView { element: view.element, get maximumSize() { return view.maximumWidth; }, get minimumSize() { return view.minimumWidth; }, - onDidChange: mapEvent(view.onDidChange, e => e && e.width), - layout: size => view.layout(size, getHeight()) + onDidChange: Event.map(view.onDidChange, e => e && e.width), + layout: size => view.layout(size, getHeight(), Orientation.HORIZONTAL) }; } @@ -78,7 +78,7 @@ export class CenteredViewLayout { this.resizeMargins(); } } else { - this.view.layout(width, height); + this.view.layout(width, height, Orientation.HORIZONTAL); } this.didLayout = true; } diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 7ceb97280cf1..ea3781a3eb8a 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -25,7 +25,7 @@ export const enum AnchorPosition { export interface IDelegate { getAnchor(): HTMLElement | IAnchor; - render(container: HTMLElement): IDisposable; + render(container: HTMLElement): IDisposable | null; focus?(): void; layout?(): void; anchorAlignment?: AnchorAlignment; // default: left diff --git a/src/vs/base/browser/ui/dropdown/dropdown.css b/src/vs/base/browser/ui/dropdown/dropdown.css index 4b75f9ac1300..7ac5df590a3c 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.css +++ b/src/vs/base/browser/ui/dropdown/dropdown.css @@ -9,23 +9,8 @@ padding: 0; } -.monaco-dropdown > .dropdown-label, -.monaco-dropdown > .dropdown-action { +.monaco-dropdown > .dropdown-label { display: inline-block; cursor: pointer; height: 100%; } - -.monaco-dropdown > .dropdown-action { - vertical-align: top; -} - -.monaco-dropdown > .dropdown-action > .action-label:hover { - color: inherit; - text-decoration: none; -} - -.monaco-dropdown > .dropdown-action, -.monaco-dropdown > .dropdown-action > .action-label { - display: inline-block; -} \ No newline at end of file diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index 964d1a9fdbd5..fc6f536fa5c8 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -16,7 +16,7 @@ import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; export interface ILabelRenderer { - (container: HTMLElement): IDisposable; + (container: HTMLElement): IDisposable | null; } export interface IBaseDropdownOptions { @@ -26,9 +26,9 @@ export interface IBaseDropdownOptions { export class BaseDropdown extends ActionRunner { private _element: HTMLElement; - private boxContainer: HTMLElement; - private _label: HTMLElement; - private contents: HTMLElement; + private boxContainer?: HTMLElement; + private _label?: HTMLElement; + private contents?: HTMLElement; private visible: boolean; constructor(container: HTMLElement, options: IBaseDropdownOptions) { @@ -40,18 +40,18 @@ export class BaseDropdown extends ActionRunner { let labelRenderer = options.labelRenderer; if (!labelRenderer) { - labelRenderer = (container: HTMLElement): IDisposable => { + labelRenderer = (container: HTMLElement): IDisposable | null => { container.textContent = options.label || ''; return null; }; } - [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => { + for (const event of [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap]) { this._register(addDisposableListener(this._label, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger - }); + } - [EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => { + for (const event of [EventType.MOUSE_DOWN, GestureEventType.Tap]) { this._register(addDisposableListener(this._label, event, e => { if (e instanceof MouseEvent && e.detail > 1) { return; // prevent multiple clicks to open multiple context menus (https://github.com/Microsoft/vscode/issues/41363) @@ -63,7 +63,7 @@ export class BaseDropdown extends ActionRunner { this.show(); } })); - }); + } this._register(addDisposableListener(this._label, EventType.KEY_UP, e => { const event = new StandardKeyboardEvent(e); @@ -90,12 +90,14 @@ export class BaseDropdown extends ActionRunner { return this._element; } - get label(): HTMLElement { + get label() { return this._label; } set tooltip(tooltip: string) { - this._label.title = tooltip; + if (this._label) { + this._label.title = tooltip; + } } show(): void { @@ -116,17 +118,17 @@ export class BaseDropdown extends ActionRunner { if (this.boxContainer) { this.boxContainer.remove(); - this.boxContainer = null; + this.boxContainer = undefined; } if (this.contents) { this.contents.remove(); - this.contents = null; + this.contents = undefined; } if (this._label) { this._label.remove(); - this._label = null; + this._label = undefined; } } } @@ -180,7 +182,7 @@ export class Dropdown extends BaseDropdown { } } - protected renderContents(container: HTMLElement): IDisposable { + protected renderContents(container: HTMLElement): IDisposable | null { return null; } } @@ -204,7 +206,7 @@ export class DropdownMenu extends BaseDropdown { private _contextMenuProvider: IContextMenuProvider; private _menuOptions: IMenuOptions; private _actions: IAction[]; - private actionProvider: IActionProvider; + private actionProvider?: IActionProvider; private menuClassName: string; constructor(container: HTMLElement, options: IDropdownMenuOptions) { @@ -246,10 +248,10 @@ export class DropdownMenu extends BaseDropdown { 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, + getKeyBinding: action => this.menuOptions && this.menuOptions.getKeyBinding ? this.menuOptions.getKeyBinding(action) : undefined, getMenuClassName: () => this.menuClassName, onHide: () => this.onHide(), - actionRunner: this.menuOptions ? this.menuOptions.actionRunner : null, + actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined, anchorAlignment: this.menuOptions.anchorAlignment }); } @@ -268,14 +270,14 @@ export class DropdownMenuActionItem extends BaseActionItem { private menuActionsOrProvider: any; private dropdownMenu: DropdownMenu; private contextMenuProvider: IContextMenuProvider; - private actionItemProvider: IActionItemProvider; - private keybindings: (action: IAction) => ResolvedKeybinding; + private actionItemProvider?: IActionItemProvider; + private keybindings?: (action: IAction) => ResolvedKeybinding | undefined; private clazz: string; private anchorAlignmentProvider: (() => AnchorAlignment) | undefined; - constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); - constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); - constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment) { + constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); + constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); + constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment) { super(null, action); this.menuActionsOrProvider = menuActionsOrProvider; @@ -288,7 +290,7 @@ export class DropdownMenuActionItem extends BaseActionItem { } render(container: HTMLElement): void { - const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable => { + const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => { this.element = append(el, $('a.action-label.icon')); addClasses(this.element, this.clazz); @@ -327,7 +329,7 @@ export class DropdownMenuActionItem extends BaseActionItem { this.dropdownMenu.menuOptions = { ...this.dropdownMenu.menuOptions, get anchorAlignment(): AnchorAlignment { - return that.anchorAlignmentProvider(); + return that.anchorAlignmentProvider!(); } }; } diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 46943d6bfaf2..793897ed6935 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -32,7 +32,7 @@ export interface IFindInputOptions extends IFindInputStyles { } export interface IFindInputStyles extends IInputBoxStyles { - inputActiveOptionBorder?: Color; + inputActiveOptionBorder?: Color | null; } const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input"); @@ -44,24 +44,24 @@ export class FindInput extends Widget { private contextViewProvider: IContextViewProvider; private width: number; private placeholder: string; - private validation: IInputValidator; + private validation?: IInputValidator; private label: string; private fixFocusOnOptionClickEnabled = true; - private inputActiveOptionBorder: Color; - private inputBackground: Color; - private inputForeground: Color; - private inputBorder: Color; - - private inputValidationInfoBorder: Color; - private inputValidationInfoBackground: Color; - private inputValidationInfoForeground: Color; - private inputValidationWarningBorder: Color; - private inputValidationWarningBackground: Color; - private inputValidationWarningForeground: Color; - private inputValidationErrorBorder: Color; - private inputValidationErrorBackground: Color; - private inputValidationErrorForeground: Color; + private inputActiveOptionBorder?: Color | null; + private inputBackground?: Color | null; + private inputForeground?: Color | null; + private inputBorder?: Color | null; + + private inputValidationInfoBorder?: Color | null; + private inputValidationInfoBackground?: Color | null; + private inputValidationInfoForeground?: Color | null; + private inputValidationWarningBorder?: Color | null; + private inputValidationWarningBackground?: Color | null; + private inputValidationWarningForeground?: Color | null; + private inputValidationErrorBorder?: Color | null; + private inputValidationErrorBackground?: Color | null; + private inputValidationErrorForeground?: Color | null; private regex: RegexCheckbox; private wholeWords: WholeWordsCheckbox; @@ -90,7 +90,7 @@ export class FindInput extends Widget { private _onRegexKeyDown = this._register(new Emitter()); public readonly onRegexKeyDown: Event = this._onRegexKeyDown.event; - constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, private readonly _showOptionButtons: boolean, options?: IFindInputOptions) { + constructor(parent: HTMLElement | null, contextViewProvider: IContextViewProvider, private readonly _showOptionButtons: boolean, options: IFindInputOptions) { super(); this.contextViewProvider = contextViewProvider; this.width = options.width || 100; @@ -113,15 +113,9 @@ export class FindInput extends Widget { this.inputValidationErrorBackground = options.inputValidationErrorBackground; this.inputValidationErrorForeground = options.inputValidationErrorForeground; - this.regex = null; - this.wholeWords = null; - this.caseSensitive = null; - this.domNode = null; - this.inputBox = null; + this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '', options.history || [], !!options.flexibleHeight); - this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '', options.history, options.flexibleHeight); - - if (Boolean(parent)) { + if (parent) { parent.appendChild(this.domNode); } @@ -208,7 +202,7 @@ export class FindInput extends Widget { protected applyStyles(): void { if (this.domNode) { const checkBoxStyles: ICheckboxStyles = { - inputActiveOptionBorder: this.inputActiveOptionBorder, + inputActiveOptionBorder: this.inputActiveOptionBorder || undefined, }; this.regex.style(checkBoxStyles); this.wholeWords.style(checkBoxStyles); @@ -298,7 +292,7 @@ export class FindInput extends Widget { placeholder: this.placeholder || '', ariaLabel: this.label || '', validationOptions: { - validation: this.validation || null + validation: this.validation }, inputBackground: this.inputBackground, inputForeground: this.inputForeground, @@ -319,7 +313,7 @@ export class FindInput extends Widget { this.regex = this._register(new RegexCheckbox({ appendTitle: appendRegexLabel, isChecked: false, - inputActiveOptionBorder: this.inputActiveOptionBorder + inputActiveOptionBorder: this.inputActiveOptionBorder || undefined })); this._register(this.regex.onChange(viaKeyboard => { this._onDidOptionChange.fire(viaKeyboard); @@ -336,7 +330,7 @@ export class FindInput extends Widget { this.wholeWords = this._register(new WholeWordsCheckbox({ appendTitle: appendWholeWordsLabel, isChecked: false, - inputActiveOptionBorder: this.inputActiveOptionBorder + inputActiveOptionBorder: this.inputActiveOptionBorder || undefined })); this._register(this.wholeWords.onChange(viaKeyboard => { this._onDidOptionChange.fire(viaKeyboard); @@ -350,7 +344,7 @@ export class FindInput extends Widget { this.caseSensitive = this._register(new CaseSensitiveCheckbox({ appendTitle: appendCaseSensitiveLabel, isChecked: false, - inputActiveOptionBorder: this.inputActiveOptionBorder + inputActiveOptionBorder: this.inputActiveOptionBorder || undefined })); this._register(this.caseSensitive.onChange(viaKeyboard => { this._onDidOptionChange.fire(viaKeyboard); @@ -370,7 +364,7 @@ export class FindInput extends Widget { if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) { let index = indexes.indexOf(document.activeElement); if (index >= 0) { - let newIndex: number; + let newIndex: number = -1; if (event.equals(KeyCode.RightArrow)) { newIndex = (index + 1) % indexes.length; } else if (event.equals(KeyCode.LeftArrow)) { @@ -405,19 +399,27 @@ export class FindInput extends Widget { } public validate(): void { - this.inputBox.validate(); + if (this.inputBox) { + this.inputBox.validate(); + } } public showMessage(message: InputBoxMessage): void { - this.inputBox.showMessage(message); + if (this.inputBox) { + this.inputBox.showMessage(message); + } } public clearMessage(): void { - this.inputBox.hideMessage(); + if (this.inputBox) { + this.inputBox.hideMessage(); + } } private clearValidation(): void { - this.inputBox.hideMessage(); + if (this.inputBox) { + this.inputBox.hideMessage(); + } } public dispose(): void { diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index fee428a6fb37..301c7e5716ba 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -8,7 +8,9 @@ import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { tail2 as tail, equals } from 'vs/base/common/arrays'; import { orthogonal, IView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles } from './gridview'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; +import { $ } from 'vs/base/browser/dom'; +import { LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; export { Orientation } from './gridview'; @@ -186,6 +188,7 @@ export interface IGridStyles extends IGridViewStyles { } export interface IGridOptions { styles?: IGridStyles; + proportionalLayout?: boolean; } export class Grid implements IDisposable { @@ -646,3 +649,63 @@ export function createSerializedGrid(gridDescriptor: GridDescriptor): ISerialize height: height || 1 }; } + +export class View implements IView { + + readonly element = $('.grid-view-view'); + + private visible = false; + private width: number | undefined; + private height: number | undefined; + private orientation: Orientation = Orientation.HORIZONTAL; + + get minimumWidth(): number { return this.visible ? this.view.minimumWidth : 0; } + get maximumWidth(): number { return this.visible ? this.view.maximumWidth : (this.orientation === Orientation.HORIZONTAL ? 0 : Number.POSITIVE_INFINITY); } + get minimumHeight(): number { return this.visible ? this.view.minimumHeight : 0; } + get maximumHeight(): number { return this.visible ? this.view.maximumHeight : (this.orientation === Orientation.VERTICAL ? 0 : Number.POSITIVE_INFINITY); } + + private onDidChangeVisibility = new Emitter<{ width: number; height: number; } | undefined>(); + readonly onDidChange: Event<{ width: number; height: number; } | undefined>; + + get priority(): LayoutPriority | undefined { return this.view.priority; } + get snapSize(): number | undefined { return this.visible ? this.view.snapSize : undefined; } + + constructor(private view: IView) { + this.show(); + this.onDidChange = Event.any(this.onDidChangeVisibility.event, Event.filter(view.onDidChange, () => this.visible)); + } + + show(): void { + if (this.visible) { + return; + } + + this.visible = true; + + this.element.appendChild(this.view.element); + this.onDidChangeVisibility.fire(typeof this.width === 'number' ? { width: this.width, height: this.height! } : undefined); + } + + hide(): void { + if (!this.visible) { + return; + } + + this.visible = false; + + this.element.removeChild(this.view.element); + this.onDidChangeVisibility.fire(undefined); + } + + layout(width: number, height: number, orientation: Orientation): void { + this.orientation = orientation; + + if (!this.visible) { + return; + } + + this.view.layout(width, height, orientation); + this.width = width; + this.height = height; + } +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 718afb0a0918..9a67e9ae261b 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./gridview'; -import { Event, anyEvent, Emitter, mapEvent, Relay } from 'vs/base/common/event'; +import { Event, Emitter, Relay } from 'vs/base/common/event'; import { Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; import { SplitView, IView as ISplitView, Sizing, LayoutPriority, ISplitViewStyles } from 'vs/base/browser/ui/splitview/splitview'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -21,10 +21,10 @@ export interface IView { readonly maximumWidth: number; readonly minimumHeight: number; readonly maximumHeight: number; - readonly onDidChange: Event<{ width: number; height: number; }>; + readonly onDidChange: Event<{ width: number; height: number; } | undefined>; readonly priority?: LayoutPriority; readonly snapSize?: number; - layout(width: number, height: number): void; + layout(width: number, height: number, orientation: Orientation): void; } export function orthogonal(orientation: Orientation): Orientation { @@ -147,10 +147,10 @@ class BranchNode implements ISplitView, IDisposable { this._orthogonalSize = orthogonalSize; this.element = $('.monaco-grid-branch-node'); - this.splitview = new SplitView(this.element, { orientation, styles }); + this.splitview = new SplitView(this.element, { orientation, styles, proportionalLayout }); this.splitview.layout(size); - const onDidSashReset = mapEvent(this.splitview.onDidSashReset, i => [i]); + const onDidSashReset = Event.map(this.splitview.onDidSashReset, i => [i]); this.splitviewSashResetDisposable = onDidSashReset(this._onDidSashReset.fire, this._onDidSashReset); } @@ -300,15 +300,15 @@ class BranchNode implements ISplitView, IDisposable { } private onDidChildrenChange(): void { - const onDidChildrenChange = anyEvent(...this.children.map(c => c.onDidChange)); + const onDidChildrenChange = Event.map(Event.any(...this.children.map(c => c.onDidChange)), () => undefined); this.childrenChangeDisposable.dispose(); this.childrenChangeDisposable = onDidChildrenChange(this._onDidChange.fire, this._onDidChange); - const onDidChildrenSashReset = anyEvent(...this.children.map((c, i) => mapEvent(c.onDidSashReset, location => [i, ...location]))); + const onDidChildrenSashReset = Event.any(...this.children.map((c, i) => Event.map(c.onDidSashReset, location => [i, ...location]))); this.childrenSashResetDisposable.dispose(); this.childrenSashResetDisposable = onDidChildrenSashReset(this._onDidSashReset.fire, this._onDidSashReset); - this._onDidChange.fire(); + this._onDidChange.fire(undefined); } trySet2x2(other: BranchNode): IDisposable { @@ -348,8 +348,8 @@ class BranchNode implements ISplitView, IDisposable { mySash.linkedSash = otherSash; otherSash.linkedSash = mySash; - this._onDidChange.fire(); - other._onDidChange.fire(); + this._onDidChange.fire(undefined); + other._onDidChange.fire(undefined); return toDisposable(() => { mySash.linkedSash = otherSash.linkedSash = undefined; @@ -391,7 +391,7 @@ class LeafNode implements ISplitView, IDisposable { set linkedWidthNode(node: LeafNode | undefined) { this._onDidLinkedWidthNodeChange.input = node ? node._onDidViewChange : Event.None; this._linkedWidthNode = node; - this._onDidSetLinkedNode.fire(); + this._onDidSetLinkedNode.fire(undefined); } private _onDidLinkedHeightNodeChange = new Relay(); @@ -400,7 +400,7 @@ class LeafNode implements ISplitView, IDisposable { set linkedHeightNode(node: LeafNode | undefined) { this._onDidLinkedHeightNodeChange.input = node ? node._onDidViewChange : Event.None; this._linkedHeightNode = node; - this._onDidSetLinkedNode.fire(); + this._onDidSetLinkedNode.fire(undefined); } private _onDidSetLinkedNode = new Emitter(); @@ -414,8 +414,8 @@ class LeafNode implements ISplitView, IDisposable { ) { this._orthogonalSize = orthogonalSize; - this._onDidViewChange = mapEvent(this.view.onDidChange, this.orientation === Orientation.HORIZONTAL ? e => e && e.width : e => e && e.height); - this.onDidChange = anyEvent(this._onDidViewChange, this._onDidSetLinkedNode.event, this._onDidLinkedWidthNodeChange.event, this._onDidLinkedHeightNodeChange.event); + this._onDidViewChange = Event.map(this.view.onDidChange, e => e && (this.orientation === Orientation.VERTICAL ? e.width : e.height)); + this.onDidChange = Event.any(this._onDidViewChange, this._onDidSetLinkedNode.event, this._onDidLinkedWidthNodeChange.event, this._onDidLinkedHeightNodeChange.event); } get width(): number { @@ -480,12 +480,12 @@ class LeafNode implements ISplitView, IDisposable { layout(size: number): void { this._size = size; - return this.view.layout(this.width, this.height); + return this.view.layout(this.width, this.height, orthogonal(this.orientation)); } orthogonalLayout(size: number): void { this._orthogonalSize = size; - return this.view.layout(this.width, this.height); + return this.view.layout(this.width, this.height, orthogonal(this.orientation)); } dispose(): void { } @@ -547,7 +547,7 @@ export class GridView implements IDisposable { this._root = root; this.element.appendChild(root.element); this.onDidSashResetRelay.input = root.onDidSashReset; - this._onDidChange.input = mapEvent(root.onDidChange, () => undefined); // TODO + this._onDidChange.input = Event.map(root.onDidChange, () => undefined); // TODO } get orientation(): Orientation { @@ -802,8 +802,7 @@ export class GridView implements IDisposable { const children: GridNode[] = []; let offset = 0; - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; + for (const child of node.children) { const childOrientation = orthogonal(orientation); const childBox: Box = orientation === Orientation.HORIZONTAL ? { top: box.top, left: box.left + offset, width: child.width, height: box.height } diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index 2a7a12fa0cd1..e2a8c936de5f 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. 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 * as objects from 'vs/base/common/objects'; import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; @@ -14,7 +13,7 @@ export interface IHighlight { end: number; } -export class HighlightedLabel implements IDisposable { +export class HighlightedLabel { private domNode: HTMLElement; private text: string; @@ -33,7 +32,7 @@ export class HighlightedLabel implements IDisposable { return this.domNode; } - set(text: string, highlights: IHighlight[] = [], title: string = '', escapeNewLines?: boolean) { + set(text: string | undefined, highlights: IHighlight[] = [], title: string = '', escapeNewLines?: boolean) { if (!text) { text = ''; } @@ -58,12 +57,10 @@ export class HighlightedLabel implements IDisposable { private render() { dom.clearNode(this.domNode); - let htmlContent: string[] = [], - highlight: IHighlight, - pos = 0; + let htmlContent: string[] = []; + let pos = 0; - for (let i = 0; i < this.highlights.length; i++) { - highlight = this.highlights[i]; + for (const highlight of this.highlights) { if (highlight.end === highlight.start) { continue; } @@ -93,11 +90,6 @@ export class HighlightedLabel implements IDisposable { this.didEverRender = true; } - dispose() { - 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 { let total = 0; diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 7ef40dcf7a2e..0dbe6dece51a 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -7,7 +7,7 @@ import 'vs/css!./iconlabel'; import * as dom from 'vs/base/browser/dom'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IMatch } from 'vs/base/common/filters'; -import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; export interface IIconLabelCreationOptions { supportHighlights?: boolean; @@ -100,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')), !options.donotSupportOcticons)); + this.labelNode = 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')), !options.donotSupportOcticons)); + this.descriptionNodeFactory = () => 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')))); } @@ -116,13 +116,7 @@ export class IconLabel extends Disposable { return this.domNode.element; } - onClick(callback: (event: MouseEvent) => void): IDisposable { - return combinedDisposable([ - dom.addDisposableListener(this.labelDescriptionContainer.element, dom.EventType.CLICK, (e: MouseEvent) => callback(e)), - ]); - } - - setValue(label?: string, description?: string, options?: IIconLabelValueOptions): void { + setLabel(label?: string, description?: string, options?: IIconLabelValueOptions): void { const classes = ['monaco-icon-label']; if (options) { if (options.extraClasses) { @@ -138,7 +132,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, options && options.title ? options.title : void 0, options && options.labelEscapeNewLines); + this.labelNode.set(label || '', options ? options.matches : undefined, options && options.title ? options.title : undefined, options && options.labelEscapeNewLines); } else { this.labelNode.textContent = label || ''; } @@ -149,7 +143,7 @@ export class IconLabel extends Disposable { } if (this.descriptionNode instanceof HighlightedLabel) { - this.descriptionNode.set(description || '', options ? options.descriptionMatches : void 0); + this.descriptionNode.set(description || '', options ? options.descriptionMatches : undefined); if (options && options.descriptionTitle) { this.descriptionNode.element.title = options.descriptionTitle; } else { @@ -163,4 +157,3 @@ export class IconLabel extends Disposable { } } } - diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 3c530fb1fee5..b2477bda6663 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -23,46 +23,46 @@ import { IHistoryNavigationWidget } from 'vs/base/browser/history'; const $ = dom.$; export interface IInputOptions extends IInputBoxStyles { - placeholder?: string; - ariaLabel?: string; - type?: string; - validationOptions?: IInputValidationOptions; - flexibleHeight?: boolean; - actions?: IAction[]; + readonly placeholder?: string; + readonly ariaLabel?: string; + readonly type?: string; + readonly validationOptions?: IInputValidationOptions; + readonly flexibleHeight?: boolean; + readonly actions?: IAction[]; // {{SQL CARBON EDIT}} Candidate for addition to vscode - min?: string; - max?: string; - useDefaultValidation?: boolean; + readonly min?: string; + readonly max?: string; + readonly useDefaultValidation?: boolean; } export interface IInputBoxStyles { - inputBackground?: Color; - inputForeground?: Color; - inputBorder?: Color; - inputValidationInfoBorder?: Color; - inputValidationInfoBackground?: Color; - inputValidationInfoForeground?: Color; - inputValidationWarningBorder?: Color; - inputValidationWarningBackground?: Color; - inputValidationWarningForeground?: Color; - inputValidationErrorBorder?: Color; - inputValidationErrorBackground?: Color; - inputValidationErrorForeground?: Color; + readonly inputBackground?: Color | null; + readonly inputForeground?: Color | null; + readonly inputBorder?: Color | null; + readonly inputValidationInfoBorder?: Color | null; + readonly inputValidationInfoBackground?: Color | null; + readonly inputValidationInfoForeground?: Color | null; + readonly inputValidationWarningBorder?: Color | null; + readonly inputValidationWarningBackground?: Color | null; + readonly inputValidationWarningForeground?: Color | null; + readonly inputValidationErrorBorder?: Color | null; + readonly inputValidationErrorBackground?: Color | null; + readonly inputValidationErrorForeground?: Color | null; } export interface IInputValidator { - (value: string): IMessage; + (value: string): IMessage | null; } export interface IMessage { - content: string; - formatContent?: boolean; // defaults to false - type?: MessageType; + readonly content: string; + readonly formatContent?: boolean; // defaults to false + readonly type?: MessageType; } export interface IInputValidationOptions { - validation: IInputValidator; + validation?: IInputValidator; } export const enum MessageType { @@ -89,34 +89,35 @@ export const defaultOpts = { }; export class InputBox extends Widget { - private contextViewProvider: IContextViewProvider; + private contextViewProvider?: IContextViewProvider; element: HTMLElement; private input: HTMLInputElement; private mirror: HTMLElement; - private actionbar: ActionBar; + private actionbar?: ActionBar; private options: IInputOptions; - private message: IMessage; + private message: IMessage | null; private placeholder: string; private ariaLabel: string; - private validation: IInputValidator; - private state = 'idle'; - private cachedHeight: number; + private validation?: IInputValidator; + private state: string | null = 'idle'; + private cachedHeight: number | null; - // {{SQL CARBON EDIT}} + // {{SQL CARBON EDIT}} - Add showValidationMessage and set inputBackground, inputForeground, and inputBorder as protected protected showValidationMessage: boolean; - protected inputBackground: Color; - protected inputForeground: Color; - protected inputBorder: Color; - - private inputValidationInfoBorder: Color; - private inputValidationInfoBackground: Color; - private inputValidationInfoForeground: Color; - private inputValidationWarningBorder: Color; - private inputValidationWarningBackground: Color; - private inputValidationWarningForeground: Color; - private inputValidationErrorBorder: Color; - private inputValidationErrorBackground: Color; - private inputValidationErrorForeground: Color; + protected inputBackground?: Color | null; + protected inputForeground?: Color | null; + protected inputBorder?: Color | null; + // {{SQL CARBON EDIT}} - End + + private inputValidationInfoBorder?: Color | null; + private inputValidationInfoBackground?: Color | null; + private inputValidationInfoForeground?: Color | null; + private inputValidationWarningBorder?: Color | null; + private inputValidationWarningBackground?: Color | null; + private inputValidationWarningForeground?: Color | null; + private inputValidationErrorBorder?: Color | null; + private inputValidationErrorBackground?: Color | null; + private inputValidationErrorForeground?: Color | null; private _onDidChange = this._register(new Emitter()); public readonly onDidChange: Event = this._onDidChange.event; @@ -124,7 +125,7 @@ export class InputBox extends Widget { private _onDidHeightChange = this._register(new Emitter()); public readonly onDidHeightChange: Event = this._onDidHeightChange.event; - constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) { + constructor(container: HTMLElement, contextViewProvider: IContextViewProvider | undefined, options?: IInputOptions) { super(); this.contextViewProvider = contextViewProvider; @@ -392,7 +393,7 @@ export class InputBox extends Widget { return errorMsg ? errorMsg.type !== MessageType.ERROR : true; } - private stylesForType(type: MessageType): { border: Color; background: Color; foreground: Color } { + private stylesForType(type: MessageType | undefined): { border: Color | undefined | null; background: Color | undefined | null; foreground: Color | undefined | null } { switch (type) { case MessageType.INFO: return { border: this.inputValidationInfoBorder, background: this.inputValidationInfoBackground, foreground: this.inputValidationInfoForeground }; case MessageType.WARNING: return { border: this.inputValidationWarningBorder, background: this.inputValidationWarningBackground, foreground: this.inputValidationWarningForeground }; @@ -400,7 +401,7 @@ export class InputBox extends Widget { } } - private classForType(type: MessageType): string { + private classForType(type: MessageType | undefined): string { switch (type) { case MessageType.INFO: return 'info'; case MessageType.WARNING: return 'warning'; @@ -423,6 +424,10 @@ export class InputBox extends Widget { getAnchor: () => this.element, anchorAlignment: AnchorAlignment.RIGHT, render: (container: HTMLElement) => { + if (!this.message) { + return null; + } + div = dom.append(container, $('.monaco-inputbox-container')); layout(); @@ -465,7 +470,7 @@ export class InputBox extends Widget { this.validate(); this.updateMirror(); - if (this.state === 'open') { + if (this.state === 'open' && this.contextViewProvider) { this.contextViewProvider.layout(); } } @@ -541,15 +546,13 @@ export class InputBox extends Widget { public dispose(): void { this._hideMessage(); - this.element = null; - this.input = null; - this.contextViewProvider = null; + this.element = null!; // StrictNullOverride: nulling out ok in dispose + this.input = null!; // StrictNullOverride: nulling out ok in dispose + this.contextViewProvider = undefined; this.message = null; - this.placeholder = null; - this.ariaLabel = null; - this.validation = null; + this.validation = undefined; this.state = null; - this.actionbar = null; + this.actionbar = undefined; super.dispose(); } @@ -563,7 +566,7 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge private readonly history: HistoryNavigator; - constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options: IHistoryInputOptions) { + constructor(container: HTMLElement, contextViewProvider: IContextViewProvider | undefined, options: IHistoryInputOptions) { super(container, contextViewProvider, options); this.history = new HistoryNavigator(options.history, 100); } @@ -614,7 +617,7 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge this.history.clear(); } - private getCurrentValue(): string { + private getCurrentValue(): string | null { let currentValue = this.history.current(); if (!currentValue) { currentValue = this.history.last(); @@ -623,11 +626,11 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge return currentValue; } - private getPreviousValue(): string { + private getPreviousValue(): string | null { return this.history.previous() || this.history.first(); } - private getNextValue(): string { + private getNextValue(): string | null { return this.history.next() || this.history.last(); } } diff --git a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts index c1aaf08beab6..4403232219d3 100644 --- a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts +++ b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./keybindingLabel'; -import { IDisposable } from 'vs/base/common/lifecycle'; import { equals } from 'vs/base/common/objects'; import { OperatingSystem } from 'vs/base/common/platform'; import { ResolvedKeybinding, ResolvedKeybindingPart } from 'vs/base/common/keyCodes'; import { UILabelProvider } from 'vs/base/common/keybindingLabels'; import * as dom from 'vs/base/browser/dom'; +import { localize } from 'vs/nls'; const $ = dom.$; @@ -26,14 +26,18 @@ export interface Matches { chordPart: PartMatches; } -export class KeybindingLabel implements IDisposable { +export interface KeybindingLabelOptions { + renderUnboundKeybindings: boolean; +} + +export class KeybindingLabel { private domNode: HTMLElement; private keybinding: ResolvedKeybinding; - private matches: Matches; + private matches: Matches | undefined; private didEverRender: boolean; - constructor(container: HTMLElement, private os: OperatingSystem) { + constructor(container: HTMLElement, private os: OperatingSystem, private options?: KeybindingLabelOptions) { this.domNode = dom.append(container, $('.monaco-keybinding')); this.didEverRender = false; container.appendChild(this.domNode); @@ -43,7 +47,7 @@ export class KeybindingLabel implements IDisposable { return this.domNode; } - set(keybinding: ResolvedKeybinding, matches: Matches) { + set(keybinding: ResolvedKeybinding, matches?: Matches) { if (this.didEverRender && this.keybinding === keybinding && KeybindingLabel.areSame(this.matches, matches)) { return; } @@ -66,6 +70,8 @@ export class KeybindingLabel implements IDisposable { this.renderPart(this.domNode, chordPart, this.matches ? this.matches.chordPart : null); } this.domNode.title = this.keybinding.getAriaLabel() || ''; + } else if (this.options && this.options.renderUnboundKeybindings) { + this.renderUnbound(this.domNode); } this.didEverRender = true; @@ -98,10 +104,11 @@ export class KeybindingLabel implements IDisposable { } } - dispose() { + private renderUnbound(parent: HTMLElement): void { + dom.append(parent, $('span.monaco-keybinding-key', undefined, localize('unbound', "Unbound"))); } - private static areSame(a: Matches, b: Matches): boolean { + private static areSame(a: Matches | undefined, b: Matches | undefined): boolean { if (a === b || (!a && !b)) { return true; } diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 1d3d75e2f2dc..fe4a6aaf01aa 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -29,6 +29,11 @@ height: 100%; } +.monaco-list.horizontal-scrolling .monaco-list-rows { + width: auto; + min-width: 100%; +} + .monaco-list-row { position: absolute; -moz-box-sizing: border-box; @@ -52,4 +57,138 @@ /* Focus */ .monaco-list.element-focused, .monaco-list.selection-single, .monaco-list.selection-multiple { outline: 0 !important; +} + +/* Dnd */ +.monaco-list-drag-image { + display: inline-block; + padding: 1px 7px; + border-radius: 10px; + font-size: 12px; + position: absolute; +} + +/* Type filter */ + +.monaco-list-type-filter { + display: flex; + align-items: center; + position: absolute; + border-radius: 2px; + padding: 0px 3px; + max-width: calc(100% - 10px); + text-overflow: ellipsis; + overflow: hidden; + text-align: right; + box-sizing: border-box; + cursor: all-scroll; + font-size: 13px; + line-height: 18px; + height: 20px; + z-index: 1; + top: 4px; +} + +.monaco-list-type-filter.dragging { + transition: top 0.2s, left 0.2s; +} + +.monaco-list-type-filter.ne { + right: 4px; +} + +.monaco-list-type-filter.nw { + left: 4px; +} + +.monaco-list-type-filter > .controls { + display: flex; + align-items: center; + box-sizing: border-box; + transition: width 0.2s; + width: 0; +} + +.monaco-list-type-filter.dragging > .controls, +.monaco-list-type-filter:hover > .controls { + width: 36px; +} + +.monaco-list-type-filter > .controls > * { + box-sizing: border-box; + width: 16px; + height: 16px; + margin: 0 0 0 2px; + flex-shrink: 0; +} + +.monaco-list-type-filter > .controls > .filter { + -webkit-appearance: none; + width: 16px; + height: 16px; + background: url("media/no-filter.svg"); + background-position: 50% 50%; + cursor: pointer; +} + +.monaco-list-type-filter > .controls > .filter:checked { + background-image: url("media/filter.svg"); +} + +.vs-dark .monaco-list-type-filter > .controls > .filter { + background-image: url("media/no-filter-dark.svg"); +} + +.vs-dark .monaco-list-type-filter > .controls > .filter:checked { + background-image: url("media/filter-dark.svg"); +} + +.hc-black .monaco-list-type-filter > .controls > .filter { + background-image: url("media/no-filter-hc.svg"); +} + +.hc-black .monaco-list-type-filter > .controls > .filter:checked { + background-image: url("media/filter-hc.svg"); +} + +.monaco-list-type-filter > .controls > .clear { + border: none; + background: url("media/close.svg"); + cursor: pointer; +} + +.vs-dark .monaco-list-type-filter > .controls > .clear { + background-image: url("media/close-dark.svg"); +} + +.hc-black .monaco-list-type-filter > .controls > .clear { + background-image: url("media/close-hc.svg"); +} + +.monaco-list-type-filter-message { + position: absolute; + box-sizing: border-box; + width: 100%; + height: 100%; + top: 0; + left: 0; + padding: 40px 1em 1em 1em; + text-align: center; + white-space: normal; + opacity: 0.7; + pointer-events: none; +} + +.monaco-list-type-filter-message:empty { + display: none; +} + +/* Electron */ + +.monaco-list-type-filter { + cursor: -webkit-grab; +} + +.monaco-list-type-filter.dragging { + cursor: -webkit-grabbing; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 0fba8c4a6a91..6ceeb8e55e35 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { GestureEvent } from 'vs/base/browser/touch'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; export interface IListVirtualDelegate { getHeight(element: T): number; @@ -15,7 +17,7 @@ export interface IListRenderer { templateId: string; renderTemplate(container: HTMLElement): TTemplateData; renderElement(element: T, index: number, templateData: TTemplateData): void; - disposeElement(element: T, index: number, templateData: TTemplateData): void; + disposeElement?(element: T, index: number, templateData: TTemplateData): void; disposeTemplate(templateData: TTemplateData): void; } @@ -43,6 +45,12 @@ export interface IListGestureEvent { index: number | undefined; } +export interface IListDragEvent { + browserEvent: DragEvent; + element: T | undefined; + index: number | undefined; +} + export interface IListContextMenuEvent { browserEvent: UIEvent; element: T | undefined; @@ -52,4 +60,47 @@ export interface IListContextMenuEvent { export interface IIdentityProvider { getId(element: T): { toString(): string; }; -} \ No newline at end of file +} + +export enum ListAriaRootRole { + /** default tree structure role */ + TREE = 'tree', + + /** role='tree' can interfere with screenreaders reading nested elements inside the tree row. Use FORM in that case. */ + FORM = 'form' +} + +export interface IKeyboardNavigationLabelProvider { + + /** + * Return a keyboard navigation label which will be used by the + * list for filtering/navigating. Return `undefined` to make an + * element always match. + */ + getKeyboardNavigationLabel(element: T): { toString(): string | undefined; } | undefined; + mightProducePrintableCharacter?(event: IKeyboardEvent): boolean; +} + +export const enum ListDragOverEffect { + Copy, + Move +} + +export interface IListDragOverReaction { + accept: boolean; + effect?: ListDragOverEffect; + feedback?: number[]; // use -1 for entire list +} + +export const ListDragOverReactions = { + reject(): IListDragOverReaction { return { accept: false }; }, + accept(): IListDragOverReaction { return { accept: true }; }, +}; + +export interface IListDragAndDrop { + getDragURI(element: T): string | null; + getDragLabel?(elements: T[]): string | undefined; + onDragStart?(data: IDragAndDropData, originalEvent: DragEvent): void; + onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IListDragOverReaction; + drop(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void; +} diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 65dd699c116c..99b514dd3aa1 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -9,7 +9,7 @@ import { range } from 'vs/base/common/arrays'; import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent } from './list'; import { List, IListStyles, IListOptions } from './listWidget'; import { IPagedModel } from 'vs/base/common/paging'; -import { Event, mapEvent } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; export interface IPagedRenderer extends IListRenderer { @@ -58,10 +58,6 @@ class PagedRenderer implements IListRenderer this.renderer.renderElement(entry, index, data.data!)); } - disposeElement(): void { - // noop - } - disposeTemplate(data: ITemplateData): void { if (data.disposable) { data.disposable.dispose(); @@ -118,23 +114,23 @@ export class PagedList implements IDisposable { } get onFocusChange(): Event> { - return mapEvent(this.list.onFocusChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); + return Event.map(this.list.onFocusChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); } get onOpen(): Event> { - return mapEvent(this.list.onOpen, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent })); + return Event.map(this.list.onDidOpen, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent })); } get onSelectionChange(): Event> { - return mapEvent(this.list.onSelectionChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); + return Event.map(this.list.onSelectionChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); } get onPin(): Event> { - return mapEvent(this.list.onPin, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); + return Event.map(this.list.onPin, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); } get onContextMenu(): Event> { - return mapEvent(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => (typeof element === 'undefined' ? { element, index, anchor, browserEvent } : { element: this._model.get(element), index, anchor, browserEvent })); + return Event.map(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => (typeof element === 'undefined' ? { element, index, anchor, browserEvent } : { element: this._model.get(element), index, anchor, browserEvent })); } get model(): IPagedModel { @@ -194,8 +190,8 @@ export class PagedList implements IDisposable { return this.list.getSelection(); } - layout(height?: number): void { - this.list.layout(height); + layout(height?: number, width?: number): void { + this.list.layout(height, width); } reveal(index: number, relativeTop?: number): void { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index a0891ac96f0c..1be89e4cd518 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -4,34 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import { getOrDefault } from 'vs/base/common/objects'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import * as DOM from 'vs/base/browser/dom'; -import { Event, mapEvent, filterEvent, Emitter, latch } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable'; import { RangeMap, shift } from './rangeMap'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IListDragAndDrop, ListDragOverEffect } from './list'; import { RowCache, IRow } from './rowCache'; import { isWindows } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; import { ISpliceable } from 'vs/base/common/sequence'; import { memoize } from 'vs/base/common/decorators'; -import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; import { Range, IRange } from 'vs/base/common/range'; - -function canUseTranslate3d(): boolean { - if (browser.isFirefox) { - return false; - } - - if (browser.getZoomLevel() !== 0) { - return false; - } - - return true; -} +import { equals, distinct } from 'vs/base/common/arrays'; +import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; +import { disposableTimeout, Delayer } from 'vs/base/common/async'; interface IItem { readonly id: string; @@ -39,25 +29,117 @@ interface IItem { readonly templateId: string; row: IRow | null; size: number; + width: number | undefined; hasDynamicHeight: boolean; - renderWidth: number | undefined; + lastDynamicHeightWidth: number | undefined; + uri: string | undefined; + dropTarget: boolean; + dragStartDisposable: IDisposable; } -export interface IListViewOptions { +export interface IListViewDragAndDrop extends IListDragAndDrop { + getDragElements(element: T): T[]; +} + +export interface IListViewOptions { + readonly dnd?: IListViewDragAndDrop; readonly useShadows?: boolean; readonly verticalScrollMode?: ScrollbarVisibility; readonly setRowLineHeight?: boolean; readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; + readonly horizontalScrolling?: boolean; } const DefaultOptions = { useShadows: true, verticalScrollMode: ScrollbarVisibility.Auto, setRowLineHeight: true, - supportDynamicHeights: false + supportDynamicHeights: false, + dnd: { + getDragElements(e) { return [e]; }, + getDragURI() { return null; }, + onDragStart(): void { }, + onDragOver() { return false; }, + drop() { } + }, + horizontalScrolling: false }; +export class ElementsDragAndDropData implements IDragAndDropData { + + readonly elements: T[]; + + constructor(elements: T[]) { + this.elements = elements; + } + + update(): void { } + + getData(): any { + return this.elements; + } +} + +export class ExternalElementsDragAndDropData implements IDragAndDropData { + + readonly elements: T[]; + + constructor(elements: T[]) { + this.elements = elements; + } + + update(): void { } + + getData(): any { + return this.elements; + } +} + +export class DesktopDragAndDropData implements IDragAndDropData { + + readonly types: any[]; + readonly files: any[]; + + constructor() { + this.types = []; + this.files = []; + } + + update(dataTransfer: DataTransfer): void { + if (dataTransfer.types) { + this.types.splice(0, this.types.length, ...dataTransfer.types); + } + + if (dataTransfer.files) { + this.files.splice(0, this.files.length); + + for (let i = 0; i < dataTransfer.files.length; i++) { + const file = dataTransfer.files.item(i); + + if (file && (file.size || file.type)) { + this.files.push(file); + } + } + } + } + + getData(): any { + return { + types: this.types, + files: this.files + }; + } +} + +function equalsDragFeedback(f1: number[] | undefined, f2: number[] | undefined): boolean { + if (Array.isArray(f1) && Array.isArray(f2)) { + return equals(f1, f2!); + } + + return f1 === f2; +} + export class ListView implements ISpliceable, IDisposable { readonly domNode: HTMLElement; @@ -75,24 +157,50 @@ export class ListView implements ISpliceable, IDisposable { private scrollableElement: ScrollableElement; private _scrollHeight: number; private scrollableElementUpdateDisposable: IDisposable | null = null; + private scrollableElementWidthDelayer = new Delayer(50); private splicing = false; - private dragAndDropScrollInterval: number; - private dragAndDropScrollTimeout: number; - private dragAndDropMouseY: number; + private dragOverAnimationDisposable: IDisposable | undefined; + private dragOverAnimationStopDisposable: IDisposable = Disposable.None; + private dragOverMouseY: number; private setRowLineHeight: boolean; private supportDynamicHeights: boolean; + private horizontalScrolling: boolean; + private scrollWidth: number | undefined; + private canUseTranslate3d: boolean | undefined = undefined; + + private dnd: IListViewDragAndDrop; + private canDrop: boolean = false; + private currentDragData: IDragAndDropData | undefined; + private currentDragFeedback: number[] | undefined; + private currentDragFeedbackDisposable: IDisposable = Disposable.None; + private onDragLeaveTimeout: IDisposable = Disposable.None; + private disposables: IDisposable[]; private _onDidChangeContentHeight = new Emitter(); - readonly onDidChangeContentHeight: Event = latch(this._onDidChangeContentHeight.event); + readonly onDidChangeContentHeight: Event = Event.latch(this._onDidChangeContentHeight.event); get contentHeight(): number { return this.rangeMap.size; } + readonly onDidScroll: Event; + + // private _onDragStart = new Emitter<{ element: T, uri: string, event: DragEvent }>(); + // readonly onDragStart = this._onDragStart.event; + + // readonly onDragOver: Event>; + // readonly onDragLeave: Event; + // readonly onDrop: Event>; + // readonly onDragEnd: Event; + constructor( container: HTMLElement, private virtualDelegate: IListVirtualDelegate, renderers: IListRenderer[], - options: IListViewOptions = DefaultOptions + options: IListViewOptions = DefaultOptions ) { + if (options.horizontalScrolling && options.supportDynamicHeights) { + throw new Error('Horizontal scrolling and dynamic heights not supported simultaneously'); + } + this.items = []; this.itemId = 0; this.rangeMap = new RangeMap(); @@ -110,13 +218,16 @@ export class ListView implements ISpliceable, IDisposable { this.domNode.className = 'monaco-list'; DOM.toggleClass(this.domNode, 'mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true); + this.horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling); + DOM.toggleClass(this.domNode, 'horizontal-scrolling', this.horizontalScrolling); + this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; Gesture.addTarget(this.rowsContainer); this.scrollableElement = new ScrollableElement(this.rowsContainer, { alwaysConsumeMouseWheel: true, - horizontal: ScrollbarVisibility.Hidden, + horizontal: this.horizontalScrolling ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden, vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows) }); @@ -126,6 +237,7 @@ export class ListView implements ISpliceable, IDisposable { this.disposables = [this.rangeMap, this.gesture, this.scrollableElement, this.cache]; + this.onDidScroll = Event.signal(this.scrollableElement.onScroll); this.scrollableElement.onScroll(this.onScroll, this, this.disposables); domEvent(this.rowsContainer, TouchEventType.Change)(this.onTouchChange, this, this.disposables); @@ -134,11 +246,14 @@ export class ListView implements ISpliceable, IDisposable { domEvent(this.scrollableElement.getDomNode(), 'scroll') (e => (e.target as HTMLElement).scrollTop = 0, null, this.disposables); - const onDragOver = mapEvent(domEvent(this.rowsContainer, 'dragover'), e => new DragMouseEvent(e)); - onDragOver(this.onDragOver, this, this.disposables); + Event.map(domEvent(this.domNode, 'dragover'), e => this.toDragEvent(e))(this.onDragOver, this, this.disposables); + Event.map(domEvent(this.domNode, 'drop'), e => this.toDragEvent(e))(this.onDrop, this, this.disposables); + domEvent(this.domNode, 'dragleave')(this.onDragLeave, this, this.disposables); + domEvent(window, 'dragend')(this.onDragEnd, this, this.disposables); this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); + this.dnd = getOrDefault, IListViewDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); this.layout(); } @@ -176,9 +291,13 @@ export class ListView implements ISpliceable, IDisposable { element, templateId: this.virtualDelegate.getTemplateId(element), size: this.virtualDelegate.getHeight(element), + width: undefined, hasDynamicHeight: !!this.virtualDelegate.hasDynamicHeight && this.virtualDelegate.hasDynamicHeight(element), - renderWidth: undefined, - row: null + lastDynamicHeightWidth: undefined, + row: null, + uri: undefined, + dropTarget: false, + dragStartDisposable: Disposable.None })); let deleted: IItem[]; @@ -222,7 +341,7 @@ export class ListView implements ISpliceable, IDisposable { } } - this.updateScrollHeight(); + this.eventuallyUpdateScrollDimensions(); if (this.supportDynamicHeights) { this.rerender(this.scrollTop, this.renderHeight); @@ -231,18 +350,62 @@ export class ListView implements ISpliceable, IDisposable { return deleted.map(i => i.element); } - private updateScrollHeight(): void { + private eventuallyUpdateScrollDimensions(): void { this._scrollHeight = this.contentHeight; this.rowsContainer.style.height = `${this._scrollHeight}px`; if (!this.scrollableElementUpdateDisposable) { this.scrollableElementUpdateDisposable = DOM.scheduleAtNextAnimationFrame(() => { - this.scrollableElement.setScrollDimensions({ scrollHeight: this._scrollHeight }); + this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight }); + this.updateScrollWidth(); this.scrollableElementUpdateDisposable = null; }); } } + private eventuallyUpdateScrollWidth(): void { + if (!this.horizontalScrolling) { + return; + } + + this.scrollableElementWidthDelayer.trigger(() => this.updateScrollWidth()); + } + + private updateScrollWidth(): void { + if (!this.horizontalScrolling) { + return; + } + + if (this.items.length === 0) { + this.scrollableElement.setScrollDimensions({ scrollWidth: 0 }); + } + + let scrollWidth = 0; + + for (const item of this.items) { + if (typeof item.width !== 'undefined') { + scrollWidth = Math.max(scrollWidth, item.width); + } + } + + this.scrollWidth = scrollWidth; + this.scrollableElement.setScrollDimensions({ scrollWidth: scrollWidth + 10 }); + } + + updateWidth(index: number): void { + if (!this.horizontalScrolling || typeof this.scrollWidth === 'undefined') { + return; + } + + const item = this.items[index]; + this.measureItemWidth(item); + + if (typeof item.width !== 'undefined' && item.width > this.scrollWidth) { + this.scrollWidth = item.width; + this.scrollableElement.setScrollDimensions({ scrollWidth: this.scrollWidth + 10 }); + } + } + get length(): number { return this.items.length; } @@ -277,31 +440,37 @@ export class ListView implements ISpliceable, IDisposable { return this.rangeMap.indexAfter(position); } - layout(height?: number): void { + layout(height?: number, width?: number): void { let scrollDimensions: INewScrollDimensions = { - height: height || DOM.getContentHeight(this.domNode) + height: typeof height === 'number' ? height : DOM.getContentHeight(this.domNode) }; if (this.scrollableElementUpdateDisposable) { this.scrollableElementUpdateDisposable.dispose(); this.scrollableElementUpdateDisposable = null; - scrollDimensions.scrollHeight = this._scrollHeight; + scrollDimensions.scrollHeight = this.scrollHeight; } this.scrollableElement.setScrollDimensions(scrollDimensions); - } - layoutWidth(width: number): void { - this.renderWidth = width; + if (typeof width !== 'undefined') { + this.renderWidth = width; - if (this.supportDynamicHeights) { - this.rerender(this.scrollTop, this.renderHeight); + if (this.supportDynamicHeights) { + this.rerender(this.scrollTop, this.renderHeight); + } + + if (this.horizontalScrolling) { + this.scrollableElement.setScrollDimensions({ + width: typeof width === 'number' ? width : DOM.getContentWidth(this.domNode) + }); + } } } // Render - private render(renderTop: number, renderHeight: number): void { + private render(renderTop: number, renderHeight: number, renderLeft: number, scrollWidth: number): void { const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); const renderRange = this.getRenderRange(renderTop, renderHeight); @@ -321,14 +490,33 @@ export class ListView implements ISpliceable, IDisposable { } } - if (canUseTranslate3d() && !isWindows /* Windows: translate3d breaks subpixel-antialias (ClearType) unless a background is defined */) { - const transform = `translate3d(0px, -${renderTop}px, 0px)`; + const canUseTranslate3d = !isWindows && !browser.isFirefox && browser.getZoomLevel() === 0; + + if (canUseTranslate3d) { + const transform = `translate3d(-${renderLeft}px, -${renderTop}px, 0px)`; this.rowsContainer.style.transform = transform; this.rowsContainer.style.webkitTransform = transform; + + if (canUseTranslate3d !== this.canUseTranslate3d) { + this.rowsContainer.style.left = '0'; + this.rowsContainer.style.top = '0'; + } } else { + this.rowsContainer.style.left = `-${renderLeft}px`; this.rowsContainer.style.top = `-${renderTop}px`; + + if (canUseTranslate3d !== this.canUseTranslate3d) { + this.rowsContainer.style.transform = ''; + this.rowsContainer.style.webkitTransform = ''; + } + } + + if (this.horizontalScrolling) { + this.rowsContainer.style.width = `${Math.max(scrollWidth, this.renderWidth)}px`; } + this.canUseTranslate3d = canUseTranslate3d; + this.lastRenderTop = renderTop; this.lastRenderHeight = renderHeight; } @@ -353,7 +541,48 @@ export class ListView implements ISpliceable, IDisposable { this.updateItemInDOM(item, index); const renderer = this.renderers.get(item.templateId); - renderer.renderElement(item.element, index, item.row.templateData); + + if (!renderer) { + throw new Error(`No renderer found for template id ${item.templateId}`); + } + + if (renderer) { + renderer.renderElement(item.element, index, item.row.templateData); + } + + const uri = this.dnd.getDragURI(item.element); + item.dragStartDisposable.dispose(); + item.row.domNode!.draggable = !!uri; + + if (uri) { + const onDragStart = domEvent(item.row.domNode!, 'dragstart'); + item.dragStartDisposable = onDragStart(event => this.onDragStart(item.element, uri, event)); + } + + if (this.horizontalScrolling) { + this.measureItemWidth(item); + this.eventuallyUpdateScrollWidth(); + } + } + + private measureItemWidth(item: IItem): void { + if (!item.row || !item.row.domNode) { + return; + } + + item.row.domNode.style.width = 'fit-content'; + item.width = DOM.getContentWidth(item.row.domNode); + const style = window.getComputedStyle(item.row.domNode); + + if (style.paddingLeft) { + item.width += parseFloat(style.paddingLeft); + } + + if (style.paddingRight) { + item.width += parseFloat(style.paddingRight); + } + + item.row.domNode.style.width = ''; } private updateItemInDOM(item: IItem, index: number): void { @@ -368,18 +597,24 @@ export class ListView implements ISpliceable, IDisposable { 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}`); + DOM.toggleClass(item.row!.domNode!, 'drop-target', item.dropTarget); } private removeItemFromDOM(index: number): void { const item = this.items[index]; - const renderer = this.renderers.get(item.templateId); + item.dragStartDisposable.dispose(); - if (renderer.disposeElement) { + const renderer = this.renderers.get(item.templateId); + if (renderer && renderer.disposeElement) { renderer.disposeElement(item.element, index, item.row!.templateData); } this.cache.release(item.row!); item.row = null; + + if (this.horizontalScrolling) { + this.eventuallyUpdateScrollWidth(); + } } getScrollTop(): number { @@ -391,7 +626,7 @@ export class ListView implements ISpliceable, IDisposable { if (this.scrollableElementUpdateDisposable) { this.scrollableElementUpdateDisposable.dispose(); this.scrollableElementUpdateDisposable = null; - this.scrollableElement.setScrollDimensions({ scrollHeight: this._scrollHeight }); + this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight }); } this.scrollableElement.setScrollPosition({ scrollTop }); @@ -406,22 +641,22 @@ export class ListView implements ISpliceable, IDisposable { } get scrollHeight(): number { - return this._scrollHeight; + return this._scrollHeight + (this.horizontalScrolling ? 10 : 0); } // Events - @memoize get onMouseClick(): Event> { return mapEvent(domEvent(this.domNode, 'click'), e => this.toMouseEvent(e)); } - @memoize get onMouseDblClick(): Event> { return mapEvent(domEvent(this.domNode, 'dblclick'), e => this.toMouseEvent(e)); } - @memoize get onMouseMiddleClick(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'auxclick'), e => this.toMouseEvent(e as MouseEvent)), e => e.browserEvent.button === 1); } - @memoize get onMouseUp(): Event> { return mapEvent(domEvent(this.domNode, 'mouseup'), e => this.toMouseEvent(e)); } - @memoize get onMouseDown(): Event> { return mapEvent(domEvent(this.domNode, 'mousedown'), e => this.toMouseEvent(e)); } - @memoize get onMouseOver(): Event> { return mapEvent(domEvent(this.domNode, 'mouseover'), e => this.toMouseEvent(e)); } - @memoize get onMouseMove(): Event> { return mapEvent(domEvent(this.domNode, 'mousemove'), e => this.toMouseEvent(e)); } - @memoize get onMouseOut(): Event> { return mapEvent(domEvent(this.domNode, 'mouseout'), e => this.toMouseEvent(e)); } - @memoize get onContextMenu(): Event> { return mapEvent(domEvent(this.domNode, 'contextmenu'), e => this.toMouseEvent(e)); } - @memoize get onTouchStart(): Event> { return mapEvent(domEvent(this.domNode, 'touchstart'), e => this.toTouchEvent(e)); } - @memoize get onTap(): Event> { return mapEvent(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)); } + @memoize get onMouseClick(): Event> { return Event.map(domEvent(this.domNode, 'click'), e => this.toMouseEvent(e)); } + @memoize get onMouseDblClick(): Event> { return Event.map(domEvent(this.domNode, 'dblclick'), e => this.toMouseEvent(e)); } + @memoize get onMouseMiddleClick(): Event> { return Event.filter(Event.map(domEvent(this.domNode, 'auxclick'), e => this.toMouseEvent(e as MouseEvent)), e => e.browserEvent.button === 1); } + @memoize get onMouseUp(): Event> { return Event.map(domEvent(this.domNode, 'mouseup'), e => this.toMouseEvent(e)); } + @memoize get onMouseDown(): Event> { return Event.map(domEvent(this.domNode, 'mousedown'), e => this.toMouseEvent(e)); } + @memoize get onMouseOver(): Event> { return Event.map(domEvent(this.domNode, 'mouseover'), e => this.toMouseEvent(e)); } + @memoize get onMouseMove(): Event> { return Event.map(domEvent(this.domNode, 'mousemove'), e => this.toMouseEvent(e)); } + @memoize get onMouseOut(): Event> { return Event.map(domEvent(this.domNode, 'mouseout'), e => this.toMouseEvent(e)); } + @memoize get onContextMenu(): Event> { return Event.map(domEvent(this.domNode, 'contextmenu'), e => this.toMouseEvent(e)); } + @memoize get onTouchStart(): Event> { return Event.map(domEvent(this.domNode, 'touchstart'), e => this.toTouchEvent(e)); } + @memoize get onTap(): Event> { return Event.map(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)); } private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent { const index = this.getItemIndexFromEventTarget(browserEvent.target || null); @@ -444,9 +679,16 @@ export class ListView implements ISpliceable, IDisposable { return { browserEvent, index, element }; } + private toDragEvent(browserEvent: DragEvent): IListDragEvent { + const index = this.getItemIndexFromEventTarget(browserEvent.target || null); + const item = typeof index === 'undefined' ? undefined : this.items[index]; + const element = item && item.element; + return { browserEvent, index, element }; + } + private onScroll(e: ScrollEvent): void { try { - this.render(e.scrollTop, e.height); + this.render(e.scrollTop, e.height, e.scrollLeft, e.scrollWidth); if (this.supportDynamicHeights) { this.rerender(e.scrollTop, e.height); @@ -464,55 +706,218 @@ export class ListView implements ISpliceable, IDisposable { this.scrollTop -= event.translationY; } - private onDragOver(event: DragMouseEvent): void { - this.setupDragAndDropScrollInterval(); - this.dragAndDropMouseY = event.posy; + // DND + + private onDragStart(element: T, uri: string, event: DragEvent): void { + if (!event.dataTransfer) { + return; + } + + const elements = this.dnd.getDragElements(element); + + event.dataTransfer.effectAllowed = 'copyMove'; + event.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([uri])); + + if (event.dataTransfer.setDragImage) { + let label: string | undefined; + + if (this.dnd.getDragLabel) { + label = this.dnd.getDragLabel(elements); + } + + if (typeof label === 'undefined') { + label = String(elements.length); + } + + const dragImage = DOM.$('.monaco-list-drag-image'); + dragImage.textContent = label; + document.body.appendChild(dragImage); + event.dataTransfer.setDragImage(dragImage, -10, -10); + setTimeout(() => document.body.removeChild(dragImage), 0); + } + + this.currentDragData = new ElementsDragAndDropData(elements); + StaticDND.CurrentDragAndDropData = new ExternalElementsDragAndDropData(elements); + + if (this.dnd.onDragStart) { + this.dnd.onDragStart(this.currentDragData, event); + } } - private setupDragAndDropScrollInterval(): void { - const viewTop = DOM.getTopLeftOffset(this.domNode).top; + private onDragOver(event: IListDragEvent): boolean { + this.onDragLeaveTimeout.dispose(); + + if (StaticDND.CurrentDragAndDropData && StaticDND.CurrentDragAndDropData.getData() === 'vscode-ui') { + return false; + } + + this.setupDragAndDropScrollTopAnimation(event.browserEvent); + + if (!event.browserEvent.dataTransfer) { + return false; + } + + // Drag over from outside + if (!this.currentDragData) { + if (StaticDND.CurrentDragAndDropData) { + // Drag over from another list + this.currentDragData = StaticDND.CurrentDragAndDropData; - if (!this.dragAndDropScrollInterval) { - this.dragAndDropScrollInterval = window.setInterval(() => { - if (this.dragAndDropMouseY === undefined) { - return; + } else { + // Drag over from the desktop + if (!event.browserEvent.dataTransfer.types) { + return false; } - var diff = this.dragAndDropMouseY - viewTop; - var scrollDiff = 0; - var upperLimit = this.renderHeight - 35; + this.currentDragData = new DesktopDragAndDropData(); + } + } + + const result = this.dnd.onDragOver(this.currentDragData, event.element, event.index, event.browserEvent); + this.canDrop = typeof result === 'boolean' ? result : result.accept; + + if (!this.canDrop) { + this.currentDragFeedback = undefined; + this.currentDragFeedbackDisposable.dispose(); + return false; + } + + event.browserEvent.dataTransfer.dropEffect = (typeof result !== 'boolean' && result.effect === ListDragOverEffect.Copy) ? 'copy' : 'move'; + + let feedback: number[]; - if (diff < 35) { - scrollDiff = Math.max(-14, 0.2 * (diff - 35)); - } else if (diff > upperLimit) { - scrollDiff = Math.min(14, 0.2 * (diff - upperLimit)); + if (typeof result !== 'boolean' && result.feedback) { + feedback = result.feedback; + } else { + if (typeof event.index === 'undefined') { + feedback = [-1]; + } else { + feedback = [event.index]; + } + } + + // sanitize feedback list + feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort(); + feedback = feedback[0] === -1 ? [-1] : feedback; + + if (feedback.length === 0) { + throw new Error('Invalid empty feedback list'); + } + + if (equalsDragFeedback(this.currentDragFeedback, feedback)) { + return true; + } + + this.currentDragFeedback = feedback; + this.currentDragFeedbackDisposable.dispose(); + + if (feedback[0] === -1) { // entire list feedback + DOM.addClass(this.domNode, 'drop-target'); + this.currentDragFeedbackDisposable = toDisposable(() => DOM.removeClass(this.domNode, 'drop-target')); + } else { + for (const index of feedback) { + const item = this.items[index]!; + item.dropTarget = true; + + if (item.row && item.row.domNode) { + DOM.addClass(item.row.domNode, 'drop-target'); + } + } + + this.currentDragFeedbackDisposable = toDisposable(() => { + for (const index of feedback) { + const item = this.items[index]!; + item.dropTarget = false; + + if (item.row && item.row.domNode) { + DOM.removeClass(item.row.domNode, 'drop-target'); + } } + }); + } + + return true; + } + + private onDragLeave(): void { + this.onDragLeaveTimeout.dispose(); + this.onDragLeaveTimeout = disposableTimeout(() => this.clearDragOverFeedback(), 100); + } - this.scrollTop += scrollDiff; - }, 10); + private onDrop(event: IListDragEvent): void { + if (!this.canDrop) { + return; + } + + const dragData = this.currentDragData; + this.teardownDragAndDropScrollTopAnimation(); + this.clearDragOverFeedback(); + this.currentDragData = undefined; + StaticDND.CurrentDragAndDropData = undefined; + + if (!dragData || !event.browserEvent.dataTransfer) { + return; + } + + event.browserEvent.preventDefault(); + dragData.update(event.browserEvent.dataTransfer); + this.dnd.drop(dragData, event.element, event.index, event.browserEvent); + } - this.cancelDragAndDropScrollTimeout(); + private onDragEnd(): void { + this.canDrop = false; + this.teardownDragAndDropScrollTopAnimation(); + this.clearDragOverFeedback(); + this.currentDragData = undefined; + StaticDND.CurrentDragAndDropData = undefined; + } + + private clearDragOverFeedback(): void { + this.currentDragFeedback = undefined; + this.currentDragFeedbackDisposable.dispose(); + this.currentDragFeedbackDisposable = Disposable.None; + } - this.dragAndDropScrollTimeout = window.setTimeout(() => { - this.cancelDragAndDropScrollInterval(); - this.dragAndDropScrollTimeout = -1; - }, 1000); + // DND scroll top animation + + private setupDragAndDropScrollTopAnimation(event: DragEvent): void { + if (!this.dragOverAnimationDisposable) { + const viewTop = DOM.getTopLeftOffset(this.domNode).top; + this.dragOverAnimationDisposable = DOM.animate(this.animateDragAndDropScrollTop.bind(this, viewTop)); } + + this.dragOverAnimationStopDisposable.dispose(); + this.dragOverAnimationStopDisposable = disposableTimeout(() => { + if (this.dragOverAnimationDisposable) { + this.dragOverAnimationDisposable.dispose(); + this.dragOverAnimationDisposable = undefined; + } + }, 1000); + + this.dragOverMouseY = event.pageY; } - private cancelDragAndDropScrollInterval(): void { - if (this.dragAndDropScrollInterval) { - window.clearInterval(this.dragAndDropScrollInterval); - this.dragAndDropScrollInterval = -1; + private animateDragAndDropScrollTop(viewTop: number): void { + if (this.dragOverMouseY === undefined) { + return; } - this.cancelDragAndDropScrollTimeout(); + const diff = this.dragOverMouseY - viewTop; + const upperLimit = this.renderHeight - 35; + + if (diff < 35) { + this.scrollTop += Math.max(-14, Math.floor(0.3 * (diff - 35))); + } else if (diff > upperLimit) { + this.scrollTop += Math.min(14, Math.floor(0.3 * (diff - upperLimit))); + } } - private cancelDragAndDropScrollTimeout(): void { - if (this.dragAndDropScrollTimeout) { - window.clearTimeout(this.dragAndDropScrollTimeout); - this.dragAndDropScrollTimeout = -1; + private teardownDragAndDropScrollTopAnimation(): void { + this.dragOverAnimationStopDisposable.dispose(); + + if (this.dragOverAnimationDisposable) { + this.dragOverAnimationDisposable.dispose(); + this.dragOverAnimationDisposable = undefined; } } @@ -554,12 +959,15 @@ export class ListView implements ISpliceable, IDisposable { // Let's remember the second element's position, this helps in scrolling up // and preserving a linear upwards scroll movement - let secondElementIndex: number | undefined; - let secondElementTopDelta: number | undefined; - - if (previousRenderRange.end - previousRenderRange.start > 1) { - secondElementIndex = previousRenderRange.start + 1; - secondElementTopDelta = this.elementTop(secondElementIndex) - renderTop; + let anchorElementIndex: number | undefined; + let anchorElementTopDelta: number | undefined; + + if (renderTop === this.elementTop(previousRenderRange.start)) { + anchorElementIndex = previousRenderRange.start; + anchorElementTopDelta = 0; + } else if (previousRenderRange.end - previousRenderRange.start > 1) { + anchorElementIndex = previousRenderRange.start + 1; + anchorElementTopDelta = this.elementTop(anchorElementIndex) - renderTop; } let heightDiff = 0; @@ -582,7 +990,7 @@ export class ListView implements ISpliceable, IDisposable { if (!didChange) { if (heightDiff !== 0) { - this.updateScrollHeight(); + this.eventuallyUpdateScrollDimensions(); } const unrenderRanges = Range.relativeComplement(previousRenderRange, renderRange); @@ -595,14 +1003,25 @@ export class ListView implements ISpliceable, IDisposable { } } + const renderRanges = Range.relativeComplement(renderRange, previousRenderRange); + + for (const range of renderRanges) { + for (let i = range.start; i < range.end; i++) { + const afterIndex = i + 1; + const beforeRow = afterIndex < this.items.length ? this.items[afterIndex].row : null; + const beforeElement = beforeRow ? beforeRow.domNode : null; + this.insertItemInDOM(i, beforeElement); + } + } + for (let i = renderRange.start; i < renderRange.end; i++) { if (this.items[i].row) { this.updateItemInDOM(this.items[i], i); } } - if (typeof secondElementIndex === 'number') { - this.scrollTop = this.elementTop(secondElementIndex) - secondElementTopDelta!; + if (typeof anchorElementIndex === 'number') { + this.scrollTop = this.elementTop(anchorElementIndex) - anchorElementTopDelta!; } this._onDidChangeContentHeight.fire(this.contentHeight); @@ -614,7 +1033,7 @@ export class ListView implements ISpliceable, IDisposable { private probeDynamicHeight(index: number): number { const item = this.items[index]; - if (!item.hasDynamicHeight || item.renderWidth === this.renderWidth) { + if (!item.hasDynamicHeight || item.lastDynamicHeightWidth === this.renderWidth) { return 0; } @@ -624,9 +1043,11 @@ export class ListView implements ISpliceable, IDisposable { row.domNode!.style.height = ''; this.rowsContainer.appendChild(row.domNode!); - renderer.renderElement(item.element, index, row.templateData); + if (renderer) { + renderer.renderElement(item.element, index, row.templateData); + } item.size = row.domNode!.offsetHeight; - item.renderWidth = this.renderWidth; + item.lastDynamicHeightWidth = this.renderWidth; this.rowsContainer.removeChild(row.domNode!); this.cache.release(row); @@ -660,7 +1081,9 @@ export class ListView implements ISpliceable, IDisposable { for (const item of this.items) { if (item.row) { const renderer = this.renderers.get(item.row.templateId); - renderer.disposeTemplate(item.row.templateData); + if (renderer) { + renderer.disposeTemplate(item.row.templateData); + } } } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index f85a7e8b8438..f819f74af602 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -13,17 +13,19 @@ import * as DOM from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; import { Gesture } from 'vs/base/browser/touch'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { Event, Emitter, EventBufferer, chain, mapEvent, anyEvent } from 'vs/base/common/event'; +import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { Event, Emitter, EventBufferer } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider } from './list'; -import { ListView, IListViewOptions } from './listView'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole } from './list'; +import { ListView, IListViewOptions, IListViewDragAndDrop } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISpliceable } from 'vs/base/common/sequence'; import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice'; import { clamp } from 'vs/base/common/numbers'; +import { matchesPrefix } from 'vs/base/common/filters'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; interface ITraitChangeEvent { indexes: number[]; @@ -66,15 +68,10 @@ class TraitRenderer implements IListRenderer this.trait.renderIndex(index, templateData); } - disposeElement(): void { - // noop - } - splice(start: number, deleteCount: number, insertCount: number): void { const rendered: IRenderedContainer[] = []; - for (let i = 0; i < this.renderedElements.length; i++) { - const renderedElement = this.renderedElements[i]; + for (const renderedElement of this.renderedElements) { if (renderedElement.index < start) { rendered.push(renderedElement); @@ -216,7 +213,7 @@ class TraitSpliceable implements ISpliceable { splice(start: number, deleteCount: number, elements: T[]): void { if (!this.identityProvider) { - return this.trait.splice(start, deleteCount, elements.map(e => false)); + return this.trait.splice(start, deleteCount, elements.map(() => false)); } const pastElementsWithTrait = this.trait.get().map(i => this.identityProvider!.getId(this.view.element(i)).toString()); @@ -245,7 +242,7 @@ class KeyboardController implements IDisposable { this.openController = options.openController || DefaultOpenController; - const onKeyDown = chain(domEvent(view.domNode, 'keydown')) + const onKeyDown = Event.chain(domEvent(view.domNode, 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -322,6 +319,106 @@ class KeyboardController implements IDisposable { } } +enum TypeLabelControllerState { + Idle, + Typing +} + +export function mightProducePrintableCharacter(event: IKeyboardEvent): boolean { + if (event.ctrlKey || event.metaKey || event.altKey) { + return false; + } + + return (event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z) + || (event.keyCode >= KeyCode.KEY_0 && event.keyCode <= KeyCode.KEY_9) + || (event.keyCode >= KeyCode.US_SEMICOLON && event.keyCode <= KeyCode.US_QUOTE); +} + +class TypeLabelController implements IDisposable { + + private enabled = false; + private state: TypeLabelControllerState = TypeLabelControllerState.Idle; + private enabledDisposables: IDisposable[] = []; + private disposables: IDisposable[] = []; + + constructor( + private list: List, + private view: ListView, + private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider + ) { + list.onDidUpdateOptions(this.onDidUpdateListOptions, this, this.disposables); + this.onDidUpdateListOptions(list.options); + } + + private onDidUpdateListOptions(options: IListOptions): void { + const enableKeyboardNavigation = typeof options.enableKeyboardNavigation === 'undefined' ? true : !!options.enableKeyboardNavigation; + + if (enableKeyboardNavigation) { + this.enable(); + } else { + this.disable(); + } + } + + private enable(): void { + if (this.enabled) { + return; + } + + const onChar = Event.chain(domEvent(this.view.domNode, 'keydown')) + .filter(e => !isInputElement(e.target as HTMLElement)) + .map(event => new StandardKeyboardEvent(event)) + .filter(this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? e => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : e => mightProducePrintableCharacter(e)) + .map(event => event.browserEvent.key) + .event; + + const onClear = Event.debounce(onChar, () => null, 800); + const onInput = Event.reduce(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i)); + + onInput(this.onInput, this, this.enabledDisposables); + + this.enabled = true; + } + + private disable(): void { + if (!this.enabled) { + return; + } + + this.enabledDisposables = dispose(this.enabledDisposables); + this.enabled = false; + } + + private onInput(word: string | null): void { + if (!word) { + this.state = TypeLabelControllerState.Idle; + return; + } + + const focus = this.list.getFocus(); + const start = focus.length > 0 ? focus[0] : 0; + const delta = this.state === TypeLabelControllerState.Idle ? 1 : 0; + this.state = TypeLabelControllerState.Typing; + + for (let i = 0; i < this.list.length; i++) { + const index = (start + i + delta) % this.list.length; + const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(this.view.element(index)); + const labelStr = label && label.toString(); + + if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) { + this.list.setFocus([index]); + this.list.reveal(index); + return; + } + } + } + + dispose() { + this.disable(); + this.disposables = dispose(this.disposables); + } +} + class DOMFocusController implements IDisposable { private disposables: IDisposable[] = []; @@ -332,7 +429,7 @@ class DOMFocusController implements IDisposable { ) { this.disposables = []; - const onKeyDown = chain(domEvent(view.domNode, 'keydown')) + const onKeyDown = Event.chain(domEvent(view.domNode, 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -408,14 +505,14 @@ const DefaultOpenController: IOpenController = { class MouseController implements IDisposable { private multipleSelectionSupport: boolean; - private multipleSelectionController: IMultipleSelectionController; + readonly multipleSelectionController: IMultipleSelectionController; private openController: IOpenController; private disposables: IDisposable[] = []; constructor( private list: List, private view: ListView, - private options: IListOptions = {} + options: IListOptions = {} ) { this.multipleSelectionSupport = !(options.multipleSelectionSupport === false); @@ -426,7 +523,9 @@ class MouseController implements IDisposable { this.openController = options.openController || DefaultOpenController; view.onMouseDown(this.onMouseDown, this, this.disposables); + view.onContextMenu(this.onContextMenu, this, this.disposables); view.onMouseClick(this.onPointer, this, this.disposables); + list.onMouseMiddleClick(this.onPointer, this, this.disposables); view.onMouseDblClick(this.onDoubleClick, this, this.disposables); view.onTouchStart(this.onMouseDown, this, this.disposables); view.onTap(this.onPointer, this, this.disposables); @@ -454,12 +553,20 @@ class MouseController implements IDisposable { } private onMouseDown(e: IListMouseEvent | IListTouchEvent): void { - if (this.options.focusOnMouseDown === false) { - e.browserEvent.preventDefault(); - e.browserEvent.stopPropagation(); - } else if (document.activeElement !== e.browserEvent.target) { + if (document.activeElement !== e.browserEvent.target) { this.view.domNode.focus(); } + } + + private onContextMenu(e: IListMouseEvent | IListTouchEvent): void { + const focus = typeof e.index === 'undefined' ? [] : [e.index]; + this.list.setFocus(focus, e.browserEvent); + } + + private onPointer(e: IListMouseEvent): void { + if (isInputElement(e.browserEvent.target as HTMLElement)) { + return; + } let reference = this.list.getFocus()[0]; const selection = this.list.getSelection(); @@ -477,15 +584,13 @@ class MouseController implements IDisposable { return this.changeSelection(e, reference); } - if (selection.every(s => s !== focus)) { - this.list.setFocus([focus], e.browserEvent); - } - if (this.multipleSelectionSupport && this.isSelectionChangeEvent(e)) { return this.changeSelection(e, reference); } - if (this.options.selectOnMouseDown && !isMouseRightClick(e.browserEvent)) { + this.list.setFocus([focus], e.browserEvent); + + if (!isMouseRightClick(e.browserEvent)) { this.list.setSelection([focus], e.browserEvent); if (this.openController.shouldOpen(e.browserEvent)) { @@ -494,22 +599,11 @@ class MouseController implements IDisposable { } } - private onPointer(e: IListMouseEvent): void { - if (this.multipleSelectionSupport && this.isSelectionChangeEvent(e)) { + private onDoubleClick(e: IListMouseEvent): void { + if (isInputElement(e.browserEvent.target as HTMLElement)) { return; } - if (!this.options.selectOnMouseDown) { - const focus = this.list.getFocus(); - this.list.setSelection(focus, e.browserEvent); - - if (this.openController.shouldOpen(e.browserEvent)) { - this.list.open(focus, e.browserEvent); - } - } - } - - private onDoubleClick(e: IListMouseEvent): void { if (this.multipleSelectionSupport && this.isSelectionChangeEvent(e)) { return; } @@ -576,9 +670,14 @@ export interface IAccessibilityProvider { * Returning null will not disable ARIA for the element. Instead it is up to the screen reader * to compute a meaningful label based on the contents of the element in the DOM * - * See also: https://www.w3.org/TR/wai-aria/states_and_properties#aria-label + * See also: https://www.w3.org/TR/wai-aria/#aria-label */ getAriaLabel(element: T): string | null; + + /** + * https://www.w3.org/TR/wai-aria/#aria-level + */ + getAriaLevel?(element: T): number | undefined; } export class DefaultStyleController implements IStyleController { @@ -608,11 +707,17 @@ export class DefaultStyleController implements IStyleController { } if (styles.listFocusAndSelectionBackground) { - content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; }`); + content.push(` + .monaco-list-drag-image, + .monaco-list${suffix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } + `); } if (styles.listFocusAndSelectionForeground) { - content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; }`); + content.push(` + .monaco-list-drag-image, + .monaco-list${suffix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } + `); } if (styles.listInactiveFocusBackground) { @@ -630,11 +735,11 @@ export class DefaultStyleController implements IStyleController { } if (styles.listHoverBackground) { - content.push(`.monaco-list${suffix} .monaco-list-row:hover { background-color: ${styles.listHoverBackground}; }`); + content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); } if (styles.listHoverForeground) { - content.push(`.monaco-list${suffix} .monaco-list-row:hover { color: ${styles.listHoverForeground}; }`); + content.push(`.monaco-list${suffix} .monaco-list-row:hover:not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`); } if (styles.listSelectionOutline) { @@ -642,7 +747,10 @@ export class DefaultStyleController implements IStyleController { } if (styles.listFocusOutline) { - content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`); + content.push(` + .monaco-list-drag-image, + .monaco-list${suffix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } + `); } if (styles.listInactiveFocusOutline) { @@ -653,6 +761,29 @@ export class DefaultStyleController implements IStyleController { content.push(`.monaco-list${suffix} .monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`); } + if (styles.listDropBackground) { + content.push(` + .monaco-list${suffix}.drop-target, + .monaco-list${suffix} .monaco-list-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; } + `); + } + + if (styles.listFilterWidgetBackground) { + content.push(`.monaco-list-type-filter { background-color: ${styles.listFilterWidgetBackground} }`); + } + + if (styles.listFilterWidgetOutline) { + content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listFilterWidgetOutline}; }`); + } + + if (styles.listFilterWidgetNoMatchesOutline) { + content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listFilterWidgetNoMatchesOutline}; }`); + } + + if (styles.listMatchesShadow) { + content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`); + } + const newStyles = content.join('\n'); if (newStyles !== this.styleElement.innerHTML) { this.styleElement.innerHTML = newStyles; @@ -660,19 +791,27 @@ export class DefaultStyleController implements IStyleController { } } -export interface IListOptions extends IListViewOptions, IListStyles { - identityProvider?: IIdentityProvider; - ariaLabel?: string; - mouseSupport?: boolean; - selectOnMouseDown?: boolean; - focusOnMouseDown?: boolean; - keyboardSupport?: boolean; - verticalScrollMode?: ScrollbarVisibility; - multipleSelectionSupport?: boolean; - multipleSelectionController?: IMultipleSelectionController; - openController?: IOpenController; - styleController?: IStyleController; - accessibilityProvider?: IAccessibilityProvider; +export interface IListOptions extends IListStyles { + readonly identityProvider?: IIdentityProvider; + readonly dnd?: IListDragAndDrop; + readonly enableKeyboardNavigation?: boolean; + readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; + readonly ariaRole?: ListAriaRootRole; + readonly ariaLabel?: string; + readonly keyboardSupport?: boolean; + readonly multipleSelectionSupport?: boolean; + readonly multipleSelectionController?: IMultipleSelectionController; + readonly openController?: IOpenController; + readonly styleController?: IStyleController; + readonly accessibilityProvider?: IAccessibilityProvider; + + // list view options + readonly useShadows?: boolean; + readonly verticalScrollMode?: ScrollbarVisibility; + readonly setRowLineHeight?: boolean; + readonly supportDynamicHeights?: boolean; + readonly mouseSupport?: boolean; + readonly horizontalScrolling?: boolean; } export interface IListStyles { @@ -692,6 +831,10 @@ export interface IListStyles { listInactiveFocusOutline?: Color; listSelectionOutline?: Color; listHoverOutline?: Color; + listFilterWidgetBackground?: Color; + listFilterWidgetOutline?: Color; + listFilterWidgetNoMatchesOutline?: Color; + listMatchesShadow?: Color; } const defaultStyles: IListStyles = { @@ -705,10 +848,17 @@ const defaultStyles: IListStyles = { listDropBackground: Color.fromHex('#383B3D') }; -const DefaultOptions: IListOptions = { +const DefaultOptions = { keyboardSupport: true, mouseSupport: true, - multipleSelectionSupport: true + multipleSelectionSupport: true, + dnd: { + getDragURI() { return null; }, + onDragStart(): void { }, + onDragOver() { return false; }, + drop() { } + }, + ariaRootRole: ListAriaRootRole.TREE }; // TODO@Joao: move these utils into a SortedArray class @@ -819,7 +969,11 @@ class PipelineRenderer implements IListRenderer { let i = 0; for (const renderer of this.renderers) { - renderer.disposeElement(element, index, templateData[i++]); + if (renderer.disposeElement) { + renderer.disposeElement(element, index, templateData[i]); + } + + i += 1; } } @@ -836,9 +990,7 @@ class AccessibiltyRenderer implements IListRenderer { templateId: string = 'a18n'; - constructor(private accessibilityProvider: IAccessibilityProvider) { - - } + constructor(private accessibilityProvider: IAccessibilityProvider) { } renderTemplate(container: HTMLElement): HTMLElement { return container; @@ -852,10 +1004,14 @@ class AccessibiltyRenderer implements IListRenderer { } else { container.removeAttribute('aria-label'); } - } - disposeElement(element: T, index: number, container: HTMLElement): void { - // noop + const ariaLevel = this.accessibilityProvider.getAriaLevel && this.accessibilityProvider.getAriaLevel(element); + + if (typeof ariaLevel === 'number') { + container.setAttribute('aria-level', `${ariaLevel}`); + } else { + container.removeAttribute('aria-level'); + } } disposeTemplate(templateData: any): void { @@ -863,6 +1019,47 @@ class AccessibiltyRenderer implements IListRenderer { } } +class ListViewDragAndDrop implements IListViewDragAndDrop { + + constructor(private list: List, private dnd: IListDragAndDrop) { } + + getDragElements(element: T): T[] { + const selection = this.list.getSelectedElements(); + const elements = selection.indexOf(element) > -1 ? selection : [element]; + return elements; + } + + getDragURI(element: T): string | null { + return this.dnd.getDragURI(element); + } + + getDragLabel?(elements: T[]): string | undefined { + if (this.dnd.getDragLabel) { + return this.dnd.getDragLabel(elements); + } + + return undefined; + } + + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + if (this.dnd.onDragStart) { + this.dnd.onDragStart(data, originalEvent); + } + } + + onDragOver(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): boolean | IListDragOverReaction { + return this.dnd.onDragOver(data, targetElement, targetIndex, originalEvent); + } + + drop(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): void { + this.dnd.drop(data, targetElement, targetIndex, originalEvent); + } +} + +export interface IListOptionsUpdate { + readonly enableKeyboardNavigation?: boolean; +} + export class List implements ISpliceable, IDisposable { private static InstanceCount = 0; @@ -873,26 +1070,36 @@ export class List implements ISpliceable, IDisposable { private eventBufferer = new EventBufferer(); private view: ListView; private spliceable: ISpliceable; - protected disposables: IDisposable[]; private styleElement: HTMLStyleElement; private styleController: IStyleController; + private mouseController: MouseController | undefined; + + get multipleSelectionController(): IMultipleSelectionController { + return (this.mouseController && this.mouseController.multipleSelectionController) || DefaultMultipleSelectionContoller; + } + + private _onDidUpdateOptions = new Emitter>(); + readonly onDidUpdateOptions = this._onDidUpdateOptions.event; + + protected disposables: IDisposable[]; @memoize get onFocusChange(): Event> { - return mapEvent(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e)); + return Event.map(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e)); } @memoize get onSelectionChange(): Event> { - return mapEvent(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e)); + return Event.map(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e)); } - private _onOpen = new Emitter>(); - readonly onOpen: Event> = this._onOpen.event; + private _onDidOpen = new Emitter>(); + readonly onDidOpen: Event> = this._onDidOpen.event; private _onPin = new Emitter(); @memoize get onPin(): Event> { - return mapEvent(this._onPin.event, indexes => this.toListEvent({ indexes })); + return Event.map(this._onPin.event, indexes => this.toListEvent({ indexes })); } + get onDidScroll(): Event { return this.view.onDidScroll; } get onMouseClick(): Event> { return this.view.onMouseClick; } get onMouseDblClick(): Event> { return this.view.onMouseDblClick; } get onMouseMiddleClick(): Event> { return this.view.onMouseMiddleClick; } @@ -906,7 +1113,7 @@ export class List implements ISpliceable, IDisposable { private didJustPressContextMenuKey: boolean = false; @memoize get onContextMenu(): Event> { - const fromKeydown = chain(domEvent(this.view.domNode, 'keydown')) + const fromKeydown = Event.chain(domEvent(this.view.domNode, 'keydown')) .map(e => new StandardKeyboardEvent(e)) .filter(e => this.didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) .filter(e => { e.preventDefault(); e.stopPropagation(); return false; }) @@ -918,7 +1125,7 @@ export class List implements ISpliceable, IDisposable { }) .event; - const fromKeyup = chain(domEvent(this.view.domNode, 'keyup')) + const fromKeyup = Event.chain(domEvent(this.view.domNode, 'keyup')) .filter(() => { const didJustPressContextMenuKey = this.didJustPressContextMenuKey; this.didJustPressContextMenuKey = false; @@ -934,12 +1141,12 @@ export class List implements ISpliceable, IDisposable { .filter(({ anchor }) => !!anchor) .event; - const fromMouse = chain(this.view.onContextMenu) + const fromMouse = Event.chain(this.view.onContextMenu) .filter(() => !this.didJustPressContextMenuKey) .map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.clientX + 1, y: browserEvent.clientY }, browserEvent })) .event; - return anyEvent>(fromKeydown, fromKeyup, fromMouse); + return Event.any>(fromKeydown, fromKeyup, fromMouse); } get onKeyDown(): Event { return domEvent(this.view.domNode, 'keydown'); } @@ -956,60 +1163,86 @@ export class List implements ISpliceable, IDisposable { container: HTMLElement, virtualDelegate: IListVirtualDelegate, renderers: IListRenderer[], - options: IListOptions = DefaultOptions + private _options: IListOptions = DefaultOptions ) { this.focus = new FocusTrait(i => this.getElementDomId(i)); this.selection = new Trait('selected'); - mixin(options, defaultStyles, false); + mixin(_options, defaultStyles, false); const baseRenderers: IListRenderer[] = [this.focus.renderer, this.selection.renderer]; - if (options.accessibilityProvider) { - baseRenderers.push(new AccessibiltyRenderer(options.accessibilityProvider)); + if (_options.accessibilityProvider) { + baseRenderers.push(new AccessibiltyRenderer(_options.accessibilityProvider)); } renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r])); - this.view = new ListView(container, virtualDelegate, renderers, options); - this.view.domNode.setAttribute('role', 'tree'); + const viewOptions: IListViewOptions = { + ..._options, + dnd: _options.dnd && new ListViewDragAndDrop(this, _options.dnd) + }; + + this.view = new ListView(container, virtualDelegate, renderers, viewOptions); + + if (typeof _options.ariaRole !== 'string') { + this.view.domNode.setAttribute('role', ListAriaRootRole.TREE); + } else { + this.view.domNode.setAttribute('role', _options.ariaRole); + } + DOM.addClass(this.view.domNode, this.idPrefix); this.view.domNode.tabIndex = 0; this.styleElement = DOM.createStyleSheet(this.view.domNode); - this.styleController = options.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), - new TraitSpliceable(this.selection, this.view, options.identityProvider), + new TraitSpliceable(this.focus, this.view, _options.identityProvider), + new TraitSpliceable(this.selection, this.view, _options.identityProvider), this.view ]); 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 = Event.map(domEvent(this.view.domNode, 'focus', true), () => null!); + this.onDidBlur = Event.map(domEvent(this.view.domNode, 'blur', true), () => null!); this.disposables.push(new DOMFocusController(this, this.view)); - if (typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport) { - const controller = new KeyboardController(this, this.view, options); + if (typeof _options.keyboardSupport !== 'boolean' || _options.keyboardSupport) { + const controller = new KeyboardController(this, this.view, _options); this.disposables.push(controller); } - if (typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true) { - this.disposables.push(new MouseController(this, this.view, options)); + if (_options.keyboardNavigationLabelProvider) { + const controller = new TypeLabelController(this, this.view, _options.keyboardNavigationLabelProvider); + this.disposables.push(controller); + } + + if (typeof _options.mouseSupport === 'boolean' ? _options.mouseSupport : true) { + this.mouseController = new MouseController(this, this.view, _options); + this.disposables.push(this.mouseController); } this.onFocusChange(this._onFocusChange, this, this.disposables); this.onSelectionChange(this._onSelectionChange, this, this.disposables); - if (options.ariaLabel) { - this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", options.ariaLabel)); + if (_options.ariaLabel) { + this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", _options.ariaLabel)); } - this.style(options); + this.style(_options); + } + + updateOptions(optionsUpdate: IListOptionsUpdate = {}): void { + this._options = { ...this._options, ...optionsUpdate }; + this._onDidUpdateOptions.fire(this._options); + } + + get options(): IListOptions { + return this._options; } splice(start: number, deleteCount: number, elements: T[] = []): void { @@ -1028,6 +1261,14 @@ export class List implements ISpliceable, IDisposable { this.eventBufferer.bufferEvents(() => this.spliceable.splice(start, deleteCount, elements)); } + updateWidth(index: number): void { + this.view.updateWidth(index); + } + + element(index: number): T { + return this.view.element(index); + } + get length(): number { return this.view.length; } @@ -1060,12 +1301,8 @@ export class List implements ISpliceable, IDisposable { this.view.domNode.focus(); } - layout(height?: number): void { - this.view.layout(height); - } - - layoutWidth(width: number): void { - this.view.layoutWidth(width); + layout(height?: number, width?: number): void { + this.view.layout(height, width); } setSelection(indexes: number[], browserEvent?: UIEvent): void { @@ -1098,41 +1335,54 @@ export class List implements ISpliceable, IDisposable { this.focus.set(indexes, browserEvent); } - focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { + focusNext(n = 1, loop = false, browserEvent?: UIEvent, filter?: (element: T) => boolean): void { 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)], browserEvent); + const index = this.findNextIndex(focus.length > 0 ? focus[0] + n : 0, loop, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } } - focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { + focusPrevious(n = 1, loop = false, browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } + 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)], browserEvent); + const index = this.findPreviousIndex(focus.length > 0 ? focus[0] - n : 0, loop, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } } - focusNextPage(browserEvent?: UIEvent): void { + focusNextPage(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { let lastPageIndex = this.view.indexAt(this.view.getScrollTop() + this.view.renderHeight); lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1; const lastPageElement = this.view.element(lastPageIndex); const currentlyFocusedElement = this.getFocusedElements()[0]; if (currentlyFocusedElement !== lastPageElement) { - this.setFocus([lastPageIndex], browserEvent); + const lastGoodPageIndex = this.findPreviousIndex(lastPageIndex, false, filter); + + if (lastGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(lastGoodPageIndex)) { + this.setFocus([lastGoodPageIndex], browserEvent); + } else { + 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(browserEvent), 0); + setTimeout(() => this.focusNextPage(browserEvent, filter), 0); } } } - focusPreviousPage(browserEvent?: UIEvent): void { + focusPreviousPage(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { let firstPageIndex: number; const scrollTop = this.view.getScrollTop(); @@ -1146,26 +1396,78 @@ export class List implements ISpliceable, IDisposable { const currentlyFocusedElement = this.getFocusedElements()[0]; if (currentlyFocusedElement !== firstPageElement) { - this.setFocus([firstPageIndex], browserEvent); + const firstGoodPageIndex = this.findNextIndex(firstPageIndex, false, filter); + + if (firstGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(firstGoodPageIndex)) { + this.setFocus([firstGoodPageIndex], browserEvent); + } else { + 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(browserEvent), 0); + setTimeout(() => this.focusPreviousPage(browserEvent, filter), 0); } } } - focusLast(browserEvent?: UIEvent): void { + focusLast(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } - this.setFocus([this.length - 1], browserEvent); + + const index = this.findPreviousIndex(this.length - 1, false, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } } - focusFirst(browserEvent?: UIEvent): void { + focusFirst(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } - this.setFocus([0], browserEvent); + + const index = this.findNextIndex(0, false, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } + } + + private findNextIndex(index: number, loop = false, filter?: (element: T) => boolean): number { + for (let i = 0; i < this.length; i++) { + if (index >= this.length && !loop) { + return -1; + } + + index = index % this.length; + + if (!filter || filter(this.element(index))) { + return index; + } + + index++; + } + + return -1; + } + + private findPreviousIndex(index: number, loop = false, filter?: (element: T) => boolean): number { + for (let i = 0; i < this.length; i++) { + if (index < 0 && !loop) { + return -1; + } + + index = (this.length + (index % this.length)) % this.length; + + if (!filter || filter(this.element(index))) { + return index; + } + + index--; + } + + return -1; } getFocus(): number[] { @@ -1242,7 +1544,7 @@ export class List implements ISpliceable, IDisposable { } } - this._onOpen.fire({ indexes, elements: indexes.map(i => this.view.element(i)), browserEvent }); + this._onDidOpen.fire({ indexes, elements: indexes.map(i => this.view.element(i)), browserEvent }); } pin(indexes: number[]): void { @@ -1288,7 +1590,7 @@ export class List implements ISpliceable, IDisposable { this._onDidDispose.fire(); this.disposables = dispose(this.disposables); - this._onOpen.dispose(); + this._onDidOpen.dispose(); this._onPin.dispose(); this._onDidDispose.dispose(); } diff --git a/src/vs/workbench/electron-browser/media/remove-dark.svg b/src/vs/base/browser/ui/list/media/close-dark.svg similarity index 100% rename from src/vs/workbench/electron-browser/media/remove-dark.svg rename to src/vs/base/browser/ui/list/media/close-dark.svg diff --git a/src/vs/base/browser/ui/list/media/close-hc.svg b/src/vs/base/browser/ui/list/media/close-hc.svg new file mode 100644 index 000000000000..c20895c60aa7 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/close-hc.svg @@ -0,0 +1,33 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/vs/workbench/electron-browser/media/remove.svg b/src/vs/base/browser/ui/list/media/close.svg similarity index 100% rename from src/vs/workbench/electron-browser/media/remove.svg rename to src/vs/base/browser/ui/list/media/close.svg diff --git a/src/vs/base/browser/ui/list/media/filter-dark.svg b/src/vs/base/browser/ui/list/media/filter-dark.svg new file mode 100644 index 000000000000..0925909a1174 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/filter-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/media/filter-hc.svg b/src/vs/base/browser/ui/list/media/filter-hc.svg new file mode 100644 index 000000000000..0e2724f52e23 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/filter-hc.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/media/filter.svg b/src/vs/base/browser/ui/list/media/filter.svg new file mode 100644 index 000000000000..f8c011064b1d --- /dev/null +++ b/src/vs/base/browser/ui/list/media/filter.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter-dark.svg b/src/vs/base/browser/ui/list/media/no-filter-dark.svg new file mode 100644 index 000000000000..5f495c006dcc --- /dev/null +++ b/src/vs/base/browser/ui/list/media/no-filter-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter-hc.svg b/src/vs/base/browser/ui/list/media/no-filter-hc.svg new file mode 100644 index 000000000000..31d8f70f05a3 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/no-filter-hc.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter.svg b/src/vs/base/browser/ui/list/media/no-filter.svg new file mode 100644 index 000000000000..760cc3c44034 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/no-filter.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index 5b2b4500f0e6..35256c7b2477 100644 --- a/src/vs/base/browser/ui/list/rowCache.ts +++ b/src/vs/base/browser/ui/list/rowCache.ts @@ -38,7 +38,7 @@ export class RowCache implements IDisposable { if (!result) { const domNode = $('.monaco-list-row'); - const renderer = this.renderers.get(templateId); + const renderer = this.getRenderer(templateId); const templateData = renderer.renderTemplate(domNode); result = { domNode, templateId, templateData }; } @@ -86,7 +86,7 @@ export class RowCache implements IDisposable { this.cache.forEach((cachedRows, templateId) => { for (const cachedRow of cachedRows) { - const renderer = this.renderers.get(templateId); + const renderer = this.getRenderer(templateId); renderer.disposeTemplate(cachedRow.templateData); cachedRow.domNode = null; cachedRow.templateData = null; @@ -101,4 +101,12 @@ export class RowCache implements IDisposable { this.cache.clear(); this.renderers = null!; // StrictNullOverride: nulling out ok in dispose } + + private getRenderer(templateId: string): IListRenderer { + const renderer = this.renderers.get(templateId); + if (!renderer) { + throw new Error(`No renderer found for ${templateId}`); + } + return renderer; + } } \ 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 fb8f91cd3b54..3c5f1ac41882 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -117,7 +117,7 @@ /* Context Menu */ .context-view.monaco-menu-container { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; outline: 0; border: none; -webkit-animation: fadeIn 0.083s linear; @@ -154,7 +154,6 @@ flex-shrink: 1; box-sizing: border-box; height: 30px; - -webkit-app-region: no-drag; overflow: hidden; flex-wrap: wrap; } @@ -183,7 +182,7 @@ } .menubar .menubar-menu-items-holder.monaco-menu-container { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; outline: 0; border: none; } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index d2af1165ab65..effb8e081c83 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -15,9 +15,10 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { Event, Emitter } from 'vs/base/common/event'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { isLinux } from 'vs/base/common/platform'; export const MENU_MNEMONIC_REGEX: RegExp = /\(&{1,2}(.)\)|&{1,2}(.)/; export const MENU_ESCAPED_MNEMONIC_REGEX: RegExp = /(?:&){1,2}(.)/; @@ -26,7 +27,7 @@ export interface IMenuOptions { context?: any; actionItemProvider?: IActionItemProvider; actionRunner?: IActionRunner; - getKeyBinding?: (action: IAction) => ResolvedKeybinding; + getKeyBinding?: (action: IAction) => ResolvedKeybinding | undefined; ariaLabel?: string; enableMnemonics?: boolean; anchorAlignment?: AnchorAlignment; @@ -44,7 +45,7 @@ export interface IMenuStyles { } export class SubmenuAction extends Action { - constructor(label: string, public entries: (SubmenuAction | IAction)[], cssClass?: string) { + constructor(label: string, public entries: Array, cssClass?: string) { super(!!cssClass ? cssClass : 'submenu', label, '', true); } } @@ -59,6 +60,7 @@ export class Menu extends ActionBar { private menuDisposables: IDisposable[]; private scrollableElement: DomScrollableElement; private menuElement: HTMLElement; + private scrollTopHold: number | undefined; private readonly _onScroll: Emitter; @@ -94,48 +96,73 @@ export class Menu extends ActionBar { const key = KeyCodeUtils.fromString(e.key); if (this.mnemonics.has(key)) { EventHelper.stop(e, true); - const actions = this.mnemonics.get(key); + const actions = this.mnemonics.get(key)!; if (actions.length === 1) { if (actions[0] instanceof SubmenuActionItem) { this.focusItemByElement(actions[0].container); } - actions[0].onClick(event); + actions[0].onClick(e); } if (actions.length > 1) { const action = actions.shift(); - this.focusItemByElement(action.container); + if (action) { + this.focusItemByElement(action.container); + actions.push(action); + } - actions.push(action); this.mnemonics.set(key, actions); } } })); } + if (isLinux) { + this._register(addDisposableListener(menuElement, EventType.KEY_DOWN, e => { + const event = new StandardKeyboardEvent(e); + + if (event.equals(KeyCode.Home) || event.equals(KeyCode.PageUp)) { + this.focusedItem = this.items.length - 1; + this.focusNext(); + EventHelper.stop(e, true); + } else if (event.equals(KeyCode.End) || event.equals(KeyCode.PageDown)) { + this.focusedItem = 0; + this.focusPrevious(); + EventHelper.stop(e, true); + } + })); + } + this._register(addDisposableListener(this.domNode, EventType.MOUSE_OUT, e => { let relatedTarget = e.relatedTarget as HTMLElement; if (!isAncestor(relatedTarget, this.domNode)) { this.focusedItem = undefined; + this.scrollTopHold = this.menuElement.scrollTop; this.updateFocus(); e.stopPropagation(); } })); + this._register(addDisposableListener(this.domNode, EventType.MOUSE_UP, e => { + // Absorb clicks in menu dead space https://github.com/Microsoft/vscode/issues/63575 + EventHelper.stop(e, true); + })); + this._register(addDisposableListener(this.actionsList, EventType.MOUSE_OVER, e => { let target = e.target as HTMLElement; if (!target || !isAncestor(target, this.actionsList) || target === this.actionsList) { return; } - while (target.parentElement !== this.actionsList) { + while (target.parentElement !== this.actionsList && target.parentElement !== null) { target = target.parentElement; } if (hasClass(target, 'action-item')) { const lastFocusedItem = this.focusedItem; + this.scrollTopHold = this.menuElement.scrollTop; this.setFocusedItem(target); if (lastFocusedItem !== this.focusedItem) { @@ -171,7 +198,11 @@ export class Menu extends ActionBar { this._onScroll.fire(); }, this, this.menuDisposables); - this._register(addDisposableListener(this.menuElement, EventType.SCROLL, (e) => { + this._register(addDisposableListener(this.menuElement, EventType.SCROLL, (e: ScrollEvent) => { + if (this.scrollTopHold !== undefined) { + this.menuElement.scrollTop = this.scrollTopHold; + this.scrollTopHold = undefined; + } this.scrollableElement.scanDomNode(); })); @@ -261,7 +292,7 @@ export class Menu extends ActionBar { if (mnemonic && menuActionItem.isEnabled()) { let actionItems: MenuActionItem[] = []; if (this.mnemonics.has(mnemonic)) { - actionItems = this.mnemonics.get(mnemonic); + actionItems = this.mnemonics.get(mnemonic)!; } actionItems.push(menuActionItem); @@ -276,7 +307,11 @@ export class Menu extends ActionBar { if (options.getKeyBinding) { const keybinding = options.getKeyBinding(action); if (keybinding) { - menuItemOptions.keybinding = keybinding.getLabel(); + const keybindingLabel = keybinding.getLabel(); + + if (keybindingLabel) { + menuItemOptions.keybinding = keybindingLabel; + } } } @@ -287,7 +322,7 @@ export class Menu extends ActionBar { if (mnemonic && menuActionItem.isEnabled()) { let actionItems: MenuActionItem[] = []; if (this.mnemonics.has(mnemonic)) { - actionItems = this.mnemonics.get(mnemonic); + actionItems = this.mnemonics.get(mnemonic)!; } actionItems.push(menuActionItem); @@ -342,6 +377,10 @@ class MenuActionItem extends BaseActionItem { render(container: HTMLElement): void { super.render(container); + if (!this.element) { + return; + } + this.container = container; this.item = append(this.element, $('a.action-menu-item')); @@ -439,7 +478,7 @@ class MenuActionItem extends BaseActionItem { removeClasses(this.item, this.cssClass); } if (this.options.icon) { - this.cssClass = this.getAction().class; + this.cssClass = this.getAction().class || ''; addClass(this.label, 'icon'); if (this.cssClass) { addClasses(this.label, this.cssClass); @@ -452,11 +491,17 @@ class MenuActionItem extends BaseActionItem { updateEnabled(): void { if (this.getAction().enabled) { - removeClass(this.element, 'disabled'); + if (this.element) { + removeClass(this.element, 'disabled'); + } + removeClass(this.item, 'disabled'); this.item.tabIndex = 0; } else { - addClass(this.element, 'disabled'); + if (this.element) { + addClass(this.element, 'disabled'); + } + addClass(this.item, 'disabled'); removeTabIndexAndUpdateFocus(this.item); } @@ -483,7 +528,7 @@ class MenuActionItem extends BaseActionItem { return; } - const isSelected = hasClass(this.element, 'focused'); + const isSelected = this.element && hasClass(this.element, 'focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : this.menuStyle.backgroundColor; const border = isSelected && this.menuStyle.selectionBorderColor ? `1px solid ${this.menuStyle.selectionBorderColor}` : null; @@ -501,8 +546,8 @@ class MenuActionItem extends BaseActionItem { } class SubmenuActionItem extends MenuActionItem { - private mysubmenu: Menu; - private submenuContainer: HTMLElement; + private mysubmenu: Menu | null; + private submenuContainer: HTMLElement | undefined; private submenuIndicator: HTMLElement; private submenuDisposables: IDisposable[] = []; private mouseOver: boolean; @@ -525,7 +570,7 @@ class SubmenuActionItem extends MenuActionItem { }, 250); this.hideScheduler = new RunOnceScheduler(() => { - if ((!isAncestor(document.activeElement, this.element) && this.parentData.submenu === this.mysubmenu)) { + if (this.element && (!isAncestor(document.activeElement, this.element) && this.parentData.submenu === this.mysubmenu)) { this.parentData.parent.focus(false); this.cleanupExistingSubmenu(true); } @@ -535,6 +580,10 @@ class SubmenuActionItem extends MenuActionItem { render(container: HTMLElement): void { super.render(container); + if (!this.element) { + return; + } + addClass(this.item, 'monaco-submenu-item'); this.item.setAttribute('aria-haspopup', 'true'); @@ -570,7 +619,7 @@ class SubmenuActionItem extends MenuActionItem { })); this._register(addDisposableListener(this.element, EventType.FOCUS_OUT, e => { - if (!isAncestor(document.activeElement, this.element)) { + if (this.element && !isAncestor(document.activeElement, this.element)) { this.hideScheduler.schedule(); } })); @@ -597,16 +646,20 @@ class SubmenuActionItem extends MenuActionItem { private cleanupExistingSubmenu(force: boolean): void { if (this.parentData.submenu && (force || (this.parentData.submenu !== this.mysubmenu))) { this.parentData.submenu.dispose(); - this.parentData.submenu = null; + this.parentData.submenu = undefined; if (this.submenuContainer) { this.submenuDisposables = dispose(this.submenuDisposables); - this.submenuContainer = null; + this.submenuContainer = undefined; } } } private createSubmenu(selectFirstItem = true): void { + if (!this.element) { + return; + } + if (!this.parentData.submenu) { this.submenuContainer = append(this.element, $('div.monaco-submenu')); addClasses(this.submenuContainer, 'menubar-menu-items-holder', 'context-view'); @@ -618,13 +671,15 @@ class SubmenuActionItem extends MenuActionItem { const boundingRect = this.element.getBoundingClientRect(); const childBoundingRect = this.submenuContainer.getBoundingClientRect(); + const computedStyles = getComputedStyle(this.parentData.parent.domNode); + const paddingTop = parseFloat(computedStyles.paddingTop || '0') || 0; if (window.innerWidth <= boundingRect.right + childBoundingRect.width) { this.submenuContainer.style.left = '10px'; this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset + boundingRect.height}px`; } else { this.submenuContainer.style.left = `${this.element.offsetWidth}px`; - this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset}px`; + this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset - paddingTop}px`; } this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_UP, e => { @@ -633,11 +688,14 @@ class SubmenuActionItem extends MenuActionItem { EventHelper.stop(e, true); this.parentData.parent.focus(); - this.parentData.submenu.dispose(); - this.parentData.submenu = null; + + if (this.parentData.submenu) { + this.parentData.submenu.dispose(); + this.parentData.submenu = undefined; + } this.submenuDisposables = dispose(this.submenuDisposables); - this.submenuContainer = null; + this.submenuContainer = undefined; } })); @@ -651,11 +709,14 @@ class SubmenuActionItem extends MenuActionItem { this.submenuDisposables.push(this.parentData.submenu.onDidCancel(() => { this.parentData.parent.focus(); - this.parentData.submenu.dispose(); - this.parentData.submenu = null; + + if (this.parentData.submenu) { + this.parentData.submenu.dispose(); + this.parentData.submenu = undefined; + } this.submenuDisposables = dispose(this.submenuDisposables); - this.submenuContainer = null; + this.submenuContainer = undefined; })); this.parentData.submenu.focus(selectFirstItem); @@ -673,7 +734,7 @@ class SubmenuActionItem extends MenuActionItem { return; } - const isSelected = hasClass(this.element, 'focused'); + const isSelected = this.element && hasClass(this.element, 'focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; this.submenuIndicator.style.backgroundColor = fgColor ? `${fgColor}` : null; @@ -695,7 +756,7 @@ class SubmenuActionItem extends MenuActionItem { if (this.submenuContainer) { this.submenuDisposables = dispose(this.submenuDisposables); - this.submenuContainer = null; + this.submenuContainer = undefined; } } } diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 47f7b18fa550..4c4eac0784f3 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -59,9 +59,9 @@ export class MenuBar extends Disposable { index: number; holder?: HTMLElement; widget?: Menu; - }; + } | undefined; - private focusToReturn: HTMLElement; + private focusToReturn: HTMLElement | undefined; private menuUpdater: RunOnceScheduler; // Input-related @@ -80,7 +80,7 @@ export class MenuBar extends Disposable { private numMenusShown: number; private menuStyle: IMenuStyles; - private overflowLayoutScheduled: IDisposable; + private overflowLayoutScheduled: IDisposable | null; constructor(private container: HTMLElement, private options: IMenuBarOptions = {}) { super(); @@ -118,7 +118,7 @@ export class MenuBar extends Disposable { } else if (event.equals(KeyCode.Escape) && this.isFocused && !this.isOpen) { this.setUnfocusedState(); } else if (!this.isOpen && !event.ctrlKey && this.options.enableMnemonics && this.mnemonicsInUse && this.mnemonics.has(key)) { - const menuIndex = this.mnemonics.get(key); + const menuIndex = this.mnemonics.get(key)!; this.onMenuTriggered(menuIndex, false); } else { eventHandled = false; @@ -152,7 +152,7 @@ export class MenuBar extends Disposable { if (event.relatedTarget) { if (!this.container.contains(event.relatedTarget as HTMLElement)) { - this.focusToReturn = null; + this.focusToReturn = undefined; this.setUnfocusedState(); } } @@ -171,7 +171,7 @@ export class MenuBar extends Disposable { this.mnemonicsInUse = true; this.updateMnemonicVisibility(true); - const menuIndex = this.mnemonics.get(key); + const menuIndex = this.mnemonics.get(key)!; this.onMenuTriggered(menuIndex, false); })); @@ -223,7 +223,7 @@ export class MenuBar extends Disposable { Gesture.addTarget(buttonElement); this._register(DOM.addDisposableListener(buttonElement, EventType.Tap, (e: GestureEvent) => { // Ignore this touch if the menu is touched - if (this.isOpen && this.focusedMenu.holder && DOM.isAncestor(e.initialTarget as HTMLElement, this.focusedMenu.holder)) { + if (this.isOpen && this.focusedMenu && this.focusedMenu.holder && DOM.isAncestor(e.initialTarget as HTMLElement, this.focusedMenu.holder)) { return; } @@ -307,7 +307,7 @@ export class MenuBar extends Disposable { Gesture.addTarget(buttonElement); this._register(DOM.addDisposableListener(buttonElement, EventType.Tap, (e: GestureEvent) => { // Ignore this touch if the menu is touched - if (this.isOpen && this.focusedMenu.holder && DOM.isAncestor(e.initialTarget as HTMLElement, this.focusedMenu.holder)) { + if (this.isOpen && this.focusedMenu && this.focusedMenu.holder && DOM.isAncestor(e.initialTarget as HTMLElement, this.focusedMenu.holder)) { return; } @@ -377,7 +377,9 @@ export class MenuBar extends Disposable { DOM.removeNode(this.overflowMenu.titleElement); DOM.removeNode(this.overflowMenu.buttonElement); - this.overflowLayoutScheduled = dispose(this.overflowLayoutScheduled); + if (this.overflowLayoutScheduled) { + this.overflowLayoutScheduled = dispose(this.overflowLayoutScheduled); + } } blur(): void { @@ -439,7 +441,7 @@ export class MenuBar extends Disposable { this.overflowMenu.actions = []; for (let idx = this.numMenusShown; idx < this.menuCache.length; idx++) { - this.overflowMenu.actions.push(new SubmenuAction(this.menuCache[idx].label, this.menuCache[idx].actions)); + this.overflowMenu.actions.push(new SubmenuAction(this.menuCache[idx].label, this.menuCache[idx].actions || [])); } DOM.removeNode(this.overflowMenu.buttonElement); @@ -496,7 +498,7 @@ export class MenuBar extends Disposable { if (!this.overflowLayoutScheduled) { this.overflowLayoutScheduled = DOM.scheduleAtNextAnimationFrame(() => { this.updateOverflowAction(); - this.overflowLayoutScheduled = void 0; + this.overflowLayoutScheduled = null; }); } @@ -518,6 +520,8 @@ export class MenuBar extends Disposable { if (this.container.style.display !== 'flex') { this.container.style.display = 'flex'; this._onVisibilityChange.fire(true); + + this.updateOverflowAction(); } } @@ -556,11 +560,11 @@ export class MenuBar extends Disposable { } if (isFocused) { - this.focusedMenu = null; + this.focusedMenu = undefined; if (this.focusToReturn) { this.focusToReturn.focus(); - this.focusToReturn = null; + this.focusToReturn = undefined; } } @@ -584,11 +588,11 @@ export class MenuBar extends Disposable { } } - this.focusedMenu = null; + this.focusedMenu = undefined; if (this.focusToReturn) { this.focusToReturn.focus(); - this.focusToReturn = null; + this.focusToReturn = undefined; } } @@ -814,7 +818,10 @@ export class MenuBar extends Disposable { } if (this.focusedMenu.holder) { - DOM.removeClass(this.focusedMenu.holder.parentElement, 'open'); + if (this.focusedMenu.holder.parentElement) { + DOM.removeClass(this.focusedMenu.holder.parentElement, 'open'); + } + this.focusedMenu.holder.remove(); } @@ -829,6 +836,11 @@ export class MenuBar extends Disposable { private showCustomMenu(menuIndex: number, selectFirst = true): void { const actualMenuIndex = menuIndex >= this.numMenusShown ? MenuBar.OVERFLOW_INDEX : menuIndex; const customMenu = actualMenuIndex === MenuBar.OVERFLOW_INDEX ? this.overflowMenu : this.menuCache[actualMenuIndex]; + + if (!customMenu.actions) { + return; + } + const menuHolder = $('div.menubar-menu-items-holder'); DOM.addClass(customMenu.buttonElement, 'open'); @@ -851,12 +863,6 @@ export class MenuBar extends Disposable { this.focusState = MenubarState.FOCUSED; })); - this._register(menuWidget.onDidBlur(() => { - setTimeout(() => { - this.cleanupCustomMenu(); - }, 100); - })); - if (actualMenuIndex !== menuIndex) { menuWidget.trigger(menuIndex - this.numMenusShown); } else { @@ -897,7 +903,7 @@ class ModifierKeyEmitter extends Emitter { ctrlKey: false }; - this._subscriptions.push(domEvent(document.body, 'keydown')(e => { + this._subscriptions.push(domEvent(document.body, 'keydown', true)(e => { const event = new StandardKeyboardEvent(e); if (e.altKey && !this._keyStatus.altKey) { @@ -920,7 +926,8 @@ class ModifierKeyEmitter extends Emitter { this.fire(this._keyStatus); } })); - this._subscriptions.push(domEvent(document.body, 'keyup')(e => { + + this._subscriptions.push(domEvent(document.body, 'keyup', true)(e => { if (!e.altKey && this._keyStatus.altKey) { this._keyStatus.lastKeyReleased = 'alt'; } else if (!e.ctrlKey && this._keyStatus.ctrlKey) { @@ -943,11 +950,11 @@ class ModifierKeyEmitter extends Emitter { this.fire(this._keyStatus); } })); - this._subscriptions.push(domEvent(document.body, 'mousedown')(e => { + + this._subscriptions.push(domEvent(document.body, 'mousedown', true)(e => { this._keyStatus.lastKeyPressed = undefined; })); - this._subscriptions.push(domEvent(window, 'blur')(e => { this._keyStatus.lastKeyPressed = undefined; this._keyStatus.lastKeyReleased = undefined; diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/README.md b/src/vs/base/browser/ui/octiconLabel/octicons/README.md deleted file mode 100644 index 100707335059..000000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/README.md +++ /dev/null @@ -1 +0,0 @@ -If you intend to install Octicons locally, install `octicons-local.ttf`. It should appear as “github-octicons” in your font list. It is specially designed not to conflict with GitHub's web fonts. diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/cgmanifest.json b/src/vs/base/browser/ui/octiconLabel/octicons/cgmanifest.json deleted file mode 100644 index 3d13f99e3464..000000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/cgmanifest.json +++ /dev/null @@ -1,140 +0,0 @@ -{ - "registrations": [ - { - "component": { - "type": "other", - "other": { - "name": "octicons-code", - "version": "3.1.0", - "downloadUrl": "https://registry.npmjs.org/octicons/-/octicons-3.1.0.tgz" - } - }, - "licenseDetail": [ - "The MIT License (MIT)", - "", - "(c) 2012-2015 GitHub", - "", - "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." - ], - "license": "MIT", - "version": "3.1.0" - }, - { - "component": { - "type": "other", - "other": { - "name": "octicons-font", - "version": "3.1.0", - "downloadUrl": "https://registry.npmjs.org/octicons/-/octicons-3.1.0.tgz" - } - }, - "licenseDetail": [ - "(c) 2012-2015 GitHub", - "", - "SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007", - "", - "PREAMBLE", - "The goals of the Open Font License (OFL) are to stimulate worldwide", - "development of collaborative font projects, to support the font creation", - "efforts of academic and linguistic communities, and to provide a free and", - "open framework in which fonts may be shared and improved in partnership", - "with others.", - "", - "The OFL allows the licensed fonts to be used, studied, modified and", - "redistributed freely as long as they are not sold by themselves. The", - "fonts, including any derivative works, can be bundled, embedded,", - "redistributed and/or sold with any software provided that any reserved", - "names are not used by derivative works. The fonts and derivatives,", - "however, cannot be released under any other type of license. The", - "requirement for fonts to remain under this license does not apply", - "to any document created using the fonts or their derivatives.", - "", - "DEFINITIONS", - "\"Font Software\" refers to the set of files released by the Copyright", - "Holder(s) under this license and clearly marked as such. This may", - "include source files, build scripts and documentation.", - "", - "\"Reserved Font Name\" refers to any names specified as such after the", - "copyright statement(s).", - "", - "\"Original Version\" refers to the collection of Font Software components as", - "distributed by the Copyright Holder(s).", - "", - "\"Modified Version\" refers to any derivative made by adding to, deleting,", - "or substituting -- in part or in whole -- any of the components of the", - "Original Version, by changing formats or by porting the Font Software to a", - "new environment.", - "", - "\"Author\" refers to any designer, engineer, programmer, technical", - "writer or other person who contributed to the Font Software.", - "", - "PERMISSION & CONDITIONS", - "Permission is hereby granted, free of charge, to any person obtaining", - "a copy of the Font Software, to use, study, copy, merge, embed, modify,", - "redistribute, and sell modified and unmodified copies of the Font", - "Software, subject to the following conditions:", - "", - "1) Neither the Font Software nor any of its individual components,", - "in Original or Modified Versions, may be sold by itself.", - "", - "2) Original or Modified Versions of the Font Software may be bundled,", - "redistributed and/or sold with any software, provided that each copy", - "contains the above copyright notice and this license. These can be", - "included either as stand-alone text files, human-readable headers or", - "in the appropriate machine-readable metadata fields within text or", - "binary files as long as those fields can be easily viewed by the user.", - "", - "3) No Modified Version of the Font Software may use the Reserved Font", - "Name(s) unless explicit written permission is granted by the corresponding", - "Copyright Holder. This restriction only applies to the primary font name as", - "presented to the users.", - "", - "4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font", - "Software shall not be used to promote, endorse or advertise any", - "Modified Version, except to acknowledge the contribution(s) of the", - "Copyright Holder(s) and the Author(s) or with their explicit written", - "permission.", - "", - "5) The Font Software, modified or unmodified, in part or in whole,", - "must be distributed entirely under this license, and must not be", - "distributed under any other license. The requirement for fonts to", - "remain under this license does not apply to any document created", - "using the Font Software.", - "", - "TERMINATION", - "This license becomes null and void if any of the above conditions are", - "not met.", - "", - "DISCLAIMER", - "THE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", - "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF", - "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT", - "OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE", - "COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,", - "INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL", - "DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", - "FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM", - "OTHER DEALINGS IN THE FONT SOFTWARE." - ], - "license": "SIL OFL 1.1", - "version": "3.1.0" - } - ], - "version": 1 -} diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons-local.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons-local.ttf deleted file mode 100644 index 2050d5e8abf1..000000000000 Binary files a/src/vs/base/browser/ui/octiconLabel/octicons/octicons-local.ttf and /dev/null differ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css index 5a2a927f9a88..d24cc0b5258f 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css @@ -1,233 +1,243 @@ -/*! ***************************************************************************** -(c) 2012-2015 GitHub - -When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) - -Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) -Applies to all font files - -Code License: MIT (http://choosealicense.com/licenses/mit/) -Applies to all other files -***************************************************************************** */ - @font-face { - font-family: 'octicons'; - src: url('octicons.eot?#iefix') format('embedded-opentype'), - url('octicons.woff') format('woff'), - url('octicons.ttf') format('truetype'), - url('octicons.svg#octicons') format('svg'); - font-weight: normal; - font-style: normal; + font-family: "octicons"; + src: url("./octicons.ttf?4cd2299755e93a2430ba5703f4476584") format("truetype"), +url("./octicons.svg?4cd2299755e93a2430ba5703f4476584#octicons") format("svg"); } -/* - -.octicon is optimized for 16px. -.mega-octicon is optimized for 32px but can be used larger. - -*/ .octicon, .mega-octicon { - font: normal normal normal 16px/1 octicons; - display: inline-block; - text-decoration: none; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + font: normal normal normal 16px/1 octicons; + display: inline-block; + text-decoration: none; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + .mega-octicon { font-size: 32px; } -.octicon-alert:before { content: '\f02d'} /*  */ -.octicon-arrow-down:before { content: '\f03f'} /*  */ -.octicon-arrow-left:before { content: '\f040'} /*  */ -.octicon-arrow-right:before { content: '\f03e'} /*  */ -.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ -.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ -.octicon-arrow-small-right:before { content: '\f071'} /*  */ -.octicon-arrow-small-up:before { content: '\f09f'} /*  */ -.octicon-arrow-up:before { content: '\f03d'} /*  */ -.octicon-microscope:before, -.octicon-beaker:before { content: '\f0dd'} /*  */ -.octicon-bell:before { content: '\f0de'} /*  */ -.octicon-book:before { content: '\f007'} /*  */ -.octicon-bookmark:before { content: '\f07b'} /*  */ -.octicon-briefcase:before { content: '\f0d3'} /*  */ -.octicon-broadcast:before { content: '\f048'} /*  */ -.octicon-browser:before { content: '\f0c5'} /*  */ -.octicon-bug:before { content: '\f091'} /*  */ -.octicon-calendar:before { content: '\f068'} /*  */ -.octicon-check:before { content: '\f03a'} /*  */ -.octicon-checklist:before { content: '\f076'} /*  */ -.octicon-chevron-down:before { content: '\f0a3'} /*  */ -.octicon-chevron-left:before { content: '\f0a4'} /*  */ -.octicon-chevron-right:before { content: '\f078'} /*  */ -.octicon-chevron-up:before { content: '\f0a2'} /*  */ -.octicon-circle-slash:before { content: '\f084'} /*  */ -.octicon-circuit-board:before { content: '\f0d6'} /*  */ -.octicon-clippy:before { content: '\f035'} /*  */ -.octicon-clock:before { content: '\f046'} /*  */ -.octicon-cloud-download:before { content: '\f00b'} /*  */ -.octicon-cloud-upload:before { content: '\f00c'} /*  */ -.octicon-code:before { content: '\f05f'} /*  */ -.octicon-color-mode:before { content: '\f065'} /*  */ -.octicon-comment-add:before, -.octicon-comment:before { content: '\f02b'} /*  */ -.octicon-comment-discussion:before { content: '\f04f'} /*  */ -.octicon-credit-card:before { content: '\f045'} /*  */ -.octicon-dash:before { content: '\f0ca'} /*  */ -.octicon-dashboard:before { content: '\f07d'} /*  */ -.octicon-database:before { content: '\f096'} /*  */ -.octicon-clone:before, -.octicon-desktop-download:before { content: '\f0dc'} /*  */ -.octicon-device-camera:before { content: '\f056'} /*  */ -.octicon-device-camera-video:before { content: '\f057'} /*  */ -.octicon-device-desktop:before { content: '\f27c'} /*  */ -.octicon-device-mobile:before { content: '\f038'} /*  */ -.octicon-diff:before { content: '\f04d'} /*  */ -.octicon-diff-added:before { content: '\f06b'} /*  */ -.octicon-diff-ignored:before { content: '\f099'} /*  */ -.octicon-diff-modified:before { content: '\f06d'} /*  */ -.octicon-diff-removed:before { content: '\f06c'} /*  */ -.octicon-diff-renamed:before { content: '\f06e'} /*  */ -.octicon-ellipsis:before { content: '\f09a'} /*  */ -.octicon-eye-unwatch:before, -.octicon-eye-watch:before, -.octicon-eye:before { content: '\f04e'} /*  */ -.octicon-file-binary:before { content: '\f094'} /*  */ -.octicon-file-code:before { content: '\f010'} /*  */ -.octicon-file-directory:before { content: '\f016'} /*  */ -.octicon-file-media:before { content: '\f012'} /*  */ -.octicon-file-pdf:before { content: '\f014'} /*  */ -.octicon-file-submodule:before { content: '\f017'} /*  */ -.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ -.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ -.octicon-file-text:before { content: '\f011'} /*  */ -.octicon-file-zip:before { content: '\f013'} /*  */ -.octicon-flame:before { content: '\f0d2'} /*  */ -.octicon-fold:before { content: '\f0cc'} /*  */ -.octicon-gear:before { content: '\f02f'} /*  */ -.octicon-gift:before { content: '\f042'} /*  */ -.octicon-gist:before { content: '\f00e'} /*  */ -.octicon-gist-secret:before { content: '\f08c'} /*  */ -.octicon-git-branch-create:before, -.octicon-git-branch-delete:before, -.octicon-git-branch:before { content: '\f020'} /*  */ -.octicon-git-commit:before { content: '\f01f'} /*  */ -.octicon-git-compare:before { content: '\f0ac'} /*  */ -.octicon-git-merge:before { content: '\f023'} /*  */ -.octicon-git-pull-request-abandoned:before, -.octicon-git-pull-request:before { content: '\f009'} /*  */ -.octicon-globe:before { content: '\f0b6'} /*  */ -.octicon-graph:before { content: '\f043'} /*  */ -.octicon-heart:before { content: '\2665'} /* ♥ */ -.octicon-history:before { content: '\f07e'} /*  */ -.octicon-home:before { content: '\f08d'} /*  */ -.octicon-horizontal-rule:before { content: '\f070'} /*  */ -.octicon-hubot:before { content: '\f09d'} /*  */ -.octicon-inbox:before { content: '\f0cf'} /*  */ -.octicon-info:before { content: '\f059'} /*  */ -.octicon-issue-closed:before { content: '\f028'} /*  */ -.octicon-issue-opened:before { content: '\f026'} /*  */ -.octicon-issue-reopened:before { content: '\f027'} /*  */ -.octicon-jersey:before { content: '\f019'} /*  */ -.octicon-key:before { content: '\f049'} /*  */ -.octicon-keyboard:before { content: '\f00d'} /*  */ -.octicon-law:before { content: '\f0d8'} /*  */ -.octicon-light-bulb:before { content: '\f000'} /*  */ -.octicon-link:before { content: '\f05c'} /*  */ -.octicon-link-external:before { content: '\f07f'} /*  */ -.octicon-list-ordered:before { content: '\f062'} /*  */ -.octicon-list-unordered:before { content: '\f061'} /*  */ -.octicon-location:before { content: '\f060'} /*  */ -.octicon-gist-private:before, -.octicon-mirror-private:before, -.octicon-git-fork-private:before, -.octicon-lock:before { content: '\f06a'} /*  */ -.octicon-logo-github:before { content: '\f092'} /*  */ -.octicon-mail:before { content: '\f03b'} /*  */ -.octicon-mail-read:before { content: '\f03c'} /*  */ -.octicon-mail-reply:before { content: '\f051'} /*  */ -.octicon-mark-github:before { content: '\f00a'} /*  */ -.octicon-markdown:before { content: '\f0c9'} /*  */ -.octicon-megaphone:before { content: '\f077'} /*  */ -.octicon-mention:before { content: '\f0be'} /*  */ -.octicon-milestone:before { content: '\f075'} /*  */ -.octicon-mirror-public:before, -.octicon-mirror:before { content: '\f024'} /*  */ -.octicon-mortar-board:before { content: '\f0d7'} /*  */ -.octicon-mute:before { content: '\f080'} /*  */ -.octicon-no-newline:before { content: '\f09c'} /*  */ -.octicon-octoface:before { content: '\f008'} /*  */ -.octicon-organization:before { content: '\f037'} /*  */ -.octicon-package:before { content: '\f0c4'} /*  */ -.octicon-paintcan:before { content: '\f0d1'} /*  */ -.octicon-pencil:before { content: '\f058'} /*  */ -.octicon-person-add:before, -.octicon-person-follow:before, -.octicon-person:before { content: '\f018'} /*  */ -.octicon-pin:before { content: '\f041'} /*  */ -.octicon-plug:before { content: '\f0d4'} /*  */ -.octicon-repo-create:before, -.octicon-gist-new:before, -.octicon-file-directory-create:before, -.octicon-file-add:before, -.octicon-plus:before { content: '\f05d'} /*  */ -.octicon-primitive-dot:before { content: '\f052'} /*  */ -.octicon-primitive-square:before { content: '\f053'} /*  */ -.octicon-pulse:before { content: '\f085'} /*  */ -.octicon-question:before { content: '\f02c'} /*  */ -.octicon-quote:before { content: '\f063'} /*  */ -.octicon-radio-tower:before { content: '\f030'} /*  */ -.octicon-repo-delete:before, -.octicon-repo:before { content: '\f001'} /*  */ -.octicon-repo-clone:before { content: '\f04c'} /*  */ -.octicon-repo-force-push:before { content: '\f04a'} /*  */ -.octicon-gist-fork:before, -.octicon-repo-forked:before { content: '\f002'} /*  */ -.octicon-repo-pull:before { content: '\f006'} /*  */ -.octicon-repo-push:before { content: '\f005'} /*  */ -.octicon-rocket:before { content: '\f033'} /*  */ -.octicon-rss:before { content: '\f034'} /*  */ -.octicon-ruby:before { content: '\f047'} /*  */ -.octicon-screen-full:before { content: '\f066'} /*  */ -.octicon-screen-normal:before { content: '\f067'} /*  */ -.octicon-search-save:before, -.octicon-search:before { content: '\f02e'} /*  */ -.octicon-server:before { content: '\f097'} /*  */ -.octicon-settings:before { content: '\f07c'} /*  */ -.octicon-shield:before { content: '\f0e1'} /*  */ -.octicon-log-in:before, -.octicon-sign-in:before { content: '\f036'} /*  */ -.octicon-log-out:before, -.octicon-sign-out:before { content: '\f032'} /*  */ -.octicon-squirrel:before { content: '\f0b2'} /*  */ -.octicon-star-add:before, -.octicon-star-delete:before, -.octicon-star:before { content: '\f02a'} /*  */ -.octicon-stop:before { content: '\f08f'} /*  */ -.octicon-repo-sync:before, -.octicon-sync:before { content: '\f087'} /*  */ -.octicon-tag-remove:before, -.octicon-tag-add:before, -.octicon-tag:before { content: '\f015'} /*  */ -.octicon-telescope:before { content: '\f088'} /*  */ -.octicon-terminal:before { content: '\f0c8'} /*  */ -.octicon-three-bars:before { content: '\f05e'} /*  */ -.octicon-thumbsdown:before { content: '\f0db'} /*  */ -.octicon-thumbsup:before { content: '\f0da'} /*  */ -.octicon-tools:before { content: '\f031'} /*  */ -.octicon-trashcan:before { content: '\f0d0'} /*  */ -.octicon-triangle-down:before { content: '\f05b'} /*  */ -.octicon-triangle-left:before { content: '\f044'} /*  */ -.octicon-triangle-right:before { content: '\f05a'} /*  */ -.octicon-triangle-up:before { content: '\f0aa'} /*  */ -.octicon-unfold:before { content: '\f039'} /*  */ -.octicon-unmute:before { content: '\f0ba'} /*  */ -.octicon-versions:before { content: '\f064'} /*  */ -.octicon-watch:before { content: '\f0e0'} /*  */ -.octicon-remove-close:before, -.octicon-x:before { content: '\f081'} /*  */ -.octicon-zap:before { content: '\26A1'} /* ⚡ */ + + +.octicon-alert:before { content: "\f02d" } +.octicon-arrow-down:before { content: "\f03f" } +.octicon-arrow-left:before { content: "\f040" } +.octicon-arrow-right:before { content: "\f03e" } +.octicon-arrow-small-down:before { content: "\f0a0" } +.octicon-arrow-small-left:before { content: "\f0a1" } +.octicon-arrow-small-right:before { content: "\f071" } +.octicon-arrow-small-up:before { content: "\f09f" } +.octicon-arrow-up:before { content: "\f03d" } +.octicon-beaker:before { content: "\f0dd" } +.octicon-bell:before { content: "\f0de" } +.octicon-bold:before { content: "\f282" } +.octicon-book:before { content: "\f007" } +.octicon-bookmark:before { content: "\f07b" } +.octicon-briefcase:before { content: "\f0d3" } +.octicon-broadcast:before { content: "\f048" } +.octicon-browser:before { content: "\f0c5" } +.octicon-bug:before { content: "\f091" } +.octicon-calendar:before { content: "\f068" } +.octicon-check:before { content: "\f03a" } +.octicon-checklist:before { content: "\f076" } +.octicon-chevron-down:before { content: "\f0a3" } +.octicon-chevron-left:before { content: "\f0a4" } +.octicon-chevron-right:before { content: "\f078" } +.octicon-chevron-up:before { content: "\f0a2" } +.octicon-circle-slash:before { content: "\f084" } +.octicon-circuit-board:before { content: "\f0d6" } +.octicon-clippy:before { content: "\f035" } +.octicon-clock:before { content: "\f046" } +.octicon-clone:before { content: "\f0dc" } +.octicon-cloud-download:before { content: "\f00b" } +.octicon-cloud-upload:before { content: "\f00c" } +.octicon-code:before { content: "\f05f" } +.octicon-color-mode:before { content: "\f065" } +.octicon-comment-add:before { content: "\f02b" } +.octicon-comment-discussion:before { content: "\f04f" } +.octicon-comment:before { content: "\f02b" } +.octicon-credit-card:before { content: "\f045" } +.octicon-dash:before { content: "\f0ca" } +.octicon-dashboard:before { content: "\f07d" } +.octicon-database:before { content: "\f096" } +.octicon-desktop-download:before { content: "\f0dc" } +.octicon-device-camera-video:before { content: "\f057" } +.octicon-device-camera:before { content: "\f056" } +.octicon-device-desktop:before { content: "\f27c" } +.octicon-device-mobile:before { content: "\f038" } +.octicon-diff-added:before { content: "\f06b" } +.octicon-diff-ignored:before { content: "\f099" } +.octicon-diff-modified:before { content: "\f06d" } +.octicon-diff-removed:before { content: "\f06c" } +.octicon-diff-renamed:before { content: "\f06e" } +.octicon-diff:before { content: "\f04d" } +.octicon-ellipsis:before { content: "\f09a" } +.octicon-eye-unwatch:before { content: "\f04e" } +.octicon-eye-watch:before { content: "\f04e" } +.octicon-eye:before { content: "\f04e" } +.octicon-file-add:before { content: "\f05d" } +.octicon-file-binary:before { content: "\f094" } +.octicon-file-code:before { content: "\f010" } +.octicon-file-directory-create:before { content: "\f05d" } +.octicon-file-directory:before { content: "\f016" } +.octicon-file-media:before { content: "\f012" } +.octicon-file-pdf:before { content: "\f014" } +.octicon-file-submodule:before { content: "\f017" } +.octicon-file-symlink-directory:before { content: "\f0b1" } +.octicon-file-symlink-file:before { content: "\f0b0" } +.octicon-file-text:before { content: "\f283" } +.octicon-file-zip:before { content: "\f013" } +.octicon-file:before { content: "\f283" } +.octicon-flame:before { content: "\f0d2" } +.octicon-fold:before { content: "\f0cc" } +.octicon-gear:before { content: "\f02f" } +.octicon-gift:before { content: "\f042" } +.octicon-gist-fork:before { content: "\f002" } +.octicon-gist-new:before { content: "\f05d" } +.octicon-gist-private:before { content: "\f06a" } +.octicon-gist-secret:before { content: "\f08c" } +.octicon-gist:before { content: "\f00e" } +.octicon-git-branch-create:before { content: "\f020" } +.octicon-git-branch-delete:before { content: "\f020" } +.octicon-git-branch:before { content: "\f020" } +.octicon-git-commit:before { content: "\f01f" } +.octicon-git-compare:before { content: "\f0ac" } +.octicon-git-fork-private:before { content: "\f06a" } +.octicon-git-merge:before { content: "\f023" } +.octicon-git-pull-request-abandoned:before { content: "\f009" } +.octicon-git-pull-request:before { content: "\f009" } +.octicon-globe:before { content: "\f0b6" } +.octicon-grabber:before { content: "\f284" } +.octicon-graph:before { content: "\f043" } +.octicon-heart:before { content: "\2665" } +.octicon-history:before { content: "\f07e" } +.octicon-home:before { content: "\f08d" } +.octicon-horizontal-rule:before { content: "\f070" } +.octicon-hubot:before { content: "\f09d" } +.octicon-inbox:before { content: "\f0cf" } +.octicon-info:before { content: "\f059" } +.octicon-issue-closed:before { content: "\f028" } +.octicon-issue-opened:before { content: "\f026" } +.octicon-issue-reopened:before { content: "\f027" } +.octicon-italic:before { content: "\f285" } +.octicon-jersey:before { content: "\f019" } +.octicon-kebab-horizontal:before { content: "\f286" } +.octicon-kebab-vertical:before { content: "\f287" } +.octicon-key:before { content: "\f049" } +.octicon-keyboard:before { content: "\f00d" } +.octicon-law:before { content: "\f0d8" } +.octicon-light-bulb:before { content: "\f000" } +.octicon-link-external:before { content: "\f07f" } +.octicon-link:before { content: "\f05c" } +.octicon-list-ordered:before { content: "\f062" } +.octicon-list-unordered:before { content: "\f061" } +.octicon-location:before { content: "\f060" } +.octicon-lock:before { content: "\f06a" } +.octicon-log-in:before { content: "\f036" } +.octicon-log-out:before { content: "\f032" } +.octicon-logo-gist:before { content: "\f288" } +.octicon-logo-github:before { content: "\f092" } +.octicon-mail-read:before { content: "\f03c" } +.octicon-mail-reply:before { content: "\f28c" } +.octicon-mail:before { content: "\f03b" } +.octicon-mark-github:before { content: "\f00a" } +.octicon-markdown:before { content: "\f0c9" } +.octicon-megaphone:before { content: "\f077" } +.octicon-mention:before { content: "\f0be" } +.octicon-microscope:before { content: "\f0dd" } +.octicon-milestone:before { content: "\f075" } +.octicon-mirror-private:before { content: "\f06a" } +.octicon-mirror-public:before { content: "\f024" } +.octicon-mirror:before { content: "\f024" } +.octicon-mortar-board:before { content: "\f0d7" } +.octicon-mute:before { content: "\f080" } +.octicon-no-newline:before { content: "\f09c" } +.octicon-note:before { content: "\f289" } +.octicon-octoface:before { content: "\f008" } +.octicon-organization:before { content: "\f037" } +.octicon-organization-filled:before { content: "\26a2" } +.octicon-organization-outline:before { content: "\f037" } +.octicon-package:before { content: "\f0c4" } +.octicon-paintcan:before { content: "\f0d1" } +.octicon-pencil:before { content: "\f058" } +.octicon-person-add:before { content: "\f018" } +.octicon-person-follow:before { content: "\f018" } +.octicon-person:before { content: "\f018" } +.octicon-person-filled:before { content: "\26a3" } +.octicon-person-outline:before { content: "\f018" } +.octicon-pin:before { content: "\f041" } +.octicon-plug:before { content: "\f0d4" } +.octicon-plus-small:before { content: "\f28a" } +.octicon-plus:before { content: "\f05d" } +.octicon-primitive-dot:before { content: "\f052" } +.octicon-primitive-square:before { content: "\f053" } +.octicon-project:before { content: "\f28b" } +.octicon-pulse:before { content: "\f085" } +.octicon-question:before { content: "\f02c" } +.octicon-quote:before { content: "\f063" } +.octicon-radio-tower:before { content: "\f030" } +.octicon-remove-close:before { content: "\f081" } +.octicon-reply:before { content: "\f28c" } +.octicon-repo-clone:before { content: "\f04c" } +.octicon-repo-create:before { content: "\f05d" } +.octicon-repo-delete:before { content: "\f001" } +.octicon-repo-force-push:before { content: "\f04a" } +.octicon-repo-forked:before { content: "\f002" } +.octicon-repo-pull:before { content: "\f006" } +.octicon-repo-push:before { content: "\f005" } +.octicon-repo-sync:before { content: "\f087" } +.octicon-repo:before { content: "\f001" } +.octicon-report:before { content: "\f28d" } +.octicon-rocket:before { content: "\f033" } +.octicon-rss:before { content: "\f034" } +.octicon-ruby:before { content: "\f047" } +.octicon-screen-full:before { content: "\f066" } +.octicon-screen-normal:before { content: "\f067" } +.octicon-search-save:before { content: "\f02e" } +.octicon-search:before { content: "\f02e" } +.octicon-server:before { content: "\f097" } +.octicon-settings:before { content: "\f07c" } +.octicon-shield:before { content: "\f0e1" } +.octicon-sign-in:before { content: "\f036" } +.octicon-sign-out:before { content: "\f032" } +.octicon-smiley:before { content: "\f27d" } +.octicon-squirrel:before { content: "\f0b2" } +.octicon-star-add:before { content: "\f02a" } +.octicon-star-delete:before { content: "\f02a" } +.octicon-star:before { content: "\f02a" } +.octicon-stop:before { content: "\f08f" } +.octicon-sync:before { content: "\f087" } +.octicon-tag-add:before { content: "\f015" } +.octicon-tag-remove:before { content: "\f015" } +.octicon-tag:before { content: "\f015" } +.octicon-tasklist:before { content: "\f27e" } +.octicon-telescope:before { content: "\f088" } +.octicon-terminal:before { content: "\f0c8" } +.octicon-text-size:before { content: "\f27f" } +.octicon-three-bars:before { content: "\f05e" } +.octicon-thumbsdown:before { content: "\f0db" } +.octicon-thumbsup:before { content: "\f0da" } +.octicon-tools:before { content: "\f031" } +.octicon-trashcan:before { content: "\f0d0" } +.octicon-triangle-down:before { content: "\f05b" } +.octicon-triangle-left:before { content: "\f044" } +.octicon-triangle-right:before { content: "\f05a" } +.octicon-triangle-up:before { content: "\f0aa" } +.octicon-unfold:before { content: "\f039" } +.octicon-unmute:before { content: "\f0ba" } +.octicon-unverified:before { content: "\f280" } +.octicon-verified:before { content: "\f281" } +.octicon-versions:before { content: "\f064" } +.octicon-watch:before { content: "\f0e0" } +.octicon-x:before { content: "\f081" } +.octicon-zap:before { content: "\26a1" } +.octicon-archive:before { content: "\f101" } +.octicon-arrow-both:before { content: "\f102" } +.octicon-eye-closed:before { content: "\f103" } +.octicon-fold-down:before { content: "\f104" } +.octicon-fold-up:before { content: "\f105" } +.octicon-github-action:before { content: "\f106" } +.octicon-play:before { content: "\f107" } +.octicon-request-changes:before { content: "\f108" } diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.eot b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.eot deleted file mode 100644 index 2bf20bca0030..000000000000 Binary files a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.eot and /dev/null differ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.less b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.less deleted file mode 100644 index d1d751ea45ea..000000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.less +++ /dev/null @@ -1,220 +0,0 @@ -@octicons-font-path: "."; -@octicons-version: "396334ee3da78f4302d25c758ae3e3ce5dc3c97d"; - -@font-face { - font-family: 'octicons'; - src: ~"url('@{octicons-font-path}/octicons.eot?#iefix&v=@{octicons-version}') format('embedded-opentype')", - ~"url('@{octicons-font-path}/octicons.woff?v=@{octicons-version}') format('woff')", - ~"url('@{octicons-font-path}/octicons.ttf?v=@{octicons-version}') format('truetype')", - ~"url('@{octicons-font-path}/octicons.svg?v=@{octicons-version}#octicons') format('svg')"; - font-weight: normal; - font-style: normal; -} - -// .octicon is optimized for 16px. -// .mega-octicon is optimized for 32px but can be used larger. -.octicon, .mega-octicon { - font: normal normal normal 16px/1 octicons; - display: inline-block; - text-decoration: none; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.mega-octicon { font-size: 32px; } - -.octicon-alert:before { content: '\f02d'} /*  */ -.octicon-arrow-down:before { content: '\f03f'} /*  */ -.octicon-arrow-left:before { content: '\f040'} /*  */ -.octicon-arrow-right:before { content: '\f03e'} /*  */ -.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ -.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ -.octicon-arrow-small-right:before { content: '\f071'} /*  */ -.octicon-arrow-small-up:before { content: '\f09f'} /*  */ -.octicon-arrow-up:before { content: '\f03d'} /*  */ -.octicon-microscope:before, -.octicon-beaker:before { content: '\f0dd'} /*  */ -.octicon-bell:before { content: '\f0de'} /*  */ -.octicon-book:before { content: '\f007'} /*  */ -.octicon-bookmark:before { content: '\f07b'} /*  */ -.octicon-briefcase:before { content: '\f0d3'} /*  */ -.octicon-broadcast:before { content: '\f048'} /*  */ -.octicon-browser:before { content: '\f0c5'} /*  */ -.octicon-bug:before { content: '\f091'} /*  */ -.octicon-calendar:before { content: '\f068'} /*  */ -.octicon-check:before { content: '\f03a'} /*  */ -.octicon-checklist:before { content: '\f076'} /*  */ -.octicon-chevron-down:before { content: '\f0a3'} /*  */ -.octicon-chevron-left:before { content: '\f0a4'} /*  */ -.octicon-chevron-right:before { content: '\f078'} /*  */ -.octicon-chevron-up:before { content: '\f0a2'} /*  */ -.octicon-circle-slash:before { content: '\f084'} /*  */ -.octicon-circuit-board:before { content: '\f0d6'} /*  */ -.octicon-clippy:before { content: '\f035'} /*  */ -.octicon-clock:before { content: '\f046'} /*  */ -.octicon-cloud-download:before { content: '\f00b'} /*  */ -.octicon-cloud-upload:before { content: '\f00c'} /*  */ -.octicon-code:before { content: '\f05f'} /*  */ -.octicon-color-mode:before { content: '\f065'} /*  */ -.octicon-comment-add:before, -.octicon-comment:before { content: '\f02b'} /*  */ -.octicon-comment-discussion:before { content: '\f04f'} /*  */ -.octicon-credit-card:before { content: '\f045'} /*  */ -.octicon-dash:before { content: '\f0ca'} /*  */ -.octicon-dashboard:before { content: '\f07d'} /*  */ -.octicon-database:before { content: '\f096'} /*  */ -.octicon-clone:before, -.octicon-desktop-download:before { content: '\f0dc'} /*  */ -.octicon-device-camera:before { content: '\f056'} /*  */ -.octicon-device-camera-video:before { content: '\f057'} /*  */ -.octicon-device-desktop:before { content: '\f27c'} /*  */ -.octicon-device-mobile:before { content: '\f038'} /*  */ -.octicon-diff:before { content: '\f04d'} /*  */ -.octicon-diff-added:before { content: '\f06b'} /*  */ -.octicon-diff-ignored:before { content: '\f099'} /*  */ -.octicon-diff-modified:before { content: '\f06d'} /*  */ -.octicon-diff-removed:before { content: '\f06c'} /*  */ -.octicon-diff-renamed:before { content: '\f06e'} /*  */ -.octicon-ellipsis:before { content: '\f09a'} /*  */ -.octicon-eye-unwatch:before, -.octicon-eye-watch:before, -.octicon-eye:before { content: '\f04e'} /*  */ -.octicon-file-binary:before { content: '\f094'} /*  */ -.octicon-file-code:before { content: '\f010'} /*  */ -.octicon-file-directory:before { content: '\f016'} /*  */ -.octicon-file-media:before { content: '\f012'} /*  */ -.octicon-file-pdf:before { content: '\f014'} /*  */ -.octicon-file-submodule:before { content: '\f017'} /*  */ -.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ -.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ -.octicon-file-text:before { content: '\f011'} /*  */ -.octicon-file-zip:before { content: '\f013'} /*  */ -.octicon-flame:before { content: '\f0d2'} /*  */ -.octicon-fold:before { content: '\f0cc'} /*  */ -.octicon-gear:before { content: '\f02f'} /*  */ -.octicon-gift:before { content: '\f042'} /*  */ -.octicon-gist:before { content: '\f00e'} /*  */ -.octicon-gist-secret:before { content: '\f08c'} /*  */ -.octicon-git-branch-create:before, -.octicon-git-branch-delete:before, -.octicon-git-branch:before { content: '\f020'} /*  */ -.octicon-git-commit:before { content: '\f01f'} /*  */ -.octicon-git-compare:before { content: '\f0ac'} /*  */ -.octicon-git-merge:before { content: '\f023'} /*  */ -.octicon-git-pull-request-abandoned:before, -.octicon-git-pull-request:before { content: '\f009'} /*  */ -.octicon-globe:before { content: '\f0b6'} /*  */ -.octicon-graph:before { content: '\f043'} /*  */ -.octicon-heart:before { content: '\2665'} /* ♥ */ -.octicon-history:before { content: '\f07e'} /*  */ -.octicon-home:before { content: '\f08d'} /*  */ -.octicon-horizontal-rule:before { content: '\f070'} /*  */ -.octicon-hubot:before { content: '\f09d'} /*  */ -.octicon-inbox:before { content: '\f0cf'} /*  */ -.octicon-info:before { content: '\f059'} /*  */ -.octicon-issue-closed:before { content: '\f028'} /*  */ -.octicon-issue-opened:before { content: '\f026'} /*  */ -.octicon-issue-reopened:before { content: '\f027'} /*  */ -.octicon-jersey:before { content: '\f019'} /*  */ -.octicon-key:before { content: '\f049'} /*  */ -.octicon-keyboard:before { content: '\f00d'} /*  */ -.octicon-law:before { content: '\f0d8'} /*  */ -.octicon-light-bulb:before { content: '\f000'} /*  */ -.octicon-link:before { content: '\f05c'} /*  */ -.octicon-link-external:before { content: '\f07f'} /*  */ -.octicon-list-ordered:before { content: '\f062'} /*  */ -.octicon-list-unordered:before { content: '\f061'} /*  */ -.octicon-location:before { content: '\f060'} /*  */ -.octicon-gist-private:before, -.octicon-mirror-private:before, -.octicon-git-fork-private:before, -.octicon-lock:before { content: '\f06a'} /*  */ -.octicon-logo-github:before { content: '\f092'} /*  */ -.octicon-mail:before { content: '\f03b'} /*  */ -.octicon-mail-read:before { content: '\f03c'} /*  */ -.octicon-mail-reply:before { content: '\f051'} /*  */ -.octicon-mark-github:before { content: '\f00a'} /*  */ -.octicon-markdown:before { content: '\f0c9'} /*  */ -.octicon-megaphone:before { content: '\f077'} /*  */ -.octicon-mention:before { content: '\f0be'} /*  */ -.octicon-milestone:before { content: '\f075'} /*  */ -.octicon-mirror-public:before, -.octicon-mirror:before { content: '\f024'} /*  */ -.octicon-mortar-board:before { content: '\f0d7'} /*  */ -.octicon-mute:before { content: '\f080'} /*  */ -.octicon-no-newline:before { content: '\f09c'} /*  */ -.octicon-octoface:before { content: '\f008'} /*  */ -.octicon-organization:before { content: '\f037'} /*  */ -.octicon-package:before { content: '\f0c4'} /*  */ -.octicon-paintcan:before { content: '\f0d1'} /*  */ -.octicon-pencil:before { content: '\f058'} /*  */ -.octicon-person-add:before, -.octicon-person-follow:before, -.octicon-person:before { content: '\f018'} /*  */ -.octicon-pin:before { content: '\f041'} /*  */ -.octicon-plug:before { content: '\f0d4'} /*  */ -.octicon-repo-create:before, -.octicon-gist-new:before, -.octicon-file-directory-create:before, -.octicon-file-add:before, -.octicon-plus:before { content: '\f05d'} /*  */ -.octicon-primitive-dot:before { content: '\f052'} /*  */ -.octicon-primitive-square:before { content: '\f053'} /*  */ -.octicon-pulse:before { content: '\f085'} /*  */ -.octicon-question:before { content: '\f02c'} /*  */ -.octicon-quote:before { content: '\f063'} /*  */ -.octicon-radio-tower:before { content: '\f030'} /*  */ -.octicon-repo-delete:before, -.octicon-repo:before { content: '\f001'} /*  */ -.octicon-repo-clone:before { content: '\f04c'} /*  */ -.octicon-repo-force-push:before { content: '\f04a'} /*  */ -.octicon-gist-fork:before, -.octicon-repo-forked:before { content: '\f002'} /*  */ -.octicon-repo-pull:before { content: '\f006'} /*  */ -.octicon-repo-push:before { content: '\f005'} /*  */ -.octicon-rocket:before { content: '\f033'} /*  */ -.octicon-rss:before { content: '\f034'} /*  */ -.octicon-ruby:before { content: '\f047'} /*  */ -.octicon-screen-full:before { content: '\f066'} /*  */ -.octicon-screen-normal:before { content: '\f067'} /*  */ -.octicon-search-save:before, -.octicon-search:before { content: '\f02e'} /*  */ -.octicon-server:before { content: '\f097'} /*  */ -.octicon-settings:before { content: '\f07c'} /*  */ -.octicon-shield:before { content: '\f0e1'} /*  */ -.octicon-log-in:before, -.octicon-sign-in:before { content: '\f036'} /*  */ -.octicon-log-out:before, -.octicon-sign-out:before { content: '\f032'} /*  */ -.octicon-squirrel:before { content: '\f0b2'} /*  */ -.octicon-star-add:before, -.octicon-star-delete:before, -.octicon-star:before { content: '\f02a'} /*  */ -.octicon-stop:before { content: '\f08f'} /*  */ -.octicon-repo-sync:before, -.octicon-sync:before { content: '\f087'} /*  */ -.octicon-tag-remove:before, -.octicon-tag-add:before, -.octicon-tag:before { content: '\f015'} /*  */ -.octicon-telescope:before { content: '\f088'} /*  */ -.octicon-terminal:before { content: '\f0c8'} /*  */ -.octicon-three-bars:before { content: '\f05e'} /*  */ -.octicon-thumbsdown:before { content: '\f0db'} /*  */ -.octicon-thumbsup:before { content: '\f0da'} /*  */ -.octicon-tools:before { content: '\f031'} /*  */ -.octicon-trashcan:before { content: '\f0d0'} /*  */ -.octicon-triangle-down:before { content: '\f05b'} /*  */ -.octicon-triangle-left:before { content: '\f044'} /*  */ -.octicon-triangle-right:before { content: '\f05a'} /*  */ -.octicon-triangle-up:before { content: '\f0aa'} /*  */ -.octicon-unfold:before { content: '\f039'} /*  */ -.octicon-unmute:before { content: '\f0ba'} /*  */ -.octicon-versions:before { content: '\f064'} /*  */ -.octicon-watch:before { content: '\f0e0'} /*  */ -.octicon-remove-close:before, -.octicon-x:before { content: '\f081'} /*  */ -.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.scss b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.scss deleted file mode 100644 index 0902cedce8f6..000000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.scss +++ /dev/null @@ -1,220 +0,0 @@ -$octicons-font-path: "." !default; -$octicons-version: "396334ee3da78f4302d25c758ae3e3ce5dc3c97d"; - -@font-face { - font-family: 'octicons'; - src: url('#{$octicons-font-path}/octicons.eot?#iefix&v=#{$octicons-version}') format('embedded-opentype'), - url('#{$octicons-font-path}/octicons.woff?v=#{$octicons-version}') format('woff'), - url('#{$octicons-font-path}/octicons.ttf?v=#{$octicons-version}') format('truetype'), - url('#{$octicons-font-path}/octicons.svg?v=#{$octicons-version}#octicons') format('svg'); - font-weight: normal; - font-style: normal; -} - -// .octicon is optimized for 16px. -// .mega-octicon is optimized for 32px but can be used larger. -.octicon, .mega-octicon { - font: normal normal normal 16px/1 octicons; - display: inline-block; - text-decoration: none; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.mega-octicon { font-size: 32px; } - -.octicon-alert:before { content: '\f02d'} /*  */ -.octicon-arrow-down:before { content: '\f03f'} /*  */ -.octicon-arrow-left:before { content: '\f040'} /*  */ -.octicon-arrow-right:before { content: '\f03e'} /*  */ -.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ -.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ -.octicon-arrow-small-right:before { content: '\f071'} /*  */ -.octicon-arrow-small-up:before { content: '\f09f'} /*  */ -.octicon-arrow-up:before { content: '\f03d'} /*  */ -.octicon-microscope:before, -.octicon-beaker:before { content: '\f0dd'} /*  */ -.octicon-bell:before { content: '\f0de'} /*  */ -.octicon-book:before { content: '\f007'} /*  */ -.octicon-bookmark:before { content: '\f07b'} /*  */ -.octicon-briefcase:before { content: '\f0d3'} /*  */ -.octicon-broadcast:before { content: '\f048'} /*  */ -.octicon-browser:before { content: '\f0c5'} /*  */ -.octicon-bug:before { content: '\f091'} /*  */ -.octicon-calendar:before { content: '\f068'} /*  */ -.octicon-check:before { content: '\f03a'} /*  */ -.octicon-checklist:before { content: '\f076'} /*  */ -.octicon-chevron-down:before { content: '\f0a3'} /*  */ -.octicon-chevron-left:before { content: '\f0a4'} /*  */ -.octicon-chevron-right:before { content: '\f078'} /*  */ -.octicon-chevron-up:before { content: '\f0a2'} /*  */ -.octicon-circle-slash:before { content: '\f084'} /*  */ -.octicon-circuit-board:before { content: '\f0d6'} /*  */ -.octicon-clippy:before { content: '\f035'} /*  */ -.octicon-clock:before { content: '\f046'} /*  */ -.octicon-cloud-download:before { content: '\f00b'} /*  */ -.octicon-cloud-upload:before { content: '\f00c'} /*  */ -.octicon-code:before { content: '\f05f'} /*  */ -.octicon-color-mode:before { content: '\f065'} /*  */ -.octicon-comment-add:before, -.octicon-comment:before { content: '\f02b'} /*  */ -.octicon-comment-discussion:before { content: '\f04f'} /*  */ -.octicon-credit-card:before { content: '\f045'} /*  */ -.octicon-dash:before { content: '\f0ca'} /*  */ -.octicon-dashboard:before { content: '\f07d'} /*  */ -.octicon-database:before { content: '\f096'} /*  */ -.octicon-clone:before, -.octicon-desktop-download:before { content: '\f0dc'} /*  */ -.octicon-device-camera:before { content: '\f056'} /*  */ -.octicon-device-camera-video:before { content: '\f057'} /*  */ -.octicon-device-desktop:before { content: '\f27c'} /*  */ -.octicon-device-mobile:before { content: '\f038'} /*  */ -.octicon-diff:before { content: '\f04d'} /*  */ -.octicon-diff-added:before { content: '\f06b'} /*  */ -.octicon-diff-ignored:before { content: '\f099'} /*  */ -.octicon-diff-modified:before { content: '\f06d'} /*  */ -.octicon-diff-removed:before { content: '\f06c'} /*  */ -.octicon-diff-renamed:before { content: '\f06e'} /*  */ -.octicon-ellipsis:before { content: '\f09a'} /*  */ -.octicon-eye-unwatch:before, -.octicon-eye-watch:before, -.octicon-eye:before { content: '\f04e'} /*  */ -.octicon-file-binary:before { content: '\f094'} /*  */ -.octicon-file-code:before { content: '\f010'} /*  */ -.octicon-file-directory:before { content: '\f016'} /*  */ -.octicon-file-media:before { content: '\f012'} /*  */ -.octicon-file-pdf:before { content: '\f014'} /*  */ -.octicon-file-submodule:before { content: '\f017'} /*  */ -.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ -.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ -.octicon-file-text:before { content: '\f011'} /*  */ -.octicon-file-zip:before { content: '\f013'} /*  */ -.octicon-flame:before { content: '\f0d2'} /*  */ -.octicon-fold:before { content: '\f0cc'} /*  */ -.octicon-gear:before { content: '\f02f'} /*  */ -.octicon-gift:before { content: '\f042'} /*  */ -.octicon-gist:before { content: '\f00e'} /*  */ -.octicon-gist-secret:before { content: '\f08c'} /*  */ -.octicon-git-branch-create:before, -.octicon-git-branch-delete:before, -.octicon-git-branch:before { content: '\f020'} /*  */ -.octicon-git-commit:before { content: '\f01f'} /*  */ -.octicon-git-compare:before { content: '\f0ac'} /*  */ -.octicon-git-merge:before { content: '\f023'} /*  */ -.octicon-git-pull-request-abandoned:before, -.octicon-git-pull-request:before { content: '\f009'} /*  */ -.octicon-globe:before { content: '\f0b6'} /*  */ -.octicon-graph:before { content: '\f043'} /*  */ -.octicon-heart:before { content: '\2665'} /* ♥ */ -.octicon-history:before { content: '\f07e'} /*  */ -.octicon-home:before { content: '\f08d'} /*  */ -.octicon-horizontal-rule:before { content: '\f070'} /*  */ -.octicon-hubot:before { content: '\f09d'} /*  */ -.octicon-inbox:before { content: '\f0cf'} /*  */ -.octicon-info:before { content: '\f059'} /*  */ -.octicon-issue-closed:before { content: '\f028'} /*  */ -.octicon-issue-opened:before { content: '\f026'} /*  */ -.octicon-issue-reopened:before { content: '\f027'} /*  */ -.octicon-jersey:before { content: '\f019'} /*  */ -.octicon-key:before { content: '\f049'} /*  */ -.octicon-keyboard:before { content: '\f00d'} /*  */ -.octicon-law:before { content: '\f0d8'} /*  */ -.octicon-light-bulb:before { content: '\f000'} /*  */ -.octicon-link:before { content: '\f05c'} /*  */ -.octicon-link-external:before { content: '\f07f'} /*  */ -.octicon-list-ordered:before { content: '\f062'} /*  */ -.octicon-list-unordered:before { content: '\f061'} /*  */ -.octicon-location:before { content: '\f060'} /*  */ -.octicon-gist-private:before, -.octicon-mirror-private:before, -.octicon-git-fork-private:before, -.octicon-lock:before { content: '\f06a'} /*  */ -.octicon-logo-github:before { content: '\f092'} /*  */ -.octicon-mail:before { content: '\f03b'} /*  */ -.octicon-mail-read:before { content: '\f03c'} /*  */ -.octicon-mail-reply:before { content: '\f051'} /*  */ -.octicon-mark-github:before { content: '\f00a'} /*  */ -.octicon-markdown:before { content: '\f0c9'} /*  */ -.octicon-megaphone:before { content: '\f077'} /*  */ -.octicon-mention:before { content: '\f0be'} /*  */ -.octicon-milestone:before { content: '\f075'} /*  */ -.octicon-mirror-public:before, -.octicon-mirror:before { content: '\f024'} /*  */ -.octicon-mortar-board:before { content: '\f0d7'} /*  */ -.octicon-mute:before { content: '\f080'} /*  */ -.octicon-no-newline:before { content: '\f09c'} /*  */ -.octicon-octoface:before { content: '\f008'} /*  */ -.octicon-organization:before { content: '\f037'} /*  */ -.octicon-package:before { content: '\f0c4'} /*  */ -.octicon-paintcan:before { content: '\f0d1'} /*  */ -.octicon-pencil:before { content: '\f058'} /*  */ -.octicon-person-add:before, -.octicon-person-follow:before, -.octicon-person:before { content: '\f018'} /*  */ -.octicon-pin:before { content: '\f041'} /*  */ -.octicon-plug:before { content: '\f0d4'} /*  */ -.octicon-repo-create:before, -.octicon-gist-new:before, -.octicon-file-directory-create:before, -.octicon-file-add:before, -.octicon-plus:before { content: '\f05d'} /*  */ -.octicon-primitive-dot:before { content: '\f052'} /*  */ -.octicon-primitive-square:before { content: '\f053'} /*  */ -.octicon-pulse:before { content: '\f085'} /*  */ -.octicon-question:before { content: '\f02c'} /*  */ -.octicon-quote:before { content: '\f063'} /*  */ -.octicon-radio-tower:before { content: '\f030'} /*  */ -.octicon-repo-delete:before, -.octicon-repo:before { content: '\f001'} /*  */ -.octicon-repo-clone:before { content: '\f04c'} /*  */ -.octicon-repo-force-push:before { content: '\f04a'} /*  */ -.octicon-gist-fork:before, -.octicon-repo-forked:before { content: '\f002'} /*  */ -.octicon-repo-pull:before { content: '\f006'} /*  */ -.octicon-repo-push:before { content: '\f005'} /*  */ -.octicon-rocket:before { content: '\f033'} /*  */ -.octicon-rss:before { content: '\f034'} /*  */ -.octicon-ruby:before { content: '\f047'} /*  */ -.octicon-screen-full:before { content: '\f066'} /*  */ -.octicon-screen-normal:before { content: '\f067'} /*  */ -.octicon-search-save:before, -.octicon-search:before { content: '\f02e'} /*  */ -.octicon-server:before { content: '\f097'} /*  */ -.octicon-settings:before { content: '\f07c'} /*  */ -.octicon-shield:before { content: '\f0e1'} /*  */ -.octicon-log-in:before, -.octicon-sign-in:before { content: '\f036'} /*  */ -.octicon-log-out:before, -.octicon-sign-out:before { content: '\f032'} /*  */ -.octicon-squirrel:before { content: '\f0b2'} /*  */ -.octicon-star-add:before, -.octicon-star-delete:before, -.octicon-star:before { content: '\f02a'} /*  */ -.octicon-stop:before { content: '\f08f'} /*  */ -.octicon-repo-sync:before, -.octicon-sync:before { content: '\f087'} /*  */ -.octicon-tag-remove:before, -.octicon-tag-add:before, -.octicon-tag:before { content: '\f015'} /*  */ -.octicon-telescope:before { content: '\f088'} /*  */ -.octicon-terminal:before { content: '\f0c8'} /*  */ -.octicon-three-bars:before { content: '\f05e'} /*  */ -.octicon-thumbsdown:before { content: '\f0db'} /*  */ -.octicon-thumbsup:before { content: '\f0da'} /*  */ -.octicon-tools:before { content: '\f031'} /*  */ -.octicon-trashcan:before { content: '\f0d0'} /*  */ -.octicon-triangle-down:before { content: '\f05b'} /*  */ -.octicon-triangle-left:before { content: '\f044'} /*  */ -.octicon-triangle-right:before { content: '\f05a'} /*  */ -.octicon-triangle-up:before { content: '\f0aa'} /*  */ -.octicon-unfold:before { content: '\f039'} /*  */ -.octicon-unmute:before { content: '\f0ba'} /*  */ -.octicon-versions:before { content: '\f064'} /*  */ -.octicon-watch:before { content: '\f0e0'} /*  */ -.octicon-remove-close:before, -.octicon-x:before { content: '\f081'} /*  */ -.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg index d932988bb577..4581795255ce 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg @@ -1,183 +1,567 @@ - - + + - -(c) 2012-2015 GitHub - -When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) - -Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) -Applies to all font files - -Code License: MIT (http://choosealicense.com/licenses/mit/) -Applies to all other files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf index 32e6720a7334..12b561cd47a7 100644 Binary files a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf and b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf differ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.woff b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.woff deleted file mode 100644 index cbf9f62ea65c..000000000000 Binary files a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.woff and /dev/null differ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/sprockets-octicons.scss b/src/vs/base/browser/ui/octiconLabel/octicons/sprockets-octicons.scss deleted file mode 100644 index cef21ae62eee..000000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/sprockets-octicons.scss +++ /dev/null @@ -1,217 +0,0 @@ -@font-face { - font-family: 'octicons'; - src: font-url('octicons.eot?#iefix') format('embedded-opentype'), - font-url('octicons.woff') format('woff'), - font-url('octicons.ttf') format('truetype'), - font-url('octicons.svg#octicons') format('svg'); - font-weight: normal; - font-style: normal; -} - -// .octicon is optimized for 16px. -// .mega-octicon is optimized for 32px but can be used larger. -.octicon, .mega-octicon { - font: normal normal normal 16px/1 octicons; - display: inline-block; - text-decoration: none; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.mega-octicon { font-size: 32px; } - -.octicon-alert:before { content: '\f02d'} /*  */ -.octicon-arrow-down:before { content: '\f03f'} /*  */ -.octicon-arrow-left:before { content: '\f040'} /*  */ -.octicon-arrow-right:before { content: '\f03e'} /*  */ -.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ -.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ -.octicon-arrow-small-right:before { content: '\f071'} /*  */ -.octicon-arrow-small-up:before { content: '\f09f'} /*  */ -.octicon-arrow-up:before { content: '\f03d'} /*  */ -.octicon-microscope:before, -.octicon-beaker:before { content: '\f0dd'} /*  */ -.octicon-bell:before { content: '\f0de'} /*  */ -.octicon-book:before { content: '\f007'} /*  */ -.octicon-bookmark:before { content: '\f07b'} /*  */ -.octicon-briefcase:before { content: '\f0d3'} /*  */ -.octicon-broadcast:before { content: '\f048'} /*  */ -.octicon-browser:before { content: '\f0c5'} /*  */ -.octicon-bug:before { content: '\f091'} /*  */ -.octicon-calendar:before { content: '\f068'} /*  */ -.octicon-check:before { content: '\f03a'} /*  */ -.octicon-checklist:before { content: '\f076'} /*  */ -.octicon-chevron-down:before { content: '\f0a3'} /*  */ -.octicon-chevron-left:before { content: '\f0a4'} /*  */ -.octicon-chevron-right:before { content: '\f078'} /*  */ -.octicon-chevron-up:before { content: '\f0a2'} /*  */ -.octicon-circle-slash:before { content: '\f084'} /*  */ -.octicon-circuit-board:before { content: '\f0d6'} /*  */ -.octicon-clippy:before { content: '\f035'} /*  */ -.octicon-clock:before { content: '\f046'} /*  */ -.octicon-cloud-download:before { content: '\f00b'} /*  */ -.octicon-cloud-upload:before { content: '\f00c'} /*  */ -.octicon-code:before { content: '\f05f'} /*  */ -.octicon-color-mode:before { content: '\f065'} /*  */ -.octicon-comment-add:before, -.octicon-comment:before { content: '\f02b'} /*  */ -.octicon-comment-discussion:before { content: '\f04f'} /*  */ -.octicon-credit-card:before { content: '\f045'} /*  */ -.octicon-dash:before { content: '\f0ca'} /*  */ -.octicon-dashboard:before { content: '\f07d'} /*  */ -.octicon-database:before { content: '\f096'} /*  */ -.octicon-clone:before, -.octicon-desktop-download:before { content: '\f0dc'} /*  */ -.octicon-device-camera:before { content: '\f056'} /*  */ -.octicon-device-camera-video:before { content: '\f057'} /*  */ -.octicon-device-desktop:before { content: '\f27c'} /*  */ -.octicon-device-mobile:before { content: '\f038'} /*  */ -.octicon-diff:before { content: '\f04d'} /*  */ -.octicon-diff-added:before { content: '\f06b'} /*  */ -.octicon-diff-ignored:before { content: '\f099'} /*  */ -.octicon-diff-modified:before { content: '\f06d'} /*  */ -.octicon-diff-removed:before { content: '\f06c'} /*  */ -.octicon-diff-renamed:before { content: '\f06e'} /*  */ -.octicon-ellipsis:before { content: '\f09a'} /*  */ -.octicon-eye-unwatch:before, -.octicon-eye-watch:before, -.octicon-eye:before { content: '\f04e'} /*  */ -.octicon-file-binary:before { content: '\f094'} /*  */ -.octicon-file-code:before { content: '\f010'} /*  */ -.octicon-file-directory:before { content: '\f016'} /*  */ -.octicon-file-media:before { content: '\f012'} /*  */ -.octicon-file-pdf:before { content: '\f014'} /*  */ -.octicon-file-submodule:before { content: '\f017'} /*  */ -.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ -.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ -.octicon-file-text:before { content: '\f011'} /*  */ -.octicon-file-zip:before { content: '\f013'} /*  */ -.octicon-flame:before { content: '\f0d2'} /*  */ -.octicon-fold:before { content: '\f0cc'} /*  */ -.octicon-gear:before { content: '\f02f'} /*  */ -.octicon-gift:before { content: '\f042'} /*  */ -.octicon-gist:before { content: '\f00e'} /*  */ -.octicon-gist-secret:before { content: '\f08c'} /*  */ -.octicon-git-branch-create:before, -.octicon-git-branch-delete:before, -.octicon-git-branch:before { content: '\f020'} /*  */ -.octicon-git-commit:before { content: '\f01f'} /*  */ -.octicon-git-compare:before { content: '\f0ac'} /*  */ -.octicon-git-merge:before { content: '\f023'} /*  */ -.octicon-git-pull-request-abandoned:before, -.octicon-git-pull-request:before { content: '\f009'} /*  */ -.octicon-globe:before { content: '\f0b6'} /*  */ -.octicon-graph:before { content: '\f043'} /*  */ -.octicon-heart:before { content: '\2665'} /* ♥ */ -.octicon-history:before { content: '\f07e'} /*  */ -.octicon-home:before { content: '\f08d'} /*  */ -.octicon-horizontal-rule:before { content: '\f070'} /*  */ -.octicon-hubot:before { content: '\f09d'} /*  */ -.octicon-inbox:before { content: '\f0cf'} /*  */ -.octicon-info:before { content: '\f059'} /*  */ -.octicon-issue-closed:before { content: '\f028'} /*  */ -.octicon-issue-opened:before { content: '\f026'} /*  */ -.octicon-issue-reopened:before { content: '\f027'} /*  */ -.octicon-jersey:before { content: '\f019'} /*  */ -.octicon-key:before { content: '\f049'} /*  */ -.octicon-keyboard:before { content: '\f00d'} /*  */ -.octicon-law:before { content: '\f0d8'} /*  */ -.octicon-light-bulb:before { content: '\f000'} /*  */ -.octicon-link:before { content: '\f05c'} /*  */ -.octicon-link-external:before { content: '\f07f'} /*  */ -.octicon-list-ordered:before { content: '\f062'} /*  */ -.octicon-list-unordered:before { content: '\f061'} /*  */ -.octicon-location:before { content: '\f060'} /*  */ -.octicon-gist-private:before, -.octicon-mirror-private:before, -.octicon-git-fork-private:before, -.octicon-lock:before { content: '\f06a'} /*  */ -.octicon-logo-github:before { content: '\f092'} /*  */ -.octicon-mail:before { content: '\f03b'} /*  */ -.octicon-mail-read:before { content: '\f03c'} /*  */ -.octicon-mail-reply:before { content: '\f051'} /*  */ -.octicon-mark-github:before { content: '\f00a'} /*  */ -.octicon-markdown:before { content: '\f0c9'} /*  */ -.octicon-megaphone:before { content: '\f077'} /*  */ -.octicon-mention:before { content: '\f0be'} /*  */ -.octicon-milestone:before { content: '\f075'} /*  */ -.octicon-mirror-public:before, -.octicon-mirror:before { content: '\f024'} /*  */ -.octicon-mortar-board:before { content: '\f0d7'} /*  */ -.octicon-mute:before { content: '\f080'} /*  */ -.octicon-no-newline:before { content: '\f09c'} /*  */ -.octicon-octoface:before { content: '\f008'} /*  */ -.octicon-organization:before { content: '\f037'} /*  */ -.octicon-package:before { content: '\f0c4'} /*  */ -.octicon-paintcan:before { content: '\f0d1'} /*  */ -.octicon-pencil:before { content: '\f058'} /*  */ -.octicon-person-add:before, -.octicon-person-follow:before, -.octicon-person:before { content: '\f018'} /*  */ -.octicon-pin:before { content: '\f041'} /*  */ -.octicon-plug:before { content: '\f0d4'} /*  */ -.octicon-repo-create:before, -.octicon-gist-new:before, -.octicon-file-directory-create:before, -.octicon-file-add:before, -.octicon-plus:before { content: '\f05d'} /*  */ -.octicon-primitive-dot:before { content: '\f052'} /*  */ -.octicon-primitive-square:before { content: '\f053'} /*  */ -.octicon-pulse:before { content: '\f085'} /*  */ -.octicon-question:before { content: '\f02c'} /*  */ -.octicon-quote:before { content: '\f063'} /*  */ -.octicon-radio-tower:before { content: '\f030'} /*  */ -.octicon-repo-delete:before, -.octicon-repo:before { content: '\f001'} /*  */ -.octicon-repo-clone:before { content: '\f04c'} /*  */ -.octicon-repo-force-push:before { content: '\f04a'} /*  */ -.octicon-gist-fork:before, -.octicon-repo-forked:before { content: '\f002'} /*  */ -.octicon-repo-pull:before { content: '\f006'} /*  */ -.octicon-repo-push:before { content: '\f005'} /*  */ -.octicon-rocket:before { content: '\f033'} /*  */ -.octicon-rss:before { content: '\f034'} /*  */ -.octicon-ruby:before { content: '\f047'} /*  */ -.octicon-screen-full:before { content: '\f066'} /*  */ -.octicon-screen-normal:before { content: '\f067'} /*  */ -.octicon-search-save:before, -.octicon-search:before { content: '\f02e'} /*  */ -.octicon-server:before { content: '\f097'} /*  */ -.octicon-settings:before { content: '\f07c'} /*  */ -.octicon-shield:before { content: '\f0e1'} /*  */ -.octicon-log-in:before, -.octicon-sign-in:before { content: '\f036'} /*  */ -.octicon-log-out:before, -.octicon-sign-out:before { content: '\f032'} /*  */ -.octicon-squirrel:before { content: '\f0b2'} /*  */ -.octicon-star-add:before, -.octicon-star-delete:before, -.octicon-star:before { content: '\f02a'} /*  */ -.octicon-stop:before { content: '\f08f'} /*  */ -.octicon-repo-sync:before, -.octicon-sync:before { content: '\f087'} /*  */ -.octicon-tag-remove:before, -.octicon-tag-add:before, -.octicon-tag:before { content: '\f015'} /*  */ -.octicon-telescope:before { content: '\f088'} /*  */ -.octicon-terminal:before { content: '\f0c8'} /*  */ -.octicon-three-bars:before { content: '\f05e'} /*  */ -.octicon-thumbsdown:before { content: '\f0db'} /*  */ -.octicon-thumbsup:before { content: '\f0da'} /*  */ -.octicon-tools:before { content: '\f031'} /*  */ -.octicon-trashcan:before { content: '\f0d0'} /*  */ -.octicon-triangle-down:before { content: '\f05b'} /*  */ -.octicon-triangle-left:before { content: '\f044'} /*  */ -.octicon-triangle-right:before { content: '\f05a'} /*  */ -.octicon-triangle-up:before { content: '\f0aa'} /*  */ -.octicon-unfold:before { content: '\f039'} /*  */ -.octicon-unmute:before { content: '\f0ba'} /*  */ -.octicon-versions:before { content: '\f064'} /*  */ -.octicon-watch:before { content: '\f0e0'} /*  */ -.octicon-remove-close:before, -.octicon-x:before { content: '\f081'} /*  */ -.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index cf3985c592c4..de08891aa768 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -34,8 +34,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { className: 'left-arrow', top: scrollbarDelta, left: arrowDelta, - bottom: void 0, - right: void 0, + bottom: undefined, + right: undefined, bgWidth: options.arrowSize, bgHeight: options.horizontalScrollbarSize, onActivate: () => this._host.onMouseWheel(new StandardWheelEvent(null, 1, 0)), @@ -44,8 +44,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { this._createArrow({ className: 'right-arrow', top: scrollbarDelta, - left: void 0, - bottom: void 0, + left: undefined, + bottom: undefined, right: arrowDelta, bgWidth: options.arrowSize, bgHeight: options.horizontalScrollbarSize, diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index d91bdb492cf4..7d90894682db 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -281,6 +281,7 @@ export abstract class AbstractScrollableElement extends Widget { let massagedOptions = resolveOptions(newOptions); this._options.handleMouseWheel = massagedOptions.handleMouseWheel; this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity; + this._options.fastScrollSensitivity = massagedOptions.fastScrollSensitivity; this._setListeningToMouseWheel(this._options.handleMouseWheel); if (!this._options.lazyRender) { @@ -340,6 +341,12 @@ export abstract class AbstractScrollableElement extends Widget { deltaY = 0; } + if (e.browserEvent && e.browserEvent.altKey) { + // fastScrolling + deltaX = deltaX * this._options.fastScrollSensitivity; + deltaY = deltaY * this._options.fastScrollSensitivity; + } + const futureScrollPosition = this._scrollable.getFutureScrollPosition(); let desiredScrollPosition: INewScrollPosition = {}; @@ -540,6 +547,7 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme alwaysConsumeMouseWheel: (typeof opts.alwaysConsumeMouseWheel !== 'undefined' ? opts.alwaysConsumeMouseWheel : false), scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false), mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1), + fastScrollSensitivity: (typeof opts.fastScrollSensitivity !== 'undefined' ? opts.fastScrollSensitivity : 5), mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true), arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11), diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts index 842839d16651..7276a5769a63 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts @@ -50,6 +50,11 @@ export interface ScrollableElementCreationOptions { * Defaults to 1. */ mouseWheelScrollSensitivity?: number; + /** + * FastScrolling mulitplier speed when pressing `Alt` + * Defaults to 5. + */ + fastScrollSensitivity?: number; /** * Height for vertical arrows (top/bottom) and width for horizontal arrows (left/right). * Defaults to 11. @@ -107,6 +112,7 @@ export interface ScrollableElementCreationOptions { export interface ScrollableElementChangeOptions { handleMouseWheel?: boolean; mouseWheelScrollSensitivity?: number; + fastScrollSensitivity: number; } export interface ScrollableElementResolvedOptions { @@ -118,6 +124,7 @@ export interface ScrollableElementResolvedOptions { scrollYToX: boolean; alwaysConsumeMouseWheel: boolean; mouseWheelScrollSensitivity: number; + fastScrollSensitivity: number; mouseWheelSmoothScroll: boolean; arrowSize: number; listenOnDomNode: HTMLElement | null; diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index ac798364d8fb..b59e7462ae1f 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -35,8 +35,8 @@ export class VerticalScrollbar extends AbstractScrollbar { className: 'up-arrow', top: arrowDelta, left: scrollbarDelta, - bottom: void 0, - right: void 0, + bottom: undefined, + right: undefined, bgWidth: options.verticalScrollbarSize, bgHeight: options.arrowSize, onActivate: () => this._host.onMouseWheel(new StandardWheelEvent(null, 0, 1)), @@ -44,10 +44,10 @@ export class VerticalScrollbar extends AbstractScrollbar { this._createArrow({ className: 'down-arrow', - top: void 0, + top: undefined, left: scrollbarDelta, bottom: arrowDelta, - right: void 0, + right: undefined, bgWidth: options.verticalScrollbarSize, bgHeight: options.arrowSize, onActivate: () => this._host.onMouseWheel(new StandardWheelEvent(null, 0, -1)), diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index 23594556e585..a1dc8a957ec5 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -14,7 +14,7 @@ import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { SelectBoxNative } from 'vs/base/browser/ui/selectBox/selectBoxNative'; import { SelectBoxList } from 'vs/base/browser/ui/selectBox/selectBoxCustom'; import { isMacintosh } from 'vs/base/common/platform'; -import { IContentActionHandler } from 'vs/base/browser/htmlContentRenderer'; + // Public SelectBox interface - Calls routed to appropriate select implementation class @@ -22,10 +22,9 @@ export interface ISelectBoxDelegate { // Public SelectBox Interface readonly onDidSelect: Event; - setOptions(options: string[], selected?: number, disabled?: number): void; + setOptions(options: ISelectOptionItem[], selected?: number); select(index: number): void; setAriaLabel(label: string); - setDetailsProvider(provider: (index: number) => { details: string, isMarkdown: boolean }); focus(): void; blur(): void; dispose(): void; @@ -37,16 +36,25 @@ export interface ISelectBoxDelegate { } export interface ISelectBoxOptions { + useCustomDrawn?: boolean; ariaLabel?: string; minBottomMargin?: number; - hasDetails?: boolean; - markdownActionHandler?: IContentActionHandler; +} + +// Utilize optionItem interface to capture all option parameters +export interface ISelectOptionItem { + text: string; + decoratorRight?: string; + description?: string; + descriptionIsMarkdown?: boolean; + isDisabled?: boolean; } export interface ISelectBoxStyles extends IListStyles { selectBackground?: Color; selectListBackground?: Color; selectForeground?: Color; + decoratorRightForeground?: Color; selectBorder?: Color; selectListBorder?: Color; focusBorder?: Color; @@ -72,13 +80,13 @@ export class SelectBox extends Widget implements ISelectBoxDelegate { private styles: ISelectBoxStyles; private selectBoxDelegate: ISelectBoxDelegate; - constructor(options: string[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles = deepClone(defaultStyles), selectBoxOptions?: ISelectBoxOptions) { + constructor(options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles = deepClone(defaultStyles), selectBoxOptions?: ISelectBoxOptions) { super(); mixin(this.styles, defaultStyles, false); - // Instantiate select implementation based on platform - if (isMacintosh && !(selectBoxOptions && selectBoxOptions.hasDetails)) { + // Default to native SelectBox for OSX unless overridden + if (isMacintosh && !(selectBoxOptions && selectBoxOptions.useCustomDrawn)) { this.selectBoxDelegate = new SelectBoxNative(options, selected, styles, selectBoxOptions); } else { this.selectBoxDelegate = new SelectBoxList(options, selected, contextViewProvider, styles, selectBoxOptions); @@ -96,8 +104,8 @@ export class SelectBox extends Widget implements ISelectBoxDelegate { return this.selectBoxDelegate.onDidSelect; } - public setOptions(options: string[], selected?: number, disabled?: number): void { - this.selectBoxDelegate.setOptions(options, selected, disabled); + public setOptions(options: ISelectOptionItem[], selected?: number): void { + this.selectBoxDelegate.setOptions(options, selected); } public select(index: number): void { @@ -108,10 +116,6 @@ export class SelectBox extends Widget implements ISelectBoxDelegate { this.selectBoxDelegate.setAriaLabel(label); } - public setDetailsProvider(provider: (index: number) => { details: string, isMarkdown: boolean }): void { - this.selectBoxDelegate.setDetailsProvider(provider); - } - public focus(): void { this.selectBoxDelegate.focus(); } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css index 2b8818b42173..24d2ad857bca 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css @@ -80,8 +80,18 @@ overflow: hidden; padding-left: 3.5px; white-space: nowrap; + float: left; } +.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row > .option-decorator-right { + text-overflow: ellipsis; + overflow: hidden; + padding-right: 10px; + white-space: nowrap; + float: right; +} + + /* Accepted CSS hiding technique for accessibility reader text */ /* https://webaim.org/techniques/css/invisiblecontent/ */ diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 41560b278a26..d565d6c25c1d 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -6,7 +6,7 @@ import 'vs/css!./selectBoxCustom'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Event, Emitter, chain } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import * as dom from 'vs/base/browser/dom'; @@ -16,7 +16,7 @@ import { List } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListEvent } from 'vs/base/browser/ui/list/list'; import { domEvent } from 'vs/base/browser/event'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { ISelectBoxDelegate, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; +import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; import { isMacintosh } from 'vs/base/common/platform'; import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; @@ -24,16 +24,11 @@ const $ = dom.$; const SELECT_OPTION_ENTRY_TEMPLATE_ID = 'selectOption.entry.template'; -export interface ISelectOptionItem { - optionText: string; - optionDescriptionText?: string; - optionDisabled: boolean; -} - interface ISelectListTemplateData { root: HTMLElement; - optionText: HTMLElement; - optionDescriptionText: HTMLElement; + text: HTMLElement; + itemDescription: HTMLElement; + decoratorRight: HTMLElement; disposables: IDisposable[]; } @@ -47,29 +42,32 @@ class SelectListRenderer implements IListRendererObject.create(null); data.disposables = []; data.root = container; - data.optionText = dom.append(container, $('.option-text')); - data.optionDescriptionText = dom.append(container, $('.option-text-description')); - dom.addClass(data.optionDescriptionText, 'visually-hidden'); + data.text = dom.append(container, $('.option-text')); + data.decoratorRight = dom.append(container, $('.option-decorator-right')); + data.itemDescription = dom.append(container, $('.option-text-description')); + dom.addClass(data.itemDescription, 'visually-hidden'); return data; } renderElement(element: ISelectOptionItem, index: number, templateData: ISelectListTemplateData): void { const data = templateData; - const optionText = (element).optionText; - const optionDisabled = (element).optionDisabled; - - data.optionText.textContent = optionText; - - if (typeof element.optionDescriptionText === 'string') { - const optionDescriptionId = (optionText.replace(/ /g, '_').toLowerCase() + '_description_' + data.root.id); - data.optionText.setAttribute('aria-describedby', optionDescriptionId); - data.optionDescriptionText.id = optionDescriptionId; - data.optionDescriptionText.innerText = element.optionDescriptionText; + const text = (element).text; + const decoratorRight = (element).decoratorRight; + const isDisabled = (element).isDisabled; + + data.text.textContent = text; + data.decoratorRight.innerText = (!!decoratorRight ? decoratorRight : ''); + + if (typeof element.description === 'string') { + const itemDescriptionId = (text.replace(/ /g, '_').toLowerCase() + '_description_' + data.root.id); + data.text.setAttribute('aria-describedby', itemDescriptionId); + data.itemDescription.id = itemDescriptionId; + data.itemDescription.innerText = element.description; } // pseudo-select disabled option - if (optionDisabled) { + if (isDisabled) { dom.addClass((data.root), 'option-disabled'); } else { // Make sure we do class removal from prior template rendering @@ -77,10 +75,6 @@ class SelectListRenderer implements IListRenderer; private toDispose: IDisposable[]; private styles: ISelectBoxStyles; @@ -111,13 +104,13 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { details: string, isMarkdown: boolean }; + private _hasDetails: boolean = false; private selectionDetailsPane: HTMLElement; private _skipLayout: boolean = false; private _sticky: boolean = false; // for dev purposes only - constructor(options: string[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { + constructor(options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { this.toDispose = []; this._isVisible = false; @@ -252,19 +245,18 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { - this.selectElement.add(this.createOption(option, i, disabled === i++)); + this.options.forEach((option, index) => { + this.selectElement.add(this.createOption(option.text, index, option.isDisabled)); + if (typeof option.description === 'string') { + this._hasDetails = true; + } }); - - if (disabled !== undefined) { - this.disabledOptionIndex = disabled; - } } if (selected !== undefined) { @@ -280,18 +272,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { details: string, isMarkdown: boolean }): void { - this.detailsProvider = provider; - } - public focus(): void { if (this.selectElement) { this.selectElement.focus(); @@ -353,6 +330,10 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate .select-box-dropdown-list-container .monaco-list .monaco-list-row.focused:not(:hover) { color: ${this.styles.listFocusForeground} !important; }`); } + if (this.styles.decoratorRightForeground) { + content.push(`.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row .option-decorator-right { color: ${this.styles.decoratorRightForeground} !important; }`); + } + if (this.styles.selectBackground && this.styles.selectBorder && !this.styles.selectBorder.equals(this.styles.selectBackground)) { content.push(`.monaco-select-box-dropdown-container { border: 1px solid ${this.styles.selectBorder} } `); content.push(`.monaco-select-box-dropdown-container > .select-box-details-pane.border-top { border-top: 1px solid ${this.styles.selectBorder} } `); @@ -533,23 +514,16 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { this.selectionDetailsPane.innerText = ''; - description = this.detailsProvider ? this.detailsProvider(index) : { details: '', isMarkdown: false }; - if (description.details) { - if (description.isMarkdown) { - this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description.details)); + if (option.description) { + if (option.descriptionIsMarkdown) { + this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(option.description)); } else { - this.selectionDetailsPane.innerText = description.details; + this.selectionDetailsPane.innerText = option.description; } this.selectionDetailsPane.style.display = 'block'; } else { @@ -562,18 +536,19 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate this.options[longest].length) { + this.options.forEach((option, index) => { + const len = option.text.length + (!!option.decoratorRight ? option.decoratorRight.length : 0); + if (len > longestLength) { longest = index; + longestLength = len; } - } + }); - container.innerHTML = this.options[longest]; + + container.innerHTML = this.options[longest].text + (!!this.options[longest].decoratorRight ? (this.options[longest].decoratorRight + ' ') : ''); elementWidth = dom.getTotalWidth(container); } @@ -763,14 +742,13 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate this.selectList.length > 0) .map(e => new StandardKeyboardEvent(e)); @@ -786,7 +764,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate this.selectList.length > 0) .on(e => this.onMouseUp(e), this, this.toDispose); @@ -809,12 +787,18 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegatee.target; + if (!target) { + return; + } + // Check our mouse event is on an option (not scrollbar) - if (!e.toElement.classList.contains('option-text')) { + if (!!target.classList.contains('slider')) { return; } - const listRowElement = e.toElement.parentElement; + const listRowElement = target.closest('.monaco-list-row'); + if (!listRowElement) { return; } @@ -839,7 +823,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate) { // Skip during initial layout - if (!this._isVisible) { + if (!this._isVisible || !this._hasDetails) { return; } this.selectionDetailsPane.innerText = ''; const selectedIndex = e.indexes[0]; - let description = this.detailsProvider ? this.detailsProvider(selectedIndex) : { details: '', isMarkdown: false }; - if (description.details) { - if (description.isMarkdown) { - this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description.details)); + const description = this.options[selectedIndex].description || null; + const descriptionIsMarkdown = this.options[selectedIndex].descriptionIsMarkdown || null; + + if (description) { + if (descriptionIsMarkdown) { + this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description)); } else { - this.selectionDetailsPane.innerText = description.details; + this.selectionDetailsPane.innerText = description; } this.selectionDetailsPane.style.display = 'block'; } else { @@ -929,7 +913,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate this.selected + 2) { + const nextOptionDisabled = this.options[this.selected + 1].isDisabled; + + if (nextOptionDisabled && this.options.length > this.selected + 2) { this.selected += 2; - } else if ((this.selected + 1) === this.disabledOptionIndex) { + } else if (nextOptionDisabled) { return; } else { this.selected++; } + // Set focus/selection - only fire event when closing drop-down or on blur this.select(this.selected); this.selectList.setFocus([this.selected]); @@ -957,7 +945,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate 0) { // Skip disabled options - if ((this.selected - 1) === this.disabledOptionIndex && this.selected > 1) { + const previousOptionDisabled = this.options[this.selected - 1].isDisabled; + if (previousOptionDisabled && this.selected > 1) { this.selected -= 2; } else { this.selected--; @@ -979,7 +968,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate 0) { + if (this.options[this.selected].isDisabled && this.selected > 0) { this.selected--; this.selectList.setFocus([this.selected]); } @@ -1014,7 +1003,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate 1) { + if (this.options[this.selected].isDisabled && this.selected > 1) { this.selected++; } this.selectList.setFocus([this.selected]); @@ -1029,7 +1018,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate 1) { + if (this.options[this.selected].isDisabled && this.selected > 1) { this.selected--; } this.selectList.setFocus([this.selected]); @@ -1044,7 +1033,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate; private toDispose: IDisposable[]; private styles: ISelectBoxStyles; - constructor(options: string[], selected: number, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { - + constructor(options: ISelectOptionItem[], selected: number, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { this.toDispose = []; this.selectBoxOptions = selectBoxOptions || Object.create(null); + this.options = []; + this.selectElement = document.createElement('select'); // Workaround for Electron 2.x @@ -84,15 +85,14 @@ export class SelectBoxNative implements ISelectBoxDelegate { return this._onDidSelect.event; } - public setOptions(options: string[], selected?: number, disabled?: number): void { + public setOptions(options: ISelectOptionItem[], selected?: number): void { if (!this.options || !arrays.equals(this.options, options)) { this.options = options; this.selectElement.options.length = 0; - let i = 0; - this.options.forEach((option) => { - this.selectElement.add(this.createOption(option, i, disabled === i++)); + this.options.forEach((option, index) => { + this.selectElement.add(this.createOption(option.text, index, option.isDisabled)); }); } @@ -103,7 +103,9 @@ export class SelectBoxNative implements ISelectBoxDelegate { } public select(index: number): void { - if (index >= 0 && index < this.options.length) { + if (this.options.length === 0) { + this.selected = 0; + } else if (index >= 0 && index < this.options.length) { this.selected = index; } else if (index > this.options.length - 1) { // Adjust index to end of list @@ -114,7 +116,11 @@ export class SelectBoxNative implements ISelectBoxDelegate { } this.selectElement.selectedIndex = this.selected; - this.selectElement.title = this.options[this.selected]; + if ((this.selected < this.options.length) && typeof this.options[this.selected].text === 'string') { + this.selectElement.title = this.options[this.selected].text; + } else { + this.selectElement.title = ''; + } } public setAriaLabel(label: string): void { @@ -122,10 +128,6 @@ export class SelectBoxNative implements ISelectBoxDelegate { this.selectElement.setAttribute('aria-label', label); } - public setDetailsProvider(provider: any): void { - console.error('details are not available for native select boxes'); - } - public focus(): void { if (this.selectElement) { this.selectElement.focus(); diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index 19394fe69e46..92dcd02bc854 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -5,7 +5,7 @@ import 'vs/css!./panelview'; import { IDisposable, dispose, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter, chain, filterEvent } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -77,7 +77,7 @@ export abstract class Panel implements IView { set minimumBodySize(size: number) { this._minimumBodySize = size; - this._onDidChange.fire(); + this._onDidChange.fire(undefined); } get maximumBodySize(): number { @@ -86,7 +86,7 @@ export abstract class Panel implements IView { set maximumBodySize(size: number) { this._maximumBodySize = size; - this._onDidChange.fire(); + this._onDidChange.fire(undefined); } private get headerSize(): number { @@ -109,6 +109,8 @@ export abstract class Panel implements IView { return headerSize + maximumBodySize; } + width: number; + constructor(options: IPanelOptions = {}) { this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded; this.ariaHeaderLabel = options.ariaHeaderLabel || ''; @@ -122,14 +124,16 @@ export abstract class Panel implements IView { return this._expanded; } - setExpanded(expanded: boolean): void { + setExpanded(expanded: boolean): boolean { if (this._expanded === !!expanded) { - return; + return false; } this._expanded = !!expanded; this.updateHeader(); this._onDidChange.fire(expanded ? this.expandedSize : undefined); + + return true; } get headerVisible(): boolean { @@ -143,7 +147,7 @@ export abstract class Panel implements IView { this._headerVisible = !!visible; this.updateHeader(); - this._onDidChange.fire(); + this._onDidChange.fire(undefined); } render(): void { @@ -160,7 +164,7 @@ export abstract class Panel implements IView { this.updateHeader(); - const onHeaderKeyDown = chain(domEvent(this.header, 'keydown')) + const onHeaderKeyDown = Event.chain(domEvent(this.header, 'keydown')) .map(e => new StandardKeyboardEvent(e)); onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space) @@ -186,12 +190,12 @@ export abstract class Panel implements IView { this.renderBody(body); } - layout(size: number): void { + layout(height: number): void { const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0; if (this.isExpanded()) { - this.layoutBody(size - headerSize); - this.expandedSize = size; + this.layoutBody(height - headerSize, this.width); + this.expandedSize = height; } } @@ -222,7 +226,7 @@ export abstract class Panel implements IView { protected abstract renderHeader(container: HTMLElement): void; protected abstract renderBody(container: HTMLElement): void; - protected abstract layoutBody(size: number): void; + protected abstract layoutBody(height: number, width: number): void; dispose(): void { this.disposables = dispose(this.disposables); @@ -367,6 +371,7 @@ export class PanelView extends Disposable { private dndContext: IDndContext = { draggable: null }; private el: HTMLElement; private panelItems: IPanelItem[] = []; + private width: number; private splitview: SplitView; private animationTimer: number | null = null; @@ -391,11 +396,12 @@ export class PanelView extends Disposable { let shouldAnimate = false; disposables.push(scheduleAtNextAnimationFrame(() => shouldAnimate = true)); - filterEvent(panel.onDidChange, () => shouldAnimate) + Event.filter(panel.onDidChange, () => shouldAnimate) (this.setupAnimation, this, disposables); const panelItem = { panel, disposable: combinedDisposable(disposables) }; this.panelItems.splice(index, 0, panelItem); + panel.width = this.width; this.splitview.addView(panel, size, index); if (this.dnd) { @@ -451,8 +457,14 @@ export class PanelView extends Disposable { return this.splitview.getViewSize(index); } - layout(size: number): void { - this.splitview.layout(size); + layout(height: number, width: number): void { + this.width = width; + + for (const panelItem of this.panelItems) { + panelItem.panel.width = width; + } + + this.splitview.layout(height); } private setupAnimation(): void { diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 7ecdf4a87ee2..220031cfd392 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -5,7 +5,7 @@ import 'vs/css!./splitview'; import { IDisposable, combinedDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { Event, mapEvent, Emitter } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import * as types from 'vs/base/common/types'; import * as dom from 'vs/base/browser/dom'; import { clamp } from 'vs/base/common/numbers'; @@ -238,11 +238,11 @@ export class SplitView extends Disposable { ? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey } as ISashEvent) : (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX, alt: e.altKey } as ISashEvent); - const onStart = mapEvent(sash.onDidStart, sashEventMapper); + const onStart = Event.map(sash.onDidStart, sashEventMapper); const onStartDisposable = onStart(this.onSashStart, this); - const onChange = mapEvent(sash.onDidChange, sashEventMapper); + const onChange = Event.map(sash.onDidChange, sashEventMapper); const onChangeDisposable = onChange(this.onSashChange, this); - const onEnd = mapEvent(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); + const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); const onEndDisposable = onEnd(this.onSashEnd, this); const onDidResetDisposable = sash.onDidReset(() => this._onDidSashReset.fire(firstIndex(this.sashItems, item => item.sash === sash))); diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index afca46f8e544..fa10ea23a39c 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -18,7 +18,7 @@ export interface IToolBarOptions { orientation?: ActionsOrientation; actionItemProvider?: IActionItemProvider; ariaLabel?: string; - getKeyBinding?: (action: IAction) => ResolvedKeybinding; + getKeyBinding?: (action: IAction) => ResolvedKeybinding | undefined; actionRunner?: IActionRunner; toggleMenuTitle?: string; anchorAlignmentProvider?: () => AnchorAlignment; @@ -31,7 +31,7 @@ export class ToolBar extends Disposable { private options: IToolBarOptions; private actionBar: ActionBar; private toggleMenuAction: ToggleMenuAction; - private toggleMenuActionItem: DropdownMenuActionItem; + private toggleMenuActionItem?: DropdownMenuActionItem; private hasSecondaryActions: boolean; private lookupKeybindings: boolean; @@ -72,9 +72,9 @@ export class ToolBar extends Disposable { 'toolbar-toggle-more', this.options.anchorAlignmentProvider ); - this.toggleMenuActionItem.setActionContext(this.actionBar.context); + this.toggleMenuActionItem!.setActionContext(this.actionBar.context); - return this.toggleMenuActionItem; + return this.toggleMenuActionItem || null; } return options.actionItemProvider ? options.actionItemProvider(action) : null; @@ -118,8 +118,8 @@ export class ToolBar extends Disposable { let primaryActionsToSet = primaryActions ? primaryActions.slice(0) : []; // Inject additional action to open secondary actions if present - this.hasSecondaryActions = secondaryActions && secondaryActions.length > 0; - if (this.hasSecondaryActions) { + this.hasSecondaryActions = !!(secondaryActions && secondaryActions.length > 0); + if (this.hasSecondaryActions && secondaryActions) { this.toggleMenuAction.menuActions = secondaryActions.slice(0); primaryActionsToSet.push(this.toggleMenuAction); } @@ -132,10 +132,10 @@ export class ToolBar extends Disposable { }; } - private getKeybindingLabel(action: IAction): string { - const key = this.lookupKeybindings ? this.options.getKeyBinding(action) : void 0; + private getKeybindingLabel(action: IAction): string | undefined { + const key = this.lookupKeybindings && this.options.getKeyBinding ? this.options.getKeyBinding(action) : undefined; - return key ? key.getLabel() : void 0; + return (key && key.getLabel()) || undefined; } addPrimaryAction(primaryAction: IAction): () => void { @@ -157,7 +157,7 @@ export class ToolBar extends Disposable { dispose(): void { if (this.toggleMenuActionItem) { this.toggleMenuActionItem.dispose(); - this.toggleMenuActionItem = void 0; + this.toggleMenuActionItem = undefined; } super.dispose(); @@ -173,7 +173,7 @@ class ToggleMenuAction extends Action { constructor(toggleDropdownMenu: () => void, title?: string) { title = title || nls.localize('moreActions', "More Actions..."); - super(ToggleMenuAction.ID, title, null, true); + super(ToggleMenuAction.ID, title, undefined, true); this.toggleDropdownMenu = toggleDropdownMenu; } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 1225231be4b3..f76e846cb889 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -4,62 +4,153 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/tree'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IListOptions, List, IMultipleSelectionController, IListStyles, IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { append, $, toggleClass } from 'vs/base/browser/dom'; -import { Event, Relay, chain, mapEvent } from 'vs/base/common/event'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IListOptions, List, IListStyles, mightProducePrintableCharacter } from 'vs/base/browser/ui/list/listWidget'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; +import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass } from 'vs/base/browser/dom'; +import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; +import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; +import { IDragAndDropData, StaticDND, DragAndDropData } from 'vs/base/browser/dnd'; +import { range } from 'vs/base/common/arrays'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { domEvent } from 'vs/base/browser/event'; +import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; +import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { localize } from 'vs/nls'; +import { disposableTimeout } from 'vs/base/common/async'; +import { isMacintosh } from 'vs/base/common/platform'; +import { values } from 'vs/base/common/map'; +import { clamp } from 'vs/base/common/numbers'; + +function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { + if (data instanceof ElementsDragAndDropData) { + const nodes = (data as ElementsDragAndDropData>).elements; + return new ElementsDragAndDropData(nodes.map(node => node.element)); + } + + return data; +} + +class TreeNodeListDragAndDrop implements IListDragAndDrop> { + + private autoExpandNode: ITreeNode | undefined; + private autoExpandDisposable: IDisposable = Disposable.None; + + constructor(private modelProvider: () => ITreeModel, private dnd: ITreeDragAndDrop) { } + + getDragURI(node: ITreeNode): string | null { + return this.dnd.getDragURI(node.element); + } + + getDragLabel(nodes: ITreeNode[]): string | undefined { + if (this.dnd.getDragLabel) { + return this.dnd.getDragLabel(nodes.map(node => node.element)); + } -function asListOptions(options?: IAbstractTreeOptions): IListOptions> | undefined { - if (!options) { return undefined; } - let identityProvider: IIdentityProvider> | undefined = undefined; + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + if (this.dnd.onDragStart) { + this.dnd.onDragStart(asTreeDragAndDropData(data), originalEvent); + } + } + + onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { + const result = this.dnd.onDragOver(asTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent); + const didChangeAutoExpandNode = this.autoExpandNode !== targetNode; - if (options.identityProvider) { - const ip = options.identityProvider; - identityProvider = { - getId(el) { - return ip.getId(el.element); + if (didChangeAutoExpandNode) { + this.autoExpandDisposable.dispose(); + this.autoExpandNode = targetNode; + } + + if (typeof targetNode === 'undefined') { + return result; + } + + if (didChangeAutoExpandNode && typeof result !== 'boolean' && result.autoExpand) { + this.autoExpandDisposable = disposableTimeout(() => { + const model = this.modelProvider(); + const ref = model.getNodeLocation(targetNode); + + if (model.isCollapsed(ref)) { + model.setCollapsed(ref, false); + } + + this.autoExpandNode = undefined; + }, 500); + } + + if (typeof result === 'boolean' || !result.accept || typeof result.bubble === 'undefined') { + if (!raw) { + const accept = typeof result === 'boolean' ? result : result.accept; + const effect = typeof result === 'boolean' ? undefined : result.effect; + return { accept, effect, feedback: [targetIndex!] }; } - }; + + return result; + } + + if (result.bubble === TreeDragOverBubble.Up) { + const parentNode = targetNode.parent; + const model = this.modelProvider(); + const parentIndex = parentNode && model.getListIndex(model.getNodeLocation(parentNode)); + + return this.onDragOver(data, parentNode, parentIndex, originalEvent, false); + } + + const model = this.modelProvider(); + const ref = model.getNodeLocation(targetNode); + const start = model.getListIndex(ref); + const length = model.getListRenderCount(ref); + + return { ...result, feedback: range(start, start + length) }; } - let multipleSelectionController: IMultipleSelectionController> | undefined = undefined; + drop(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { + this.autoExpandDisposable.dispose(); + this.autoExpandNode = undefined; + + this.dnd.drop(asTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent); + } +} - if (options.multipleSelectionController) { - const msc = options.multipleSelectionController; - multipleSelectionController = { +function asListOptions(modelProvider: () => ITreeModel, options?: IAbstractTreeOptions): IListOptions> | undefined { + return options && { + ...options, + identityProvider: options.identityProvider && { + getId(el) { + return options.identityProvider!.getId(el.element); + } + }, + dnd: options.dnd && new TreeNodeListDragAndDrop(modelProvider, options.dnd), + multipleSelectionController: options.multipleSelectionController && { isSelectionSingleChangeEvent(e) { - return msc.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); + return options.multipleSelectionController!.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); }, isSelectionRangeChangeEvent(e) { - return msc.isSelectionRangeChangeEvent({ ...e, element: e.element } as any); + return options.multipleSelectionController!.isSelectionRangeChangeEvent({ ...e, element: e.element } as any); } - }; - } - - let accessibilityProvider: IAccessibilityProvider> | undefined = undefined; - - if (options.accessibilityProvider) { - const ap = options.accessibilityProvider; - accessibilityProvider = { + }, + accessibilityProvider: options.accessibilityProvider && { getAriaLabel(e) { - return ap.getAriaLabel(e.element); + return options.accessibilityProvider!.getAriaLabel(e.element); + }, + getAriaLevel(node) { + return node.depth; } - }; - } - - return { - ...options, - identityProvider, - multipleSelectionController, - accessibilityProvider + }, + keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && { + ...options.keyboardNavigationLabelProvider, + getKeyboardNavigationLabel(node) { + return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(node.element); + } + }, + enableKeyboardNavigation: options.simpleKeyboardNavigation }; } @@ -81,51 +172,74 @@ export class ComposedTreeDelegate implements IListV } interface ITreeListTemplateData { - twistie: HTMLElement; - templateData: T; + readonly container: HTMLElement; + readonly twistie: HTMLElement; + readonly templateData: T; +} + +interface ITreeRendererOptions { + readonly indent?: number; } class TreeRenderer implements IListRenderer, ITreeListTemplateData> { + private static DefaultIndent = 8; + readonly templateId: string; private renderedElements = new Map>(); private renderedNodes = new Map, ITreeListTemplateData>(); + private indent: number; private disposables: IDisposable[] = []; constructor( private renderer: ITreeRenderer, - onDidChangeCollapseState: Event> + onDidChangeCollapseState: Event>, + options: ITreeRendererOptions = {} ) { this.templateId = renderer.templateId; + this.updateOptions(options); - onDidChangeCollapseState(this.onDidChangeNodeTwistieState, this, this.disposables); + Event.map(onDidChangeCollapseState, e => e.node)(this.onDidChangeNodeTwistieState, this, this.disposables); if (renderer.onDidChangeTwistieState) { renderer.onDidChangeTwistieState(this.onDidChangeTwistieState, this, this.disposables); } } + updateOptions(options: ITreeRendererOptions = {}): void { + this.indent = typeof options.indent === 'number' ? clamp(options.indent, 0, 20) : TreeRenderer.DefaultIndent; + + this.renderedNodes.forEach((templateData, node) => { + templateData.twistie.style.marginLeft = `${node.depth * this.indent}px`; + }); + } + renderTemplate(container: HTMLElement): ITreeListTemplateData { const el = append(container, $('.monaco-tl-row')); const twistie = append(el, $('.monaco-tl-twistie')); const contents = append(el, $('.monaco-tl-contents')); const templateData = this.renderer.renderTemplate(contents); - return { twistie, templateData }; + return { container, twistie, templateData }; } renderElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData): void { this.renderedNodes.set(node, templateData); this.renderedElements.set(node.element, node); - templateData.twistie.style.width = `${10 + node.depth * 10}px`; - this.renderTwistie(node, templateData.twistie); + const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent; + templateData.twistie.style.marginLeft = `${indent}px`; + templateData.container.setAttribute('aria-posinset', String(node.visibleChildIndex + 1)); + templateData.container.setAttribute('aria-setsize', String(node.parent!.visibleChildrenCount)); + this.update(node, templateData); this.renderer.renderElement(node, index, templateData.templateData); } disposeElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData): void { - this.renderer.disposeElement(node, index, templateData.templateData); + if (this.renderer.disposeElement) { + this.renderer.disposeElement(node, index, templateData.templateData); + } this.renderedNodes.delete(node); this.renderedElements.set(node.element); } @@ -151,16 +265,22 @@ class TreeRenderer implements IListRenderer, twistieElement: HTMLElement) { + private update(node: ITreeNode, templateData: ITreeListTemplateData) { if (this.renderer.renderTwistie) { - this.renderer.renderTwistie(node.element, twistieElement); + this.renderer.renderTwistie(node.element, templateData.twistie); } - toggleClass(twistieElement, 'collapsible', node.collapsible); - toggleClass(twistieElement, 'collapsed', node.collapsible && node.collapsed); + toggleClass(templateData.twistie, 'collapsible', node.collapsible); + toggleClass(templateData.twistie, 'collapsed', node.collapsible && node.collapsed); + + if (node.collapsible) { + templateData.container.setAttribute('aria-expanded', String(!node.collapsed)); + } else { + templateData.container.removeAttribute('aria-expanded'); + } } dispose(): void { @@ -170,6 +290,362 @@ class TreeRenderer implements IListRenderer implements ITreeFilter, IDisposable { + + private _totalCount = 0; + get totalCount(): number { return this._totalCount; } + private _matchCount = 0; + get matchCount(): number { return this._matchCount; } + + private _pattern: string; + private _lowercasePattern: string; + private disposables: IDisposable[] = []; + + set pattern(pattern: string) { + this._pattern = pattern; + this._lowercasePattern = pattern.toLowerCase(); + } + + constructor( + private tree: AbstractTree, + private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider, + private _filter?: ITreeFilter + ) { + tree.onWillRefilter(this.reset, this, this.disposables); + } + + filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult { + if (this._filter) { + const result = this._filter.filter(element, parentVisibility); + + if (this.tree.options.simpleKeyboardNavigation) { + return result; + } + + let visibility: TreeVisibility; + + if (typeof result === 'boolean') { + visibility = result ? TreeVisibility.Visible : TreeVisibility.Hidden; + } else if (isFilterResult(result)) { + visibility = getVisibleState(result.visibility); + } else { + visibility = result; + } + + if (visibility === TreeVisibility.Hidden) { + return false; + } + } + + this._totalCount++; + + if (this.tree.options.simpleKeyboardNavigation || !this._pattern) { + this._matchCount++; + return { data: FuzzyScore.Default, visibility: true }; + } + + const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(element); + const labelStr = label && label.toString(); + + if (typeof labelStr === 'undefined') { + return { data: FuzzyScore.Default, visibility: true }; + } + + const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, labelStr, labelStr.toLowerCase(), 0, true); + + if (!score) { + if (this.tree.options.filterOnType) { + return TreeVisibility.Recurse; + } else { + return { data: FuzzyScore.Default, visibility: true }; + } + + // DEMO: smarter filter ? + // return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; + } + + this._matchCount++; + return { data: score, visibility: true }; + } + + private reset(): void { + this._totalCount = 0; + this._matchCount = 0; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + +class TypeFilterController implements IDisposable { + + private _enabled = false; + get enabled(): boolean { return this._enabled; } + + private _pattern = ''; + get pattern(): string { return this._pattern; } + + private positionClassName = 'ne'; + private domNode: HTMLElement; + private messageDomNode: HTMLElement; + private labelDomNode: HTMLElement; + private filterOnTypeDomNode: HTMLInputElement; + private clearDomNode: HTMLElement; + private keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; + + private automaticKeyboardNavigation: boolean; + private triggered = false; + + private enabledDisposables: IDisposable[] = []; + private disposables: IDisposable[] = []; + + constructor( + private tree: AbstractTree, + model: ITreeModel, + private view: List>, + private filter: TypeFilter, + private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider + ) { + this.domNode = $(`.monaco-list-type-filter.${this.positionClassName}`); + this.domNode.draggable = true; + domEvent(this.domNode, 'dragstart')(this.onDragStart, this, this.disposables); + + this.messageDomNode = append(view.getHTMLElement(), $(`.monaco-list-type-filter-message`)); + + this.labelDomNode = append(this.domNode, $('span.label')); + const controls = append(this.domNode, $('.controls')); + + this.filterOnTypeDomNode = append(controls, $('input.filter')); + this.filterOnTypeDomNode.type = 'checkbox'; + this.filterOnTypeDomNode.checked = !!tree.options.filterOnType; + this.filterOnTypeDomNode.tabIndex = -1; + this.updateFilterOnTypeTitle(); + domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables); + + this.clearDomNode = append(controls, $('button.clear')); + this.clearDomNode.tabIndex = -1; + this.clearDomNode.title = localize('clear', "Clear"); + + this.keyboardNavigationEventFilter = tree.options.keyboardNavigationEventFilter; + + model.onDidSplice(this.onDidSpliceModel, this, this.disposables); + this.updateOptions(tree.options); + } + + updateOptions(options: IAbstractTreeOptions): void { + if (options.simpleKeyboardNavigation) { + this.disable(); + } else { + this.enable(); + } + + this.filterOnTypeDomNode.checked = !!options.filterOnType; + this.automaticKeyboardNavigation = typeof options.automaticKeyboardNavigation === 'undefined' ? true : options.automaticKeyboardNavigation; + this.tree.refilter(); + this.render(); + + if (!this.automaticKeyboardNavigation) { + this.onEventOrInput(''); + } + } + + toggle(): void { + this.triggered = !this.triggered; + + if (!this.triggered) { + this.onEventOrInput(''); + } + } + + private enable(): void { + if (this._enabled) { + return; + } + + const isPrintableCharEvent = this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); + const onKeyDown = Event.chain(domEvent(this.view.getHTMLElement(), 'keydown')) + .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode) + .filter(this.keyboardNavigationEventFilter || (() => true)) + .filter(() => this.automaticKeyboardNavigation || this.triggered) + .map(e => new StandardKeyboardEvent(e)) + .filter(e => isPrintableCharEvent(e) || ((this._pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey)))) + .forEach(e => { e.stopPropagation(); e.preventDefault(); }) + .event; + + const onClear = domEvent(this.clearDomNode, 'click'); + + Event.chain(Event.any(onKeyDown, onClear)) + .event(this.onEventOrInput, this, this.enabledDisposables); + + this.filter.pattern = ''; + this.tree.refilter(); + this.render(); + this._enabled = true; + this.triggered = false; + } + + private disable(): void { + if (!this._enabled) { + return; + } + + this.domNode.remove(); + this.enabledDisposables = dispose(this.enabledDisposables); + this.tree.refilter(); + this.render(); + this._enabled = false; + this.triggered = false; + } + + private onEventOrInput(e: MouseEvent | StandardKeyboardEvent | string): void { + if (typeof e === 'string') { + this.onInput(e); + } else if (e instanceof MouseEvent || e.keyCode === KeyCode.Escape || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey))) { + this.onInput(''); + } else if (e.keyCode === KeyCode.Backspace) { + this.onInput(this.pattern.length === 0 ? '' : this.pattern.substr(0, this.pattern.length - 1)); + } else { + this.onInput(this.pattern + e.browserEvent.key); + } + } + + private onInput(pattern: string): void { + const container = this.view.getHTMLElement(); + + if (pattern && !this.domNode.parentElement) { + container.append(this.domNode); + } else if (!pattern && this.domNode.parentElement) { + this.domNode.remove(); + this.tree.domFocus(); + } + + this._pattern = pattern; + this.filter.pattern = pattern; + this.tree.refilter(); + this.tree.focusNext(0, true); + + const focus = this.tree.getFocus(); + + if (focus.length > 0) { + const element = focus[0]; + + if (this.tree.getRelativeTop(element) === null) { + this.tree.reveal(element, 0.5); + } + } + + this.render(); + + if (!pattern) { + this.triggered = false; + } + } + + private onDragStart(): void { + const container = this.view.getHTMLElement(); + const { left } = getDomNodePagePosition(container); + const containerWidth = container.clientWidth; + const midContainerWidth = containerWidth / 2; + const width = this.domNode.clientWidth; + const disposables: IDisposable[] = []; + let positionClassName = this.positionClassName; + + const updatePosition = () => { + switch (positionClassName) { + case 'nw': + this.domNode.style.top = `4px`; + this.domNode.style.left = `4px`; + break; + case 'ne': + this.domNode.style.top = `4px`; + this.domNode.style.left = `${containerWidth - width - 6}px`; + break; + } + }; + + const onDragOver = (event: DragEvent) => { + const x = event.screenX - left; + if (event.dataTransfer) { + event.dataTransfer.dropEffect = 'none'; + } + + if (x < midContainerWidth) { + positionClassName = 'nw'; + } else { + positionClassName = 'ne'; + } + + updatePosition(); + }; + + const onDragEnd = () => { + this.positionClassName = positionClassName; + this.domNode.className = `monaco-list-type-filter ${this.positionClassName}`; + this.domNode.style.top = null; + this.domNode.style.left = null; + + dispose(disposables); + }; + + updatePosition(); + removeClass(this.domNode, positionClassName); + + addClass(this.domNode, 'dragging'); + disposables.push(toDisposable(() => removeClass(this.domNode, 'dragging'))); + + domEvent(document, 'dragover')(onDragOver, null, disposables); + domEvent(this.domNode, 'dragend')(onDragEnd, null, disposables); + + StaticDND.CurrentDragAndDropData = new DragAndDropData('vscode-ui'); + disposables.push(toDisposable(() => StaticDND.CurrentDragAndDropData = undefined)); + } + + private onDidSpliceModel(): void { + if (!this._enabled || this.pattern.length === 0) { + return; + } + + this.tree.refilter(); + this.render(); + } + + private onDidChangeFilterOnType(): void { + this.tree.updateOptions({ filterOnType: this.filterOnTypeDomNode.checked }); + this.tree.refilter(); + this.tree.domFocus(); + this.render(); + this.updateFilterOnTypeTitle(); + } + + private updateFilterOnTypeTitle(): void { + if (this.filterOnTypeDomNode.checked) { + this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type"); + } else { + this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type"); + } + } + + private render(): void { + const noMatches = this.filter.totalCount > 0 && this.filter.matchCount === 0; + + if (this.pattern && this.tree.options.filterOnType && noMatches) { + this.messageDomNode.textContent = localize('empty', "No elements found"); + } else { + this.messageDomNode.innerHTML = ''; + } + + toggleClass(this.domNode, 'no-matches', noMatches); + this.domNode.title = localize('found', "Matched {0} out of {1} elements", this.filter.matchCount, this.filter.totalCount); + this.labelDomNode.textContent = this.pattern.length > 16 ? '…' + this.pattern.substr(this.pattern.length - 16) : this.pattern; + } + + dispose() { + this.disable(); + this.disposables = dispose(this.disposables); + } +} + function isInputElement(e: HTMLElement): boolean { return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA'; } @@ -196,51 +672,251 @@ function asTreeContextMenuEvent(event: IListContextMenuEvent extends IListOptions { - filter?: ITreeFilter; +export interface IKeyboardNavigationEventFilter { + (e: KeyboardEvent): boolean; +} + +export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { + readonly automaticKeyboardNavigation?: boolean; + readonly simpleKeyboardNavigation?: boolean; + readonly filterOnType?: boolean; + readonly openOnSingleClick?: boolean; +} + +export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions { + readonly collapseByDefault?: boolean; // defaults to false + readonly filter?: ITreeFilter; + readonly dnd?: ITreeDragAndDrop; + readonly autoExpandSingleChildren?: boolean; + readonly keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; +} + +/** + * The trait concept needs to exist at the tree level, because collapsed + * tree nodes will not be known by the list. + */ +class Trait { + + private nodes: ITreeNode[] = []; + private elements: T[] | undefined; + + private _onDidChange = new Emitter>(); + readonly onDidChange = this._onDidChange.event; + + private _nodeSet: Set> | undefined; + private get nodeSet(): Set> { + if (!this._nodeSet) { + this._nodeSet = new Set(); + + for (const node of this.nodes) { + this._nodeSet.add(node); + } + } + + return this._nodeSet; + } + + set(nodes: ITreeNode[], browserEvent?: UIEvent): void { + this.nodes = [...nodes]; + this.elements = undefined; + this._nodeSet = undefined; + + const that = this; + this._onDidChange.fire({ get elements() { return that.get(); }, browserEvent }); + } + + get(): T[] { + if (!this.elements) { + this.elements = this.nodes.map(node => node.element); + } + + return [...this.elements]; + } + + has(node: ITreeNode): boolean { + return this.nodeSet.has(node); + } + + remove(nodes: ITreeNode[]): void { + if (nodes.length === 0) { + return; + } + + const set = this.nodeSet; + const visit = (node: ITreeNode) => { + set.delete(node); + node.children.forEach(visit); + }; + + nodes.forEach(visit); + this.set(values(set)); + } +} + +/** + * We use this List subclass to restore selection and focus as nodes + * get rendered in the list, possibly due to a node expand() call. + */ +class TreeNodeList extends List> { + + constructor( + container: HTMLElement, + virtualDelegate: IListVirtualDelegate>, + renderers: IListRenderer[], + private focusTrait: Trait, + private selectionTrait: Trait, + options?: IListOptions> + ) { + super(container, virtualDelegate, renderers, options); + } + + splice(start: number, deleteCount: number, elements: ITreeNode[] = []): void { + super.splice(start, deleteCount, elements); + + if (elements.length === 0) { + return; + } + + const additionalFocus: number[] = []; + const additionalSelection: number[] = []; + + elements.forEach((node, index) => { + if (this.selectionTrait.has(node)) { + additionalFocus.push(start + index); + } + + if (this.selectionTrait.has(node)) { + additionalSelection.push(start + index); + } + }); + + if (additionalFocus.length > 0) { + super.setFocus([...super.getFocus(), ...additionalFocus]); + } + + if (additionalSelection.length > 0) { + super.setSelection([...super.getSelection(), ...additionalSelection]); + } + } + + setFocus(indexes: number[], browserEvent?: UIEvent, fromAPI = false): void { + super.setFocus(indexes, browserEvent); + + if (!fromAPI) { + this.focusTrait.set(indexes.map(i => this.element(i)), browserEvent); + } + } + + setSelection(indexes: number[], browserEvent?: UIEvent, fromAPI = false): void { + super.setSelection(indexes, browserEvent); + + if (!fromAPI) { + this.selectionTrait.set(indexes.map(i => this.element(i)), browserEvent); + } + } } export abstract class AbstractTree implements IDisposable { - private view: List>; + private view: TreeNodeList; + private renderers: TreeRenderer[]; + private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; protected model: ITreeModel; + private focus = new Trait(); + private selection = new Trait(); + private eventBufferer = new EventBufferer(); + private typeFilterController?: TypeFilterController; protected disposables: IDisposable[] = []; - get onDidChangeFocus(): Event> { return mapEvent(this.view.onFocusChange, asTreeEvent); } - get onDidChangeSelection(): Event> { return mapEvent(this.view.onSelectionChange, asTreeEvent); } + get onDidScroll(): Event { return this.view.onDidScroll; } + + readonly onDidChangeFocus: Event> = this.eventBufferer.wrapEvent(this.focus.onDidChange); + readonly onDidChangeSelection: Event> = this.eventBufferer.wrapEvent(this.selection.onDidChange); + get onDidOpen(): Event> { return Event.map(this.view.onDidOpen, asTreeEvent); } + + get onMouseClick(): Event> { return Event.map(this.view.onMouseClick, asTreeMouseEvent); } + get onMouseDblClick(): Event> { return Event.map(this.view.onMouseDblClick, asTreeMouseEvent); } + get onContextMenu(): Event> { return Event.map(this.view.onContextMenu, asTreeContextMenuEvent); } + + get onKeyDown(): Event { return this.view.onKeyDown; } + get onKeyUp(): Event { return this.view.onKeyUp; } + get onKeyPress(): Event { return this.view.onKeyPress; } - get onMouseClick(): Event> { return mapEvent(this.view.onMouseClick, asTreeMouseEvent); } - get onMouseDblClick(): Event> { return mapEvent(this.view.onMouseDblClick, asTreeMouseEvent); } - get onContextMenu(): Event> { return mapEvent(this.view.onContextMenu, asTreeContextMenuEvent); } get onDidFocus(): Event { return this.view.onDidFocus; } get onDidBlur(): Event { return this.view.onDidBlur; } - get onDidChangeCollapseState(): Event> { return this.model.onDidChangeCollapseState; } + get onDidChangeCollapseState(): Event> { return this.model.onDidChangeCollapseState; } get onDidChangeRenderNodeCount(): Event> { return this.model.onDidChangeRenderNodeCount; } + private _onWillRefilter = new Emitter(); + readonly onWillRefilter: Event = this._onWillRefilter.event; + + get filterOnType(): boolean { return !!this._options.filterOnType; } + get openOnSingleClick(): boolean { return typeof this._options.openOnSingleClick === 'undefined' ? true : this._options.openOnSingleClick; } + get onDidDispose(): Event { return this.view.onDidDispose; } constructor( container: HTMLElement, delegate: IListVirtualDelegate, renderers: ITreeRenderer[], - options: IAbstractTreeOptions = {} + private _options: IAbstractTreeOptions = {} ) { const treeDelegate = new ComposedTreeDelegate>(delegate); - const onDidChangeCollapseStateRelay = new Relay>(); - const treeRenderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event)); - this.disposables.push(...treeRenderers); + const onDidChangeCollapseStateRelay = new Relay>(); + this.renderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event, _options)); + this.disposables.push(...this.renderers); + + let filter: TypeFilter | undefined; - this.view = new List(container, treeDelegate, treeRenderers, asListOptions(options)); + if (_options.keyboardNavigationLabelProvider) { + filter = new TypeFilter(this, _options.keyboardNavigationLabelProvider, _options.filter as ITreeFilter); + _options = { ..._options, filter: filter as ITreeFilter }; // TODO need typescript help here + this.disposables.push(filter); + } + + this.view = new TreeNodeList(container, treeDelegate, this.renderers, this.focus, this.selection, asListOptions(() => this.model, _options)); - this.model = this.createModel(this.view, options); + this.model = this.createModel(this.view, _options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; + if (this.options.identityProvider) { + const identityProvider = this.options.identityProvider; + + this.model.onDidSplice(e => { + if (e.deletedNodes.length === 0) { + return; + } + + this.eventBufferer.bufferEvents(() => { + const map = new Map>(); + + for (const node of e.deletedNodes) { + map.set(identityProvider.getId(node.element).toString(), node); + } + + for (const node of e.insertedNodes) { + map.delete(identityProvider.getId(node.element).toString()); + } + + if (map.size === 0) { + return; + } + + const deletedNodes = values(map); + + this.focus.remove(deletedNodes); + this.selection.remove(deletedNodes); + }); + }, null, this.disposables); + } + + this.view.onTap(this.reactOnMouseClick, this, this.disposables); this.view.onMouseClick(this.reactOnMouseClick, this, this.disposables); - if (options.keyboardSupport !== false) { - const onKeyDown = chain(this.view.onKeyDown) + if (_options.keyboardSupport !== false) { + const onKeyDown = Event.chain(this.view.onKeyDown) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -248,6 +924,50 @@ export abstract class AbstractTree implements IDisposable onKeyDown.filter(e => e.keyCode === KeyCode.RightArrow).on(this.onRightArrow, this, this.disposables); onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables); } + + if (_options.keyboardNavigationLabelProvider) { + this.typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, _options.keyboardNavigationLabelProvider); + this.focusNavigationFilter = node => { + if (!this.typeFilterController!.enabled || !this.typeFilterController!.pattern) { + return true; + } + + if (filter!.totalCount > 0 && filter!.matchCount <= 1) { + return true; + } + + return !FuzzyScore.isDefault(node.filterData as any as FuzzyScore); + }; + this.disposables.push(this.typeFilterController!); + } + } + + updateOptions(optionsUpdate: IAbstractTreeOptionsUpdate = {}): void { + this._options = { ...this._options, ...optionsUpdate }; + + for (const renderer of this.renderers) { + renderer.updateOptions(optionsUpdate); + } + + this.view.updateOptions({ enableKeyboardNavigation: this._options.simpleKeyboardNavigation }); + + if (this.typeFilterController) { + this.typeFilterController.updateOptions(this._options); + } + } + + get options(): IAbstractTreeOptions { + return this._options; + } + + updateWidth(element: TRef): void { + const index = this.model.getListIndex(element); + + if (index === -1) { + return; + } + + this.view.updateWidth(index); } // Widget @@ -284,12 +1004,12 @@ export abstract class AbstractTree implements IDisposable this.view.domFocus(); } - layout(height?: number): void { - this.view.layout(height); + isDOMFocused(): boolean { + return this.getHTMLElement() === document.activeElement; } - layoutWidth(width: number): void { - this.view.layoutWidth(width); + layout(height?: number, width?: number): void { + this.view.layout(height, width); } style(styles: IListStyles): void { @@ -306,30 +1026,30 @@ export abstract class AbstractTree implements IDisposable return this.model.getFirstElementChild(location); } - getLastElementAncestor(location?: TRef): T | undefined { - return this.model.getLastElementAncestor(location); - } - // Tree getNode(location?: TRef): ITreeNode { return this.model.getNode(location); } - collapse(location: TRef): boolean { - return this.model.setCollapsed(location, true); + collapse(location: TRef, recursive: boolean = false): boolean { + return this.model.setCollapsed(location, true, recursive); } - expand(location: TRef): boolean { - return this.model.setCollapsed(location, false); + expand(location: TRef, recursive: boolean = false): boolean { + return this.model.setCollapsed(location, false, recursive); } - toggleCollapsed(location: TRef): void { - this.model.toggleCollapsed(location); + toggleCollapsed(location: TRef, recursive: boolean = false): boolean { + return this.model.setCollapsed(location, undefined, recursive); + } + + expandAll(): void { + this.model.setCollapsed(this.model.rootRef, false, true); } collapseAll(): void { - this.model.collapseAll(); + this.model.setCollapsed(this.model.rootRef, true, true); } isCollapsible(location: TRef): boolean { @@ -340,56 +1060,65 @@ export abstract class AbstractTree implements IDisposable return this.model.isCollapsed(location); } - isExpanded(location: TRef): boolean { - return !this.isCollapsed(location); + toggleKeyboardNavigation(): void { + if (!this.typeFilterController) { + return; + } + + this.typeFilterController.toggle(); } refilter(): void { + this._onWillRefilter.fire(undefined); this.model.refilter(); } setSelection(elements: TRef[], browserEvent?: UIEvent): void { - const indexes = elements.map(e => this.model.getListIndex(e)); - this.view.setSelection(indexes, browserEvent); + const nodes = elements.map(e => this.model.getNode(e)); + this.selection.set(nodes, browserEvent); + + const indexes = elements.map(e => this.model.getListIndex(e)).filter(i => i > -1); + this.view.setSelection(indexes, browserEvent, true); } getSelection(): T[] { - const nodes = this.view.getSelectedElements(); - return nodes.map(n => n.element); + return this.selection.get(); } setFocus(elements: TRef[], browserEvent?: UIEvent): void { - const indexes = elements.map(e => this.model.getListIndex(e)); - this.view.setFocus(indexes, browserEvent); + const nodes = elements.map(e => this.model.getNode(e)); + this.focus.set(nodes, browserEvent); + + const indexes = elements.map(e => this.model.getListIndex(e)).filter(i => i > -1); + this.view.setFocus(indexes, browserEvent, true); } focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { - this.view.focusNext(n, loop, browserEvent); + this.view.focusNext(n, loop, browserEvent, this.focusNavigationFilter); } focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { - this.view.focusPrevious(n, loop, browserEvent); + this.view.focusPrevious(n, loop, browserEvent, this.focusNavigationFilter); } focusNextPage(browserEvent?: UIEvent): void { - this.view.focusNextPage(browserEvent); + this.view.focusNextPage(browserEvent, this.focusNavigationFilter); } focusPreviousPage(browserEvent?: UIEvent): void { - this.view.focusPreviousPage(browserEvent); + this.view.focusPreviousPage(browserEvent, this.focusNavigationFilter); } focusLast(browserEvent?: UIEvent): void { - this.view.focusLast(browserEvent); + this.view.focusLast(browserEvent, this.focusNavigationFilter); } focusFirst(browserEvent?: UIEvent): void { - this.view.focusFirst(browserEvent); + this.view.focusFirst(browserEvent, this.focusNavigationFilter); } getFocus(): T[] { - const nodes = this.view.getFocusedElements(); - return nodes.map(n => n.element); + return this.focus.get(); } open(elements: TRef[]): void { @@ -399,6 +1128,11 @@ export abstract class AbstractTree implements IDisposable reveal(location: TRef, relativeTop?: number): void { const index = this.model.getListIndex(location); + + if (index === -1) { + return; + } + this.view.reveal(index, relativeTop); } @@ -408,6 +1142,11 @@ export abstract class AbstractTree implements IDisposable */ getRelativeTop(location: TRef): number | null { const index = this.model.getListIndex(location); + + if (index === -1) { + return null; + } + return this.view.getRelativeTop(index); } @@ -418,14 +1157,30 @@ export abstract class AbstractTree implements IDisposable } private reactOnMouseClick(e: IListMouseEvent>): void { + if (isInputElement(e.browserEvent.target as HTMLElement)) { + return; + } + const node = e.element; if (!node) { return; } + if (this.view.multipleSelectionController.isSelectionRangeChangeEvent(e) || this.view.multipleSelectionController.isSelectionSingleChangeEvent(e)) { + return; + } + + const onTwistie = hasClass(e.browserEvent.target as HTMLElement, 'monaco-tl-twistie'); + + if (!this.openOnSingleClick && e.browserEvent.detail !== 2 && !onTwistie) { + return; + } + const location = this.model.getNodeLocation(node); - this.model.toggleCollapsed(location); + const recursive = e.browserEvent.altKey; + + this.model.setCollapsed(location, undefined, recursive); } private onLeftArrow(e: StandardKeyboardEvent): void { @@ -471,7 +1226,7 @@ export abstract class AbstractTree implements IDisposable const didChange = this.model.setCollapsed(location, false); if (!didChange) { - if (node.children.length === 0) { + if (!node.children.some(child => child.visible)) { return; } @@ -495,13 +1250,81 @@ export abstract class AbstractTree implements IDisposable const node = nodes[0]; const location = this.model.getNodeLocation(node); - this.model.toggleCollapsed(location); + const recursive = e.browserEvent.altKey; + + this.model.setCollapsed(location, undefined, recursive); } protected abstract createModel(view: ISpliceable>, options: IAbstractTreeOptions): ITreeModel; + navigate(start?: TRef): ITreeNavigator { + return new TreeNavigator(this.view, this.model, start); + } + dispose(): void { this.disposables = dispose(this.disposables); this.view.dispose(); } } + +interface ITreeNavigatorView, TFilterData> { + readonly length: number; + element(index: number): ITreeNode; +} + +class TreeNavigator, TFilterData, TRef> implements ITreeNavigator { + + private index: number; + + constructor(private view: ITreeNavigatorView, private model: ITreeModel, start?: TRef) { + if (start) { + this.index = this.model.getListIndex(start); + } else { + this.index = -1; + } + } + + current(): T | null { + if (this.index < 0 || this.index >= this.view.length) { + return null; + } + + return this.view.element(this.index).element; + } + + previous(): T | null { + this.index--; + return this.current(); + } + + next(): T | null { + this.index++; + return this.current(); + } + + parent(): T | null { + if (this.index < 0 || this.index >= this.view.length) { + return null; + } + + const node = this.view.element(this.index); + + if (!node.parent) { + this.index = -1; + return this.current(); + } + + this.index = this.model.getListIndex(this.model.getNodeLocation(node.parent)); + return this.current(); + } + + first(): T | null { + this.index = 0; + return this.current(); + } + + last(): T | null { + this.index = this.view.length - 1; + return this.current(); + } +} diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index cb5ee601d2f2..1ff84fa8d573 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -3,64 +3,81 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ComposedTreeDelegate, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; +import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; -import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; +import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; +import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop } from 'vs/base/browser/ui/tree/tree'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Emitter, Event, mapEvent } from 'vs/base/common/event'; -import { timeout, always } from 'vs/base/common/async'; +import { Emitter, Event } from 'vs/base/common/event'; +import { timeout, always, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; -import { toggleClass } from 'vs/base/browser/dom'; import { Iterator } from 'vs/base/common/iterator'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; +import { toggleClass } from 'vs/base/browser/dom'; -export interface IDataSource> { - hasChildren(element: T | null): boolean; - getChildren(element: T | null): Thenable; -} - -enum AsyncDataTreeNodeState { - Uninitialized, - Loaded, - Loading, - Slow +const enum AsyncDataTreeNodeState { + Uninitialized = 'uninitialized', + Loaded = 'loaded', + Loading = 'loading' } -interface IAsyncDataTreeNode> { - element: T | null; - readonly parent: IAsyncDataTreeNode | null; +interface IAsyncDataTreeNode { + element: TInput | T; + readonly parent: IAsyncDataTreeNode | null; + readonly children: IAsyncDataTreeNode[]; readonly id?: string | null; - readonly children?: IAsyncDataTreeNode[]; state: AsyncDataTreeNodeState; + hasChildren: boolean; + needsRefresh: boolean; + slow: boolean; + disposed: boolean; +} + +function isAncestor(ancestor: IAsyncDataTreeNode, descendant: IAsyncDataTreeNode): boolean { + if (!descendant.parent) { + return false; + } else if (descendant.parent === ancestor) { + return true; + } else { + return isAncestor(ancestor, descendant.parent); + } +} + +function intersects(node: IAsyncDataTreeNode, other: IAsyncDataTreeNode): boolean { + return node === other || isAncestor(node, other) || isAncestor(other, node); } interface IDataTreeListTemplateData { templateData: T; } -class AsyncDataTreeNodeWrapper implements ITreeNode { +class AsyncDataTreeNodeWrapper implements ITreeNode { - get element(): T { return this.node.element!.element!; } + get element(): T { return this.node.element!.element as T; } get parent(): ITreeNode | undefined { return this.node.parent && new AsyncDataTreeNodeWrapper(this.node.parent); } get children(): ITreeNode[] { return this.node.children.map(node => new AsyncDataTreeNodeWrapper(node)); } get depth(): number { return this.node.depth; } + get visibleChildrenCount(): number { return this.node.visibleChildrenCount; } + get visibleChildIndex(): number { return this.node.visibleChildIndex; } get collapsible(): boolean { return this.node.collapsible; } get collapsed(): boolean { return this.node.collapsed; } get visible(): boolean { return this.node.visible; } get filterData(): TFilterData | undefined { return this.node.filterData; } - constructor(private node: ITreeNode | null, TFilterData>) { } + constructor(private node: ITreeNode | null, TFilterData>) { } } -class DataTreeRenderer implements ITreeRenderer, TFilterData, IDataTreeListTemplateData> { +class DataTreeRenderer implements ITreeRenderer, TFilterData, IDataTreeListTemplateData> { readonly templateId: string; - private renderedNodes = new Map, IDataTreeListTemplateData>(); + private renderedNodes = new Map, IDataTreeListTemplateData>(); private disposables: IDisposable[] = []; constructor( private renderer: ITreeRenderer, - readonly onDidChangeTwistieState: Event> + readonly onDidChangeTwistieState: Event> ) { this.templateId = renderer.templateId; } @@ -70,17 +87,19 @@ class DataTreeRenderer implements ITreeRenderer, TFilterData>, index: number, templateData: IDataTreeListTemplateData): void { + renderElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData): void { this.renderer.renderElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData); } - renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { - toggleClass(twistieElement, 'loading', element.state === AsyncDataTreeNodeState.Slow); + renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { + toggleClass(twistieElement, 'loading', element.slow); return false; } - disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData): void { - this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData); + disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData): void { + if (this.renderer.disposeElement) { + this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData); + } } disposeTemplate(templateData: IDataTreeListTemplateData): void { @@ -93,24 +112,24 @@ class DataTreeRenderer implements ITreeRenderer(e: ITreeEvent>): ITreeEvent { +function asTreeEvent(e: ITreeEvent>): ITreeEvent { return { browserEvent: e.browserEvent, - elements: e.elements.map(e => e.element!) + elements: e.elements.map(e => e.element as T) }; } -function asTreeMouseEvent(e: ITreeMouseEvent>): ITreeMouseEvent { +function asTreeMouseEvent(e: ITreeMouseEvent>): ITreeMouseEvent { return { browserEvent: e.browserEvent, - element: e.element && e.element.element! + element: e.element && e.element.element as T }; } -function asTreeContextMenuEvent(e: ITreeContextMenuEvent>): ITreeContextMenuEvent { +function asTreeContextMenuEvent(e: ITreeContextMenuEvent>): ITreeContextMenuEvent { return { browserEvent: e.browserEvent, - element: e.element && e.element.element!, + element: e.element && e.element.element as T, anchor: e.anchor }; } @@ -125,14 +144,56 @@ export interface IChildrenResolutionEvent { readonly reason: ChildrenResolutionReason; } -function asObjectTreeOptions(options?: IAsyncDataTreeOptions): IObjectTreeOptions, TFilterData> | undefined { +function asAsyncDataTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { + if (data instanceof ElementsDragAndDropData) { + const nodes = (data as ElementsDragAndDropData>).elements; + return new ElementsDragAndDropData(nodes.map(node => node.element)); + } + + return data; +} + +class AsyncDataTreeNodeListDragAndDrop implements IListDragAndDrop> { + + constructor(private dnd: ITreeDragAndDrop) { } + + getDragURI(node: IAsyncDataTreeNode): string | null { + return this.dnd.getDragURI(node.element as T); + } + + getDragLabel(nodes: IAsyncDataTreeNode[]): string | undefined { + if (this.dnd.getDragLabel) { + return this.dnd.getDragLabel(nodes.map(node => node.element as T)); + } + + return undefined; + } + + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + if (this.dnd.onDragStart) { + this.dnd.onDragStart(asAsyncDataTreeDragAndDropData(data), originalEvent); + } + } + + onDragOver(data: IDragAndDropData, targetNode: IAsyncDataTreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { + return this.dnd.onDragOver(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element as T, targetIndex, originalEvent); + } + + drop(data: IDragAndDropData, targetNode: IAsyncDataTreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { + this.dnd.drop(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element as T, targetIndex, originalEvent); + } +} + +function asObjectTreeOptions(options?: IAsyncDataTreeOptions): IObjectTreeOptions, TFilterData> | undefined { return options && { ...options, + collapseByDefault: true, identityProvider: options.identityProvider && { getId(el) { - return options.identityProvider!.getId(el.element!); + return options.identityProvider!.getId(el.element as T); } }, + dnd: options.dnd && new AsyncDataTreeNodeListDragAndDrop(options.dnd), multipleSelectionController: options.multipleSelectionController && { isSelectionSingleChangeEvent(e) { return options.multipleSelectionController!.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); @@ -143,90 +204,137 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOptions(node: IAsyncDataTreeNode): ITreeElement> { +function asTreeElement(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): ITreeElement> { + let collapsed: boolean | undefined; + + if (viewStateContext && node.id) { + collapsed = viewStateContext.viewState.expanded.indexOf(node.id) === -1; + } + return { element: node, - children: Iterator.map(Iterator.fromArray(node.children!), asTreeElement) + children: Iterator.map(Iterator.fromArray(node.children), child => asTreeElement(child, viewStateContext)), + collapsible: node.hasChildren, + collapsed }; } -export interface IAsyncDataTreeOptions extends IAbstractTreeOptions { +export interface IAsyncDataTreeOptionsUpdate extends IAbstractTreeOptionsUpdate { } + +export interface IAsyncDataTreeOptions extends IAsyncDataTreeOptionsUpdate, IAbstractTreeOptions { identityProvider?: IIdentityProvider; + sorter?: ITreeSorter; + autoExpandSingleChildren?: boolean; } -export class AsyncDataTree, TFilterData = void> implements IDisposable { +export interface IAsyncDataTreeViewState { + readonly focus: string[]; + readonly selection: string[]; + readonly expanded: string[]; +} + +interface IAsyncDataTreeViewStateContext { + readonly viewState: IAsyncDataTreeViewState; + readonly selection: IAsyncDataTreeNode[]; + readonly focus: IAsyncDataTreeNode[]; +} + +export class AsyncDataTree implements IDisposable { + + private readonly tree: ObjectTree, TFilterData>; + private readonly root: IAsyncDataTreeNode; + private readonly renderedNodes = new Map>(); + private readonly sorter?: ITreeSorter; + + private readonly subTreeRefreshPromises = new Map, Promise>(); + private readonly refreshPromises = new Map, CancelablePromise>(); - private readonly tree: ObjectTree, TFilterData>; - private readonly root: IAsyncDataTreeNode; - private readonly nodes = new Map>(); - private readonly refreshPromises = new Map, Thenable>(); private readonly identityProvider?: IIdentityProvider; + private readonly autoExpandSingleChildren: boolean; - private readonly _onDidChangeNodeState = new Emitter>(); + private readonly _onDidRender = new Emitter(); + private readonly _onDidChangeNodeSlowState = new Emitter>(); protected readonly disposables: IDisposable[] = []; - get onDidChangeFocus(): Event> { return mapEvent(this.tree.onDidChangeFocus, asTreeEvent); } - get onDidChangeSelection(): Event> { return mapEvent(this.tree.onDidChangeSelection, asTreeEvent); } - get onDidChangeCollapseState(): Event { return mapEvent(this.tree.onDidChangeCollapseState, e => e.element!.element!); } + get onDidScroll(): Event { return this.tree.onDidScroll; } - private readonly _onDidResolveChildren = new Emitter>(); - readonly onDidResolveChildren: Event> = this._onDidResolveChildren.event; + get onDidChangeFocus(): Event> { return Event.map(this.tree.onDidChangeFocus, asTreeEvent); } + get onDidChangeSelection(): Event> { return Event.map(this.tree.onDidChangeSelection, asTreeEvent); } + get onDidOpen(): Event> { return Event.map(this.tree.onDidOpen, asTreeEvent); } - get onMouseClick(): Event> { return mapEvent(this.tree.onMouseClick, asTreeMouseEvent); } - get onMouseDblClick(): Event> { return mapEvent(this.tree.onMouseDblClick, asTreeMouseEvent); } - get onContextMenu(): Event> { return mapEvent(this.tree.onContextMenu, asTreeContextMenuEvent); } + get onMouseClick(): Event> { return Event.map(this.tree.onMouseClick, asTreeMouseEvent); } + get onMouseDblClick(): Event> { return Event.map(this.tree.onMouseDblClick, asTreeMouseEvent); } + get onContextMenu(): Event> { return Event.map(this.tree.onContextMenu, asTreeContextMenuEvent); } get onDidFocus(): Event { return this.tree.onDidFocus; } get onDidBlur(): Event { return this.tree.onDidBlur; } + get filterOnType(): boolean { return this.tree.filterOnType; } + get openOnSingleClick(): boolean { return this.tree.openOnSingleClick; } + get onDidDispose(): Event { return this.tree.onDidDispose; } constructor( container: HTMLElement, delegate: IListVirtualDelegate, renderers: ITreeRenderer[], - private dataSource: IDataSource, - options?: IAsyncDataTreeOptions + private dataSource: IAsyncDataSource, + options: IAsyncDataTreeOptions = {} ) { - this.identityProvider = options && options.identityProvider; + this.identityProvider = options.identityProvider; + this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren; + this.sorter = options.sorter; - const objectTreeDelegate = new ComposedTreeDelegate>(delegate); - const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeState.event)); - const objectTreeOptions = asObjectTreeOptions(options) || {}; - objectTreeOptions.collapseByDefault = true; + const objectTreeDelegate = new ComposedTreeDelegate>(delegate); + const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeSlowState.event)); + const objectTreeOptions = asObjectTreeOptions(options) || {}; this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions); this.root = { - element: null, + element: undefined!, parent: null, + children: [], state: AsyncDataTreeNodeState.Uninitialized, + hasChildren: true, + needsRefresh: false, + disposed: false, + slow: false }; if (this.identityProvider) { this.root = { ...this.root, - id: null, - children: [], + id: null }; } - this.nodes.set(null, this.root); + this.renderedNodes.set(null, this.root); this.tree.onDidChangeCollapseState(this._onDidChangeCollapseState, this, this.disposables); } + updateOptions(options: IAsyncDataTreeOptionsUpdate = {}): void { + this.tree.updateOptions(options); + } + // Widget getHTMLElement(): HTMLElement { @@ -261,50 +369,110 @@ export class AsyncDataTree, TFilterData = void> imple this.tree.domFocus(); } - layout(height?: number): void { - this.tree.layout(height); + layout(height?: number, width?: number): void { + this.tree.layout(height, width); } style(styles: IListStyles): void { this.tree.style(styles); } - // Data Tree + // Model + + getInput(): TInput | undefined { + return this.root.element as TInput; + } + + async setInput(input: TInput, viewState?: IAsyncDataTreeViewState): Promise { + this.refreshPromises.forEach(promise => promise.cancel()); + this.refreshPromises.clear(); + + this.root.element = input!; + + const viewStateContext = viewState && { viewState, focus: [], selection: [] } as IAsyncDataTreeViewStateContext; + + await this.updateChildren(input, true, viewStateContext); + + if (viewStateContext) { + this.tree.setFocus(viewStateContext.focus); + this.tree.setSelection(viewStateContext.selection); + } + } + + async updateChildren(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + if (typeof this.root.element === 'undefined') { + throw new Error('Tree input not set'); + } + + if (this.root.state === AsyncDataTreeNodeState.Loading) { + await this.subTreeRefreshPromises.get(this.root)!; + await Event.toPromise(this._onDidRender.event); + } + + await this.refreshAndRenderNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext); + } + + hasNode(element: TInput | T): boolean { + return element === this.root.element || this.renderedNodes.has(element as T); + } + + // View + + refresh(element: T): void { + const node = this.getDataNode(element); + this.tree.refresh(node); + } - refresh(element: T | null, recursive = true): Thenable { - return this.refreshNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh); + updateWidth(element: T): void { + const node = this.getDataNode(element); + this.tree.updateWidth(node); } // Tree - getNode(element: T | null): ITreeNode { + getNode(element: TInput | T = this.root.element): ITreeNode { const dataNode = this.getDataNode(element); const node = this.tree.getNode(dataNode === this.root ? null : dataNode); - return new AsyncDataTreeNodeWrapper(node); + return new AsyncDataTreeNodeWrapper(node); } - collapse(element: T): boolean { - return this.tree.collapse(this.getDataNode(element)); + collapse(element: T, recursive: boolean = false): boolean { + const node = this.getDataNode(element); + return this.tree.collapse(node === this.root ? null : node, recursive); } - async expand(element: T): Promise { + async expand(element: T, recursive: boolean = false): Promise { + if (typeof this.root.element === 'undefined') { + throw new Error('Tree input not set'); + } + + if (this.root.state === AsyncDataTreeNodeState.Loading) { + await this.subTreeRefreshPromises.get(this.root)!; + await Event.toPromise(this._onDidRender.event); + } + const node = this.getDataNode(element); - if (!this.tree.isCollapsed(node)) { + if (node !== this.root && node.state !== AsyncDataTreeNodeState.Loading && !this.tree.isCollapsed(node)) { return false; } - this.tree.expand(node); + const result = this.tree.expand(node === this.root ? null : node, recursive); - if (node.state !== AsyncDataTreeNodeState.Loaded) { - await this.refreshNode(node, false, ChildrenResolutionReason.Expand); + if (node.state === AsyncDataTreeNodeState.Loading) { + await this.subTreeRefreshPromises.get(node)!; + await Event.toPromise(this._onDidRender.event); } - return true; + return result; } - toggleCollapsed(element: T): void { - this.tree.toggleCollapsed(this.getDataNode(element)); + toggleCollapsed(element: T, recursive: boolean = false): boolean { + return this.tree.toggleCollapsed(this.getDataNode(element), recursive); + } + + expandAll(): void { + this.tree.expandAll(); } collapseAll(): void { @@ -319,8 +487,8 @@ export class AsyncDataTree, TFilterData = void> imple return this.tree.isCollapsed(this.getDataNode(element)); } - isExpanded(element: T): boolean { - return this.tree.isExpanded(this.getDataNode(element)); + toggleKeyboardNavigation(): void { + this.tree.toggleKeyboardNavigation(); } refilter(): void { @@ -334,7 +502,7 @@ export class AsyncDataTree, TFilterData = void> imple getSelection(): T[] { const nodes = this.tree.getSelection(); - return nodes.map(n => n!.element!); + return nodes.map(n => n!.element as T); } setFocus(elements: T[], browserEvent?: UIEvent): void { @@ -368,7 +536,7 @@ export class AsyncDataTree, TFilterData = void> imple getFocus(): T[] { const nodes = this.tree.getFocus(); - return nodes.map(n => n!.element!); + return nodes.map(n => n!.element as T); } open(elements: T[]): void { @@ -386,21 +554,15 @@ export class AsyncDataTree, TFilterData = void> imple // Tree navigation - getParentElement(element: T): T | null { + getParentElement(element: T): TInput | T { const node = this.tree.getParentElement(this.getDataNode(element)); - return node && node.element; + return (node && node.element)!; } - getFirstElementChild(element: T | null = null): T | null | undefined { + getFirstElementChild(element: TInput | T = this.root.element): TInput | T | undefined { const dataNode = this.getDataNode(element); const node = this.tree.getFirstElementChild(dataNode === this.root ? null : dataNode); - return node && node.element; - } - - getLastElementAncestor(element: T | null = null): T | null | undefined { - const dataNode = this.getDataNode(element); - const node = this.tree.getLastElementAncestor(dataNode === this.root ? null : dataNode); - return node && node.element; + return (node && node.element)!; } // List @@ -411,175 +573,287 @@ export class AsyncDataTree, TFilterData = void> imple // Implementation - private getDataNode(element: T | null): IAsyncDataTreeNode { - const node: IAsyncDataTreeNode = this.nodes.get(element); + private getDataNode(element: TInput | T): IAsyncDataTreeNode { + const node: IAsyncDataTreeNode | undefined = this.renderedNodes.get((element === this.root.element ? null : element) as T); - if (typeof node === 'undefined') { + if (!node) { throw new Error(`Data tree node not found: ${element}`); } return node; } - private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { - await this._refreshNode(node, recursive, reason); + private async refreshAndRenderNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + await this.refreshNode(node, recursive, viewStateContext); + this.render(node, viewStateContext); + + if (node !== this.root && this.autoExpandSingleChildren && reason === ChildrenResolutionReason.Expand) { + const treeNode = this.tree.getNode(node); + const visibleChildren = treeNode.children.filter(node => node.visible); - if (recursive && node.children) { - await Promise.all(node.children.map(child => this.refreshNode(child, recursive, reason))); + if (visibleChildren.length === 1) { + await this.tree.expand(visibleChildren[0].element, false); + } } } - private _refreshNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Thenable { - let result = this.refreshPromises.get(node); + private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + if (node.disposed) { + console.error('Async data tree node is disposed'); + return; + } + + let result: Promise | undefined; + + this.subTreeRefreshPromises.forEach((refreshPromise, refreshNode) => { + if (!result && intersects(refreshNode, node)) { + result = refreshPromise.then(() => this.refreshNode(node, recursive, viewStateContext)); + } + }); if (result) { return result; } - result = this.doRefresh(node, recursive, reason); - this.refreshPromises.set(node, result); - return always(result, () => this.refreshPromises.delete(node)); + result = this.doRefreshSubTree(node, recursive, viewStateContext); + this.subTreeRefreshPromises.set(node, result); + + try { + await result; + } finally { + this.subTreeRefreshPromises.delete(node); + } } - private doRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Thenable { - const hasChildren = !!this.dataSource.hasChildren(node.element); + private async doRefreshSubTree(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + node.state = AsyncDataTreeNodeState.Loading; - if (!hasChildren) { - this.setChildren(node, [], recursive); - return Promise.resolve(); - } else if (node !== this.root && (!this.tree.isCollapsible(node) || this.tree.isCollapsed(node))) { - return Promise.resolve(); - } else { - node.state = AsyncDataTreeNodeState.Loading; - this._onDidChangeNodeState.fire(node); + try { + await this.doRefreshNode(node, recursive, viewStateContext); + + if (recursive) { + const childrenToRefresh = node.children + .filter(child => { + if (child.needsRefresh) { + child.needsRefresh = false; + return true; + } + + // TODO@joao: is this still needed? + if (child.hasChildren && child.state === AsyncDataTreeNodeState.Loaded) { + return true; + } + + if (!viewStateContext || !child.id) { + return false; + } + + return viewStateContext.viewState.expanded.indexOf(child.id) > -1; + }); + + await Promise.all(childrenToRefresh.map(child => this.doRefreshSubTree(child, recursive, viewStateContext))); + } + } finally { + node.state = AsyncDataTreeNodeState.Loaded; + } + } + private async doRefreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + node.hasChildren = !!this.dataSource.hasChildren(node.element!); + + let childrenPromise: Promise; + + if (!node.hasChildren) { + childrenPromise = Promise.resolve([]); + } else { const slowTimeout = timeout(800); slowTimeout.then(() => { - node.state = AsyncDataTreeNodeState.Slow; - this._onDidChangeNodeState.fire(node); + node.slow = true; + this._onDidChangeNodeSlowState.fire(node); }, _ => null); - return this.dataSource.getChildren(node.element) - .then(children => { - slowTimeout.cancel(); - node.state = AsyncDataTreeNodeState.Loaded; - this._onDidChangeNodeState.fire(node); - - this.setChildren(node, children, recursive); - this._onDidResolveChildren.fire({ element: node.element, reason }); - }, err => { - slowTimeout.cancel(); - node.state = AsyncDataTreeNodeState.Uninitialized; - this._onDidChangeNodeState.fire(node); - - if (node !== this.root) { - this.tree.collapse(node); - } + childrenPromise = always(this.doGetChildren(node), () => slowTimeout.cancel()); + } + + try { + const children = await childrenPromise; + this.setChildren(node, children, recursive, viewStateContext); + } catch (err) { + node.needsRefresh = true; + + if (node !== this.root) { + this.tree.collapse(node === this.root ? null : node); + } + + if (isPromiseCanceledError(err)) { + return; + } - return Promise.reject(err); - }); + throw err; + } finally { + if (node.slow) { + node.slow = false; + this._onDidChangeNodeSlowState.fire(node); + } } } - private _onDidChangeCollapseState(treeNode: ITreeNode, any>): void { - if (!treeNode.collapsed && treeNode.element.state === AsyncDataTreeNodeState.Uninitialized) { - this.refreshNode(treeNode.element, false, ChildrenResolutionReason.Expand); + private doGetChildren(node: IAsyncDataTreeNode): Promise { + let result = this.refreshPromises.get(node); + + if (result) { + return result; } + + result = createCancelablePromise(async () => { + const children = await this.dataSource.getChildren(node.element!); + + if (this.sorter) { + children.sort(this.sorter.compare.bind(this.sorter)); + } + + return children; + }); + this.refreshPromises.set(node, result); + return always(result, () => this.refreshPromises.delete(node)); } - private setChildren(node: IAsyncDataTreeNode, childrenElements: T[], recursive: boolean): void { - const children = childrenElements.map>>(element => { - if (!this.identityProvider) { - return { - element: { - element, - parent: node, - state: AsyncDataTreeNodeState.Uninitialized - }, - collapsible: !!this.dataSource.hasChildren(element), - collapsed: true - }; + private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent, any>): void { + if (!node.collapsed && (node.element.state === AsyncDataTreeNodeState.Uninitialized || node.element.needsRefresh)) { + if (deep) { + this.collapse(node.element.element as T); + } else { + this.refreshAndRenderNode(node.element, false, ChildrenResolutionReason.Expand) + .catch(onUnexpectedError); } + } + } - const nodeChildren = new Map>(); + private setChildren(node: IAsyncDataTreeNode, childrenElements: T[], recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): void { + let nodeChildren: Map> | undefined; - for (const child of node.children!) { + if (this.identityProvider) { + nodeChildren = new Map(); + + for (const child of node.children) { nodeChildren.set(child.id!, child); } + } - const id = this.identityProvider.getId(element).toString(); - const asyncDataTreeNode = nodeChildren.get(id); + const children = childrenElements.map>(element => { + if (!this.identityProvider) { + const hasChildren = !!this.dataSource.hasChildren(element); - if (!asyncDataTreeNode) { return { - element: { - element, - parent: node, - id, - children: [], - state: AsyncDataTreeNodeState.Uninitialized - }, - collapsible: !!this.dataSource.hasChildren(element), - collapsed: true + element, + parent: node, + children: [], + state: AsyncDataTreeNodeState.Uninitialized, + hasChildren, + needsRefresh: false, + disposed: false, + slow: false }; } - asyncDataTreeNode.element = element; - - const collapsible = !!this.dataSource.hasChildren(element); - const collapsed = !collapsible || this.tree.isCollapsed(asyncDataTreeNode); + const id = this.identityProvider.getId(element).toString(); + const asyncDataTreeNode = nodeChildren!.get(id); - if (recursive) { - asyncDataTreeNode.state = AsyncDataTreeNodeState.Uninitialized; + if (!asyncDataTreeNode) { + const childAsyncDataTreeNode: IAsyncDataTreeNode = { + element, + parent: node, + children: [], + id, + state: AsyncDataTreeNodeState.Uninitialized, + hasChildren: !!this.dataSource.hasChildren(element), + needsRefresh: false, + disposed: false, + slow: false + }; - if (this.tree.isCollapsed(asyncDataTreeNode)) { - asyncDataTreeNode.children!.length = 0; + if (viewStateContext) { + if (viewStateContext.viewState.focus.indexOf(id) > -1) { + viewStateContext.focus.push(childAsyncDataTreeNode); + } - return { - element: asyncDataTreeNode, - collapsible, - collapsed - }; + if (viewStateContext.viewState.selection.indexOf(id) > -1) { + viewStateContext.selection.push(childAsyncDataTreeNode); + } } + + return childAsyncDataTreeNode; } - let children: Iterator>> | undefined = undefined; + asyncDataTreeNode.element = element; - if (collapsible) { - children = Iterator.map(Iterator.fromArray(asyncDataTreeNode.children!), asTreeElement); + if (asyncDataTreeNode.state === AsyncDataTreeNodeState.Loaded || asyncDataTreeNode.hasChildren !== !!this.dataSource.hasChildren(asyncDataTreeNode.element)) { + asyncDataTreeNode.needsRefresh = true; } - return { - element: asyncDataTreeNode, - children, - collapsible, - collapsed - }; + return asyncDataTreeNode; }); + // perf: if the node was and still is a leaf, avoid all these expensive no-ops + if (node.children.length === 0 && childrenElements.length === 0) { + return; + } + + node.children.splice(0, node.children.length, ...children); + } + + private render(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): void { const insertedElements = new Set(); - const onDidCreateNode = (treeNode: ITreeNode, TFilterData>) => { + const onDidCreateNode = (treeNode: ITreeNode, TFilterData>) => { if (treeNode.element.element) { - insertedElements.add(treeNode.element.element); - this.nodes.set(treeNode.element.element, treeNode.element); + insertedElements.add(treeNode.element.element as T); + this.renderedNodes.set(treeNode.element.element as T, treeNode.element); } }; - const onDidDeleteNode = (treeNode: ITreeNode, TFilterData>) => { + const onDidDeleteNode = (treeNode: ITreeNode, TFilterData>) => { if (treeNode.element.element) { - if (!insertedElements.has(treeNode.element.element)) { - this.nodes.delete(treeNode.element.element); + if (!insertedElements.has(treeNode.element.element as T)) { + treeNode.element.disposed = true; + this.renderedNodes.delete(treeNode.element.element as T); } } }; + const children = node.children.map(c => asTreeElement(c, viewStateContext)); this.tree.setChildren(node === this.root ? null : node, children, onDidCreateNode, onDidDeleteNode); - if (this.identityProvider) { - node.children!.splice(0, node.children!.length, ...children.map(c => c.element)); + this._onDidRender.fire(); + } + + // view state + + getViewState(): IAsyncDataTreeViewState { + if (!this.identityProvider) { + throw new Error('Can\'t get tree view state without an identity provider'); + } + + const getId = (element: T) => this.identityProvider!.getId(element).toString(); + const focus = this.getFocus().map(getId); + const selection = this.getSelection().map(getId); + + const expanded: string[] = []; + const root = this.tree.getNode(); + const queue = [root]; + + while (queue.length > 0) { + const node = queue.shift()!; + + if (node !== root && node.collapsible && !node.collapsed) { + expanded.push(getId(node.element!.element as T)); + } + + queue.push(...node.children); } + + return { focus, selection, expanded }; } dispose(): void { diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts new file mode 100644 index 000000000000..2a5f30240c00 --- /dev/null +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; +import { ISpliceable } from 'vs/base/common/sequence'; +import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, IDataSource } from 'vs/base/browser/ui/tree/tree'; +import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; +import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { Iterator } from 'vs/base/common/iterator'; + +export interface IDataTreeOptions extends IAbstractTreeOptions { + sorter?: ITreeSorter; +} + +export interface IDataTreeViewState { + readonly focus: string[]; + readonly selection: string[]; + readonly collapsed: string[]; +} + +export class DataTree extends AbstractTree { + + protected model: ObjectTreeModel; + private input: TInput | undefined; + + private identityProvider: IIdentityProvider | undefined; + + constructor( + container: HTMLElement, + delegate: IListVirtualDelegate, + renderers: ITreeRenderer[], + private dataSource: IDataSource, + options: IDataTreeOptions = {} + ) { + super(container, delegate, renderers, options); + this.identityProvider = options.identityProvider; + } + + // Model + + getInput(): TInput | undefined { + return this.input; + } + + setInput(input: TInput, viewState?: IDataTreeViewState): void { + if (viewState && !this.identityProvider) { + throw new Error('Can\'t restore tree view state without an identity provider'); + } + + this.input = input; + + if (!viewState) { + this._refresh(input); + return; + } + + const focus: T[] = []; + const selection: T[] = []; + + const isCollapsed = (element: T) => { + const id = this.identityProvider!.getId(element).toString(); + + if (viewState.focus.indexOf(id) > -1) { + focus.push(element); + } + + if (viewState.selection.indexOf(id) > -1) { + selection.push(element); + } + + return id in viewState.collapsed; + }; + + this._refresh(input, isCollapsed); + this.setFocus(focus); + this.setSelection(selection); + } + + updateChildren(element: TInput | T = this.input!): void { + if (typeof this.input === 'undefined') { + throw new Error('Tree input not set'); + } + + this._refresh(element); + } + + // View + + refresh(element: T): void { + this.model.refresh(element); + } + + // Implementation + + private _refresh(element: TInput | T, isCollapsed?: (el: T) => boolean): void { + this.model.setChildren((element === this.input ? null : element) as T, this.createIterator(element, isCollapsed)); + } + + private createIterator(element: TInput | T, isCollapsed?: (el: T) => boolean): Iterator> { + const children = Iterator.fromArray(this.dataSource.getChildren(element)); + + return Iterator.map>(children, element => ({ + element, + children: this.createIterator(element), + collapsed: isCollapsed && isCollapsed(element) + })); + } + + protected createModel(view: ISpliceable>, options: IDataTreeOptions): ITreeModel { + return new ObjectTreeModel(view, options); + } + + // view state + + getViewState(): IDataTreeViewState { + if (!this.identityProvider) { + throw new Error('Can\'t get tree view state without an identity provider'); + } + + const getId = (element: T) => this.identityProvider!.getId(element).toString(); + const focus = this.getFocus().map(getId); + const selection = this.getSelection().map(getId); + + const collapsed: string[] = []; + const root = this.model.getNode(); + const queue = [root]; + + while (queue.length > 0) { + const node = queue.shift()!; + + if (node !== root && node.collapsed) { + collapsed.push(getId(node.element!)); + } + + queue.push(...node.children); + } + + return { focus, selection, collapsed }; + } +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/indexTree.ts b/src/vs/base/browser/ui/tree/indexTree.ts index 39c7206de810..51b7f36b5475 100644 --- a/src/vs/base/browser/ui/tree/indexTree.ts +++ b/src/vs/base/browser/ui/tree/indexTree.ts @@ -11,9 +11,7 @@ import { IndexTreeModel } from 'vs/base/browser/ui/tree/indexTreeModel'; import { ITreeElement, ITreeModel, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -export interface IIndexTreeOptions extends IAbstractTreeOptions { - collapseByDefault?: boolean; // defaults to false -} +export interface IIndexTreeOptions extends IAbstractTreeOptions { } export class IndexTree extends AbstractTree { @@ -33,6 +31,10 @@ export class IndexTree extends AbstractTree>, options: IIndexTreeOptions): ITreeModel { return new IndexTreeModel(view, this.rootElement, options); } diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 8761ce336b05..ed0c23876e61 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -3,15 +3,17 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -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 { ICollapseStateChangeEvent, ITreeElement, ITreeFilter, ITreeFilterDataResult, ITreeModel, ITreeNode, TreeVisibility, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; import { tail2 } from 'vs/base/common/arrays'; -import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree'; +import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; +import { ISequence, Iterator } from 'vs/base/common/iterator'; +import { ISpliceable } from 'vs/base/common/sequence'; interface IMutableTreeNode extends ITreeNode { readonly parent: IMutableTreeNode | undefined; readonly children: IMutableTreeNode[]; + visibleChildrenCount: number; + visibleChildIndex: number; collapsible: boolean; collapsed: boolean; renderNodeCount: number; @@ -19,18 +21,11 @@ interface IMutableTreeNode extends ITreeNode { filterData: TFilterData | undefined; } -function isFilterResult(obj: any): obj is ITreeFilterDataResult { +export function isFilterResult(obj: any): obj is ITreeFilterDataResult { return typeof obj === 'object' && 'visibility' in obj && 'data' in obj; } -function treeNodeToElement(node: IMutableTreeNode): ITreeElement { - const { element, collapsed } = node; - const children = Iterator.map(Iterator.fromArray(node.children), treeNodeToElement); - - return { element, children, collapsed }; -} - -function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility { +export function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility { switch (visibility) { case true: return TreeVisibility.Visible; case false: return TreeVisibility.Hidden; @@ -38,34 +33,53 @@ function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility { } } +function treeNodeToElement(node: IMutableTreeNode): ITreeElement { + const { element, collapsed } = node; + const children = Iterator.map(Iterator.fromArray(node.children), treeNodeToElement); + + return { element, children, collapsed }; +} + export interface IIndexTreeModelOptions { - collapseByDefault?: boolean; // defaults to false - filter?: ITreeFilter; + readonly collapseByDefault?: boolean; // defaults to false + readonly filter?: ITreeFilter; + readonly autoExpandSingleChildren?: boolean; } export class IndexTreeModel, TFilterData = void> implements ITreeModel { + readonly rootRef = []; + private root: IMutableTreeNode; private eventBufferer = new EventBufferer(); - private _onDidChangeCollapseState = new Emitter>(); - readonly onDidChangeCollapseState: Event> = this.eventBufferer.wrapEvent(this._onDidChangeCollapseState.event); + private _onDidChangeCollapseState = new Emitter>(); + readonly onDidChangeCollapseState: Event> = this.eventBufferer.wrapEvent(this._onDidChangeCollapseState.event); private _onDidChangeRenderNodeCount = new Emitter>(); readonly onDidChangeRenderNodeCount: Event> = this.eventBufferer.wrapEvent(this._onDidChangeRenderNodeCount.event); private collapseByDefault: boolean; private filter?: ITreeFilter; + private autoExpandSingleChildren: boolean; + + private _onDidSplice = new Emitter>(); + readonly onDidSplice = this._onDidSplice.event; constructor(private list: ISpliceable>, rootElement: T, options: IIndexTreeModelOptions = {}) { this.collapseByDefault = typeof options.collapseByDefault === 'undefined' ? false : options.collapseByDefault; this.filter = options.filter; + this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren; + + // this.onDidChangeCollapseState(node => console.log(node.collapsed, node)); this.root = { parent: undefined, element: rootElement, children: [], depth: 0, + visibleChildrenCount: 0, + visibleChildIndex: -1, collapsible: false, collapsed: false, renderNodeCount: 0, @@ -85,22 +99,64 @@ export class IndexTreeModel, TFilterData = voi throw new Error('Invalid tree location'); } - const { parentNode, listIndex, revealed } = this.getParentNodeWithListIndex(location); + const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location); const treeListElementsToInsert: ITreeNode[] = []; const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode)); + const lastIndex = location[location.length - 1]; + + // figure out what's the visible child start index right before the + // splice point + let visibleChildStartIndex = 0; + + for (let i = lastIndex; i >= 0 && i < parentNode.children.length; i--) { + const child = parentNode.children[i]; + + if (child.visible) { + visibleChildStartIndex = child.visibleChildIndex; + break; + } + } + const nodesToInsert: IMutableTreeNode[] = []; + let insertedVisibleChildrenCount = 0; let renderNodeCount = 0; - Iterator.forEach(nodesToInsertIterator, node => { - nodesToInsert.push(node); - renderNodeCount += node.renderNodeCount; + Iterator.forEach(nodesToInsertIterator, child => { + nodesToInsert.push(child); + renderNodeCount += child.renderNodeCount; + + if (child.visible) { + child.visibleChildIndex = visibleChildStartIndex + insertedVisibleChildrenCount++; + } }); - const lastIndex = location[location.length - 1]; const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert); - if (revealed) { + // figure out what is the count of deleted visible children + let deletedVisibleChildrenCount = 0; + + for (const child of deletedNodes) { + if (child.visible) { + deletedVisibleChildrenCount++; + } + } + + // and adjust for all visible children after the splice point + if (deletedVisibleChildrenCount !== 0) { + for (let i = lastIndex + nodesToInsert.length; i < parentNode.children.length; i++) { + const child = parentNode.children[i]; + + if (child.visible) { + child.visibleChildIndex -= deletedVisibleChildrenCount; + } + } + } + + // update parent's visible children count + parentNode.visibleChildrenCount += insertedVisibleChildrenCount - deletedVisibleChildrenCount; + + if (revealed && visible) { const visibleDeleteCount = deletedNodes.reduce((r, node) => r + node.renderNodeCount, 0); this._updateAncestorsRenderNodeCount(parentNode, renderNodeCount - visibleDeleteCount); @@ -116,37 +172,30 @@ export class IndexTreeModel, TFilterData = voi deletedNodes.forEach(visit); } - return Iterator.map(Iterator.fromArray(deletedNodes), treeNodeToElement); + const result = Iterator.map(Iterator.fromArray(deletedNodes), treeNodeToElement); + this._onDidSplice.fire({ insertedNodes: nodesToInsert, deletedNodes }); + return result; } - getListIndex(location: number[]): number { - return this.getTreeNodeWithListIndex(location).listIndex; - } + refresh(location: number[]): void { + if (location.length === 0) { + throw new Error('Invalid tree location'); + } - setCollapsed(location: number[], collapsed: boolean): boolean { const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location); - return this.eventBufferer.bufferEvents(() => this._setCollapsed(node, listIndex, revealed, collapsed)); - } - toggleCollapsed(location: number[]): void { - const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location); - this.eventBufferer.bufferEvents(() => this._setCollapsed(node, listIndex, revealed)); + if (revealed) { + this.list.splice(listIndex, 1, [node]); + } } - collapseAll(): void { - const queue = [...this.root.children]; - let listIndex = 0; - - this.eventBufferer.bufferEvents(() => { - while (queue.length > 0) { - const node = queue.shift()!; - const revealed = listIndex < this.root.children.length; - this._setCollapsed(node, listIndex, revealed, true); + getListIndex(location: number[]): number { + const { listIndex, visible, revealed } = this.getTreeNodeWithListIndex(location); + return visible && revealed ? listIndex : -1; + } - queue.push(...node.children); - listIndex++; - } - }); + getListRenderCount(location: number[]): number { + return this.getTreeNode(location).renderNodeCount; } isCollapsible(location: number[]): boolean { @@ -157,36 +206,84 @@ export class IndexTreeModel, TFilterData = voi return this.getTreeNode(location).collapsed; } - refilter(): void { - const previousRenderNodeCount = this.root.renderNodeCount; - const toInsert = this.updateNodeAfterFilterChange(this.root); - this.list.splice(0, previousRenderNodeCount, toInsert); - } - - private _setCollapsed(node: IMutableTreeNode, listIndex: number, revealed: boolean, collapsed?: boolean | undefined): boolean { - if (!node.collapsible) { - return false; - } + setCollapsed(location: number[], collapsed?: boolean, recursive?: boolean): boolean { + const node = this.getTreeNode(location); if (typeof collapsed === 'undefined') { collapsed = !node.collapsed; } - if (node.collapsed === collapsed) { - return false; + return this.eventBufferer.bufferEvents(() => this._setCollapsed(location, collapsed!, recursive)); + } + + private _setCollapsed(location: number[], collapsed: boolean, recursive?: boolean): boolean { + const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location); + + const result = this._setListNodeCollapsed(node, listIndex, revealed, collapsed!, recursive || false); + + if (this.autoExpandSingleChildren && !collapsed! && !recursive) { + let onlyVisibleChildIndex = -1; + + for (let i = 0; i < node.children.length; i++) { + const child = node.children[i]; + + if (child.visible) { + if (onlyVisibleChildIndex > -1) { + onlyVisibleChildIndex = -1; + break; + } else { + onlyVisibleChildIndex = i; + } + } + } + + if (onlyVisibleChildIndex > -1) { + this._setCollapsed([...location, onlyVisibleChildIndex], false, false); + } } - node.collapsed = collapsed; + return result; + } - if (revealed) { - const previousRenderNodeCount = node.renderNodeCount; - const toInsert = this.updateNodeAfterCollapseChange(node); + private _setListNodeCollapsed(node: IMutableTreeNode, listIndex: number, revealed: boolean, collapsed: boolean, recursive: boolean): boolean { + const result = this._setNodeCollapsed(node, collapsed, recursive, false); + + if (!revealed || !node.visible) { + return result; + } + + const previousRenderNodeCount = node.renderNodeCount; + const toInsert = this.updateNodeAfterCollapseChange(node); + const deleteCount = previousRenderNodeCount - (listIndex === -1 ? 0 : 1); + this.list.splice(listIndex + 1, deleteCount, toInsert.slice(1)); - this.list.splice(listIndex + 1, previousRenderNodeCount - 1, toInsert.slice(1)); - this._onDidChangeCollapseState.fire(node); + return result; + } + + private _setNodeCollapsed(node: IMutableTreeNode, collapsed: boolean, recursive: boolean, deep: boolean): boolean { + let result = node.collapsible && node.collapsed !== collapsed; + + if (node.collapsible) { + node.collapsed = collapsed; + + if (result) { + this._onDidChangeCollapseState.fire({ node, deep }); + } + } + + if (recursive) { + for (const child of node.children) { + result = this._setNodeCollapsed(child, collapsed, true, true) || result; + } } - return true; + return result; + } + + refilter(): void { + const previousRenderNodeCount = this.root.renderNodeCount; + const toInsert = this.updateNodeAfterFilterChange(this.root); + this.list.splice(0, previousRenderNodeCount, toInsert); } private createTreeNode( @@ -202,6 +299,8 @@ export class IndexTreeModel, TFilterData = voi element: treeElement.element, children: [], depth: parent.depth + 1, + visibleChildrenCount: 0, + visibleChildIndex: -1, collapsible: typeof treeElement.collapsible === 'boolean' ? treeElement.collapsible : (typeof treeElement.collapsed !== 'undefined'), collapsed: typeof treeElement.collapsed === 'undefined' ? this.collapseByDefault : treeElement.collapsed, renderNodeCount: 1, @@ -219,17 +318,21 @@ export class IndexTreeModel, TFilterData = voi 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 visibleChildrenCount = 0; let renderNodeCount = 1; Iterator.forEach(childNodes, child => { node.children.push(child); - hasVisibleDescendants = hasVisibleDescendants || child.visible; renderNodeCount += child.renderNodeCount; + + if (child.visible) { + child.visibleChildIndex = visibleChildrenCount++; + } }); node.collapsible = node.collapsible || node.children.length > 0; - node.visible = visibility === TreeVisibility.Recurse ? hasVisibleDescendants : (visibility === TreeVisibility.Visible); + node.visibleChildrenCount = visibleChildrenCount; + node.visible = visibility === TreeVisibility.Recurse ? visibleChildrenCount > 0 : (visibility === TreeVisibility.Visible); if (!node.visible) { node.renderNodeCount = 0; @@ -307,9 +410,19 @@ export class IndexTreeModel, TFilterData = voi let hasVisibleDescendants = false; if (!node.collapsed || visibility! !== TreeVisibility.Hidden) { + let visibleChildIndex = 0; + for (const child of node.children) { hasVisibleDescendants = this._updateNodeAfterFilterChange(child, visibility!, result, revealed && !node.collapsed) || hasVisibleDescendants; + + if (child.visible) { + child.visibleChildIndex = visibleChildIndex++; + } } + + node.visibleChildrenCount = visibleChildIndex; + } else { + node.visibleChildrenCount = 0; } if (node !== this.root) { @@ -373,8 +486,12 @@ export class IndexTreeModel, TFilterData = voi } // expensive - private getTreeNodeWithListIndex(location: number[]): { node: IMutableTreeNode, listIndex: number, revealed: boolean } { - const { parentNode, listIndex, revealed } = this.getParentNodeWithListIndex(location); + private getTreeNodeWithListIndex(location: number[]): { node: IMutableTreeNode, listIndex: number, revealed: boolean, visible: boolean } { + if (location.length === 0) { + return { node: this.root, listIndex: -1, revealed: true, visible: false }; + } + + const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location); const index = location[location.length - 1]; if (index < 0 || index > parentNode.children.length) { @@ -383,10 +500,10 @@ export class IndexTreeModel, TFilterData = voi const node = parentNode.children[index]; - return { node, listIndex, revealed }; + return { node, listIndex, revealed, visible: visible && node.visible }; } - private getParentNodeWithListIndex(location: number[], node: IMutableTreeNode = this.root, listIndex: number = 0, revealed = true): { parentNode: IMutableTreeNode; listIndex: number; revealed: boolean; } { + private getParentNodeWithListIndex(location: number[], node: IMutableTreeNode = this.root, listIndex: number = 0, revealed = true, visible = true): { parentNode: IMutableTreeNode; listIndex: number; revealed: boolean; visible: boolean; } { const [index, ...rest] = location; if (index < 0 || index > node.children.length) { @@ -399,12 +516,13 @@ export class IndexTreeModel, TFilterData = voi } revealed = revealed && !node.collapsed; + visible = visible && node.visible; if (rest.length === 0) { - return { parentNode: node, listIndex, revealed }; + return { parentNode: node, listIndex, revealed, visible }; } - return this.getParentNodeWithListIndex(rest, node.children[index], listIndex + 1, revealed); + return this.getParentNodeWithListIndex(rest, node.children[index], listIndex + 1, revealed, visible); } getNode(location: number[] = []): ITreeNode { diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index aa78e5869aef..aaf730c91496 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -19,6 +19,7 @@ text-align: right; margin-right: 6px; flex-shrink: 0; + width: 16px; } .monaco-tl-contents { @@ -28,7 +29,7 @@ .monaco-tl-twistie.collapsible { background-size: 16px; - background-position: 100% 50%; + background-position: 3px 50%; background-repeat: no-repeat; background-image: url("expanded.svg"); } @@ -56,6 +57,7 @@ .monaco-tl-twistie.loading { background-image: url("loading.svg"); + background-position: 0 center; } .vs-dark .monaco-tl-twistie.loading { diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index 2a75fea0173f..fff75d7bccaa 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -6,12 +6,12 @@ import { Iterator, ISequence } from 'vs/base/common/iterator'; import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; import { ISpliceable } from 'vs/base/common/sequence'; -import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; export interface IObjectTreeOptions extends IAbstractTreeOptions { - collapseByDefault?: boolean; // defaults to false + sorter?: ITreeSorter; } export class ObjectTree, TFilterData = void> extends AbstractTree { @@ -36,6 +36,10 @@ export class ObjectTree, TFilterData = void> extends return this.model.setChildren(element, children, onDidCreateNode, onDidDeleteNode); } + refresh(element: T): void { + this.model.refresh(element); + } + protected createModel(view: ISpliceable>, options: IObjectTreeOptions): ITreeModel { return new ObjectTreeModel(view, options); } diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index a1ea02603cbf..9029cab3d331 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -7,24 +7,39 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { Iterator, ISequence, getSequenceIterator } from 'vs/base/common/iterator'; import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; import { Event } from 'vs/base/common/event'; -import { ITreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; -export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { } +export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { + readonly sorter?: ITreeSorter; +} export class ObjectTreeModel, TFilterData extends NonNullable = void> implements ITreeModel { + readonly rootRef = null; + private model: IndexTreeModel; private nodes = new Map>(); + private sorter?: ITreeSorter>; - readonly onDidChangeCollapseState: Event>; + readonly onDidSplice: Event>; + readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; get size(): number { return this.nodes.size; } constructor(list: ISpliceable>, options: IObjectTreeModelOptions = {}) { this.model = new IndexTreeModel(list, null, options); - this.onDidChangeCollapseState = this.model.onDidChangeCollapseState as Event>; + this.onDidSplice = this.model.onDidSplice; + this.onDidChangeCollapseState = this.model.onDidChangeCollapseState as Event>; this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount as Event>; + + if (options.sorter) { + this.sorter = { + compare(a, b) { + return options.sorter!.compare(a.element, b.element); + } + }; + } } setChildren( @@ -65,7 +80,11 @@ export class ObjectTreeModel, TFilterData extends Non } private preserveCollapseState(elements: ISequence> | undefined): ISequence> { - const iterator = elements ? getSequenceIterator(elements) : Iterator.empty>(); + let iterator = elements ? getSequenceIterator(elements) : Iterator.empty>(); + + if (this.sorter) { + iterator = Iterator.fromArray(Iterator.collect(iterator).sort(this.sorter.compare.bind(this.sorter))); + } return Iterator.map(iterator, treeElement => { const node = this.nodes.get(treeElement.element); @@ -86,6 +105,11 @@ export class ObjectTreeModel, TFilterData extends Non }); } + refresh(element: T): void { + const location = this.getElementLocation(element); + this.model.refresh(location); + } + getParentElement(ref: T | null = null): T | null { const location = this.getElementLocation(ref); return this.model.getParentElement(location); @@ -106,18 +130,9 @@ export class ObjectTreeModel, TFilterData extends Non return this.model.getListIndex(location); } - setCollapsed(element: T, collapsed: boolean): boolean { + getListRenderCount(element: T): number { const location = this.getElementLocation(element); - return this.model.setCollapsed(location, collapsed); - } - - toggleCollapsed(element: T): void { - const location = this.getElementLocation(element); - this.model.toggleCollapsed(location); - } - - collapseAll(): void { - this.model.collapseAll(); + return this.model.getListRenderCount(location); } isCollapsible(element: T): boolean { @@ -130,6 +145,11 @@ export class ObjectTreeModel, TFilterData extends Non return this.model.isCollapsed(location); } + setCollapsed(element: T, collapsed?: boolean, recursive?: boolean): boolean { + const location = this.getElementLocation(element); + return this.model.setCollapsed(location, collapsed, recursive); + } + refilter(): void { this.model.refilter(); } diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 59ceca06dc67..11d2a03509ce 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -5,7 +5,8 @@ import { Event } from 'vs/base/common/event'; import { Iterator } from 'vs/base/common/iterator'; -import { IListRenderer } from 'vs/base/browser/ui/list/list'; +import { IListRenderer, IListDragOverReaction, IListDragAndDrop, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; export const enum TreeVisibility { @@ -67,6 +68,10 @@ export interface ITreeFilter { filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult; } +export interface ITreeSorter { + compare(element: T, otherElement: T): number; +} + export interface ITreeElement { readonly element: T; readonly children?: Iterator> | ITreeElement[]; @@ -79,17 +84,33 @@ export interface ITreeNode { readonly parent: ITreeNode | undefined; readonly children: ITreeNode[]; readonly depth: number; + readonly visibleChildrenCount: number; + readonly visibleChildIndex: number; readonly collapsible: boolean; readonly collapsed: boolean; readonly visible: boolean; readonly filterData: TFilterData | undefined; } +export interface ICollapseStateChangeEvent { + node: ITreeNode; + deep: boolean; +} + +export interface ITreeModelSpliceEvent { + insertedNodes: ITreeNode[]; + deletedNodes: ITreeNode[]; +} + export interface ITreeModel { - readonly onDidChangeCollapseState: Event>; + readonly rootRef: TRef; + + readonly onDidSplice: Event>; + readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; getListIndex(location: TRef): number; + getListRenderCount(location: TRef): number; getNode(location?: TRef): ITreeNode; getNodeLocation(node: ITreeNode): TRef; getParentNodeLocation(location: TRef): TRef; @@ -100,9 +121,7 @@ export interface ITreeModel { isCollapsible(location: TRef): boolean; isCollapsed(location: TRef): boolean; - setCollapsed(location: TRef, collapsed: boolean): boolean; - toggleCollapsed(location: TRef): void; - collapseAll(): void; + setCollapsed(location: TRef, collapsed?: boolean, recursive?: boolean): boolean; refilter(): void; } @@ -127,3 +146,42 @@ export interface ITreeContextMenuEvent { element: T | null; anchor: HTMLElement | { x: number; y: number; } | undefined; } + +export interface ITreeNavigator { + current(): T | null; + previous(): T | null; + parent(): T | null; + first(): T | null; + last(): T | null; + next(): T | null; +} + +export interface IDataSource { + getChildren(element: TInput | T): T[]; +} + +export interface IAsyncDataSource { + hasChildren(element: TInput | T): boolean; + getChildren(element: TInput | T): T[] | Promise; +} + +export const enum TreeDragOverBubble { + Down, + Up +} + +export interface ITreeDragOverReaction extends IListDragOverReaction { + bubble?: TreeDragOverBubble; + autoExpand?: boolean; +} + +export const TreeDragOverReactions = { + acceptBubbleUp(): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Up }; }, + acceptBubbleDown(autoExpand = false): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand }; }, + acceptCopyBubbleUp(): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Up, effect: ListDragOverEffect.Copy }; }, + acceptCopyBubbleDown(autoExpand = false): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Down, effect: ListDragOverEffect.Copy, autoExpand }; } +}; + +export interface ITreeDragAndDrop extends IListDragAndDrop { + onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | ITreeDragOverReaction; +} diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index dfa3effb4955..93f55a6409ae 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -20,11 +20,11 @@ export interface IAction extends IDisposable { enabled: boolean; checked: boolean; radio: boolean; - run(event?: any): Thenable; + run(event?: any): Promise; } export interface IActionRunner extends IDisposable { - run(action: IAction, context?: any): Thenable; + run(action: IAction, context?: any): Promise; onDidRun: Event; onDidBeforeRun: Event; } @@ -60,9 +60,9 @@ export class Action implements IAction { protected _enabled: boolean; protected _checked: boolean; protected _radio: boolean; - protected _actionCallback?: (event?: any) => Thenable; + protected _actionCallback?: (event?: any) => Promise; - constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: any) => Thenable) { + constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: any) => Promise) { this._id = id; this._label = label; this._cssClass = cssClass; @@ -164,7 +164,7 @@ export class Action implements IAction { } } - run(event?: any, _data?: ITelemetryData): Thenable { + run(event?: any, _data?: ITelemetryData): Promise { if (this._actionCallback) { return this._actionCallback(event); } @@ -191,7 +191,7 @@ export class ActionRunner extends Disposable implements IActionRunner { private _onDidRun = this._register(new Emitter()); readonly onDidRun: Event = this._onDidRun.event; - run(action: IAction, context?: any): Thenable { + run(action: IAction, context?: any): Promise { if (!action.enabled) { return Promise.resolve(null); } @@ -205,7 +205,7 @@ export class ActionRunner extends Disposable implements IActionRunner { }); } - protected runAction(action: IAction, context?: any): Thenable { + protected runAction(action: IAction, context?: any): Promise { const res = context ? action.run(context) : action.run(); return Promise.resolve(res); } diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index b0f144eed26e..50c4e4671d16 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -46,7 +46,7 @@ export function equals(one: ReadonlyArray | undefined, other: ReadonlyArra return true; } -export function binarySearch(array: T[], key: T, comparator: (op1: T, op2: T) => number): number { +export function binarySearch(array: ReadonlyArray, key: T, comparator: (op1: T, op2: T) => number): number { let low = 0, high = array.length - 1; @@ -69,7 +69,7 @@ export function binarySearch(array: T[], key: T, comparator: (op1: T, op2: T) * are located before all elements where p(x) is true. * @returns the least x for which p(x) is true or array.length if no element fullfills the given function. */ -export function findFirstInSorted(array: T[], p: (x: T) => boolean): number { +export function findFirstInSorted(array: ReadonlyArray, p: (x: T) => boolean): number { let low = 0, high = array.length; if (high === 0) { return 0; // no children @@ -135,7 +135,7 @@ function _sort(a: T[], compare: Compare, lo: number, hi: number, aux: T[]) } -export function groupBy(data: T[], compare: (a: T, b: T) => number): T[][] { +export function groupBy(data: ReadonlyArray, compare: (a: T, b: T) => number): T[][] { const result: T[][] = []; let currentGroup: T[] | undefined = undefined; for (const element of mergeSort(data.slice(0), compare)) { @@ -156,7 +156,7 @@ interface IMutableSplice extends ISplice { /** * Diffs two *sorted* arrays and computes the splices which apply the diff. */ -export function sortedDiff(before: T[], after: T[], compare: (a: T, b: T) => number): ISplice[] { +export function sortedDiff(before: ReadonlyArray, after: ReadonlyArray, compare: (a: T, b: T) => number): ISplice[] { const result: IMutableSplice[] = []; function pushSplice(start: number, deleteCount: number, toInsert: T[]): void { @@ -211,11 +211,8 @@ export function sortedDiff(before: T[], after: T[], compare: (a: T, b: T) => /** * Takes two *sorted* arrays and computes their delta (removed, added elements). * Finishes in `Math.min(before.length, after.length)` steps. - * @param before - * @param after - * @param compare */ -export function delta(before: T[], after: T[], compare: (a: T, b: T) => number): { removed: T[], added: T[] } { +export function delta(before: ReadonlyArray, after: ReadonlyArray, compare: (a: T, b: T) => number): { removed: T[], added: T[] } { const splices = sortedDiff(before, after, compare); const removed: T[] = []; const added: T[] = []; @@ -238,7 +235,7 @@ export function delta(before: T[], after: T[], compare: (a: T, b: T) => numbe * @param n The number of elements to return. * @return The first n elemnts from array when sorted with compare. */ -export function top(array: T[], compare: (a: T, b: T) => number, n: number): T[] { +export function top(array: ReadonlyArray, compare: (a: T, b: T) => number, n: number): T[] { if (n === 0) { return []; } @@ -284,7 +281,7 @@ export function topAsync(array: T[], compare: (a: T, b: T) => number, n: numb }); } -function topStep(array: T[], compare: (a: T, b: T) => number, result: T[], i: number, m: number): void { +function topStep(array: ReadonlyArray, compare: (a: T, b: T) => number, result: T[], i: number, m: number): void { for (const n = result.length; i < m; i++) { const element = array[i]; if (compare(element, result[n - 1]) < 0) { @@ -298,7 +295,7 @@ function topStep(array: T[], compare: (a: T, b: T) => number, result: T[], i: /** * @returns a new array with all falsy values removed. The original array IS NOT modified. */ -export function coalesce(array: (T | undefined | null)[]): T[] { +export function coalesce(array: Array): T[] { if (!array) { return array; } @@ -308,7 +305,7 @@ export function coalesce(array: (T | undefined | null)[]): T[] { /** * Remove all falsey values from `array`. The original array IS modified. */ -export function coalesceInPlace(array: (T | undefined | null)[]): void { +export function coalesceInPlace(array: Array): void { if (!array) { return; } @@ -330,15 +327,14 @@ export function move(array: any[], from: number, to: number): void { } /** - * @returns {{false}} if the provided object is an array - * and not empty. + * @returns false if the provided object is an array and not empty. */ export function isFalsyOrEmpty(obj: any): boolean { return !Array.isArray(obj) || obj.length === 0; } /** - * @returns {{true}} if the provided object is an array and has at least one element. + * @returns True if the provided object is an array and has at least one element. */ export function isNonEmptyArray(obj: ReadonlyArray | undefined | null): obj is Array { return Array.isArray(obj) && obj.length > 0; @@ -348,7 +344,7 @@ export function isNonEmptyArray(obj: ReadonlyArray | undefined | null): ob * Removes duplicates from the given array. The optional keyFn allows to specify * how elements are checked for equalness by returning a unique string for each. */ -export function distinct(array: T[], keyFn?: (t: T) => string): T[] { +export function distinct(array: ReadonlyArray, keyFn?: (t: T) => string): T[] { if (!keyFn) { return array.filter((element, position) => { return array.indexOf(element) === position; @@ -383,7 +379,7 @@ export function uniqueFilter(keyFn: (t: T) => string): (t: T) => boolean { }; } -export function firstIndex(array: T[] | ReadonlyArray, fn: (item: T) => boolean): number { +export function firstIndex(array: ReadonlyArray, fn: (item: T) => boolean): number { for (let i = 0; i < array.length; i++) { const element = array[i]; @@ -395,14 +391,14 @@ export function firstIndex(array: T[] | ReadonlyArray, fn: (item: T) => bo return -1; } -export function first(array: T[] | ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; -export function first(array: T[] | ReadonlyArray, fn: (item: T) => boolean): T | null; -export function first(array: T[] | ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T | null = null): T | null { +export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; +export function first(array: ReadonlyArray, fn: (item: T) => boolean): T | null; +export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T | null = null): T | null { const index = firstIndex(array, fn); return index < 0 ? notFoundValue : array[index]; } -export function commonPrefixLength(one: T[], other: T[], equals: (a: T, b: T) => boolean = (a, b) => a === b): number { +export function commonPrefixLength(one: ReadonlyArray, other: ReadonlyArray, equals: (a: T, b: T) => boolean = (a, b) => a === b): number { let result = 0; for (let i = 0, len = Math.min(one.length, other.length); i < len && equals(one[i], other[i]); i++) { @@ -443,17 +439,17 @@ export function range(arg: number, to?: number): number[] { return result; } -export function fill(num: number, valueFn: () => T, arr: T[] = []): T[] { +export function fill(num: number, value: T, arr: T[] = []): T[] { for (let i = 0; i < num; i++) { - arr[i] = valueFn(); + arr[i] = value; } return arr; } -export function index(array: T[], indexer: (t: T) => string): { [key: string]: T; }; -export function index(array: T[], indexer: (t: T) => string, merger?: (t: T, r: R) => R): { [key: string]: R; }; -export function index(array: T[], indexer: (t: T) => string, merger: (t: T, r: R) => R = t => t as any): { [key: string]: R; } { +export function index(array: ReadonlyArray, indexer: (t: T) => string): { [key: string]: T; }; +export function index(array: ReadonlyArray, indexer: (t: T) => string, merger?: (t: T, r: R) => R): { [key: string]: R; }; +export function index(array: ReadonlyArray, indexer: (t: T) => string, merger: (t: T, r: R) => R = t => t as any): { [key: string]: R; } { return array.reduce((r, t) => { const key = indexer(t); r[key] = merger(t, r[key]); @@ -488,7 +484,6 @@ export function arrayInsert(target: T[], insertIndex: number, insertArr: T[]) /** * Uses Fisher-Yates shuffle to shuffle the given array - * @param array */ export function shuffle(array: T[], _seed?: number): void { let rand: () => number; @@ -498,7 +493,7 @@ export function shuffle(array: T[], _seed?: number): void { // Seeded random number generator in JS. Modified from: // https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript rand = () => { - var x = Math.sin(seed++) * 179426549; // throw away most significant digits and reduce any potential bias + const x = Math.sin(seed++) * 179426549; // throw away most significant digits and reduce any potential bias return x - Math.floor(x); }; } else { diff --git a/src/vs/base/common/assert.ts b/src/vs/base/common/assert.ts index f1e0f348c849..b0fe9b5af84f 100644 --- a/src/vs/base/common/assert.ts +++ b/src/vs/base/common/assert.ts @@ -7,7 +7,7 @@ * Throws an error with the provided message if the provided value does not evaluate to a true Javascript value. */ export function ok(value?: any, message?: string) { - if (!value || value === null) { + if (!value) { throw new Error(message ? 'Assertion failed (' + message + ')' : 'Assertion Failed'); } } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index c44b2b68da20..5d3678d0bd09 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -6,18 +6,18 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; 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 { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -export function isThenable(obj: any): obj is Thenable { - return obj && typeof (>obj).then === 'function'; +export function isThenable(obj: any): obj is Promise { + return obj && typeof (>obj).then === 'function'; } export interface CancelablePromise extends Promise { cancel(): void; } -export function createCancelablePromise(callback: (token: CancellationToken) => Thenable): CancelablePromise { +export function createCancelablePromise(callback: (token: CancellationToken) => Promise): CancelablePromise { const source = new CancellationTokenSource(); const thenable = callback(source.token); @@ -38,16 +38,19 @@ export function createCancelablePromise(callback: (token: CancellationToken) cancel() { source.cancel(); } - then(resolve?: ((value: T) => TResult1 | Thenable) | undefined | null, reject?: ((reason: any) => TResult2 | Thenable) | undefined | null): Promise { + then(resolve?: ((value: T) => TResult1 | Promise) | undefined | null, reject?: ((reason: any) => TResult2 | Promise) | undefined | null): Promise { return promise.then(resolve, reject); } - catch(reject?: ((reason: any) => TResult | Thenable) | undefined | null): Promise { + catch(reject?: ((reason: any) => TResult | Promise) | undefined | null): Promise { return this.then(undefined, reject); } + finally(onfinally?: (() => void) | undefined | null): Promise { + return always(promise, onfinally || (() => { })); + } }; } -export function asThenable(callback: () => T | Thenable): Promise { +export function asPromise(callback: () => T | Thenable): Promise { return new Promise((resolve, reject) => { let item = callback(); if (isThenable(item)) { @@ -90,9 +93,9 @@ export interface ITask { */ export class Throttler { - private activePromise: Thenable | null; - private queuedPromise: Thenable | null; - private queuedPromiseFactory: ITask> | null; + private activePromise: Promise | null; + private queuedPromise: Promise | null; + private queuedPromiseFactory: ITask> | null; constructor() { this.activePromise = null; @@ -100,7 +103,7 @@ export class Throttler { this.queuedPromiseFactory = null; } - queue(promiseFactory: ITask>): Thenable { + queue(promiseFactory: ITask>): Promise { if (this.activePromise) { this.queuedPromiseFactory = promiseFactory; @@ -142,7 +145,7 @@ export class Sequencer { private current: Promise = Promise.resolve(null); - queue(promiseTask: ITask>): Thenable { + queue(promiseTask: ITask>): Promise { return this.current = this.current.then(() => promiseTask()); } } @@ -173,10 +176,10 @@ export class Sequencer { export class Delayer implements IDisposable { private timeout: any; - private completionPromise: Thenable | null; - private doResolve: ((value?: any | Thenable) => void) | null; + private completionPromise: Promise | null; + private doResolve: ((value?: any | Promise) => void) | null; private doReject: (err: any) => void; - private task: ITask> | null; + private task: ITask> | null; constructor(public defaultDelay: number) { this.timeout = null; @@ -185,7 +188,7 @@ export class Delayer implements IDisposable { this.task = null; } - trigger(task: ITask>, delay: number = this.defaultDelay): Thenable { + trigger(task: ITask>, delay: number = this.defaultDelay): Promise { this.task = task; this.cancelTimeout(); @@ -247,7 +250,7 @@ export class Delayer implements IDisposable { */ export class ThrottledDelayer { - private delayer: Delayer>; + private delayer: Delayer>; private throttler: Throttler; constructor(defaultDelay: number) { @@ -255,8 +258,8 @@ export class ThrottledDelayer { this.throttler = new Throttler(); } - trigger(promiseFactory: ITask>, delay?: number): Thenable { - return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as any as Thenable; + trigger(promiseFactory: ITask>, delay?: number): Promise { + return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as any as Promise; } isTriggered(): boolean { @@ -303,8 +306,8 @@ export class Barrier { } export function timeout(millis: number): CancelablePromise; -export function timeout(millis: number, token: CancellationToken): Thenable; -export function timeout(millis: number, token?: CancellationToken): CancelablePromise | Thenable { +export function timeout(millis: number, token: CancellationToken): Promise; +export function timeout(millis: number, token?: CancellationToken): CancelablePromise | Promise { if (!token) { return createCancelablePromise(token => timeout(millis, token)); } @@ -318,13 +321,9 @@ export function timeout(millis: number, token?: CancellationToken): CancelablePr }); } -export function disposableTimeout(handler: Function, timeout = 0): IDisposable { +export function disposableTimeout(handler: () => void, timeout = 0): IDisposable { const timer = setTimeout(handler, timeout); - return { - dispose() { - clearTimeout(timer); - } - }; + return toDisposable(() => clearTimeout(timer)); } /** @@ -334,7 +333,7 @@ export function disposableTimeout(handler: Function, timeout = 0): IDisposable { * @param promise a promise * @param callback a function that will be call in the success and error case. */ -export function always(promise: Thenable, callback: () => void): Promise { +export function always(promise: Promise, callback: () => void): Promise { function safeCallback() { try { callback(); @@ -346,7 +345,7 @@ export function always(promise: Thenable, callback: () => void): Promise(promise: Thenable): Thenable { +export function ignoreErrors(promise: Promise): Promise { return promise.then(undefined, _ => undefined); } @@ -355,16 +354,16 @@ export function ignoreErrors(promise: Thenable): Thenable { * promise will complete to an array of results from each promise. */ -export function sequence(promiseFactories: ITask>[]): Promise { +export function sequence(promiseFactories: ITask>[]): Promise { const results: T[] = []; let index = 0; const len = promiseFactories.length; - function next(): Thenable | null { + function next(): Promise | null { return index < len ? promiseFactories[index++]() : null; } - function thenHandler(result: any): Thenable { + function thenHandler(result: any): Promise { if (result !== undefined && result !== null) { results.push(result); } @@ -380,7 +379,7 @@ export function sequence(promiseFactories: ITask>[]): Promise(promiseFactories: ITask>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null): Promise { +export function first(promiseFactories: ITask>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null): Promise { let index = 0; const len = promiseFactories.length; @@ -405,8 +404,8 @@ export function first(promiseFactories: ITask>[], shouldStop: (t: } interface ILimitedTaskFactory { - factory: ITask>; - c: (value?: T | Thenable) => void; + factory: ITask>; + c: (value?: T | Promise) => void; e: (error?: any) => void; } @@ -438,7 +437,7 @@ export class Limiter { // return this.runningPromises + this.outstandingPromises.length; } - queue(factory: ITask>): Thenable { + queue(factory: ITask>): Promise { this._size++; return new Promise((c, e) => { @@ -685,8 +684,8 @@ export function nfcall(fn: Function, ...args: any[]): any { return new Promise((c, e) => fn(...args, (err: any, result: any) => err ? e(err) : c(result))); } -export function ninvoke(thisArg: any, fn: Function, ...args: any[]): Thenable; -export function ninvoke(thisArg: any, fn: Function, ...args: any[]): Thenable; +export function ninvoke(thisArg: any, fn: Function, ...args: any[]): Promise; +export function ninvoke(thisArg: any, fn: Function, ...args: any[]): Promise; export function ninvoke(thisArg: any, fn: Function, ...args: any[]): any { return new Promise((resolve, reject) => fn.call(thisArg, ...args, (err: any, result: any) => err ? reject(err) : resolve(result))); } @@ -712,8 +711,8 @@ declare function cancelIdleCallback(handle: number): void; didTimeout: true, timeRemaining() { return 15; } }); - runWhenIdle = (runner, timeout = 0) => { - let handle = setTimeout(() => runner(dummyIdle), timeout); + runWhenIdle = (runner) => { + let handle = setTimeout(() => runner(dummyIdle)); let disposed = false; return { dispose() { diff --git a/src/vs/base/common/buildunit.json b/src/vs/base/common/buildunit.json index fecb5c6eac67..50e3d7506767 100644 --- a/src/vs/base/common/buildunit.json +++ b/src/vs/base/common/buildunit.json @@ -1,7 +1,10 @@ { "name": "vs/base", "dependencies": [ - { "name": "vs", "internal": false } + { + "name": "vs", + "internal": false + } ], "libs": [ "lib.core.d.ts" @@ -9,7 +12,5 @@ "sources": [ "**/*.ts" ], - "declares": [ - "vs/base/winjs.base.d.ts" - ] -} \ No newline at end of file + "declares": [] +} diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index cad8685c70d8..a861efe66484 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -58,7 +58,7 @@ export function first(from: IStringDictionary | INumberDictionary): T | * Iterates over each entry in the provided set. The iterator allows * to remove elements and will stop when the callback returns {{false}}. */ -export function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: Function) => any): void { +export function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any): void { for (let key in from) { if (hasOwnProperty.call(from, key)) { const result = callback({ key: key, value: (from as any)[key] }, function () { diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index 802b3bc68faf..fa4792df1016 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -388,7 +388,7 @@ export class Color { const colorA = rgba.a; let a = thisA + colorA * (1 - thisA); - if (a < 1.0e-6) { + if (a < 1e-6) { return Color.transparent; } diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index 296d9b28336f..3d3f3fd7f079 100644 --- a/src/vs/base/common/comparers.ts +++ b/src/vs/base/common/comparers.ts @@ -13,7 +13,7 @@ export function setFileNameComparer(collator: IdleValue<{ collator: Intl.Collato intlFileNameCollator = collator; } -export function compareFileNames(one: string, other: string, caseSensitive = false): number { +export function compareFileNames(one: string | null, other: string | null, caseSensitive = false): number { if (intlFileNameCollator) { const a = one || ''; const b = other || ''; @@ -33,7 +33,7 @@ export function compareFileNames(one: string, other: string, caseSensitive = fal const FileNameMatch = /^(.*?)(\.([^.]*))?$/; -export function noIntlCompareFileNames(one: string, other: string, caseSensitive = false): number { +export function noIntlCompareFileNames(one: string | null, other: string | null, caseSensitive = false): number { if (!caseSensitive) { one = one && one.toLowerCase(); other = other && other.toLowerCase(); @@ -53,7 +53,7 @@ export function noIntlCompareFileNames(one: string, other: string, caseSensitive return oneExtension < otherExtension ? -1 : 1; } -export function compareFileExtensions(one: string, other: string): number { +export function compareFileExtensions(one: string | null, other: string | null): number { if (intlFileNameCollator) { const [oneName, oneExtension] = extractNameAndExtension(one); const [otherName, otherExtension] = extractNameAndExtension(other); @@ -81,7 +81,7 @@ export function compareFileExtensions(one: string, other: string): number { return noIntlCompareFileExtensions(one, other); } -function noIntlCompareFileExtensions(one: string, other: string): number { +function noIntlCompareFileExtensions(one: string | null, other: string | null): number { const [oneName, oneExtension] = extractNameAndExtension(one && one.toLowerCase()); const [otherName, otherExtension] = extractNameAndExtension(other && other.toLowerCase()); @@ -96,7 +96,7 @@ function noIntlCompareFileExtensions(one: string, other: string): number { return oneName < otherName ? -1 : 1; } -function extractNameAndExtension(str?: string): [string, string] { +function extractNameAndExtension(str?: string | null): [string, string] { const match = str ? FileNameMatch.exec(str) as Array : ([] as Array); return [(match && match[1]) || '', (match && match[3]) || '']; diff --git a/src/vs/base/common/decorators.ts b/src/vs/base/common/decorators.ts index 38bd3cb219f5..8a9cc8d70cbc 100644 --- a/src/vs/base/common/decorators.ts +++ b/src/vs/base/common/decorators.ts @@ -71,7 +71,7 @@ export function debounce(delay: number, reducer?: IDebouceReducer, initial return function (this: any, ...args: any[]) { if (!this[resultKey]) { - this[resultKey] = initialValueProvider ? initialValueProvider() : void 0; + this[resultKey] = initialValueProvider ? initialValueProvider() : undefined; } clearTimeout(this[timerKey]); @@ -83,7 +83,7 @@ export function debounce(delay: number, reducer?: IDebouceReducer, initial this[timerKey] = setTimeout(() => { fn.apply(this, args); - this[resultKey] = initialValueProvider ? initialValueProvider() : void 0; + this[resultKey] = initialValueProvider ? initialValueProvider() : undefined; }, delay); }; }); diff --git a/src/vs/base/common/diff/diff.ts b/src/vs/base/common/diff/diff.ts index 89fc2f8d8fe6..d3a607384161 100644 --- a/src/vs/base/common/diff/diff.ts +++ b/src/vs/base/common/diff/diff.ts @@ -763,7 +763,7 @@ export class LcsDiff { change.modifiedStart++; } - let mergedChangeArr: (DiffChange | null)[] = [null]; + let mergedChangeArr: Array = [null]; if (i < changes.length - 1 && this.ChangesOverlap(changes[i], changes[i + 1], mergedChangeArr)) { changes[i] = mergedChangeArr[0]!; changes.splice(i + 1, 1); @@ -913,7 +913,7 @@ export class LcsDiff { * @param mergedChange The merged change if the two overlap, null otherwise * @returns True if the two changes overlap */ - private ChangesOverlap(left: DiffChange, right: DiffChange, mergedChangeArr: (DiffChange | null)[]): boolean { + private ChangesOverlap(left: DiffChange, right: DiffChange, mergedChangeArr: Array): boolean { Debug.Assert(left.originalStart <= right.originalStart, 'Left change is not less than or equal to right change'); Debug.Assert(left.modifiedStart <= right.modifiedStart, 'Left change is not less than or equal to right change'); diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index f774d107b792..342a8803a2f8 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -3,59 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise, IPromiseError, IPromiseErrorDetail } from 'vs/base/common/winjs.base'; - -// ------ BEGIN Hook up error listeners to winjs promises - -let outstandingPromiseErrors: { [id: string]: IPromiseErrorDetail; } = {}; -function promiseErrorHandler(e: IPromiseError): void { - - // - // e.detail looks like: { exception, error, promise, handler, id, parent } - // - const details = e.detail; - const id = details.id; - - // If the error has a parent promise then this is not the origination of the - // error so we check if it has a handler, and if so we mark that the error - // was handled by removing it from outstandingPromiseErrors - // - if (details.parent) { - if (details.handler && outstandingPromiseErrors) { - delete outstandingPromiseErrors[id]; - } - return; - } - - // Indicate that this error was originated and needs to be handled - outstandingPromiseErrors[id] = details; - - // The first time the queue fills up this iteration, schedule a timeout to - // check if any errors are still unhandled. - if (Object.keys(outstandingPromiseErrors).length === 1) { - setTimeout(function () { - const errors = outstandingPromiseErrors; - outstandingPromiseErrors = {}; - Object.keys(errors).forEach(function (errorId) { - const error = errors[errorId]; - if (error.exception) { - onUnexpectedError(error.exception); - } else if (error.error) { - onUnexpectedError(error.error); - } - console.log('WARNING: Promise with no error callback:' + error.id); - console.log(error); - if (error.exception) { - console.log(error.exception.stack); - } - }); - }, 0); - } -} -TPromise.addEventListener('error', promiseErrorHandler); - -// ------ END Hook up error listeners to winjs promises - export interface ErrorListenerCallback { (error: any): void; } diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index b0f5b2295e87..272dd2e65927 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -19,9 +19,348 @@ export interface Event { export namespace Event { const _disposable = { dispose() { } }; export const None: Event = function () { return _disposable; }; + + /** + * Given an event, returns another event which only fires once. + */ + export function once(event: Event): Event { + return (listener, thisArgs = null, disposables?) => { + // we need this, in case the event fires during the listener call + let didFire = false; + + const result = event(e => { + if (didFire) { + return; + } else if (result) { + result.dispose(); + } else { + didFire = true; + } + + return listener.call(thisArgs, e); + }, null, disposables); + + if (didFire) { + result.dispose(); + } + + return result; + }; + } + + /** + * Given an event and a `map` function, returns another event which maps each element + * throught the mapping function. + */ + export function map(event: Event, map: (i: I) => O): Event { + return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables); + } + + /** + * Given an event and an `each` function, returns another identical event and calls + * the `each` function per each element. + */ + export function forEach(event: Event, each: (i: I) => void): Event { + return (listener, thisArgs = null, disposables?) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables); + } + + /** + * Given an event and a `filter` function, returns another event which emits those + * elements for which the `filter` function returns `true`. + */ + export function filter(event: Event, filter: (e: T) => boolean): Event; + export function filter(event: Event, filter: (e: T | R) => e is R): Event; + export function filter(event: Event, filter: (e: T) => boolean): Event { + return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); + } + + /** + * Given an event, returns the same event but typed as `Event`. + */ + export function signal(event: Event): Event { + return event as Event as Event; + } + + /** + * Given a collection of events, returns a single event which emits + * whenever any of the provided events emit. + */ + export function any(...events: Event[]): Event { + return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(e => listener.call(thisArgs, e), null, disposables))); + } + + /** + * Given an event and a `merge` function, returns another event which maps each element + * and the cummulative result throught the `merge` function. Similar to `map`, but with memory. + */ + export function reduce(event: Event, merge: (last: O | undefined, event: I) => O, initial?: O): Event { + let output: O | undefined = initial; + + return map(event, e => { + output = merge(output, e); + return output; + }); + } + + /** + * Debounces the provided event, given a `merge` function. + * + * @param event The input event. + * @param merge The reducing function. + * @param delay The debouncing delay in millis. + * @param leading Whether the event should fire in the leading phase of the timeout. + * @param leakWarningThreshold The leak warning threshold override. + */ + export function debounce(event: Event, merge: (last: T, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event; + export function debounce(event: Event, merge: (last: O | undefined, event: I) => O, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event; + export function debounce(event: Event, merge: (last: O | undefined, event: I) => O, delay: number = 100, leading = false, leakWarningThreshold?: number): Event { + + let subscription: IDisposable; + let output: O | undefined = undefined; + let handle: any = undefined; + let numDebouncedCalls = 0; + + const emitter = new Emitter({ + leakWarningThreshold, + onFirstListenerAdd() { + subscription = event(cur => { + numDebouncedCalls++; + output = merge(output, cur); + + if (leading && !handle) { + emitter.fire(output); + } + + clearTimeout(handle); + handle = setTimeout(() => { + let _output = output; + output = undefined; + handle = undefined; + if (!leading || numDebouncedCalls > 1) { + emitter.fire(_output!); + } + + numDebouncedCalls = 0; + }, delay); + }); + }, + onLastListenerRemove() { + subscription.dispose(); + } + }); + + return emitter.event; + } + + /** + * Given an event, it returns another event which fires only once and as soon as + * the input event emits. The event data is the number of millis it took for the + * event to fire. + */ + export function stopwatch(event: Event): Event { + const start = new Date().getTime(); + return map(once(event), _ => new Date().getTime() - start); + } + + /** + * Given an event, it returns another event which fires only when the event + * element changes. + */ + export function latch(event: Event): Event { + let firstCall = true; + let cache: T; + + return filter(event, value => { + let shouldEmit = firstCall || value !== cache; + firstCall = false; + cache = value; + return shouldEmit; + }); + } + + /** + * Buffers the provided event until a first listener comes + * along, at which point fire all the events at once and + * pipe the event from then on. + * + * ```typescript + * const emitter = new Emitter(); + * const event = emitter.event; + * const bufferedEvent = buffer(event); + * + * emitter.fire(1); + * emitter.fire(2); + * emitter.fire(3); + * // nothing... + * + * const listener = bufferedEvent(num => console.log(num)); + * // 1, 2, 3 + * + * emitter.fire(4); + * // 4 + * ``` + */ + export function buffer(event: Event, nextTick = false, _buffer: T[] = []): Event { + let buffer: T[] | null = _buffer.slice(); + + let listener: IDisposable | null = event(e => { + if (buffer) { + buffer.push(e); + } else { + emitter.fire(e); + } + }); + + const flush = () => { + if (buffer) { + buffer.forEach(e => emitter.fire(e)); + } + buffer = null; + }; + + const emitter = new Emitter({ + onFirstListenerAdd() { + if (!listener) { + listener = event(e => emitter.fire(e)); + } + }, + + onFirstListenerDidAdd() { + if (buffer) { + if (nextTick) { + setTimeout(flush); + } else { + flush(); + } + } + }, + + onLastListenerRemove() { + if (listener) { + listener.dispose(); + } + listener = null; + } + }); + + return emitter.event; + } + + /** + * Similar to `buffer` but it buffers indefinitely and repeats + * the buffered events to every new listener. + */ + export function echo(event: Event, nextTick = false, buffer: T[] = []): Event { + buffer = buffer.slice(); + + event(e => { + buffer.push(e); + emitter.fire(e); + }); + + const flush = (listener: (e: T) => any, thisArgs?: any) => buffer.forEach(e => listener.call(thisArgs, e)); + + const emitter = new Emitter({ + onListenerDidAdd(emitter, listener: (e: T) => any, thisArgs?: any) { + if (nextTick) { + setTimeout(() => flush(listener, thisArgs)); + } else { + flush(listener, thisArgs); + } + } + }); + + return emitter.event; + } + + export interface IChainableEvent { + event: Event; + map(fn: (i: T) => O): IChainableEvent; + forEach(fn: (i: T) => void): IChainableEvent; + filter(fn: (e: T) => boolean): IChainableEvent; + reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent; + latch(): IChainableEvent; + on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; + once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; + } + + class ChainableEvent implements IChainableEvent { + + get event(): Event { return this._event; } + + constructor(private _event: Event) { } + + map(fn: (i: T) => O): IChainableEvent { + return new ChainableEvent(map(this._event, fn)); + } + + forEach(fn: (i: T) => void): IChainableEvent { + return new ChainableEvent(forEach(this._event, fn)); + } + + filter(fn: (e: T) => boolean): IChainableEvent { + return new ChainableEvent(filter(this._event, fn)); + } + + reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent { + return new ChainableEvent(reduce(this._event, merge, initial)); + } + + latch(): IChainableEvent { + return new ChainableEvent(latch(this._event)); + } + + on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { + return this._event(listener, thisArgs, disposables); + } + + once(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { + return once(this._event)(listener, thisArgs, disposables); + } + } + + export function chain(event: Event): IChainableEvent { + return new ChainableEvent(event); + } + + export interface NodeEventEmitter { + on(event: string | symbol, listener: Function): this; + removeListener(event: string | symbol, listener: Function): this; + } + + export function fromNodeEventEmitter(emitter: NodeEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event { + const fn = (...args: any[]) => result.fire(map(...args)); + const onFirstListenerAdd = () => emitter.on(eventName, fn); + const onLastListenerRemove = () => emitter.removeListener(eventName, fn); + const result = new Emitter({ onFirstListenerAdd, onLastListenerRemove }); + + return result.event; + } + + export function fromPromise(promise: Promise): Event { + const emitter = new Emitter(); + let shouldEmit = false; + + promise + .then(undefined, () => null) + .then(() => { + if (!shouldEmit) { + setTimeout(() => emitter.fire(undefined), 0); + } else { + emitter.fire(undefined); + } + }); + + shouldEmit = true; + return emitter.event; + } + + export function toPromise(event: Event): Promise { + return new Promise(c => once(event)(c)); + } } -type Listener = [Function, any] | Function; +type Listener = [(e: T) => void, any] | ((e: T) => void); export interface EmitterOptions { onFirstListenerAdd?: Function; @@ -80,7 +419,7 @@ class LeakageMonitor { if (this._warnCountdown <= 0) { // only warn on first exceed and then every time the limit // is exceeded by 50% again - this._warnCountdown = threshold * .5; + this._warnCountdown = threshold * 0.5; // find most frequent listener and print warning let topStack: string; @@ -128,12 +467,12 @@ export class Emitter { private static readonly _noop = function () { }; - private readonly _options: EmitterOptions | undefined; - private readonly _leakageMon: LeakageMonitor | undefined; + private readonly _options?: EmitterOptions; + private readonly _leakageMon?: LeakageMonitor; private _disposed: boolean = false; - private _event: Event | undefined; - private _deliveryQueue: [Listener, (T | undefined)][] | undefined; - protected _listeners: LinkedList | undefined; + private _event?: Event; + private _deliveryQueue: [Listener, T][]; + protected _listeners?: LinkedList>; constructor(options?: EmitterOptions) { this._options = options; @@ -207,7 +546,7 @@ export class Emitter { * To be kept private to fire an event to * subscribers */ - fire(event?: T): any { + fire(event: T): void { if (this._listeners) { // put all [listener,event]-pairs into delivery queue // then emit all event. an inner/nested event might be @@ -251,14 +590,14 @@ export class Emitter { } export interface IWaitUntil { - waitUntil(thenable: Thenable): void; + waitUntil(thenable: Promise): void; } export class AsyncEmitter extends Emitter { - private _asyncDeliveryQueue: [Listener, T, Thenable[]][]; + private _asyncDeliveryQueue: [Listener, T, Promise[]][]; - async fireAsync(eventFn: (thenables: Thenable[], listener: Function) => T): Promise { + async fireAsync(eventFn: (thenables: Promise[], listener: Function) => T): Promise { if (!this._listeners) { return; } @@ -271,7 +610,7 @@ export class AsyncEmitter extends Emitter { } for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) { - let thenables: Thenable[] = []; + let thenables: Promise[] = []; this._asyncDeliveryQueue.push([e.value, eventFn(thenables, typeof e.value === 'function' ? e.value : e.value[0]), thenables]); } @@ -359,98 +698,6 @@ export class EventMultiplexer implements IDisposable { } } -export function fromPromise(promise: Thenable): Event { - const emitter = new Emitter(); - let shouldEmit = false; - - promise - .then(undefined, () => null) - .then(() => { - if (!shouldEmit) { - setTimeout(() => emitter.fire(), 0); - } else { - emitter.fire(); - } - }); - - shouldEmit = true; - return emitter.event; -} - -export function toPromise(event: Event): Thenable { - return new Promise(c => once(event)(c)); -} - -export function once(event: Event): Event { - return (listener, thisArgs = null, disposables?) => { - // we need this, in case the event fires during the listener call - let didFire = false; - - const result = event(e => { - if (didFire) { - return; - } else if (result) { - result.dispose(); - } else { - didFire = true; - } - - return listener.call(thisArgs, e); - }, null, disposables); - - if (didFire) { - result.dispose(); - } - - return result; - }; -} - -export function anyEvent(...events: Event[]): Event { - return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(e => listener.call(thisArgs, e), null, disposables))); -} - -export function debounceEvent(event: Event, merger: (last: T, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event; -export function debounceEvent(event: Event, merger: (last: O | undefined, event: I) => O, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event; -export function debounceEvent(event: Event, merger: (last: O | undefined, event: I) => O, delay: number = 100, leading = false, leakWarningThreshold?: number): Event { - - let subscription: IDisposable; - let output: O | undefined = undefined; - let handle: any = undefined; - let numDebouncedCalls = 0; - - const emitter = new Emitter({ - leakWarningThreshold, - onFirstListenerAdd() { - subscription = event(cur => { - numDebouncedCalls++; - output = merger(output, cur); - - if (leading && !handle) { - emitter.fire(output); - } - - clearTimeout(handle); - handle = setTimeout(() => { - let _output = output; - output = undefined; - handle = undefined; - if (!leading || numDebouncedCalls > 1) { - emitter.fire(_output); - } - - numDebouncedCalls = 0; - }, delay); - }); - }, - onLastListenerRemove() { - subscription.dispose(); - } - }); - - return emitter.event; -} - /** * The EventBufferer is useful in situations in which you want * to delay firing your events during some code. @@ -485,12 +732,12 @@ export class EventBufferer { } else { listener.call(thisArgs, i); } - }, void 0, disposables); + }, undefined, disposables); }; } bufferEvents(fn: () => R): R { - const buffer: Function[] = []; + const buffer: Array<() => R> = []; this.buffers.push(buffer); const r = fn(); this.buffers.pop(); @@ -499,169 +746,12 @@ export class EventBufferer { } } -export interface IChainableEvent { - event: Event; - map(fn: (i: T) => O): IChainableEvent; - forEach(fn: (i: T) => void): IChainableEvent; - filter(fn: (e: T) => boolean): IChainableEvent; - latch(): IChainableEvent; - on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; - once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; -} - -export function mapEvent(event: Event, map: (i: I) => O): Event { - return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables); -} - -export function forEach(event: Event, each: (i: I) => void): Event { - return (listener, thisArgs = null, disposables?) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables); -} - -export function filterEvent(event: Event, filter: (e: T) => boolean): Event; -export function filterEvent(event: Event, filter: (e: T | R) => e is R): Event; -export function filterEvent(event: Event, filter: (e: T) => boolean): Event { - return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); -} - -export function signalEvent(event: Event): Event { - return event as Event as Event; -} - -class ChainableEvent implements IChainableEvent { - - get event(): Event { return this._event; } - - constructor(private _event: Event) { } - - map(fn: (i: T) => O): IChainableEvent { - return new ChainableEvent(mapEvent(this._event, fn)); - } - - forEach(fn: (i: T) => void): IChainableEvent { - return new ChainableEvent(forEach(this._event, fn)); - } - - filter(fn: (e: T) => boolean): IChainableEvent { - return new ChainableEvent(filterEvent(this._event, fn)); - } - - latch(): IChainableEvent { - return new ChainableEvent(latch(this._event)); - } - - on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { - return this._event(listener, thisArgs, disposables); - } - - once(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { - return once(this._event)(listener, thisArgs, disposables); - } -} - -export function chain(event: Event): IChainableEvent { - return new ChainableEvent(event); -} - -export function stopwatch(event: Event): Event { - const start = new Date().getTime(); - return mapEvent(once(event), _ => new Date().getTime() - start); -} - /** - * Buffers the provided event until a first listener comes - * along, at which point fire all the events at once and - * pipe the event from then on. - * - * ```typescript - * const emitter = new Emitter(); - * const event = emitter.event; - * const bufferedEvent = buffer(event); - * - * emitter.fire(1); - * emitter.fire(2); - * emitter.fire(3); - * // nothing... - * - * const listener = bufferedEvent(num => console.log(num)); - * // 1, 2, 3 - * - * emitter.fire(4); - * // 4 - * ``` + * A Relay is an event forwarder which functions as a replugabble event pipe. + * Once created, you can connect an input event to it and it will simply forward + * events from that input event through its own `event` property. The `input` + * can be changed at any point in time. */ -export function buffer(event: Event, nextTick = false, _buffer: T[] = []): Event { - let buffer: T[] | null = _buffer.slice(); - - let listener: IDisposable | null = event(e => { - if (buffer) { - buffer.push(e); - } else { - emitter.fire(e); - } - }); - - const flush = () => { - if (buffer) { - buffer.forEach(e => emitter.fire(e)); - } - buffer = null; - }; - - const emitter = new Emitter({ - onFirstListenerAdd() { - if (!listener) { - listener = event(e => emitter.fire(e)); - } - }, - - onFirstListenerDidAdd() { - if (buffer) { - if (nextTick) { - setTimeout(flush); - } else { - flush(); - } - } - }, - - onLastListenerRemove() { - if (listener) { - listener.dispose(); - } - listener = null; - } - }); - - return emitter.event; -} - -/** - * Similar to `buffer` but it buffers indefinitely and repeats - * the buffered events to every new listener. - */ -export function echo(event: Event, nextTick = false, buffer: T[] = []): Event { - buffer = buffer.slice(); - - event(e => { - buffer.push(e); - emitter.fire(e); - }); - - const flush = (listener: (e: T) => any, thisArgs?: any) => buffer.forEach(e => listener.call(thisArgs, e)); - - const emitter = new Emitter({ - onListenerDidAdd(emitter, listener: (e: T) => any, thisArgs?: any) { - if (nextTick) { - setTimeout(() => flush(listener, thisArgs)); - } else { - flush(listener, thisArgs); - } - } - }); - - return emitter.event; -} - export class Relay implements IDisposable { private listening = false; @@ -695,29 +785,3 @@ export class Relay implements IDisposable { this.emitter.dispose(); } } - -export interface NodeEventEmitter { - on(event: string | symbol, listener: Function): this; - removeListener(event: string | symbol, listener: Function): this; -} - -export function fromNodeEventEmitter(emitter: NodeEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event { - const fn = (...args: any[]) => result.fire(map(...args)); - const onFirstListenerAdd = () => emitter.on(eventName, fn); - const onLastListenerRemove = () => emitter.removeListener(eventName, fn); - const result = new Emitter({ onFirstListenerAdd, onLastListenerRemove }); - - return result.event; -} - -export function latch(event: Event): Event { - let firstCall = true; - let cache: T; - - return filterEvent(event, value => { - let shouldEmit = firstCall || value !== cache; - firstCall = false; - cache = value; - return shouldEmit; - }); -} diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 72cb12d56a55..42e34577d4a9 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -317,7 +317,7 @@ function nextWord(word: string, start: number): number { // Fuzzy -export const fuzzyContiguousFilter = or(matchesPrefix, matchesCamelCase, matchesContiguousSubString); +const fuzzyContiguousFilter = or(matchesPrefix, matchesCamelCase, matchesContiguousSubString); const fuzzySeparateFilter = or(matchesPrefix, matchesCamelCase, matchesSubString); const fuzzyRegExpCache = new LRUCache(10000); // bounded to 10000 elements @@ -343,54 +343,67 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep return enableSeparateSubstringMatching ? fuzzySeparateFilter(word, wordToMatchAgainst) : fuzzyContiguousFilter(word, wordToMatchAgainst); } -export function anyScore(pattern: string, word: string, patternMaxWhitespaceIgnore?: number): FuzzyScore { - pattern = pattern.toLowerCase(); - word = word.toLowerCase(); - - const matches: number[] = []; - let idx = 0; - for (let pos = 0; pos < pattern.length; ++pos) { - const thisIdx = word.indexOf(pattern.charAt(pos), idx); - if (thisIdx >= 0) { - matches.push(thisIdx); - idx = thisIdx + 1; +/** + * Match pattern againt word in a fuzzy way. As in IntelliSense and faster and more + * powerfull than `matchesFuzzy` + */ +export function matchesFuzzy2(pattern: string, word: string): IMatch[] | null { + let score = fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, true); + return score ? createMatches(score) : null; +} + +export function anyScore(pattern: string, lowPattern: string, _patternPos: number, word: string, lowWord: string, _wordPos: number): FuzzyScore { + const result = fuzzyScore(pattern, lowPattern, 0, word, lowWord, 0, true); + if (result) { + return result; + } + let matches = 0; + let score = 0; + let idx = _wordPos; + for (let patternPos = 0; patternPos < lowPattern.length && patternPos < _maxLen; ++patternPos) { + const wordPos = lowWord.indexOf(lowPattern.charAt(patternPos), idx); + if (wordPos >= 0) { + score += 1; + matches += 2 ** wordPos; + idx = wordPos + 1; } } - return [matches.length, matches]; + return [score, matches, _wordPos]; } //#region --- fuzzyScore --- -export function createMatches(offsetOrScore: number[] | FuzzyScore): IMatch[] { - let ret: IMatch[] = []; - if (!offsetOrScore) { - return ret; - } - let offsets: number[]; - if (Array.isArray(offsetOrScore[1])) { - offsets = (offsetOrScore as FuzzyScore)[1]; - } else { - offsets = offsetOrScore as number[]; +export function createMatches(score: undefined | FuzzyScore): IMatch[] { + if (typeof score === 'undefined') { + return []; } - let last: IMatch | undefined; - for (const pos of offsets) { - if (last && last.end === pos) { - last.end += 1; - } else { - last = { start: pos, end: pos + 1 }; - ret.push(last); + + const matches = score[1].toString(2); + const wordStart = score[2]; + const res: IMatch[] = []; + + for (let pos = wordStart; pos < _maxLen; pos++) { + if (matches[matches.length - (pos + 1)] === '1') { + const last = res[res.length - 1]; + if (last && last.end === pos) { + last.end = pos + 1; + } else { + res.push({ start: pos, end: pos + 1 }); + } } } - return ret; + return res; } +const _maxLen = 53; + function initTable() { const table: number[][] = []; const row: number[] = [0]; - for (let i = 1; i <= 100; i++) { + for (let i = 1; i <= _maxLen; i++) { row.push(-i); } - for (let i = 0; i <= 100; i++) { + for (let i = 0; i <= _maxLen; i++) { let thisRow = row.slice(0); thisRow[0] = -i; table.push(thisRow); @@ -438,6 +451,7 @@ function isSeparatorAtPos(value: string, index: number): boolean { case CharCode.SingleQuote: case CharCode.DoubleQuote: case CharCode.Colon: + case CharCode.DollarSign: return true; default: return false; @@ -458,18 +472,49 @@ function isWhitespaceAtPos(value: string, index: number): boolean { } } +function isUpperCaseAtPos(pos: number, word: string, wordLow: string): boolean { + return word[pos] !== wordLow[pos]; +} + +function isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean { + while (patternPos < patternLen && wordPos < wordLen) { + if (patternLow[patternPos] === wordLow[wordPos]) { + patternPos += 1; + } + wordPos += 1; + } + return patternPos === patternLen; // pattern must be exhausted +} + const enum Arrow { Top = 0b1, Diag = 0b10, Left = 0b100 } -export type FuzzyScore = [number, number[]]; +/** + * A tuple of three values. + * 0. the score + * 1. the matches encoded as bitmask (2^53) + * 2. the offset at which matching started + */ +export type FuzzyScore = [number, number, number]; + +export namespace FuzzyScore { + /** + * No matches and value `-100` + */ + export const Default: [-100, 0, 0] = [-100, 0, 0]; + + export function isDefault(score?: FuzzyScore): score is [-100, 0, 0] { + return !score || (score[0] === -100 && score[1] === 0 && score[2] === 0); + } +} export interface FuzzyScorer { (pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined; } -export function fuzzyScore(pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined { +export function fuzzyScore(pattern: string, patternLow: string, patternPos: number, word: string, wordLow: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined { - const patternLen = pattern.length > 100 ? 100 : pattern.length; - const wordLen = word.length > 100 ? 100 : word.length; + const patternLen = pattern.length > _maxLen ? _maxLen : pattern.length; + const wordLen = word.length > _maxLen ? _maxLen : word.length; if (patternPos >= patternLen || wordPos >= wordLen || patternLen > wordLen) { return undefined; @@ -478,20 +523,12 @@ export function fuzzyScore(pattern: string, lowPattern: string, patternPos: numb // Run a simple check if the characters of pattern occur // (in order) at all in word. If that isn't the case we // stop because no match will be possible - const patternStartPos = patternPos; - const wordStartPos = wordPos; - while (patternPos < patternLen && wordPos < wordLen) { - if (lowPattern[patternPos] === lowWord[wordPos]) { - patternPos += 1; - } - wordPos += 1; - } - if (patternPos !== patternLen) { + if (!isPatternInWord(patternLow, patternPos, patternLen, wordLow, wordPos, wordLen)) { return undefined; } - patternPos = patternStartPos; - wordPos = wordStartPos; + const patternStartPos = patternPos; + const wordStartPos = wordPos; // There will be a mach, fill in tables for (patternPos = patternStartPos + 1; patternPos <= patternLen; patternPos++) { @@ -499,24 +536,27 @@ export function fuzzyScore(pattern: string, lowPattern: string, patternPos: numb for (wordPos = 1; wordPos <= wordLen; wordPos++) { let score = -1; - let lowWordChar = lowWord[wordPos - 1]; - if (lowPattern[patternPos - 1] === lowWordChar) { + if (patternLow[patternPos - 1] === wordLow[wordPos - 1]) { + if (wordPos === (patternPos - patternStartPos)) { // common prefix: `foobar <-> foobaz` + // ^^^^^ if (pattern[patternPos - 1] === word[wordPos - 1]) { score = 7; } else { score = 5; } - } else if (lowWordChar !== word[wordPos - 1] && (wordPos === 1 || lowWord[wordPos - 2] === word[wordPos - 2])) { + } else if (isUpperCaseAtPos(wordPos - 1, word, wordLow) && (wordPos === 1 || !isUpperCaseAtPos(wordPos - 2, word, wordLow))) { // hitting upper-case: `foo <-> forOthers` + // ^^ ^ if (pattern[patternPos - 1] === word[wordPos - 1]) { score = 7; } else { score = 5; } - } else if (isSeparatorAtPos(lowWord, wordPos - 2) || isWhitespaceAtPos(lowWord, wordPos - 2)) { + } else if (isSeparatorAtPos(wordLow, wordPos - 2) || isWhitespaceAtPos(wordLow, wordPos - 2)) { // post separator: `foo <-> bar_foo` + // ^^^ score = 5; } else { @@ -564,29 +604,26 @@ export function fuzzyScore(pattern: string, lowPattern: string, patternPos: numb console.log(printTable(_scores, pattern, patternLen, word, wordLen)); } - // _bucket is an array of [PrefixArray] we use to keep - // track of scores and matches. After calling `_findAllMatches` - // the best match (if available) is the first item in the array _matchesCount = 0; _topScore = -100; _patternStartPos = patternStartPos; _firstMatchCanBeWeak = firstMatchCanBeWeak; - _findAllMatches(patternLen, wordLen, patternLen === wordLen ? 1 : 0, new LazyArray(), false); - + _findAllMatches2(patternLen, wordLen, patternLen === wordLen ? 1 : 0, 0, false); if (_matchesCount === 0) { return undefined; } - return [_topScore, _topMatch.toArray()]; + return [_topScore, _topMatch2, wordStartPos]; } + let _matchesCount: number = 0; -let _topMatch: LazyArray; +let _topMatch2: number = 0; let _topScore: number = 0; let _patternStartPos: number = 0; let _firstMatchCanBeWeak: boolean = false; -function _findAllMatches(patternPos: number, wordPos: number, total: number, matches: LazyArray, lastMatched: boolean): void { +function _findAllMatches2(patternPos: number, wordPos: number, total: number, matches: number, lastMatched: boolean): void { if (_matchesCount >= 10 || total < -25) { // stop when having already 10 results, or @@ -602,11 +639,11 @@ function _findAllMatches(patternPos: number, wordPos: number, total: number, mat let arrow = _arrows[patternPos][wordPos]; if (arrow === Arrow.Left) { - // left + // left -> no match, skip a word character wordPos -= 1; if (lastMatched) { total -= 5; // new gap penalty - } else if (!matches.isEmpty()) { + } else if (matches !== 0) { total -= 1; // gap penalty after first match } lastMatched = false; @@ -616,11 +653,11 @@ function _findAllMatches(patternPos: number, wordPos: number, total: number, mat if (arrow & Arrow.Left) { // left - _findAllMatches( + _findAllMatches2( patternPos, wordPos - 1, - !matches.isEmpty() ? total - 1 : total, // gap penalty after first match - matches.slice(), + matches !== 0 ? total - 1 : total, // gap penalty after first match + matches, lastMatched ); } @@ -629,9 +666,11 @@ function _findAllMatches(patternPos: number, wordPos: number, total: number, mat total += score; patternPos -= 1; wordPos -= 1; - matches.unshift(wordPos); lastMatched = true; + // match -> set a 1 at the word pos + matches += 2 ** wordPos; + // count simple matches and boost a row of // simple matches when they yield in a // strong match. @@ -662,47 +701,7 @@ function _findAllMatches(patternPos: number, wordPos: number, total: number, mat _matchesCount += 1; if (total > _topScore) { _topScore = total; - _topMatch = matches; - } -} - -class LazyArray { - - private _parent: LazyArray; - private _parentLen: number; - private _data: number[]; - - isEmpty(): boolean { - return !this._data && (!this._parent || this._parent.isEmpty()); - } - - unshift(n: number) { - if (!this._data) { - this._data = [n]; - } else { - this._data.unshift(n); - } - } - - slice(): LazyArray { - const ret = new LazyArray(); - ret._parent = this; - ret._parentLen = this._data ? this._data.length : 0; return ret; - } - - toArray(): number[] { - if (!this._data) { - return this._parent.toArray(); - } - const bucket: number[][] = []; - let element = this; - while (element) { - if (element._parent && element._parent._data) { - bucket.push(element._parent._data.slice(element._parent._data.length - element._parentLen)); - } - element = element._parent; - } - return Array.prototype.concat.apply(this._data, bucket); + _topMatch2 = matches; } } diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index e0f60525a7a4..948574287abc 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -58,11 +58,8 @@ export function splitGlobAware(pattern: string, splitChar: string): string[] { let inBraces = false; let inBrackets = false; - let char: string; let curVal = ''; - for (let i = 0; i < pattern.length; i++) { - char = pattern[i]; - + for (const char of pattern) { switch (char) { case splitChar: if (!inBraces && !inBrackets) { @@ -136,10 +133,7 @@ function parseRegExp(pattern: string): string { let inBrackets = false; let bracketVal = ''; - let char: string; - for (let i = 0; i < segment.length; i++) { - char = segment[i]; - + for (const char of segment) { // Support brace expansion if (char !== '}' && inBraces) { braceVal += char; @@ -658,7 +652,7 @@ function parseExpressionPattern(pattern: string, value: any, options: IGlobOptio return parsedPattern; } -function aggregateBasenameMatches(parsedPatterns: (ParsedStringPattern | ParsedExpressionPattern)[], result?: string): (ParsedStringPattern | ParsedExpressionPattern)[] { +function aggregateBasenameMatches(parsedPatterns: Array, result?: string): Array { const basenamePatterns = parsedPatterns.filter(parsedPattern => !!(parsedPattern).basenames); if (basenamePatterns.length < 2) { return parsedPatterns; diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index ab109946ece2..e9772091dd1f 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -16,7 +16,6 @@ export class MarkdownString implements IMarkdownString { value: string; isTrusted?: boolean; - sanitize: boolean = true; constructor(value: string = '') { this.value = value; @@ -58,7 +57,7 @@ export function isMarkdownString(thing: any): thing is IMarkdownString { return true; } else if (thing && typeof thing === 'object') { return typeof (thing).value === 'string' - && (typeof (thing).isTrusted === 'boolean' || (thing).isTrusted === void 0); + && (typeof (thing).isTrusted === 'boolean' || (thing).isTrusted === undefined); } return false; } diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index 34ee821dfe18..2c2e9b563c93 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -205,10 +205,10 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON token: SyntaxKind = SyntaxKind.Unknown, scanError: ScanError = ScanError.None; - function scanHexDigits(count: number, exact?: boolean): number { + function scanHexDigits(count: number): number { let digits = 0; let value = 0; - while (digits < count || !exact) { + while (digits < count) { let ch = text.charCodeAt(pos); if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) { value = value * 16 + ch - CharacterCodes._0; @@ -331,7 +331,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON result += '\t'; break; case CharacterCodes.u: - let ch = scanHexDigits(4, true); + let ch = scanHexDigits(4); if (ch >= 0) { result += String.fromCharCode(ch); } else { @@ -344,7 +344,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON start = pos; continue; } - if (ch >= 0 && ch <= 0x1f) { + if (ch >= 0 && ch <= 0x1F) { if (isLineBreak(ch)) { result += text.substring(start, pos); scanError = ScanError.UnexpectedEndOfString; @@ -668,7 +668,7 @@ const enum CharacterCodes { W = 0x57, X = 0x58, Y = 0x59, - Z = 0x5a, + Z = 0x5A, ampersand = 0x26, // & asterisk = 0x2A, // * @@ -722,13 +722,13 @@ interface NodeImpl extends Node { export function getLocation(text: string, position: number): Location { let segments: Segment[] = []; // strings or numbers let earlyReturnException = new Object(); - let previousNode: NodeImpl | undefined = void 0; + let previousNode: NodeImpl | undefined = undefined; const previousNodeInst: NodeImpl = { value: {}, offset: 0, length: 0, type: 'object', - parent: void 0 + parent: undefined }; let isAtPropertyKey = false; function setPreviousNode(value: string, offset: number, length: number, type: NodeType) { @@ -736,7 +736,7 @@ export function getLocation(text: string, position: number): Location { previousNodeInst.offset = offset; previousNodeInst.length = length; previousNodeInst.type = type; - previousNodeInst.colonOffset = void 0; + previousNodeInst.colonOffset = undefined; previousNode = previousNodeInst; } try { @@ -746,7 +746,7 @@ export function getLocation(text: string, position: number): Location { if (position <= offset) { throw earlyReturnException; } - previousNode = void 0; + previousNode = undefined; isAtPropertyKey = position > offset; segments.push(''); // push a placeholder (will be replaced) }, @@ -764,21 +764,21 @@ export function getLocation(text: string, position: number): Location { if (position <= offset) { throw earlyReturnException; } - previousNode = void 0; + previousNode = undefined; segments.pop(); }, onArrayBegin: (offset: number, length: number) => { if (position <= offset) { throw earlyReturnException; } - previousNode = void 0; + previousNode = undefined; segments.push(0); }, onArrayEnd: (offset: number, length: number) => { if (position <= offset) { throw earlyReturnException; } - previousNode = void 0; + previousNode = undefined; segments.pop(); }, onLiteralValue: (value: any, offset: number, length: number) => { @@ -798,7 +798,7 @@ export function getLocation(text: string, position: number): Location { if (sep === ':' && previousNode && previousNode.type === 'property') { previousNode.colonOffset = offset; isAtPropertyKey = false; - previousNode = void 0; + previousNode = undefined; } else if (sep === ',') { let last = segments[segments.length - 1]; if (typeof last === 'number') { @@ -807,7 +807,7 @@ export function getLocation(text: string, position: number): Location { isAtPropertyKey = true; segments[segments.length - 1] = ''; } - previousNode = void 0; + previousNode = undefined; } } }); @@ -891,7 +891,7 @@ export function parse(text: string, errors: ParseError[] = [], options: ParseOpt * 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 = ParseOptions.DEFAULT): Node { - let currentParent: NodeImpl = { type: 'array', offset: -1, length: -1, children: [], parent: void 0 }; // artificial root + let currentParent: NodeImpl = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root function ensurePropertyComplete(endOffset: number) { if (currentParent.type === 'property') { @@ -957,13 +957,13 @@ export function parseTree(text: string, errors: ParseError[] = [], options: Pars */ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined { if (!root) { - return void 0; + return undefined; } let node = root; for (let segment of path) { if (typeof segment === 'string') { if (node.type !== 'object' || !Array.isArray(node.children)) { - return void 0; + return undefined; } let found = false; for (const propertyNode of node.children) { @@ -974,12 +974,12 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined } } if (!found) { - return void 0; + return undefined; } } else { let index = segment; if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) { - return void 0; + return undefined; } node = node.children[index]; } @@ -1029,7 +1029,7 @@ export function getNodeValue(node: Node): any { case 'boolean': return node.value; default: - return void 0; + return undefined; } } @@ -1055,7 +1055,7 @@ export function findNodeAtOffset(node: Node, offset: number, includeRightBound = } return node; } - return void 0; + return undefined; } @@ -1322,7 +1322,7 @@ export function stripComments(text: string, replaceCh?: string): string { if (offset !== pos) { parts.push(text.substring(offset, pos)); } - if (replaceCh !== void 0) { + if (replaceCh !== undefined) { parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh)); } offset = _scanner.getPosition(); diff --git a/src/vs/base/common/jsonEdit.ts b/src/vs/base/common/jsonEdit.ts index 0dab3b59f7cc..02d646498dfa 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -8,20 +8,20 @@ import { Edit, format, isEOL, FormattingOptions } from './jsonFormatter'; export function removeProperty(text: string, path: JSONPath, formattingOptions: FormattingOptions): Edit[] { - return setProperty(text, path, void 0, formattingOptions); + return setProperty(text, path, undefined, formattingOptions); } 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; + let parent: Node | undefined = undefined; - let lastSegment: Segment | undefined = void 0; + let lastSegment: Segment | undefined = undefined; while (path.length > 0) { lastSegment = path.pop(); parent = findNodeAtLocation(root, path); - if (parent === void 0 && value !== void 0) { + if (parent === undefined && value !== undefined) { if (typeof lastSegment === 'string') { value = { [lastSegment]: value }; } else { @@ -34,14 +34,14 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo if (!parent) { // empty document - if (value === void 0) { // delete + if (value === undefined) { // delete 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' && Array.isArray(parent.children)) { let existing = findNodeAtLocation(parent, [lastSegment]); - if (existing !== void 0) { - if (value === void 0) { // delete + if (existing !== undefined) { + if (value === undefined) { // delete if (!existing.parent) { throw new Error('Malformed AST'); } @@ -66,7 +66,7 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, formattingOptions); } } else { - if (value === void 0) { // delete + if (value === undefined) { // delete return []; // property does not exist, nothing to do } let newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`; @@ -96,7 +96,7 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo } return withFormatting(text, edit, formattingOptions); } else { - if (value === void 0 && parent.children.length >= 0) { + if (value === undefined && parent.children.length >= 0) { //Removal let removalIndex = lastSegment; let toRemove = parent.children[removalIndex]; diff --git a/src/vs/base/common/keyCodes.ts b/src/vs/base/common/keyCodes.ts index 03a9e57299e4..dd2085e39f18 100644 --- a/src/vs/base/common/keyCodes.ts +++ b/src/vs/base/common/keyCodes.ts @@ -395,7 +395,7 @@ const enum BinaryKeybindingsMask { Shift = (1 << 10) >>> 0, Alt = (1 << 9) >>> 0, WinCtrl = (1 << 8) >>> 0, - KeyCode = 0x000000ff + KeyCode = 0x000000FF } export const enum KeyMod { @@ -406,7 +406,7 @@ export const enum KeyMod { } export function KeyChord(firstPart: number, secondPart: number): number { - let chordPart = ((secondPart & 0x0000ffff) << 16) >>> 0; + let chordPart = ((secondPart & 0x0000FFFF) << 16) >>> 0; return (firstPart | chordPart) >>> 0; } @@ -414,8 +414,8 @@ export function createKeybinding(keybinding: number, OS: OperatingSystem): Keybi if (keybinding === 0) { return null; } - const firstPart = (keybinding & 0x0000ffff) >>> 0; - const chordPart = (keybinding & 0xffff0000) >>> 16; + const firstPart = (keybinding & 0x0000FFFF) >>> 0; + const chordPart = (keybinding & 0xFFFF0000) >>> 16; if (chordPart !== 0) { return new ChordKeybinding( createSimpleKeybinding(firstPart, OS), diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 154cacb76efd..a3af1921c477 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -70,7 +70,9 @@ export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHom return res; } -export function getBaseLabel(resource: URI | string): string | undefined { +export function getBaseLabel(resource: URI | string): string; +export function getBaseLabel(resource: URI | string | undefined): string | undefined; +export function getBaseLabel(resource: URI | string | undefined): string | undefined { if (!resource) { return undefined; } @@ -108,7 +110,7 @@ export function tildify(path: string, userHome: string): string { } // Keep a normalized user home path as cache to prevent accumulated string creation - let normalizedUserHome = normalizedUserHomeCached.original === userHome ? normalizedUserHomeCached.normalized : void 0; + let normalizedUserHome = normalizedUserHomeCached.original === userHome ? normalizedUserHomeCached.normalized : undefined; if (!normalizedUserHome) { normalizedUserHome = `${rtrim(userHome, sep)}${sep}`; normalizedUserHomeCached = { original: userHome, normalized: normalizedUserHome }; @@ -284,11 +286,8 @@ export function template(template: string, values: { [key: string]: string | ISe const segments: ISegment[] = []; let inVariable = false; - let char: string; let curVal = ''; - for (let i = 0; i < template.length; i++) { - char = template[i]; - + for (const char of template) { // Beginning of variable if (char === '$' || (inVariable && char === '{')) { if (curVal) { @@ -364,7 +363,7 @@ export function mnemonicMenuLabel(label: string, forceDisableMnemonics?: boolean /** * Handles mnemonics for buttons. Depending on OS: - * - Windows: Supported via & character (replace && with &) + * - Windows: Supported via & character (replace && with & and & with && for escaping) * - Linux: Supported via _ character (replace && with _) * - macOS: Unsupported (replace && with empty string) */ @@ -373,7 +372,11 @@ export function mnemonicButtonLabel(label: string): string { return label.replace(/\(&&\w\)|&&/g, ''); } - return label.replace(/&&/g, isWindows ? '&' : '_'); + if (isWindows) { + return label.replace(/&&|&/g, m => m === '&' ? '&&' : '&'); + } + + return label.replace(/&&/g, '_'); } export function unmnemonicLabel(label: string): string { diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index a8e7b110f9d6..178e9c0ee34b 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -15,7 +15,7 @@ export function isDisposable(thing: E): thing is E & IDisposab } export function dispose(disposable: T): T; -export function dispose(...disposables: (T | undefined)[]): T[]; +export function dispose(...disposables: Array): T[]; export function dispose(disposables: T[]): T[]; export function dispose(first: T | T[], ...rest: T[]): T | T[] | undefined { if (Array.isArray(first)) { @@ -49,12 +49,20 @@ export abstract class Disposable implements IDisposable { protected _toDispose: IDisposable[] = []; protected get toDispose(): IDisposable[] { return this._toDispose; } + private _lifecycle_disposable_isDisposed = false; + public dispose(): void { + this._lifecycle_disposable_isDisposed = true; this._toDispose = dispose(this._toDispose); } protected _register(t: T): T { - this._toDispose.push(t); + if (this._lifecycle_disposable_isDisposed) { + console.warn('Registering disposable on object that has already been disposed.'); + t.dispose(); + } else { + this._toDispose.push(t); + } return t; } diff --git a/src/vs/base/common/linkedList.ts b/src/vs/base/common/linkedList.ts index 73da63eb822d..02610624fb74 100644 --- a/src/vs/base/common/linkedList.ts +++ b/src/vs/base/common/linkedList.ts @@ -32,17 +32,18 @@ export class LinkedList { clear(): void { this._first = undefined; this._last = undefined; + this._size = 0; } - unshift(element: E) { - return this.insert(element, false); + unshift(element: E): () => void { + return this._insert(element, false); } - push(element: E) { - return this.insert(element, true); + push(element: E): () => void { + return this._insert(element, true); } - private insert(element: E, atTheEnd: boolean) { + private _insert(element: E, atTheEnd: boolean): () => void { const newNode = new Node(element); if (!this._first) { this._first = newNode; @@ -63,41 +64,63 @@ export class LinkedList { oldFirst.prev = newNode; } this._size += 1; + return this._remove.bind(this, newNode); + } - return () => { - let candidate: Node | undefined = this._first; - while (candidate instanceof Node) { - if (candidate !== newNode) { - candidate = candidate.next; - continue; - } - if (candidate.prev && candidate.next) { - // middle - let anchor = candidate.prev; - anchor.next = candidate.next; - candidate.next.prev = anchor; - - } else if (!candidate.prev && !candidate.next) { - // only node - this._first = undefined; - this._last = undefined; - - } else if (!candidate.next) { - // last - this._last = this._last!.prev!; - this._last.next = undefined; - - } else if (!candidate.prev) { - // first - this._first = this._first!.next!; - this._first.prev = undefined; - } - // done - this._size -= 1; - break; + shift(): E | undefined { + if (!this._first) { + return undefined; + } else { + const res = this._first.element; + this._remove(this._first); + return res; + } + } + + pop(): E | undefined { + if (!this._last) { + return undefined; + } else { + const res = this._last.element; + this._remove(this._last); + return res; + } + } + + private _remove(node: Node): void { + let candidate: Node | undefined = this._first; + while (candidate instanceof Node) { + if (candidate !== node) { + candidate = candidate.next; + continue; } - }; + if (candidate.prev && candidate.next) { + // middle + let anchor = candidate.prev; + anchor.next = candidate.next; + candidate.next.prev = anchor; + + } else if (!candidate.prev && !candidate.next) { + // only node + this._first = undefined; + this._last = undefined; + + } else if (!candidate.next) { + // last + this._last = this._last!.prev!; + this._last.next = undefined; + + } else if (!candidate.prev) { + // first + this._first = this._first!.next!; + this._first.prev = undefined; + } + + // done + this._size -= 1; + break; + } } iterator(): Iterator { diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 490d029c52ad..5dfac6d38b1d 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -24,7 +24,7 @@ export function keys(map: Map): K[] { export function getOrSet(map: Map, key: K, value: V): V { let result = map.get(key); - if (result === void 0) { + if (result === undefined) { result = value; map.set(key, result); } @@ -431,7 +431,7 @@ export class ResourceMap { this.map.set(this.toKey(resource), value); } - get(resource: URI): T { + get(resource: URI): T | undefined { return this.map.get(this.toKey(resource)); } @@ -687,7 +687,7 @@ export class LinkedMap { this._head = current; this._size = currentSize; if (current) { - current.previous = void 0; + current.previous = undefined; } } @@ -719,8 +719,8 @@ export class LinkedMap { private removeItem(item: Item): void { if (item === this._head && item === this._tail) { - this._head = void 0; - this._tail = void 0; + this._head = undefined; + this._tail = undefined; } else if (item === this._head) { this._head = item.next; @@ -759,7 +759,7 @@ export class LinkedMap { if (item === this._tail) { // previous must be defined since item was not head but is tail // So there are more than on item in the map - previous!.next = void 0; + previous!.next = undefined; this._tail = previous; } else { @@ -769,7 +769,7 @@ export class LinkedMap { } // Insert the node at head - item.previous = void 0; + item.previous = undefined; item.next = this._head; this._head.previous = item; this._head = item; @@ -785,14 +785,14 @@ export class LinkedMap { if (item === this._head) { // next must be defined since item was not tail but is head // So there are more than on item in the map - next!.previous = void 0; + next!.previous = undefined; this._head = next; } else { // Both next and previous are not undefined since item was neither head nor tail. next!.previous = previous; previous!.next = next; } - item.next = void 0; + item.next = undefined; item.previous = this._tail; this._tail.next = item; this._tail = item; diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index 3e73320556d6..0a06e2cf66e8 100644 --- a/src/vs/base/common/marshalling.ts +++ b/src/vs/base/common/marshalling.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; +import { regExpFlags } from 'vs/base/common/strings'; export function stringify(obj: any): string { return JSON.stringify(obj, replacer); @@ -24,8 +25,8 @@ function replacer(key: string, value: any): any { if (value instanceof RegExp) { return { $mid: 2, - source: (value).source, - flags: ((value).global ? 'g' : '') + ((value).ignoreCase ? 'i' : '') + ((value).multiline ? 'm' : ''), + source: value.source, + flags: regExpFlags(value), }; } return value; diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index 37794523019e..145dc5ccdb78 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -13,20 +13,20 @@ export const MIME_BINARY = 'application/octet-stream'; export const MIME_UNKNOWN = 'application/unknown'; export interface ITextMimeAssociation { - id: string; - mime: string; - filename?: string; - extension?: string; - filepattern?: string; - firstline?: RegExp; - userConfigured?: boolean; + readonly id: string; + readonly mime: string; + readonly filename?: string; + readonly extension?: string; + readonly filepattern?: string; + readonly firstline?: RegExp; + readonly userConfigured?: boolean; } interface ITextMimeAssociationItem extends ITextMimeAssociation { - filenameLowercase?: string; - extensionLowercase?: string; - filepatternLowercase?: string; - filepatternOnPath?: boolean; + readonly filenameLowercase?: string; + readonly extensionLowercase?: string; + readonly filepatternLowercase?: string; + readonly filepatternOnPath?: boolean; } let registeredAssociations: ITextMimeAssociationItem[] = []; @@ -82,9 +82,9 @@ function toTextMimeAssociationItem(association: ITextMimeAssociation): ITextMime filepattern: association.filepattern, firstline: association.firstline, userConfigured: association.userConfigured, - filenameLowercase: association.filename ? association.filename.toLowerCase() : void 0, - extensionLowercase: association.extension ? association.extension.toLowerCase() : void 0, - filepatternLowercase: association.filepattern ? association.filepattern.toLowerCase() : void 0, + filenameLowercase: association.filename ? association.filename.toLowerCase() : undefined, + extensionLowercase: association.extension ? association.extension.toLowerCase() : undefined, + filepatternLowercase: association.filepattern ? association.filepattern.toLowerCase() : undefined, filepatternOnPath: association.filepattern ? association.filepattern.indexOf(paths.sep) >= 0 : false }; } @@ -106,7 +106,7 @@ export function clearTextMimes(onlyUserConfigured?: boolean): void { /** * Given a file, return the best matching mime type for it */ -export function guessMimeTypes(path: string, firstLine?: string, skipUserAssociations: boolean = false): string[] { +export function guessMimeTypes(path: string | null, firstLine?: string): string[] { if (!path) { return [MIME_UNKNOWN]; } @@ -114,12 +114,10 @@ export function guessMimeTypes(path: string, firstLine?: string, skipUserAssocia path = path.toLowerCase(); const filename = paths.basename(path); - if (!skipUserAssociations) { - // 1.) User configured mappings have highest priority - const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations); - if (configuredMime) { - return [configuredMime, MIME_TEXT]; - } + // 1.) User configured mappings have highest priority + const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations); + if (configuredMime) { + return [configuredMime, MIME_TEXT]; } // 2.) Registered mappings have middle priority @@ -199,8 +197,7 @@ function guessMimeTypeByFirstline(firstLine: string): string | null { } if (firstLine.length > 0) { - for (let i = 0; i < registeredAssociations.length; ++i) { - const association = registeredAssociations[i]; + for (const association of registeredAssociations) { if (!association.firstline) { continue; } diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 81e8c920a4d2..7a0a7071d1e2 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -42,4 +42,6 @@ export namespace Schemas { export const untitled: string = 'untitled'; export const data: string = 'data'; + + export const command: string = 'command'; } diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 6764bd4cdba6..420ba15d9230 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -62,8 +62,8 @@ function _cloneAndChange(obj: any, changer: (orig: any) => any, seen: Set): if (isArray(obj)) { const r1: any[] = []; - for (let i1 = 0; i1 < obj.length; i1++) { - r1.push(_cloneAndChange(obj[i1], changer, seen)); + for (const e of obj) { + r1.push(_cloneAndChange(e, changer, seen)); } return r1; } @@ -177,8 +177,8 @@ export function equals(one: any, other: any): boolean { function arrayToHash(array: string[]): { [name: string]: true } { const result: any = {}; - for (let i = 0; i < array.length; ++i) { - result[array[i]] = true; + for (const e of array) { + result[e] = true; } return result; } diff --git a/src/vs/base/common/octicon.ts b/src/vs/base/common/octicon.ts index f15e008da8c5..e74104c2f90a 100644 --- a/src/vs/base/common/octicon.ts +++ b/src/vs/base/common/octicon.ts @@ -30,7 +30,7 @@ function doParseOcticons(text: string, firstOcticonIndex: number): IParsedOctico if (chars) { textWithoutOcticons += chars; - for (let i = 0; i < chars.length; i++) { + for (const _ of chars) { octiconOffsets.push(octiconsOffset); // make sure to fill in octicon offsets } } @@ -115,10 +115,10 @@ export function matchesFuzzyOcticonAware(query: string, target: IParsedOcticons, // Map matches back to offsets with octicons and trimming if (matches) { - for (let i = 0; i < matches.length; i++) { - const octiconOffset = octiconOffsets[matches[i].start + leadingWhitespaceOffset] /* octicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */; - matches[i].start += octiconOffset; - matches[i].end += octiconOffset; + for (const match of matches) { + const octiconOffset = octiconOffsets[match.start + leadingWhitespaceOffset] /* octicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */; + match.start += octiconOffset; + match.end += octiconOffset; } } diff --git a/src/vs/base/common/paging.ts b/src/vs/base/common/paging.ts index 4baea7a7ef26..8657515d7201 100644 --- a/src/vs/base/common/paging.ts +++ b/src/vs/base/common/paging.ts @@ -15,12 +15,12 @@ export interface IPager { firstPage: T[]; total: number; pageSize: number; - getPage(pageIndex: number, cancellationToken: CancellationToken): Thenable; + getPage(pageIndex: number, cancellationToken: CancellationToken): Promise; } interface IPage { isResolved: boolean; - promise: Thenable | null; + promise: Promise | null; cts: CancellationTokenSource | null; promiseIndexes: Set; elements: T[]; @@ -43,7 +43,7 @@ export interface IPagedModel { length: number; isResolved(index: number): boolean; get(index: number): T; - resolve(index: number, cancellationToken: CancellationToken): Thenable; + resolve(index: number, cancellationToken: CancellationToken): Promise; } export function singlePagePager(elements: T[]): IPager { @@ -51,7 +51,7 @@ export function singlePagePager(elements: T[]): IPager { firstPage: elements, total: elements.length, pageSize: elements.length, - getPage: (pageIndex: number, cancellationToken: CancellationToken): Thenable => { + getPage: (pageIndex: number, cancellationToken: CancellationToken): Promise => { return Promise.resolve(elements); } }; @@ -90,7 +90,7 @@ export class PagedModel implements IPagedModel { return page.elements[indexInPage]; } - resolve(index: number, cancellationToken: CancellationToken): Thenable { + resolve(index: number, cancellationToken: CancellationToken): Promise { if (cancellationToken.isCancellationRequested) { return Promise.reject(canceled()); } @@ -151,7 +151,7 @@ export class DelayedPagedModel implements IPagedModel { return this.model.get(index); } - resolve(index: number, cancellationToken: CancellationToken): Thenable { + resolve(index: number, cancellationToken: CancellationToken): Promise { return new Promise((c, e) => { if (cancellationToken.isCancellationRequested) { return e(canceled()); @@ -196,7 +196,7 @@ export function mergePagers(one: IPager, other: IPager): IPager { firstPage: [...one.firstPage, ...other.firstPage], total: one.total + other.total, pageSize: one.pageSize + other.pageSize, - getPage(pageIndex: number, token): Thenable { + getPage(pageIndex: number, token): Promise { return Promise.all([one.getPage(pageIndex, token), other.getPage(pageIndex, token)]) .then(([onePage, otherPage]) => [...onePage, ...otherPage]); } diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts index 476c86b89507..e559574d80ae 100644 --- a/src/vs/base/common/paths.ts +++ b/src/vs/base/common/paths.ts @@ -17,27 +17,45 @@ export const sep = '/'; */ export const nativeSep = isWindows ? '\\' : '/'; + +function isPathSeparator(code: number) { + return code === CharCode.Slash || code === CharCode.Backslash; +} + /** * @param path the path to get the dirname from * @param separator the separator to use * @returns the directory name of a path. - * + * '.' is returned for empty paths or single segment relative paths (as done by NodeJS) + * For paths consisting only of a root, the input path is returned */ export function dirname(path: string, separator = nativeSep): string { - const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); - if (idx === 0) { + const len = path.length; + if (len === 0) { return '.'; - } else if (~idx === 0) { - return path[0]; - } else if (~idx === path.length - 1) { - return dirname(path.substring(0, path.length - 1)); - } else { - let res = path.substring(0, ~idx); - if (isWindows && res[res.length - 1] === ':') { - res += separator; // make sure drive letters end with backslash + } else if (len === 1) { + return isPathSeparator(path.charCodeAt(0)) ? path : '.'; + } + const root = getRoot(path, separator); + let rootLength = root.length; + if (rootLength >= len) { + return path; // matched the root + } + if (rootLength === 0 && isPathSeparator(path.charCodeAt(0))) { + rootLength = 1; // absolute paths stay absolute paths. + } + + let i = len - 1; + if (i > rootLength) { + i--; // no need to look at the last character. If it's a trailing slash, we ignore it. + while (i > rootLength && !isPathSeparator(path.charCodeAt(i))) { + i--; } - return res; } + if (i === 0) { + return '.'; // it was a relative path with a single segment, no root. Nodejs returns '.' here. + } + return path.substr(0, i); } /** @@ -77,7 +95,7 @@ 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) { + if (path === null || path === undefined) { return path; } @@ -102,7 +120,7 @@ export function normalize(path: string | null | undefined, toOSPath?: boolean): for (let end = root.length; end <= len; end++) { // either at the end or at a path-separator character - if (end === len || path.charCodeAt(end) === CharCode.Slash || path.charCodeAt(end) === CharCode.Backslash) { + if (end === len || isPathSeparator(path.charCodeAt(end))) { if (streql(path, start, end, '..')) { // skip current and remove parent (if there is already something) @@ -148,29 +166,23 @@ export function getRoot(path: string, sep: string = '/'): string { } let len = path.length; - let code = path.charCodeAt(0); - if (code === CharCode.Slash || code === CharCode.Backslash) { - - code = path.charCodeAt(1); - if (code === CharCode.Slash || code === CharCode.Backslash) { + const firstLetter = path.charCodeAt(0); + if (isPathSeparator(firstLetter)) { + if (isPathSeparator(path.charCodeAt(1))) { // UNC candidate \\localhost\shares\ddd // ^^^^^^^^^^^^^^^^^^^ - code = path.charCodeAt(2); - if (code !== CharCode.Slash && code !== CharCode.Backslash) { + if (!isPathSeparator(path.charCodeAt(2))) { let pos = 3; let start = pos; for (; pos < len; pos++) { - code = path.charCodeAt(pos); - if (code === CharCode.Slash || code === CharCode.Backslash) { + if (isPathSeparator(path.charCodeAt(pos))) { break; } } - code = path.charCodeAt(pos + 1); - if (start !== pos && code !== CharCode.Slash && code !== CharCode.Backslash) { + if (start !== pos && !isPathSeparator(path.charCodeAt(pos + 1))) { pos += 1; for (; pos < len; pos++) { - code = path.charCodeAt(pos); - if (code === CharCode.Slash || code === CharCode.Backslash) { + if (isPathSeparator(path.charCodeAt(pos))) { return path.slice(0, pos + 1) // consume this separator .replace(/[\\/]/g, sep); } @@ -183,12 +195,11 @@ export function getRoot(path: string, sep: string = '/'): string { // ^ return sep; - } else if ((code >= CharCode.A && code <= CharCode.Z) || (code >= CharCode.a && code <= CharCode.z)) { + } else if (isWindowsDriveLetter(firstLetter)) { // check for windows drive letter c:\ or c: if (path.charCodeAt(1) === CharCode.Colon) { - code = path.charCodeAt(2); - if (code === CharCode.Slash || code === CharCode.Backslash) { + if (isPathSeparator(path.charCodeAt(2))) { // C:\fff // ^^^ return path.slice(0, 2) + sep; @@ -207,8 +218,7 @@ export function getRoot(path: string, sep: string = '/'): string { if (pos !== -1) { pos += 3; // 3 -> "://".length for (; pos < len; pos++) { - code = path.charCodeAt(pos); - if (code === CharCode.Slash || code === CharCode.Backslash) { + if (isPathSeparator(path.charCodeAt(pos))) { return path.slice(0, pos + 1); // consume this separator } } @@ -229,9 +239,9 @@ export const join: (...parts: string[]) => string = function () { // add the separater between two parts unless // there already is one let last = value.charCodeAt(value.length - 1); - if (last !== CharCode.Slash && last !== CharCode.Backslash) { + if (!isPathSeparator(last)) { let next = part.charCodeAt(0); - if (next !== CharCode.Slash && next !== CharCode.Backslash) { + if (!isPathSeparator(next)) { value += sep; } @@ -386,12 +396,12 @@ export function isAbsolute_win32(path: string): boolean { } const char0 = path.charCodeAt(0); - if (char0 === CharCode.Slash || char0 === CharCode.Backslash) { + if (isPathSeparator(char0)) { return true; - } else if ((char0 >= CharCode.A && char0 <= CharCode.Z) || (char0 >= CharCode.a && char0 <= CharCode.z)) { + } else if (isWindowsDriveLetter(char0)) { if (path.length > 2 && path.charCodeAt(1) === CharCode.Colon) { const char2 = path.charCodeAt(2); - if (char2 === CharCode.Slash || char2 === CharCode.Backslash) { + if (isPathSeparator(char2)) { return true; } } @@ -403,3 +413,7 @@ export function isAbsolute_win32(path: string): boolean { export function isAbsolute_posix(path: string): boolean { return !!(path && path.charCodeAt(0) === CharCode.Slash); } + +export function isWindowsDriveLetter(char0: number): boolean { + return char0 >= CharCode.A && char0 <= CharCode.Z || char0 >= CharCode.a && char0 <= CharCode.z; +} diff --git a/src/vs/base/common/performance.d.ts b/src/vs/base/common/performance.d.ts index 496103e0ebbf..2ae345b03498 100644 --- a/src/vs/base/common/performance.d.ts +++ b/src/vs/base/common/performance.d.ts @@ -4,22 +4,18 @@ *--------------------------------------------------------------------------------------------*/ export interface PerformanceEntry { - readonly type: 'mark' | 'measure'; readonly name: string; - readonly startTime: number; - readonly duration: number; + readonly timestamp: number; } export function mark(name: string): void; -export function measure(name: string, from?: string, to?: string): PerformanceEntry; - /** * All entries filtered by type and sorted by `startTime`. */ -export function getEntries(type: 'mark' | 'measure'): PerformanceEntry[]; +export function getEntries(): PerformanceEntry[]; -export function getEntry(type: 'mark' | 'measure', name: string): PerformanceEntry; +export function getEntry(name: string): PerformanceEntry; export function getDuration(from: string, to: string): number; diff --git a/src/vs/base/common/performance.js b/src/vs/base/common/performance.js index c57038283c75..3fc564cf19dd 100644 --- a/src/vs/base/common/performance.js +++ b/src/vs/base/common/performance.js @@ -10,11 +10,11 @@ // This module can be loaded in an amd and commonjs-context. // Because we want both instances to use the same perf-data // we store them globally -// stores data as 'type','name','startTime','duration' +// stores data as: 'name','timestamp' if (typeof define !== "function" && typeof module === "object" && typeof module.exports === "object") { // this is commonjs, fake amd - global.define = function (dep, callback) { + global.define = function (_dep, callback) { module.exports = callback(); global.define = undefined; }; @@ -22,14 +22,10 @@ if (typeof define !== "function" && typeof module === "object" && typeof module. define([], function () { - var _global = this; - if (typeof global !== 'undefined') { - _global = global; - } - _global._performanceEntries = _global._performanceEntries || []; + global._performanceEntries = global._performanceEntries || []; - // const _now = global.performance && performance.now ? performance.now : Date.now - const _now = Date.now; + const _dataLen = 2; + const _timeStamp = typeof console.timeStamp === 'function' ? console.timeStamp.bind(console) : () => { }; function importEntries(entries) { global._performanceEntries.splice(0, 0, ...entries); @@ -39,36 +35,25 @@ define([], function () { return global._performanceEntries.slice(0); } - function getEntries(type, name) { + function getEntries() { const result = []; const entries = global._performanceEntries; - for (let i = 0; i < entries.length; i += 5) { - if (entries[i] === type && (name === void 0 || entries[i + 1] === name)) { - result.push({ - type: entries[i], - name: entries[i + 1], - startTime: entries[i + 2], - duration: entries[i + 3], - seq: entries[i + 4], - }); - } + for (let i = 0; i < entries.length; i += _dataLen) { + result.push({ + name: entries[i], + timestamp: entries[i + 1], + }); } - - return result.sort((a, b) => { - return a.startTime - b.startTime || a.seq - b.seq; - }); + return result; } - function getEntry(type, name) { + function getEntry(name) { const entries = global._performanceEntries; - for (let i = 0; i < entries.length; i += 5) { - if (entries[i] === type && entries[i + 1] === name) { + for (let i = 0; i < entries.length; i += _dataLen) { + if (entries[i] === name) { return { - type: entries[i], - name: entries[i + 1], - startTime: entries[i + 2], - duration: entries[i + 3], - seq: entries[i + 4], + name: entries[i], + timestamp: entries[i + 1], }; } } @@ -77,65 +62,29 @@ define([], function () { function getDuration(from, to) { const entries = global._performanceEntries; let target = to; - let endTime = 0; - for (let i = entries.length - 1; i >= 0; i -= 5) { - if (entries[i - 3] === target) { + let endIndex = 0; + for (let i = entries.length - _dataLen; i >= 0; i -= _dataLen) { + if (entries[i] === target) { if (target === to) { // found `to` (end of interval) - endTime = entries[i - 2]; + endIndex = i; target = from; } else { - return endTime - entries[i - 2]; + // found `from` (start of interval) + return entries[endIndex + 1] - entries[i + 1]; } } } return 0; } - let seq = 0; - function mark(name) { - global._performanceEntries.push('mark', name, _now(), 0, seq++); - if (typeof console.timeStamp === 'function') { - console.timeStamp(name); - } - } - - function measure(name, from, to) { - - let startTime; - let duration; - let now = _now(); - - if (!from) { - startTime = now; - } else { - startTime = _getLastStartTime(from); - } - - if (!to) { - duration = now - startTime; - } else { - duration = _getLastStartTime(to) - startTime; - } - - global._performanceEntries.push('measure', name, startTime, duration); - } - - function _getLastStartTime(name) { - const entries = global._performanceEntries; - for (let i = entries.length - 1; i >= 0; i -= 5) { - if (entries[i - 3] === name) { - return entries[i - 2]; - } - } - - throw new Error(name + ' not found'); + global._performanceEntries.push(name, Date.now()); + _timeStamp(name); } var exports = { mark: mark, - measure: measure, getEntries: getEntries, getEntry: getEntry, getDuration: getDuration, diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index d4dc02835fa3..c82c4fb355cd 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -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.path.length === 0) { + return resource; + } if (resource.scheme === Schemas.file) { return URI.file(paths.dirname(fsPath(resource))); } @@ -111,6 +114,9 @@ export function joinPath(resource: URI, pathFragment: string): URI { * @returns The URI with the normalized path. */ export function normalizePath(resource: URI): URI { + if (!resource.path.length) { + return resource; + } let normalizedPath: string; if (resource.scheme === Schemas.file) { normalizedPath = URI.file(paths.normalize(fsPath(resource))).path; @@ -128,19 +134,20 @@ export function normalizePath(resource: URI): URI { */ export function fsPath(uri: URI): string { let value: string; - if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') { + const uriPath = uri.path; + if (uri.authority && uriPath.length > 1 && uri.scheme === 'file') { // unc path: file://shares/c$/far/boo - value = `//${uri.authority}${uri.path}`; + value = `//${uri.authority}${uriPath}`; } else if ( isWindows - && uri.path.charCodeAt(0) === CharCode.Slash - && (uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z || uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z) - && uri.path.charCodeAt(2) === CharCode.Colon + && uriPath.charCodeAt(0) === CharCode.Slash + && paths.isWindowsDriveLetter(uriPath.charCodeAt(1)) + && uriPath.charCodeAt(2) === CharCode.Colon ) { - value = uri.path.substr(1); + value = uriPath.substr(1); } else { // other path - value = uri.path; + value = uriPath; } if (isWindows) { value = value.replace(/\//g, '\\'); @@ -186,7 +193,7 @@ export function isMalformedFileUri(candidate: URI): URI | undefined { if (!candidate.scheme || isWindows && candidate.scheme.match(/^[a-zA-Z]$/)) { return URI.file((candidate.scheme ? candidate.scheme + ':' : '') + candidate.path); } - return void 0; + return undefined; } diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index d7bff39be723..dce97a87be67 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -56,7 +56,7 @@ export function format(value: string, ...args: any[]): string { * being used e.g. in HTMLElement.innerHTML. */ export function escape(html: string): string { - return html.replace(/[<|>|&]/g, function (match) { + return html.replace(/[<>&]/g, function (match) { switch (match) { case '<': return '<'; case '>': return '>'; @@ -188,6 +188,7 @@ export interface RegExpOptions { wholeWord?: boolean; multiline?: boolean; global?: boolean; + unicode?: boolean; } export function createRegExp(searchString: string, isRegex: boolean, options: RegExpOptions = {}): RegExp { @@ -215,6 +216,9 @@ export function createRegExp(searchString: string, isRegex: boolean, options: Re if (options.multiline) { modifiers += 'm'; } + if (options.unicode) { + modifiers += 'u'; + } return new RegExp(searchString, modifiers); } @@ -236,6 +240,13 @@ export function regExpContainsBackreference(regexpValue: string): boolean { return !!regexpValue.match(/([^\\]|^)(\\\\)*\\\d+/); } +export function regExpFlags(regexp: RegExp): string { + return (regexp.global ? 'g' : '') + + (regexp.ignoreCase ? 'i' : '') + + (regexp.multiline ? 'm' : '') + + ((regexp as any).unicode ? 'u' : ''); +} + /** * Returns first index of the string that is not whitespace. * If string is empty or contains only whitespaces, returns -1 diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 3f1fa6df33e0..11168281bb46 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -124,12 +124,12 @@ export function isFunction(obj: any): obj is Function { * @returns whether the provided parameters is are JavaScript Function or not. */ export function areFunctions(...objects: any[]): boolean { - return objects && objects.length > 0 && objects.every(isFunction); + return objects.length > 0 && objects.every(isFunction); } export type TypeConstraint = string | Function; -export function validateConstraints(args: any[], constraints: (TypeConstraint | undefined)[]): void { +export function validateConstraints(args: any[], constraints: Array): void { const len = Math.min(args.length, constraints.length); for (let i = 0; i < len; i++) { validateConstraint(args[i], constraints[i]); diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 60367ccc6a0e..38bdd15a0142 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -108,7 +108,10 @@ export class URI implements UriComponents { && typeof (thing).fragment === 'string' && typeof (thing).path === 'string' && typeof (thing).query === 'string' - && typeof (thing).scheme === 'string'; + && typeof (thing).scheme === 'string' + && typeof (thing).fsPath === 'function' + && typeof (thing).with === 'function' + && typeof (thing).toString === 'function'; } /** @@ -215,27 +218,27 @@ export class URI implements UriComponents { } let { scheme, authority, path, query, fragment } = change; - if (scheme === void 0) { + if (scheme === undefined) { scheme = this.scheme; } else if (scheme === null) { scheme = _empty; } - if (authority === void 0) { + if (authority === undefined) { authority = this.authority; } else if (authority === null) { authority = _empty; } - if (path === void 0) { + if (path === undefined) { path = this.path; } else if (path === null) { path = _empty; } - if (query === void 0) { + if (query === undefined) { query = this.query; } else if (query === null) { query = _empty; } - if (fragment === void 0) { + if (fragment === undefined) { fragment = this.fragment; } else if (fragment === null) { fragment = _empty; @@ -546,7 +549,6 @@ function encodeURIComponentMinimal(path: string): string { /** * Compute `fsPath` for the given uri - * @param uri */ function _makeFsPath(uri: URI): string { diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts index 0b15580f0797..96fe0791051e 100644 --- a/src/vs/base/common/uriIpc.ts +++ b/src/vs/base/common/uriIpc.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { URI, UriComponents } from 'vs/base/common/uri'; +import { MarshalledObject } from 'vs/base/common/marshalling'; export interface IURITransformer { transformIncoming(uri: UriComponents): UriComponents; - transformOutgoing(uri: URI): URI; transformOutgoing(uri: UriComponents): UriComponents; + transformOutgoingURI(uri: URI): URI; } export const DefaultURITransformer: IURITransformer = new class { @@ -16,9 +17,90 @@ export const DefaultURITransformer: IURITransformer = new class { return uri; } - transformOutgoing(uri: URI): URI; - transformOutgoing(uri: UriComponents): UriComponents; - transformOutgoing(uri: URI | UriComponents): URI | UriComponents { + transformOutgoing(uri: UriComponents): UriComponents { + return uri; + } + + transformOutgoingURI(uri: URI): URI { return uri; } -}; \ No newline at end of file +}; + +function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: number): any { + + if (!obj || depth > 200) { + return null; + } + + if (typeof obj === 'object') { + if (obj instanceof URI) { + return transformer.transformOutgoing(obj); + } + + // walk object (or array) + for (let key in obj) { + if (Object.hasOwnProperty.call(obj, key)) { + const r = _transformOutgoingURIs(obj[key], transformer, depth + 1); + if (r !== null) { + obj[key] = r; + } + } + } + } + + return null; +} + +export function transformOutgoingURIs(obj: T, transformer: IURITransformer): T { + const result = _transformOutgoingURIs(obj, transformer, 0); + if (result === null) { + // no change + return obj; + } + return result; +} + + +function _transformIncomingURIs(obj: any, transformer: IURITransformer, revive: boolean, depth: number): any { + + if (!obj || depth > 200) { + return null; + } + + if (typeof obj === 'object') { + + if ((obj).$mid === 1) { + return revive ? URI.revive(transformer.transformIncoming(obj)) : transformer.transformIncoming(obj); + } + + // walk object (or array) + for (let key in obj) { + if (Object.hasOwnProperty.call(obj, key)) { + const r = _transformIncomingURIs(obj[key], transformer, revive, depth + 1); + if (r !== null) { + obj[key] = r; + } + } + } + } + + return null; +} + +export function transformIncomingURIs(obj: T, transformer: IURITransformer): T { + const result = _transformIncomingURIs(obj, transformer, false, 0); + if (result === null) { + // no change + return obj; + } + return result; +} + +export function transformAndReviveIncomingURIs(obj: T, transformer: IURITransformer): T { + const result = _transformIncomingURIs(obj, transformer, true, 0); + if (result === null) { + // no change + return obj; + } + return result; +} \ No newline at end of file diff --git a/src/vs/base/common/winjs.base.d.ts b/src/vs/base/common/winjs.base.d.ts deleted file mode 100644 index 8b28d0325813..000000000000 --- a/src/vs/base/common/winjs.base.d.ts +++ /dev/null @@ -1,53 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/// Interfaces for WinJS - -export type ErrorCallback = (error: any) => void; - -export class Promise { - constructor(executor: (resolve: (value: T | PromiseLike) => void, reject: (reason: any) => void) => void); - - public then( - onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onrejected?: ((reason: any) => TResult2 | PromiseLike) | null): Promise; - - - public static as(value: null): Promise; - public static as(value: undefined): Promise; - public static as(value: PromiseLike): PromiseLike; - public static as>(value: SomePromise): SomePromise; - public static as(value: T): Promise; - - public static join(promises: [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; - public static join(promises: (T | PromiseLike)[]): Promise; - - public static wrap(value: T | PromiseLike): Promise; - - public static wrapError(error: Error): Promise; - - /** - * @internal - */ - public static addEventListener(event: 'error', promiseErrorHandler: (e: IPromiseError) => void): void; -} - -export type TValueCallback = (value: T | PromiseLike) => void; - -export { - Promise as TPromise, - TValueCallback as ValueCallback -}; - -export interface IPromiseErrorDetail { - parent: Promise; - error: any; - id: number; - handler: Function; - exception: Error; -} - -export interface IPromiseError { - detail: IPromiseErrorDetail; -} diff --git a/src/vs/base/common/winjs.base.js b/src/vs/base/common/winjs.base.js deleted file mode 100644 index c9d40d92f2fe..000000000000 --- a/src/vs/base/common/winjs.base.js +++ /dev/null @@ -1,2096 +0,0 @@ -/** - * Extracted from https://github.com/winjs/winjs - * Version: 4.4.0(ec3258a9f3a36805a187848984e3bb938044178d) - * Copyright (c) Microsoft Corporation. - * All Rights Reserved. - * Licensed under the Source EULA. - */ -var __winjs_exports; - -(function() { - -var _modules = Object.create(null);//{}; -_modules["WinJS/Core/_WinJS"] = {}; - -var _winjs = function(moduleId, deps, factory) { - var exports = {}; - var exportsPassedIn = false; - - var depsValues = deps.map(function(dep) { - if (dep === 'exports') { - exportsPassedIn = true; - return exports; - } - return _modules[dep]; - }); - - var result = factory.apply({}, depsValues); - - _modules[moduleId] = exportsPassedIn ? exports : result; -}; - - -_winjs("WinJS/Core/_Global", [], function () { - "use strict"; - - // Appease jshint - /* global window, self, global */ - - var globalObject = - typeof window !== 'undefined' ? window : - typeof self !== 'undefined' ? self : - typeof global !== 'undefined' ? global : - {}; - return globalObject; -}); - -_winjs("WinJS/Core/_BaseCoreUtils", ["WinJS/Core/_Global"], function baseCoreUtilsInit(_Global) { - "use strict"; - - var hasWinRT = !!_Global.Windows; - - function markSupportedForProcessing(func) { - /// - /// - /// Marks a function as being compatible with declarative processing, such as WinJS.UI.processAll - /// or WinJS.Binding.processAll. - /// - /// - /// The function to be marked as compatible with declarative processing. - /// - /// - /// The input function. - /// - /// - func.supportedForProcessing = true; - return func; - } - - var actualSetImmediate = null; - - return { - hasWinRT: hasWinRT, - markSupportedForProcessing: markSupportedForProcessing, - _setImmediate: function (callback) { - // BEGIN monaco change - if (actualSetImmediate === null) { - if (_Global.setImmediate) { - actualSetImmediate = _Global.setImmediate.bind(_Global); - } else if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { - actualSetImmediate = process.nextTick.bind(process); - } else { - actualSetImmediate = _Global.setTimeout.bind(_Global); - } - } - actualSetImmediate(callback); - // END monaco change - } - }; -}); -_winjs("WinJS/Core/_WriteProfilerMark", ["WinJS/Core/_Global"], function profilerInit(_Global) { - "use strict"; - - return _Global.msWriteProfilerMark || function () { }; -}); -_winjs("WinJS/Core/_Base", ["WinJS/Core/_WinJS","WinJS/Core/_Global","WinJS/Core/_BaseCoreUtils","WinJS/Core/_WriteProfilerMark"], function baseInit(_WinJS, _Global, _BaseCoreUtils, _WriteProfilerMark) { - "use strict"; - - function initializeProperties(target, members, prefix) { - var keys = Object.keys(members); - var isArray = Array.isArray(target); - var properties; - var i, len; - for (i = 0, len = keys.length; i < len; i++) { - var key = keys[i]; - var enumerable = key.charCodeAt(0) !== /*_*/95; - var member = members[key]; - if (member && typeof member === 'object') { - if (member.value !== undefined || typeof member.get === 'function' || typeof member.set === 'function') { - if (member.enumerable === undefined) { - member.enumerable = enumerable; - } - if (prefix && member.setName && typeof member.setName === 'function') { - member.setName(prefix + "." + key); - } - properties = properties || {}; - properties[key] = member; - continue; - } - } - if (!enumerable) { - properties = properties || {}; - properties[key] = { value: member, enumerable: enumerable, configurable: true, writable: true }; - continue; - } - if (isArray) { - target.forEach(function (target) { - target[key] = member; - }); - } else { - target[key] = member; - } - } - if (properties) { - if (isArray) { - target.forEach(function (target) { - Object.defineProperties(target, properties); - }); - } else { - Object.defineProperties(target, properties); - } - } - } - - (function () { - - var _rootNamespace = _WinJS; - if (!_rootNamespace.Namespace) { - _rootNamespace.Namespace = Object.create(Object.prototype); - } - - function createNamespace(parentNamespace, name) { - var currentNamespace = parentNamespace || {}; - if (name) { - var namespaceFragments = name.split("."); - if (currentNamespace === _Global && namespaceFragments[0] === "WinJS") { - currentNamespace = _WinJS; - namespaceFragments.splice(0, 1); - } - for (var i = 0, len = namespaceFragments.length; i < len; i++) { - var namespaceName = namespaceFragments[i]; - if (!currentNamespace[namespaceName]) { - Object.defineProperty(currentNamespace, namespaceName, - { value: {}, writable: false, enumerable: true, configurable: true } - ); - } - currentNamespace = currentNamespace[namespaceName]; - } - } - return currentNamespace; - } - - function defineWithParent(parentNamespace, name, members) { - /// - /// - /// Defines a new namespace with the specified name under the specified parent namespace. - /// - /// - /// The parent namespace. - /// - /// - /// The name of the new namespace. - /// - /// - /// The members of the new namespace. - /// - /// - /// The newly-defined namespace. - /// - /// - var currentNamespace = createNamespace(parentNamespace, name); - - if (members) { - initializeProperties(currentNamespace, members, name || ""); - } - - return currentNamespace; - } - - function define(name, members) { - /// - /// - /// Defines a new namespace with the specified name. - /// - /// - /// The name of the namespace. This could be a dot-separated name for nested namespaces. - /// - /// - /// The members of the new namespace. - /// - /// - /// The newly-defined namespace. - /// - /// - return defineWithParent(_Global, name, members); - } - - var LazyStates = { - uninitialized: 1, - working: 2, - initialized: 3, - }; - - function lazy(f) { - var name; - var state = LazyStates.uninitialized; - var result; - return { - setName: function (value) { - name = value; - }, - get: function () { - switch (state) { - case LazyStates.initialized: - return result; - - case LazyStates.uninitialized: - state = LazyStates.working; - try { - _WriteProfilerMark("WinJS.Namespace._lazy:" + name + ",StartTM"); - result = f(); - } finally { - _WriteProfilerMark("WinJS.Namespace._lazy:" + name + ",StopTM"); - state = LazyStates.uninitialized; - } - f = null; - state = LazyStates.initialized; - return result; - - case LazyStates.working: - throw "Illegal: reentrancy on initialization"; - - default: - throw "Illegal"; - } - }, - set: function (value) { - switch (state) { - case LazyStates.working: - throw "Illegal: reentrancy on initialization"; - - default: - state = LazyStates.initialized; - result = value; - break; - } - }, - enumerable: true, - configurable: true, - }; - } - - // helper for defining AMD module members - function moduleDefine(exports, name, members) { - var target = [exports]; - var publicNS = null; - if (name) { - publicNS = createNamespace(_Global, name); - target.push(publicNS); - } - initializeProperties(target, members, name || ""); - return publicNS; - } - - // Establish members of the "WinJS.Namespace" namespace - Object.defineProperties(_rootNamespace.Namespace, { - - defineWithParent: { value: defineWithParent, writable: true, enumerable: true, configurable: true }, - - define: { value: define, writable: true, enumerable: true, configurable: true }, - - _lazy: { value: lazy, writable: true, enumerable: true, configurable: true }, - - _moduleDefine: { value: moduleDefine, writable: true, enumerable: true, configurable: true } - - }); - - })(); - - (function () { - - function define(constructor, instanceMembers, staticMembers) { - /// - /// - /// Defines a class using the given constructor and the specified instance members. - /// - /// - /// A constructor function that is used to instantiate this class. - /// - /// - /// The set of instance fields, properties, and methods made available on the class. - /// - /// - /// The set of static fields, properties, and methods made available on the class. - /// - /// - /// The newly-defined class. - /// - /// - constructor = constructor || function () { }; - _BaseCoreUtils.markSupportedForProcessing(constructor); - if (instanceMembers) { - initializeProperties(constructor.prototype, instanceMembers); - } - if (staticMembers) { - initializeProperties(constructor, staticMembers); - } - return constructor; - } - - function derive(baseClass, constructor, instanceMembers, staticMembers) { - /// - /// - /// Creates a sub-class based on the supplied baseClass parameter, using prototypal inheritance. - /// - /// - /// The class to inherit from. - /// - /// - /// A constructor function that is used to instantiate this class. - /// - /// - /// The set of instance fields, properties, and methods to be made available on the class. - /// - /// - /// The set of static fields, properties, and methods to be made available on the class. - /// - /// - /// The newly-defined class. - /// - /// - if (baseClass) { - constructor = constructor || function () { }; - var basePrototype = baseClass.prototype; - constructor.prototype = Object.create(basePrototype); - _BaseCoreUtils.markSupportedForProcessing(constructor); - Object.defineProperty(constructor.prototype, "constructor", { value: constructor, writable: true, configurable: true, enumerable: true }); - if (instanceMembers) { - initializeProperties(constructor.prototype, instanceMembers); - } - if (staticMembers) { - initializeProperties(constructor, staticMembers); - } - return constructor; - } else { - return define(constructor, instanceMembers, staticMembers); - } - } - - function mix(constructor) { - /// - /// - /// Defines a class using the given constructor and the union of the set of instance members - /// specified by all the mixin objects. The mixin parameter list is of variable length. - /// - /// - /// A constructor function that is used to instantiate this class. - /// - /// - /// The newly-defined class. - /// - /// - constructor = constructor || function () { }; - var i, len; - for (i = 1, len = arguments.length; i < len; i++) { - initializeProperties(constructor.prototype, arguments[i]); - } - return constructor; - } - - // Establish members of "WinJS.Class" namespace - _WinJS.Namespace.define("WinJS.Class", { - define: define, - derive: derive, - mix: mix - }); - - })(); - - return { - Namespace: _WinJS.Namespace, - Class: _WinJS.Class - }; - -}); -_winjs("WinJS/Core/_ErrorFromName", ["WinJS/Core/_Base"], function errorsInit(_Base) { - "use strict"; - - var ErrorFromName = _Base.Class.derive(Error, function (name, message) { - /// - /// - /// Creates an Error object with the specified name and message properties. - /// - /// The name of this error. The name is meant to be consumed programmatically and should not be localized. - /// The message for this error. The message is meant to be consumed by humans and should be localized. - /// Error instance with .name and .message properties populated - /// - this.name = name; - this.message = message || name; - }, { - /* empty */ - }, { - supportedForProcessing: false, - }); - - _Base.Namespace.define("WinJS", { - // ErrorFromName establishes a simple pattern for returning error codes. - // - ErrorFromName: ErrorFromName - }); - - return ErrorFromName; - -}); - - -_winjs("WinJS/Core/_Events", ["exports","WinJS/Core/_Base"], function eventsInit(exports, _Base) { - "use strict"; - - - function createEventProperty(name) { - var eventPropStateName = "_on" + name + "state"; - - return { - get: function () { - var state = this[eventPropStateName]; - return state && state.userHandler; - }, - set: function (handler) { - var state = this[eventPropStateName]; - if (handler) { - if (!state) { - state = { wrapper: function (evt) { return state.userHandler(evt); }, userHandler: handler }; - Object.defineProperty(this, eventPropStateName, { value: state, enumerable: false, writable:true, configurable: true }); - this.addEventListener(name, state.wrapper, false); - } - state.userHandler = handler; - } else if (state) { - this.removeEventListener(name, state.wrapper, false); - this[eventPropStateName] = null; - } - }, - enumerable: true - }; - } - - function createEventProperties() { - /// - /// - /// Creates an object that has one property for each name passed to the function. - /// - /// - /// A variable list of property names. - /// - /// - /// The object with the specified properties. The names of the properties are prefixed with 'on'. - /// - /// - var props = {}; - for (var i = 0, len = arguments.length; i < len; i++) { - var name = arguments[i]; - props["on" + name] = createEventProperty(name); - } - return props; - } - - var EventMixinEvent = _Base.Class.define( - function EventMixinEvent_ctor(type, detail, target) { - this.detail = detail; - this.target = target; - this.timeStamp = Date.now(); - this.type = type; - }, - { - bubbles: { value: false, writable: false }, - cancelable: { value: false, writable: false }, - currentTarget: { - get: function () { return this.target; } - }, - defaultPrevented: { - get: function () { return this._preventDefaultCalled; } - }, - trusted: { value: false, writable: false }, - eventPhase: { value: 0, writable: false }, - target: null, - timeStamp: null, - type: null, - - preventDefault: function () { - this._preventDefaultCalled = true; - }, - stopImmediatePropagation: function () { - this._stopImmediatePropagationCalled = true; - }, - stopPropagation: function () { - } - }, { - supportedForProcessing: false, - } - ); - - var eventMixin = { - _listeners: null, - - addEventListener: function (type, listener, useCapture) { - /// - /// - /// Adds an event listener to the control. - /// - /// - /// The type (name) of the event. - /// - /// - /// The listener to invoke when the event is raised. - /// - /// - /// if true initiates capture, otherwise false. - /// - /// - useCapture = useCapture || false; - this._listeners = this._listeners || {}; - var eventListeners = (this._listeners[type] = this._listeners[type] || []); - for (var i = 0, len = eventListeners.length; i < len; i++) { - var l = eventListeners[i]; - if (l.useCapture === useCapture && l.listener === listener) { - return; - } - } - eventListeners.push({ listener: listener, useCapture: useCapture }); - }, - dispatchEvent: function (type, details) { - /// - /// - /// Raises an event of the specified type and with the specified additional properties. - /// - /// - /// The type (name) of the event. - /// - /// - /// The set of additional properties to be attached to the event object when the event is raised. - /// - /// - /// true if preventDefault was called on the event. - /// - /// - var listeners = this._listeners && this._listeners[type]; - if (listeners) { - var eventValue = new EventMixinEvent(type, details, this); - // Need to copy the array to protect against people unregistering while we are dispatching - listeners = listeners.slice(0, listeners.length); - for (var i = 0, len = listeners.length; i < len && !eventValue._stopImmediatePropagationCalled; i++) { - listeners[i].listener(eventValue); - } - return eventValue.defaultPrevented || false; - } - return false; - }, - removeEventListener: function (type, listener, useCapture) { - /// - /// - /// Removes an event listener from the control. - /// - /// - /// The type (name) of the event. - /// - /// - /// The listener to remove. - /// - /// - /// Specifies whether to initiate capture. - /// - /// - useCapture = useCapture || false; - var listeners = this._listeners && this._listeners[type]; - if (listeners) { - for (var i = 0, len = listeners.length; i < len; i++) { - var l = listeners[i]; - if (l.listener === listener && l.useCapture === useCapture) { - listeners.splice(i, 1); - if (listeners.length === 0) { - delete this._listeners[type]; - } - // Only want to remove one element for each call to removeEventListener - break; - } - } - } - } - }; - - _Base.Namespace._moduleDefine(exports, "WinJS.Utilities", { - _createEventProperty: createEventProperty, - createEventProperties: createEventProperties, - eventMixin: eventMixin - }); - -}); - - -_winjs("WinJS/Core/_Trace", ["WinJS/Core/_Global"], function traceInit(_Global) { - "use strict"; - - function nop(v) { - return v; - } - - return { - _traceAsyncOperationStarting: (_Global.Debug && _Global.Debug.msTraceAsyncOperationStarting && _Global.Debug.msTraceAsyncOperationStarting.bind(_Global.Debug)) || nop, - _traceAsyncOperationCompleted: (_Global.Debug && _Global.Debug.msTraceAsyncOperationCompleted && _Global.Debug.msTraceAsyncOperationCompleted.bind(_Global.Debug)) || nop, - _traceAsyncCallbackStarting: (_Global.Debug && _Global.Debug.msTraceAsyncCallbackStarting && _Global.Debug.msTraceAsyncCallbackStarting.bind(_Global.Debug)) || nop, - _traceAsyncCallbackCompleted: (_Global.Debug && _Global.Debug.msTraceAsyncCallbackCompleted && _Global.Debug.msTraceAsyncCallbackCompleted.bind(_Global.Debug)) || nop - }; -}); -_winjs("WinJS/Promise/_StateMachine", ["WinJS/Core/_Global","WinJS/Core/_BaseCoreUtils","WinJS/Core/_Base","WinJS/Core/_ErrorFromName","WinJS/Core/_Events","WinJS/Core/_Trace"], function promiseStateMachineInit(_Global, _BaseCoreUtils, _Base, _ErrorFromName, _Events, _Trace) { - "use strict"; - - _Global.Debug && (_Global.Debug.setNonUserCodeExceptions = true); - - var ListenerType = _Base.Class.mix(_Base.Class.define(null, { /*empty*/ }, { supportedForProcessing: false }), _Events.eventMixin); - var promiseEventListeners = new ListenerType(); - // make sure there is a listeners collection so that we can do a more trivial check below - promiseEventListeners._listeners = {}; - var errorET = "error"; - var canceledName = "Canceled"; - var tagWithStack = false; - var tag = { - promise: 0x01, - thenPromise: 0x02, - errorPromise: 0x04, - exceptionPromise: 0x08, - completePromise: 0x10, - }; - tag.all = tag.promise | tag.thenPromise | tag.errorPromise | tag.exceptionPromise | tag.completePromise; - - // - // Global error counter, for each error which enters the system we increment this once and then - // the error number travels with the error as it traverses the tree of potential handlers. - // - // When someone has registered to be told about errors (WinJS.Promise.callonerror) promises - // which are in error will get tagged with a ._errorId field. This tagged field is the - // contract by which nested promises with errors will be identified as chaining for the - // purposes of the callonerror semantics. If a nested promise in error is encountered without - // a ._errorId it will be assumed to be foreign and treated as an interop boundary and - // a new error id will be minted. - // - var error_number = 1; - - // - // The state machine has a interesting hiccup in it with regards to notification, in order - // to flatten out notification and avoid recursion for synchronous completion we have an - // explicit set of *_notify states which are responsible for notifying their entire tree - // of children. They can do this because they know that immediate children are always - // ThenPromise instances and we can therefore reach into their state to access the - // _listeners collection. - // - // So, what happens is that a Promise will be fulfilled through the _completed or _error - // messages at which point it will enter a *_notify state and be responsible for to move - // its children into an (as appropriate) success or error state and also notify that child's - // listeners of the state transition, until leaf notes are reached. - // - - var state_created, // -> working - state_working, // -> error | error_notify | success | success_notify | canceled | waiting - state_waiting, // -> error | error_notify | success | success_notify | waiting_canceled - state_waiting_canceled, // -> error | error_notify | success | success_notify | canceling - state_canceled, // -> error | error_notify | success | success_notify | canceling - state_canceling, // -> error_notify - state_success_notify, // -> success - state_success, // -> . - state_error_notify, // -> error - state_error; // -> . - - // Noop function, used in the various states to indicate that they don't support a given - // message. Named with the somewhat cute name '_' because it reads really well in the states. - - function _() { } - - // Initial state - // - state_created = { - name: "created", - enter: function (promise) { - promise._setState(state_working); - }, - cancel: _, - done: _, - then: _, - _completed: _, - _error: _, - _notify: _, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // Ready state, waiting for a message (completed/error/progress), able to be canceled - // - state_working = { - name: "working", - enter: _, - cancel: function (promise) { - promise._setState(state_canceled); - }, - done: done, - then: then, - _completed: completed, - _error: error, - _notify: _, - _progress: progress, - _setCompleteValue: setCompleteValue, - _setErrorValue: setErrorValue - }; - - // Waiting state, if a promise is completed with a value which is itself a promise - // (has a then() method) it signs up to be informed when that child promise is - // fulfilled at which point it will be fulfilled with that value. - // - state_waiting = { - name: "waiting", - enter: function (promise) { - var waitedUpon = promise._value; - // We can special case our own intermediate promises which are not in a - // terminal state by just pushing this promise as a listener without - // having to create new indirection functions - if (waitedUpon instanceof ThenPromise && - waitedUpon._state !== state_error && - waitedUpon._state !== state_success) { - pushListener(waitedUpon, { promise: promise }); - } else { - var error = function (value) { - if (waitedUpon._errorId) { - promise._chainedError(value, waitedUpon); - } else { - // Because this is an interop boundary we want to indicate that this - // error has been handled by the promise infrastructure before we - // begin a new handling chain. - // - callonerror(promise, value, detailsForHandledError, waitedUpon, error); - promise._error(value); - } - }; - error.handlesOnError = true; - waitedUpon.then( - promise._completed.bind(promise), - error, - promise._progress.bind(promise) - ); - } - }, - cancel: function (promise) { - promise._setState(state_waiting_canceled); - }, - done: done, - then: then, - _completed: completed, - _error: error, - _notify: _, - _progress: progress, - _setCompleteValue: setCompleteValue, - _setErrorValue: setErrorValue - }; - - // Waiting canceled state, when a promise has been in a waiting state and receives a - // request to cancel its pending work it will forward that request to the child promise - // and then waits to be informed of the result. This promise moves itself into the - // canceling state but understands that the child promise may instead push it to a - // different state. - // - state_waiting_canceled = { - name: "waiting_canceled", - enter: function (promise) { - // Initiate a transition to canceling. Triggering a cancel on the promise - // that we are waiting upon may result in a different state transition - // before the state machine pump runs again. - promise._setState(state_canceling); - var waitedUpon = promise._value; - if (waitedUpon.cancel) { - waitedUpon.cancel(); - } - }, - cancel: _, - done: done, - then: then, - _completed: completed, - _error: error, - _notify: _, - _progress: progress, - _setCompleteValue: setCompleteValue, - _setErrorValue: setErrorValue - }; - - // Canceled state, moves to the canceling state and then tells the promise to do - // whatever it might need to do on cancelation. - // - state_canceled = { - name: "canceled", - enter: function (promise) { - // Initiate a transition to canceling. The _cancelAction may change the state - // before the state machine pump runs again. - promise._setState(state_canceling); - promise._cancelAction(); - }, - cancel: _, - done: done, - then: then, - _completed: completed, - _error: error, - _notify: _, - _progress: progress, - _setCompleteValue: setCompleteValue, - _setErrorValue: setErrorValue - }; - - // Canceling state, commits to the promise moving to an error state with an error - // object whose 'name' and 'message' properties contain the string "Canceled" - // - state_canceling = { - name: "canceling", - enter: function (promise) { - var error = new Error(canceledName); - error.name = error.message; - promise._value = error; - promise._setState(state_error_notify); - }, - cancel: _, - done: _, - then: _, - _completed: _, - _error: _, - _notify: _, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // Success notify state, moves a promise to the success state and notifies all children - // - state_success_notify = { - name: "complete_notify", - enter: function (promise) { - promise.done = CompletePromise.prototype.done; - promise.then = CompletePromise.prototype.then; - if (promise._listeners) { - var queue = [promise]; - var p; - while (queue.length) { - p = queue.shift(); - p._state._notify(p, queue); - } - } - promise._setState(state_success); - }, - cancel: _, - done: null, /*error to get here */ - then: null, /*error to get here */ - _completed: _, - _error: _, - _notify: notifySuccess, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // Success state, moves a promise to the success state and does NOT notify any children. - // Some upstream promise is owning the notification pass. - // - state_success = { - name: "success", - enter: function (promise) { - promise.done = CompletePromise.prototype.done; - promise.then = CompletePromise.prototype.then; - promise._cleanupAction(); - }, - cancel: _, - done: null, /*error to get here */ - then: null, /*error to get here */ - _completed: _, - _error: _, - _notify: notifySuccess, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // Error notify state, moves a promise to the error state and notifies all children - // - state_error_notify = { - name: "error_notify", - enter: function (promise) { - promise.done = ErrorPromise.prototype.done; - promise.then = ErrorPromise.prototype.then; - if (promise._listeners) { - var queue = [promise]; - var p; - while (queue.length) { - p = queue.shift(); - p._state._notify(p, queue); - } - } - promise._setState(state_error); - }, - cancel: _, - done: null, /*error to get here*/ - then: null, /*error to get here*/ - _completed: _, - _error: _, - _notify: notifyError, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // Error state, moves a promise to the error state and does NOT notify any children. - // Some upstream promise is owning the notification pass. - // - state_error = { - name: "error", - enter: function (promise) { - promise.done = ErrorPromise.prototype.done; - promise.then = ErrorPromise.prototype.then; - promise._cleanupAction(); - }, - cancel: _, - done: null, /*error to get here*/ - then: null, /*error to get here*/ - _completed: _, - _error: _, - _notify: notifyError, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // - // The statemachine implementation follows a very particular pattern, the states are specified - // as static stateless bags of functions which are then indirected through the state machine - // instance (a Promise). As such all of the functions on each state have the promise instance - // passed to them explicitly as a parameter and the Promise instance members do a little - // dance where they indirect through the state and insert themselves in the argument list. - // - // We could instead call directly through the promise states however then every caller - // would have to remember to do things like pumping the state machine to catch state transitions. - // - - var PromiseStateMachine = _Base.Class.define(null, { - _listeners: null, - _nextState: null, - _state: null, - _value: null, - - cancel: function () { - /// - /// - /// Attempts to cancel the fulfillment of a promised value. If the promise hasn't - /// already been fulfilled and cancellation is supported, the promise enters - /// the error state with a value of Error("Canceled"). - /// - /// - this._state.cancel(this); - this._run(); - }, - done: function Promise_done(onComplete, onError, onProgress) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// After the handlers have finished executing, this function throws any error that would have been returned - /// from then() as a promise in the error state. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The fulfilled value is passed as the single argument. If the value is null, - /// the fulfilled value is returned. The value returned - /// from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while executing the function, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function is the fulfilled value of the promise returned by then(). - /// - /// - /// the function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - this._state.done(this, onComplete, onError, onProgress); - }, - then: function Promise_then(onComplete, onError, onProgress) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The value is passed as the single argument. If the value is null, the value is returned. - /// The value returned from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while this function is being executed, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function becomes the fulfilled value of the promise returned by then(). - /// - /// - /// The function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - /// The promise whose value is the result of executing the complete or - /// error function. - /// - /// - // BEGIN monaco change - if (this.then !== Promise_then) { - this.then(onComplete, onError, onProgress); - return; - } - // END monaco change - return this._state.then(this, onComplete, onError, onProgress); - }, - - _chainedError: function (value, context) { - var result = this._state._error(this, value, detailsForChainedError, context); - this._run(); - return result; - }, - _completed: function (value) { - var result = this._state._completed(this, value); - this._run(); - return result; - }, - _error: function (value) { - var result = this._state._error(this, value, detailsForError); - this._run(); - return result; - }, - _progress: function (value) { - this._state._progress(this, value); - }, - _setState: function (state) { - this._nextState = state; - }, - _setCompleteValue: function (value) { - this._state._setCompleteValue(this, value); - this._run(); - }, - _setChainedErrorValue: function (value, context) { - var result = this._state._setErrorValue(this, value, detailsForChainedError, context); - this._run(); - return result; - }, - _setExceptionValue: function (value) { - var result = this._state._setErrorValue(this, value, detailsForException); - this._run(); - return result; - }, - _run: function () { - while (this._nextState) { - this._state = this._nextState; - this._nextState = null; - this._state.enter(this); - } - } - }, { - supportedForProcessing: false - }); - - // - // Implementations of shared state machine code. - // - - function completed(promise, value) { - var targetState; - if (value && typeof value === "object" && typeof value.then === "function") { - targetState = state_waiting; - } else { - targetState = state_success_notify; - } - promise._value = value; - promise._setState(targetState); - } - function createErrorDetails(exception, error, promise, id, parent, handler) { - return { - exception: exception, - error: error, - promise: promise, - handler: handler, - id: id, - parent: parent - }; - } - function detailsForHandledError(promise, errorValue, context, handler) { - var exception = context._isException; - var errorId = context._errorId; - return createErrorDetails( - exception ? errorValue : null, - exception ? null : errorValue, - promise, - errorId, - context, - handler - ); - } - function detailsForChainedError(promise, errorValue, context) { - var exception = context._isException; - var errorId = context._errorId; - setErrorInfo(promise, errorId, exception); - return createErrorDetails( - exception ? errorValue : null, - exception ? null : errorValue, - promise, - errorId, - context - ); - } - function detailsForError(promise, errorValue) { - var errorId = ++error_number; - setErrorInfo(promise, errorId); - return createErrorDetails( - null, - errorValue, - promise, - errorId - ); - } - function detailsForException(promise, exceptionValue) { - var errorId = ++error_number; - setErrorInfo(promise, errorId, true); - return createErrorDetails( - exceptionValue, - null, - promise, - errorId - ); - } - function done(promise, onComplete, onError, onProgress) { - var asyncOpID = _Trace._traceAsyncOperationStarting("WinJS.Promise.done"); - pushListener(promise, { c: onComplete, e: onError, p: onProgress, asyncOpID: asyncOpID }); - } - function error(promise, value, onerrorDetails, context) { - promise._value = value; - callonerror(promise, value, onerrorDetails, context); - promise._setState(state_error_notify); - } - function notifySuccess(promise, queue) { - var value = promise._value; - var listeners = promise._listeners; - if (!listeners) { - return; - } - promise._listeners = null; - var i, len; - for (i = 0, len = Array.isArray(listeners) ? listeners.length : 1; i < len; i++) { - var listener = len === 1 ? listeners : listeners[i]; - var onComplete = listener.c; - var target = listener.promise; - - _Trace._traceAsyncOperationCompleted(listener.asyncOpID, _Global.Debug && _Global.Debug.MS_ASYNC_OP_STATUS_SUCCESS); - - if (target) { - _Trace._traceAsyncCallbackStarting(listener.asyncOpID); - try { - target._setCompleteValue(onComplete ? onComplete(value) : value); - } catch (ex) { - target._setExceptionValue(ex); - } finally { - _Trace._traceAsyncCallbackCompleted(); - } - if (target._state !== state_waiting && target._listeners) { - queue.push(target); - } - } else { - CompletePromise.prototype.done.call(promise, onComplete); - } - } - } - function notifyError(promise, queue) { - var value = promise._value; - var listeners = promise._listeners; - if (!listeners) { - return; - } - promise._listeners = null; - var i, len; - for (i = 0, len = Array.isArray(listeners) ? listeners.length : 1; i < len; i++) { - var listener = len === 1 ? listeners : listeners[i]; - var onError = listener.e; - var target = listener.promise; - - var errorID = _Global.Debug && (value && value.name === canceledName ? _Global.Debug.MS_ASYNC_OP_STATUS_CANCELED : _Global.Debug.MS_ASYNC_OP_STATUS_ERROR); - _Trace._traceAsyncOperationCompleted(listener.asyncOpID, errorID); - - if (target) { - var asyncCallbackStarted = false; - try { - if (onError) { - _Trace._traceAsyncCallbackStarting(listener.asyncOpID); - asyncCallbackStarted = true; - if (!onError.handlesOnError) { - callonerror(target, value, detailsForHandledError, promise, onError); - } - target._setCompleteValue(onError(value)); - } else { - target._setChainedErrorValue(value, promise); - } - } catch (ex) { - target._setExceptionValue(ex); - } finally { - if (asyncCallbackStarted) { - _Trace._traceAsyncCallbackCompleted(); - } - } - if (target._state !== state_waiting && target._listeners) { - queue.push(target); - } - } else { - ErrorPromise.prototype.done.call(promise, null, onError); - } - } - } - function callonerror(promise, value, onerrorDetailsGenerator, context, handler) { - if (promiseEventListeners._listeners[errorET]) { - if (value instanceof Error && value.message === canceledName) { - return; - } - promiseEventListeners.dispatchEvent(errorET, onerrorDetailsGenerator(promise, value, context, handler)); - } - } - function progress(promise, value) { - var listeners = promise._listeners; - if (listeners) { - var i, len; - for (i = 0, len = Array.isArray(listeners) ? listeners.length : 1; i < len; i++) { - var listener = len === 1 ? listeners : listeners[i]; - var onProgress = listener.p; - if (onProgress) { - try { onProgress(value); } catch (ex) { } - } - if (!(listener.c || listener.e) && listener.promise) { - listener.promise._progress(value); - } - } - } - } - function pushListener(promise, listener) { - var listeners = promise._listeners; - if (listeners) { - // We may have either a single listener (which will never be wrapped in an array) - // or 2+ listeners (which will be wrapped). Since we are now adding one more listener - // we may have to wrap the single listener before adding the second. - listeners = Array.isArray(listeners) ? listeners : [listeners]; - listeners.push(listener); - } else { - listeners = listener; - } - promise._listeners = listeners; - } - // The difference beween setCompleteValue()/setErrorValue() and complete()/error() is that setXXXValue() moves - // a promise directly to the success/error state without starting another notification pass (because one - // is already ongoing). - function setErrorInfo(promise, errorId, isException) { - promise._isException = isException || false; - promise._errorId = errorId; - } - function setErrorValue(promise, value, onerrorDetails, context) { - promise._value = value; - callonerror(promise, value, onerrorDetails, context); - promise._setState(state_error); - } - function setCompleteValue(promise, value) { - var targetState; - if (value && typeof value === "object" && typeof value.then === "function") { - targetState = state_waiting; - } else { - targetState = state_success; - } - promise._value = value; - promise._setState(targetState); - } - function then(promise, onComplete, onError, onProgress) { - var result = new ThenPromise(promise); - var asyncOpID = _Trace._traceAsyncOperationStarting("WinJS.Promise.then"); - pushListener(promise, { promise: result, c: onComplete, e: onError, p: onProgress, asyncOpID: asyncOpID }); - return result; - } - - // - // Internal implementation detail promise, ThenPromise is created when a promise needs - // to be returned from a then() method. - // - var ThenPromise = _Base.Class.derive(PromiseStateMachine, - function (creator) { - - if (tagWithStack && (tagWithStack === true || (tagWithStack & tag.thenPromise))) { - this._stack = Promise._getStack(); - } - - this._creator = creator; - this._setState(state_created); - this._run(); - }, { - _creator: null, - - _cancelAction: function () { if (this._creator) { this._creator.cancel(); } }, - _cleanupAction: function () { this._creator = null; } - }, { - supportedForProcessing: false - } - ); - - // - // Slim promise implementations for already completed promises, these are created - // under the hood on synchronous completion paths as well as by WinJS.Promise.wrap - // and WinJS.Promise.wrapError. - // - - var ErrorPromise = _Base.Class.define( - function ErrorPromise_ctor(value) { - - if (tagWithStack && (tagWithStack === true || (tagWithStack & tag.errorPromise))) { - this._stack = Promise._getStack(); - } - - this._value = value; - callonerror(this, value, detailsForError); - }, { - cancel: function () { - /// - /// - /// Attempts to cancel the fulfillment of a promised value. If the promise hasn't - /// already been fulfilled and cancellation is supported, the promise enters - /// the error state with a value of Error("Canceled"). - /// - /// - }, - done: function ErrorPromise_done(unused, onError) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// After the handlers have finished executing, this function throws any error that would have been returned - /// from then() as a promise in the error state. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The fulfilled value is passed as the single argument. If the value is null, - /// the fulfilled value is returned. The value returned - /// from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while executing the function, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function is the fulfilled value of the promise returned by then(). - /// - /// - /// the function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - var value = this._value; - if (onError) { - try { - if (!onError.handlesOnError) { - callonerror(null, value, detailsForHandledError, this, onError); - } - var result = onError(value); - if (result && typeof result === "object" && typeof result.done === "function") { - // If a promise is returned we need to wait on it. - result.done(); - } - return; - } catch (ex) { - value = ex; - } - } - if (value instanceof Error && value.message === canceledName) { - // suppress cancel - return; - } - // force the exception to be thrown asyncronously to avoid any try/catch blocks - // - Promise._doneHandler(value); - }, - then: function ErrorPromise_then(unused, onError) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The value is passed as the single argument. If the value is null, the value is returned. - /// The value returned from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while this function is being executed, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function becomes the fulfilled value of the promise returned by then(). - /// - /// - /// The function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - /// The promise whose value is the result of executing the complete or - /// error function. - /// - /// - - // If the promise is already in a error state and no error handler is provided - // we optimize by simply returning the promise instead of creating a new one. - // - if (!onError) { return this; } - var result; - var value = this._value; - try { - if (!onError.handlesOnError) { - callonerror(null, value, detailsForHandledError, this, onError); - } - result = new CompletePromise(onError(value)); - } catch (ex) { - // If the value throw from the error handler is the same as the value - // provided to the error handler then there is no need for a new promise. - // - if (ex === value) { - result = this; - } else { - result = new ExceptionPromise(ex); - } - } - return result; - } - }, { - supportedForProcessing: false - } - ); - - var ExceptionPromise = _Base.Class.derive(ErrorPromise, - function ExceptionPromise_ctor(value) { - - if (tagWithStack && (tagWithStack === true || (tagWithStack & tag.exceptionPromise))) { - this._stack = Promise._getStack(); - } - - this._value = value; - callonerror(this, value, detailsForException); - }, { - /* empty */ - }, { - supportedForProcessing: false - } - ); - - var CompletePromise = _Base.Class.define( - function CompletePromise_ctor(value) { - - if (tagWithStack && (tagWithStack === true || (tagWithStack & tag.completePromise))) { - this._stack = Promise._getStack(); - } - - if (value && typeof value === "object" && typeof value.then === "function") { - var result = new ThenPromise(null); - result._setCompleteValue(value); - return result; - } - this._value = value; - }, { - cancel: function () { - /// - /// - /// Attempts to cancel the fulfillment of a promised value. If the promise hasn't - /// already been fulfilled and cancellation is supported, the promise enters - /// the error state with a value of Error("Canceled"). - /// - /// - }, - done: function CompletePromise_done(onComplete) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// After the handlers have finished executing, this function throws any error that would have been returned - /// from then() as a promise in the error state. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The fulfilled value is passed as the single argument. If the value is null, - /// the fulfilled value is returned. The value returned - /// from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while executing the function, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function is the fulfilled value of the promise returned by then(). - /// - /// - /// the function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - if (!onComplete) { return; } - try { - var result = onComplete(this._value); - if (result && typeof result === "object" && typeof result.done === "function") { - result.done(); - } - } catch (ex) { - // force the exception to be thrown asynchronously to avoid any try/catch blocks - Promise._doneHandler(ex); - } - }, - then: function CompletePromise_then(onComplete) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The value is passed as the single argument. If the value is null, the value is returned. - /// The value returned from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while this function is being executed, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function becomes the fulfilled value of the promise returned by then(). - /// - /// - /// The function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - /// The promise whose value is the result of executing the complete or - /// error function. - /// - /// - try { - // If the value returned from the completion handler is the same as the value - // provided to the completion handler then there is no need for a new promise. - // - var newValue = onComplete ? onComplete(this._value) : this._value; - return newValue === this._value ? this : new CompletePromise(newValue); - } catch (ex) { - return new ExceptionPromise(ex); - } - } - }, { - supportedForProcessing: false - } - ); - - // - // Promise is the user-creatable WinJS.Promise object. - // - - function timeout(timeoutMS) { - var id; - return new Promise( - function (c) { - if (timeoutMS) { - id = _Global.setTimeout(c, timeoutMS); - } else { - _BaseCoreUtils._setImmediate(c); - } - }, - function () { - if (id) { - _Global.clearTimeout(id); - } - } - ); - } - - function timeoutWithPromise(timeout, promise) { - var cancelPromise = function () { promise.cancel(); }; - var cancelTimeout = function () { timeout.cancel(); }; - timeout.then(cancelPromise); - promise.then(cancelTimeout, cancelTimeout); - return promise; - } - - var staticCanceledPromise; - - var Promise = _Base.Class.derive(PromiseStateMachine, - function Promise_ctor(init, oncancel) { - /// - /// - /// A promise provides a mechanism to schedule work to be done on a value that - /// has not yet been computed. It is a convenient abstraction for managing - /// interactions with asynchronous APIs. - /// - /// - /// The function that is called during construction of the promise. The function - /// is given three arguments (complete, error, progress). Inside this function - /// you should add event listeners for the notifications supported by this value. - /// - /// - /// The function to call if a consumer of this promise wants - /// to cancel its undone work. Promises are not required to - /// support cancellation. - /// - /// - - if (tagWithStack && (tagWithStack === true || (tagWithStack & tag.promise))) { - this._stack = Promise._getStack(); - } - - this._oncancel = oncancel; - this._setState(state_created); - this._run(); - - try { - var complete = this._completed.bind(this); - var error = this._error.bind(this); - var progress = this._progress.bind(this); - init(complete, error, progress); - } catch (ex) { - this._setExceptionValue(ex); - } - }, { - _oncancel: null, - - _cancelAction: function () { - // BEGIN monaco change - try { - if (this._oncancel) { - this._oncancel(); - } else { - throw new Error('Promise did not implement oncancel'); - } - } catch (ex) { - // Access fields to get them created - var msg = ex.message; - var stack = ex.stack; - promiseEventListeners.dispatchEvent('error', ex); - } - // END monaco change - }, - _cleanupAction: function () { this._oncancel = null; } - }, { - - addEventListener: function Promise_addEventListener(eventType, listener, capture) { - /// - /// - /// Adds an event listener to the control. - /// - /// - /// The type (name) of the event. - /// - /// - /// The listener to invoke when the event is raised. - /// - /// - /// Specifies whether or not to initiate capture. - /// - /// - promiseEventListeners.addEventListener(eventType, listener, capture); - }, - any: function Promise_any(values) { - /// - /// - /// Returns a promise that is fulfilled when one of the input promises - /// has been fulfilled. - /// - /// - /// An array that contains promise objects or objects whose property - /// values include promise objects. - /// - /// - /// A promise that on fulfillment yields the value of the input (complete or error). - /// - /// - return new Promise( - function (complete, error) { - var keys = Object.keys(values); - if (keys.length === 0) { - complete(); - } - var canceled = 0; - keys.forEach(function (key) { - Promise.as(values[key]).then( - function () { complete({ key: key, value: values[key] }); }, - function (e) { - if (e instanceof Error && e.name === canceledName) { - if ((++canceled) === keys.length) { - complete(Promise.cancel); - } - return; - } - error({ key: key, value: values[key] }); - } - ); - }); - }, - function () { - var keys = Object.keys(values); - keys.forEach(function (key) { - var promise = Promise.as(values[key]); - if (typeof promise.cancel === "function") { - promise.cancel(); - } - }); - } - ); - }, - as: function Promise_as(value) { - /// - /// - /// Returns a promise. If the object is already a promise it is returned; - /// otherwise the object is wrapped in a promise. - /// - /// - /// The value to be treated as a promise. - /// - /// - /// A promise. - /// - /// - if (value && typeof value === "object" && typeof value.then === "function") { - return value; - } - return new CompletePromise(value); - }, - /// - /// Canceled promise value, can be returned from a promise completion handler - /// to indicate cancelation of the promise chain. - /// - cancel: { - get: function () { - return (staticCanceledPromise = staticCanceledPromise || new ErrorPromise(new _ErrorFromName(canceledName))); - } - }, - dispatchEvent: function Promise_dispatchEvent(eventType, details) { - /// - /// - /// Raises an event of the specified type and properties. - /// - /// - /// The type (name) of the event. - /// - /// - /// The set of additional properties to be attached to the event object. - /// - /// - /// Specifies whether preventDefault was called on the event. - /// - /// - return promiseEventListeners.dispatchEvent(eventType, details); - }, - is: function Promise_is(value) { - /// - /// - /// Determines whether a value fulfills the promise contract. - /// - /// - /// A value that may be a promise. - /// - /// - /// true if the specified value is a promise, otherwise false. - /// - /// - return value && typeof value === "object" && typeof value.then === "function"; - }, - join: function Promise_join(values) { - /// - /// - /// Creates a promise that is fulfilled when all the values are fulfilled. - /// - /// - /// An object whose fields contain values, some of which may be promises. - /// - /// - /// A promise whose value is an object with the same field names as those of the object in the values parameter, where - /// each field value is the fulfilled value of a promise. - /// - /// - return new Promise( - function (complete, error, progress) { - var keys = Object.keys(values); - var errors = Array.isArray(values) ? [] : {}; - var results = Array.isArray(values) ? [] : {}; - var undefineds = 0; - var pending = keys.length; - var argDone = function (key) { - if ((--pending) === 0) { - var errorCount = Object.keys(errors).length; - if (errorCount === 0) { - complete(results); - } else { - var canceledCount = 0; - keys.forEach(function (key) { - var e = errors[key]; - if (e instanceof Error && e.name === canceledName) { - canceledCount++; - } - }); - if (canceledCount === errorCount) { - complete(Promise.cancel); - } else { - error(errors); - } - } - } else { - progress({ Key: key, Done: true }); - } - }; - keys.forEach(function (key) { - var value = values[key]; - if (value === undefined) { - undefineds++; - } else { - Promise.then(value, - function (value) { results[key] = value; argDone(key); }, - function (value) { errors[key] = value; argDone(key); } - ); - } - }); - pending -= undefineds; - if (pending === 0) { - complete(results); - return; - } - }, - function () { - Object.keys(values).forEach(function (key) { - var promise = Promise.as(values[key]); - if (typeof promise.cancel === "function") { - promise.cancel(); - } - }); - } - ); - }, - removeEventListener: function Promise_removeEventListener(eventType, listener, capture) { - /// - /// - /// Removes an event listener from the control. - /// - /// - /// The type (name) of the event. - /// - /// - /// The listener to remove. - /// - /// - /// Specifies whether or not to initiate capture. - /// - /// - promiseEventListeners.removeEventListener(eventType, listener, capture); - }, - supportedForProcessing: false, - then: function Promise_then(value, onComplete, onError, onProgress) { - /// - /// - /// A static version of the promise instance method then(). - /// - /// - /// the value to be treated as a promise. - /// - /// - /// The function to be called if the promise is fulfilled with a value. - /// If it is null, the promise simply - /// returns the value. The value is passed as the single argument. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. - /// - /// - /// The function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - /// A promise whose value is the result of executing the provided complete function. - /// - /// - return Promise.as(value).then(onComplete, onError, onProgress); - }, - thenEach: function Promise_thenEach(values, onComplete, onError, onProgress) { - /// - /// - /// Performs an operation on all the input promises and returns a promise - /// that has the shape of the input and contains the result of the operation - /// that has been performed on each input. - /// - /// - /// A set of values (which could be either an array or an object) of which some or all are promises. - /// - /// - /// The function to be called if the promise is fulfilled with a value. - /// If the value is null, the promise returns the value. - /// The value is passed as the single argument. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. - /// - /// - /// The function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - /// A promise that is the result of calling Promise.join on the values parameter. - /// - /// - var result = Array.isArray(values) ? [] : {}; - Object.keys(values).forEach(function (key) { - result[key] = Promise.as(values[key]).then(onComplete, onError, onProgress); - }); - return Promise.join(result); - }, - timeout: function Promise_timeout(time, promise) { - /// - /// - /// Creates a promise that is fulfilled after a timeout. - /// - /// - /// The timeout period in milliseconds. If this value is zero or not specified - /// setImmediate is called, otherwise setTimeout is called. - /// - /// - /// A promise that will be canceled if it doesn't complete before the - /// timeout has expired. - /// - /// - /// A promise that is completed asynchronously after the specified timeout. - /// - /// - var to = timeout(time); - return promise ? timeoutWithPromise(to, promise) : to; - }, - wrap: function Promise_wrap(value) { - /// - /// - /// Wraps a non-promise value in a promise. You can use this function if you need - /// to pass a value to a function that requires a promise. - /// - /// - /// Some non-promise value to be wrapped in a promise. - /// - /// - /// A promise that is successfully fulfilled with the specified value - /// - /// - return new CompletePromise(value); - }, - wrapError: function Promise_wrapError(error) { - /// - /// - /// Wraps a non-promise error value in a promise. You can use this function if you need - /// to pass an error to a function that requires a promise. - /// - /// - /// A non-promise error value to be wrapped in a promise. - /// - /// - /// A promise that is in an error state with the specified value. - /// - /// - return new ErrorPromise(error); - }, - - _veryExpensiveTagWithStack: { - get: function () { return tagWithStack; }, - set: function (value) { tagWithStack = value; } - }, - _veryExpensiveTagWithStack_tag: tag, - _getStack: function () { - if (_Global.Debug && _Global.Debug.debuggerEnabled) { - try { throw new Error(); } catch (e) { return e.stack; } - } - }, - - _cancelBlocker: function Promise__cancelBlocker(input, oncancel) { - // - // Returns a promise which on cancelation will still result in downstream cancelation while - // protecting the promise 'input' from being canceled which has the effect of allowing - // 'input' to be shared amoung various consumers. - // - if (!Promise.is(input)) { - return Promise.wrap(input); - } - var complete; - var error; - var output = new Promise( - function (c, e) { - complete = c; - error = e; - }, - function () { - complete = null; - error = null; - oncancel && oncancel(); - } - ); - input.then( - function (v) { complete && complete(v); }, - function (e) { error && error(e); } - ); - return output; - }, - - } - ); - Object.defineProperties(Promise, _Events.createEventProperties(errorET)); - - Promise._doneHandler = function (value) { - _BaseCoreUtils._setImmediate(function Promise_done_rethrow() { - throw value; - }); - }; - - return { - PromiseStateMachine: PromiseStateMachine, - Promise: Promise, - state_created: state_created - }; -}); - -_winjs("WinJS/Promise", ["WinJS/Core/_Base","WinJS/Promise/_StateMachine"], function promiseInit( _Base, _StateMachine) { - "use strict"; - - _Base.Namespace.define("WinJS", { - Promise: _StateMachine.Promise - }); - - return _StateMachine.Promise; -}); - -__winjs_exports = _modules["WinJS/Core/_WinJS"]; -__winjs_exports.TPromise = __winjs_exports.Promise; -__winjs_exports.PPromise = __winjs_exports.Promise; - -// ESM-comment-begin -if (typeof exports === 'undefined' && typeof define === 'function' && define.amd) { - define([], __winjs_exports); -} else { - module.exports = __winjs_exports; -} -// ESM-comment-end - -})(); - -// ESM-uncomment-begin -// export var Promise = __winjs_exports.Promise; -// export var TPromise = __winjs_exports.TPromise; -// export var PPromise = __winjs_exports.PPromise; -// ESM-uncomment-end diff --git a/src/vs/base/common/winjs.polyfill.promise.ts b/src/vs/base/common/winjs.polyfill.promise.ts deleted file mode 100644 index 614ffd69a581..000000000000 --- a/src/vs/base/common/winjs.polyfill.promise.ts +++ /dev/null @@ -1,133 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Promise as WinJSPromise } from './winjs.base'; -import * as platform from 'vs/base/common/platform'; -import { isThenable } from 'vs/base/common/async'; - -function isWinJSPromise(candidate: any): candidate is WinJSPromise { - return isThenable(candidate) && typeof (candidate as any).done === 'function'; -} - -declare class WinJSPromiseRemovals { - any(promises: (T | PromiseLike)[]): WinJSPromise<{ key: string; value: WinJSPromise; }>; -} - -/** - * A polyfill for the native promises. The implementation is based on - * WinJS promises but tries to gap differences between winjs promises - * and native promises. - */ -export class PolyfillPromise implements Promise { - - static all(thenables: Thenable[]): PolyfillPromise { - return new PolyfillPromise(WinJSPromise.join(thenables).then(null, values => { - // WinJSPromise returns a sparse array whereas - // native promises return the *first* error - for (var key in values) { - if (values.hasOwnProperty(key)) { - return values[key]; - } - } - })); - } - - static race(thenables: Thenable[]): PolyfillPromise { - // WinJSPromise returns `{ key: , value: }` - // from the `any` call and Promise.race just wants the value - return new PolyfillPromise((WinJSPromise as any as WinJSPromiseRemovals).any(thenables).then(entry => entry.value, err => err.value)); - } - - static resolve(value): PolyfillPromise { - return new PolyfillPromise(WinJSPromise.wrap(value)); - } - - static reject(value): PolyfillPromise { - return new PolyfillPromise(WinJSPromise.wrapError(value)); - } - - private _winjsPromise: WinJSPromise; - - constructor(winjsPromise: WinJSPromise); - constructor(callback: (resolve: (value?: T) => void, reject: (err?: any) => void) => any); - constructor(initOrPromise: WinJSPromise | ((resolve: (value?: T) => void, reject: (err?: any) => void) => any)) { - - if (isWinJSPromise(initOrPromise)) { - this._winjsPromise = initOrPromise; - } else { - this._winjsPromise = new WinJSPromise((resolve, reject) => { - let initializing = true; - initOrPromise(function (value) { - if (!initializing) { - resolve(value); - } else { - platform.setImmediate(() => resolve(value)); - } - }, function (err) { - if (!initializing) { - reject(err); - } else { - platform.setImmediate(() => reject(err)); - } - }); - initializing = false; - }); - } - } - - then(onFulfilled?: any, onRejected?: any): PolyfillPromise { - let sync = true; - // To support chaining, we need to return the value of the - // onFulfilled and onRejected callback. - // WinJSPromise supports a flat-map style #then, ie. the callbacks - // passed to WinJSPromise#then can return a Promise. - let promise = new PolyfillPromise(this._winjsPromise.then( - onFulfilled && function (value) { - if (!sync) { - return onFulfilled(value); - } else { - return new WinJSPromise((resolve, reject) => { - platform.setImmediate(() => { - let result; - try { - result = onFulfilled(value); - } - catch (err2) { - reject(err2); - return; - } - resolve(result); - }); - }); - } - }, - onRejected && function (err) { - if (!sync) { - return onRejected(err); - } else { - return new WinJSPromise((resolve, reject) => { - platform.setImmediate(() => { - let result; - try { - result = onRejected(err); - } - catch (err2) { - reject(err2); - return; - } - resolve(result); - }); - }); - } - } - )); - sync = false; - return promise; - } - - catch(onRejected?: any): PolyfillPromise { - return this.then(null, onRejected); - } -} diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index ac16122544e7..39e15d0088ef 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -6,15 +6,6 @@ import { transformErrorForSerialization } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; -import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; - -var global: any = self; - -// When missing, polyfill the native promise -// with our winjs-based polyfill -if (typeof global.Promise === 'undefined') { - global.Promise = PolyfillPromise; -} const INITIALIZE = '$initialize'; @@ -243,8 +234,8 @@ export class SimpleWorkerClient extends Disposable { lazyProxyReject = reject; this._onModuleLoaded.then((availableMethods: string[]) => { let proxy = {}; - for (let i = 0; i < availableMethods.length; i++) { - (proxy as any)[availableMethods[i]] = createProxyMethod(availableMethods[i], proxyMethodRequest); + for (const methodName of availableMethods) { + (proxy as any)[methodName] = createProxyMethod(methodName, proxyMethodRequest); } resolve(proxy); }, (e) => { @@ -254,11 +245,11 @@ export class SimpleWorkerClient extends Disposable { }); // Create proxy to loaded code - let proxyMethodRequest = (method: string, args: any[]): Promise => { + const proxyMethodRequest = (method: string, args: any[]): Promise => { return this._request(method, args); }; - let createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => Promise): Function => { + const createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => Promise): () => Promise => { return function () { let args = Array.prototype.slice.call(arguments, 0); return proxyMethodRequest(method, args); diff --git a/src/vs/base/node/console.ts b/src/vs/base/node/console.ts index 938d822bf4fe..70232b47d27a 100644 --- a/src/vs/base/node/console.ts +++ b/src/vs/base/node/console.ts @@ -83,7 +83,7 @@ export function getFirstFrame(arg0: IRemoteConsoleLog | string | undefined): ISt } } - return void 0; + return undefined; } function findFirstFrame(stack: string | undefined): string | undefined { diff --git a/src/vs/base/node/crypto.ts b/src/vs/base/node/crypto.ts index 46dc55f04600..778799af9c63 100644 --- a/src/vs/base/node/crypto.ts +++ b/src/vs/base/node/crypto.ts @@ -8,7 +8,7 @@ import * as crypto from 'crypto'; import * as stream from 'stream'; import { once } from 'vs/base/common/functional'; -export function checksum(path: string, sha1hash: string): Promise { +export function checksum(path: string, sha1hash: string | undefined): Promise { const promise = new Promise((c, e) => { const input = fs.createReadStream(path); const hash = crypto.createHash('sha1'); diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index 81658e1233fe..c41217ad8d33 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -7,7 +7,7 @@ import * as stream from 'vs/base/node/stream'; import * as iconv from 'iconv-lite'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { exec } from 'child_process'; -import { Readable, Writable, WritableOptions } from 'stream'; +import { Readable, Writable } from 'stream'; export const UTF8 = 'utf8'; export const UTF8_with_bom = 'utf8bom'; @@ -36,15 +36,10 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions readable.pipe(new class extends Writable { private _decodeStream: NodeJS.ReadWriteStream; - private _decodeStreamConstruction: Thenable; + private _decodeStreamConstruction: Promise; private _buffer: Buffer[] = []; private _bytesBuffered = 0; - constructor(opts?: WritableOptions) { - super(opts); - this.once('finish', () => this._finish()); - } - _write(chunk: any, encoding: string, callback: Function): void { if (!Buffer.isBuffer(chunk)) { callback(new Error('data must be a buffer')); @@ -93,14 +88,13 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions callback(err); }); } - - _finish(): void { + _final(callback: (err?: any) => any) { if (this._decodeStream) { // normal finish - this._decodeStream.end(); + this._decodeStream.end(callback); } else { // we were still waiting for data... - this._startDecodeStream(() => this._decodeStream.end()); + this._startDecodeStream(() => this._decodeStream.end(callback)); } } }); @@ -393,15 +387,14 @@ export function resolveTerminalEncoding(verbose?: boolean): Promise { exec('chcp', (err, stdout, stderr) => { if (stdout) { const windowsTerminalEncodingKeys = Object.keys(windowsTerminalEncodings); - for (let i = 0; i < windowsTerminalEncodingKeys.length; i++) { - const key = windowsTerminalEncodingKeys[i]; + for (const key of windowsTerminalEncodingKeys) { if (stdout.indexOf(key) >= 0) { return resolve(windowsTerminalEncodings[key]); } } } - return resolve(void 0); + return resolve(undefined); }); }); } diff --git a/src/vs/base/node/extfs.ts b/src/vs/base/node/extfs.ts index 7f0d50faf8ef..73a0cd792ede 100644 --- a/src/vs/base/node/extfs.ts +++ b/src/vs/base/node/extfs.ts @@ -128,7 +128,7 @@ function doCopyFile(source: string, target: string, mode: number, callback: (err export function mkdirp(path: string, mode?: number, token?: CancellationToken): Promise { const mkdir = (): Promise => { - return nfcall(fs.mkdir, path, mode).then(null, (mkdirErr: NodeJS.ErrnoException) => { + return nfcall(fs.mkdir, path, mode).then(undefined, (mkdirErr: NodeJS.ErrnoException) => { // ENOENT: a parent folder does not exist yet if (mkdirErr.code === 'ENOENT') { @@ -155,7 +155,7 @@ export function mkdirp(path: string, mode?: number, token?: CancellationToken): } // recursively mkdir - return mkdir().then(null, (err: NodeJS.ErrnoException) => { + return mkdir().then(undefined, (err: NodeJS.ErrnoException) => { // Respect cancellation if (token && token.isCancellationRequested) { @@ -390,7 +390,7 @@ function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, if (error) { if (isOpen) { writer.once('close', () => callback(error)); - writer.close(); + writer.destroy(); } else { callback(error); } @@ -450,10 +450,10 @@ function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, canFlush = false; } - writer.close(); + writer.destroy(); }); } else { - writer.close(); + writer.destroy(); } }); diff --git a/src/vs/base/node/flow.ts b/src/vs/base/node/flow.ts index 065e59f2f4f9..f0b1dba3da7e 100644 --- a/src/vs/base/node/flow.ts +++ b/src/vs/base/node/flow.ts @@ -9,7 +9,7 @@ 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: Array | null, result: E[]) => void): void { +export function parallel(list: T[], fn: (item: T, callback: (err: Error | null, result: E | null) => void) => void, callback: (err: Array | null, result: E[]) => void): void { let results = new Array(list.length); let errors = new Array(list.length); let didErrorOccur = false; diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index f8482ec109ac..797ab960f5f9 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -9,7 +9,7 @@ import { nfcall, Queue } from 'vs/base/common/async'; import * as fs from 'fs'; import * as os from 'os'; import * as platform from 'vs/base/common/platform'; -import { once } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; export function readdir(path: string): Promise { return nfcall(extfs.readdir, path); @@ -36,7 +36,7 @@ export function rimraf(path: string): Promise { } }, (err: NodeJS.ErrnoException) => { if (err.code === 'ENOENT') { - return void 0; + return undefined; } return Promise.reject(err); @@ -77,12 +77,6 @@ export function unlink(path: string): Promise { return nfcall(fs.unlink, path); } -export function unlinkIgnoreError(path: string): Promise { - return new Promise(resolve => { - fs.unlink(path, () => resolve()); - }); -} - export function symlink(target: string, path: string, type?: string): Promise { return nfcall(fs.symlink, target, path, type); } @@ -132,7 +126,7 @@ function ensureWriteFileQueue(queueKey: string): Queue { writeFileQueue = new Queue(); writeFilePathQueue[queueKey] = writeFileQueue; - const onFinish = once(writeFileQueue.onFinished); + const onFinish = Event.once(writeFileQueue.onFinished); onFinish(() => { delete writeFilePathQueue[queueKey]; writeFileQueue.dispose(); @@ -194,7 +188,7 @@ export function whenDeleted(path: string): Promise { if (!exists) { clearInterval(interval); - resolve(void 0); + resolve(undefined); } }); } diff --git a/src/vs/base/node/ports.ts b/src/vs/base/node/ports.ts index c2781f502054..60d40fb49d00 100644 --- a/src/vs/base/node/ports.ts +++ b/src/vs/base/node/ports.ts @@ -18,7 +18,7 @@ export function randomPort(): number { * Given a start point and a max number of retries, will find a port that * is openable. Will return 0 in case no free port can be found. */ -export function findFreePort(startPort: number, giveUpAfter: number, timeout: number): Thenable { +export function findFreePort(startPort: number, giveUpAfter: number, timeout: number): Promise { let done = false; return new Promise(resolve => { diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 5d65b3442d1e..2e530d75d0b8 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -17,7 +17,7 @@ import { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, Te import { getPathFromAmdModule } from 'vs/base/common/amd'; export { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode }; -export type ValueCallback = (value?: T | Thenable) => void; +export type ValueCallback = (value?: T | Promise) => void; export type ErrorCallback = (error?: any) => void; export type ProgressCallback = (progress: T) => void; @@ -72,6 +72,26 @@ export function getWindowsShell(): string { return process.env['comspec'] || 'cmd.exe'; } +/** + * Sanitizes a VS Code process environment by removing all Electron/VS Code-related values. + */ +export function sanitizeProcessEnvironment(env: Platform.IProcessEnvironment): void { + const keysToRemove = [ + /^ELECTRON_.+$/, + /^GOOGLE_API_KEY$/, + /^VSCODE_.+$/ + ]; + const envKeys = Object.keys(env); + envKeys.forEach(envKey => { + for (let i = 0; i < keysToRemove.length; i++) { + if (envKey.search(keysToRemove[i]) !== -1) { + delete env[envKey]; + break; + } + } + }); +} + export abstract class AbstractProcess { private cmd: string; private args: string[]; @@ -107,7 +127,7 @@ export abstract class AbstractProcess { public constructor(executable: Executable); 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) { + if (arg2 !== undefined && arg3 !== undefined && arg4 !== undefined) { this.cmd = arg1; this.args = arg2; this.shell = arg3; @@ -409,7 +429,7 @@ export namespace win32 { if (path.isAbsolute(command)) { return command; } - if (cwd === void 0) { + if (cwd === undefined) { cwd = process.cwd(); } let dir = path.dirname(command); @@ -418,11 +438,11 @@ export namespace win32 { // to the current working directory. return path.join(cwd, command); } - if (paths === void 0 && Types.isString(process.env.PATH)) { + if (paths === undefined && 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) { + if (paths === undefined || paths.length === 0) { return path.join(cwd, command); } // We have a simple file name. We get the path variable from the env diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index eb105f26fc2d..947884ecb781 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -22,7 +22,7 @@ export function listProcesses(rootPid: number): Promise { return new Promise((resolve, reject) => { - let rootItem: ProcessItem; + let rootItem: ProcessItem | undefined; const map = new Map(); function addToTree(pid: number, ppid: number, cmd: string, load: number, mem: number) { @@ -109,7 +109,7 @@ export function listProcesses(rootPid: number): Promise { } while (matches); if (result) { - if (cmd.indexOf('node ') !== 0) { + if (cmd.indexOf('node ') < 0 && cmd.indexOf('node.exe') < 0) { return `electron_node ${result}`; } } @@ -218,7 +218,7 @@ export function listProcesses(rootPid: number): Promise { } else { const cpuUsage = stdout.toString().split('\n'); for (let i = 0; i < pids.length; i++) { - const processInfo = map.get(pids[i]); + const processInfo = map.get(pids[i])!; processInfo.load = parseFloat(cpuUsage[i]); } diff --git a/src/vs/base/node/request.ts b/src/vs/base/node/request.ts index 5ae94a351fc4..36726387a6ff 100644 --- a/src/vs/base/node/request.ts +++ b/src/vs/base/node/request.ts @@ -81,7 +81,7 @@ export function request(options: IRequestOptions, token: CancellationToken): Pro opts.auth = options.user + ':' + options.password; } - req = rawRequest(opts, (res: http.ClientResponse) => { + req = rawRequest(opts, (res: http.IncomingMessage) => { const followRedirects: number = isNumber(options.followRedirects) ? options.followRedirects : 3; if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { request(assign({}, options, { @@ -136,7 +136,7 @@ export function download(filePath: string, context: IRequestContext): Promise((c, e) => { const out = createWriteStream(filePath); - out.once('finish', () => c(void 0)); + out.once('finish', () => c(undefined)); context.stream.once('error', e); context.stream.pipe(out); }); diff --git a/src/vs/base/node/stats.ts b/src/vs/base/node/stats.ts index c1d12b9dcabb..b64286b753b7 100644 --- a/src/vs/base/node/stats.ts +++ b/src/vs/base/node/stats.ts @@ -49,9 +49,8 @@ export function collectLaunchConfigs(folder: string): Promise { if (fileTypes.has(fileType)) { - fileTypes.set(fileType, fileTypes.get(fileType) + 1); + fileTypes.set(fileType, fileTypes.get(fileType)! + 1); } else { fileTypes.set(fileType, 1); @@ -162,7 +161,7 @@ export function collectWorkspaceStats(folder: string, filter: string[]): Promise for (const each of configFilePatterns) { if (each.pattern.test(fileName)) { if (configFiles.has(each.tag)) { - configFiles.set(each.tag, configFiles.get(each.tag) + 1); + configFiles.set(each.tag, configFiles.get(each.tag)! + 1); } else { configFiles.set(each.tag, 1); } diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 4a078cf0a8c8..8063720253c8 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -11,7 +11,8 @@ 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, unlinkIgnoreError, copy, renameIgnoreError } from 'vs/base/node/pfs'; +import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs'; +import { fill } from 'vs/base/common/arrays'; export enum StorageHint { @@ -39,12 +40,12 @@ export interface IStorageDatabase { readonly onDidChangeItemsExternal: Event; - getItems(): Thenable>; - updateItems(request: IUpdateRequest): Thenable; + getItems(): Promise>; + updateItems(request: IUpdateRequest): Promise; - close(): Thenable; + close(recovery?: () => Map): Promise; - checkIntegrity(full: boolean): Thenable; + checkIntegrity(full: boolean): Promise; } export interface IStorage extends IDisposable { @@ -53,7 +54,7 @@ export interface IStorage extends IDisposable { readonly size: number; readonly onDidChangeStorage: Event; - init(): Thenable; + init(): Promise; get(key: string, fallbackValue: string): string; get(key: string, fallbackValue?: string): string | undefined; @@ -64,13 +65,12 @@ export interface IStorage extends IDisposable { getInteger(key: string, fallbackValue: number): number; getInteger(key: string, fallbackValue?: number): number | undefined; - set(key: string, value: any): Thenable; - delete(key: string): Thenable; + set(key: string, value: string | boolean | number): Promise; + delete(key: string): Promise; - beforeClose(): void; - close(): Thenable; + close(): Promise; - checkIntegrity(full: boolean): Thenable; + checkIntegrity(full: boolean): Promise; } enum StorageState { @@ -92,7 +92,6 @@ export class Storage extends Disposable implements IStorage { private cache: Map = new Map(); private flushDelayer: ThrottledDelayer; - private flushDelay = Storage.DEFAULT_FLUSH_DELAY; private pendingDeletes: Set = new Set(); private pendingInserts: Map = new Map(); @@ -103,7 +102,7 @@ export class Storage extends Disposable implements IStorage { ) { super(); - this.flushDelayer = this._register(new ThrottledDelayer(this.flushDelay)); + this.flushDelayer = this._register(new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY)); this.registerListeners(); } @@ -154,7 +153,7 @@ export class Storage extends Disposable implements IStorage { return this.cache.size; } - init(): Thenable { + init(): Promise { if (this.state !== StorageState.None) { return Promise.resolve(); // either closed or already initialized } @@ -209,7 +208,7 @@ export class Storage extends Disposable implements IStorage { return parseInt(value, 10); } - set(key: string, value: any): Thenable { + set(key: string, value: string | boolean | number): Promise { if (this.state === StorageState.Closed) { return Promise.resolve(); // Return early if we are already closed } @@ -237,10 +236,10 @@ export class Storage extends Disposable implements IStorage { this._onDidChangeStorage.fire(key); // Accumulate work by scheduling after timeout - return this.flushDelayer.trigger(() => this.flushPending(), this.flushDelay); + return this.flushDelayer.trigger(() => this.flushPending()); } - delete(key: string): Thenable { + delete(key: string): Promise { if (this.state === StorageState.Closed) { return Promise.resolve(); // Return early if we are already closed } @@ -261,14 +260,10 @@ export class Storage extends Disposable implements IStorage { this._onDidChangeStorage.fire(key); // Accumulate work by scheduling after timeout - return this.flushDelayer.trigger(() => this.flushPending(), this.flushDelay); + return this.flushDelayer.trigger(() => this.flushPending()); } - beforeClose(): void { - this.flushDelay = 0; // when we are about to close, reduce our flush delay to 0 to consume too much time - } - - close(): Thenable { + close(): Promise { if (this.state === StorageState.Closed) { return Promise.resolve(); // return if already closed } @@ -279,11 +274,14 @@ export class Storage extends Disposable implements IStorage { // 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.database.close(); + // + // Recovery: we pass our cache over as recovery option in case + // the DB is not healthy. + const onDone = () => this.database.close(() => this.cache); return this.flushDelayer.trigger(() => this.flushPending(), 0 /* as soon as possible */).then(onDone, onDone); } - private flushPending(): Thenable { + private flushPending(): Promise { if (this.pendingInserts.size === 0 && this.pendingDeletes.size === 0) { return Promise.resolve(); // return early if nothing to do } @@ -299,14 +297,18 @@ export class Storage extends Disposable implements IStorage { return this.database.updateItems(updateRequest); } - checkIntegrity(full: boolean): Thenable { + checkIntegrity(full: boolean): Promise { return this.database.checkIntegrity(full); } } -interface IOpenDatabaseResult { +interface IDatabaseConnection { db: Database; - path: string; + + isInMemory: boolean; + + isErroneous?: boolean; + lastError?: string; } export interface ISQLiteStorageDatabaseOptions { @@ -327,24 +329,29 @@ export class SQLiteStorageDatabase implements IStorageDatabase { 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 static MAX_HOST_PARAMETERS = 256; // maximum number of parameters within a statement + private path: string; private name: string; + private logger: SQLiteStorageDatabaseLogger; - private whenOpened: Promise; + private whenConnected: Promise; constructor(path: string, options: ISQLiteStorageDatabaseOptions = Object.create(null)) { + this.path = path; this.name = basename(path); + this.logger = new SQLiteStorageDatabaseLogger(options.logging); - this.whenOpened = this.open(path); + this.whenConnected = this.connect(path); } getItems(): Promise> { - return this.whenOpened.then(({ db }) => { + return this.whenConnected.then(connection => { const items = new Map(); - return this.all(db, 'SELECT * FROM ItemTable').then(rows => { + return this.all(connection, 'SELECT * FROM ItemTable').then(rows => { rows.forEach(row => items.set(row.key, row.value)); if (this.logger.isTracing) { @@ -357,6 +364,10 @@ export class SQLiteStorageDatabase implements IStorageDatabase { } updateItems(request: IUpdateRequest): Promise { + return this.whenConnected.then(connection => this.doUpdateItems(connection, request)); + } + + private doUpdateItems(connection: IDatabaseConnection, request: IUpdateRequest): Promise { let updateCount = 0; if (request.insert) { updateCount += request.insert.size; @@ -373,64 +384,148 @@ export class SQLiteStorageDatabase implements IStorageDatabase { this.logger.trace(`[storage ${this.name}] updateItems(): insert(${request.insert ? mapToString(request.insert) : '0'}), delete(${request.delete ? setToString(request.delete) : '0'})`); } - return this.whenOpened.then(({ db }) => { - return this.transaction(db, () => { - if (request.insert && request.insert.size > 0) { - this.prepare(db, 'INSERT INTO ItemTable VALUES (?,?)', stmt => { + return this.transaction(connection, () => { + + // INSERT + if (request.insert && request.insert.size > 0) { + const keysValuesChunks: (string[])[] = []; + keysValuesChunks.push([]); // seed with initial empty chunk + + // Split key/values into chunks of SQLiteStorageDatabase.MAX_HOST_PARAMETERS + // so that we can efficiently run the INSERT with as many HOST parameters as possible + let currentChunkIndex = 0; + request.insert.forEach((value, key) => { + let keyValueChunk = keysValuesChunks[currentChunkIndex]; + + if (keyValueChunk.length > SQLiteStorageDatabase.MAX_HOST_PARAMETERS) { + currentChunkIndex++; + keyValueChunk = []; + keysValuesChunks.push(keyValueChunk); + } + + keyValueChunk.push(key, value); + }); + + keysValuesChunks.forEach(keysValuesChunk => { + this.prepare(connection, `INSERT INTO ItemTable VALUES ${fill(keysValuesChunk.length / 2, '(?,?)').join(',')}`, stmt => stmt.run(keysValuesChunk), () => { + const keys: string[] = []; + let length = 0; request.insert!.forEach((value, key) => { - stmt.run([key, value]); + keys.push(key); + length += value.length; }); + + return `Keys: ${keys.join(', ')} Length: ${length}`; }); - } + }); + } + + // DELETE + if (request.delete && request.delete.size) { + const keysChunks: (string[])[] = []; + keysChunks.push([]); // seed with initial empty chunk + + // Split keys into chunks of SQLiteStorageDatabase.MAX_HOST_PARAMETERS + // so that we can efficiently run the DELETE with as many HOST parameters + // as possible + let currentChunkIndex = 0; + request.delete.forEach(key => { + let keyChunk = keysChunks[currentChunkIndex]; + + if (keyChunk.length > SQLiteStorageDatabase.MAX_HOST_PARAMETERS) { + currentChunkIndex++; + keyChunk = []; + keysChunks.push(keyChunk); + } + + keyChunk.push(key); + }); - if (request.delete && request.delete.size) { - this.prepare(db, 'DELETE FROM ItemTable WHERE key=?', stmt => { + keysChunks.forEach(keysChunk => { + this.prepare(connection, `DELETE FROM ItemTable WHERE key IN (${fill(keysChunk.length, '?').join(',')})`, stmt => stmt.run(keysChunk), () => { + const keys: string[] = []; request.delete!.forEach(key => { - stmt.run(key); + keys.push(key); }); + + return `Keys: ${keys.join(', ')}`; }); - } - }); + }); + } }); } - close(): Promise { + close(recovery?: () => Map): Promise { this.logger.trace(`[storage ${this.name}] close()`); - return this.whenOpened.then(result => { - return new Promise((resolve, reject) => { - result.db.close(error => { - if (error) { - this.logger.error(`[storage ${this.name}] close(): ${error}`); + return this.whenConnected.then(connection => this.doClose(connection, recovery)); + } - return reject(error); - } + private doClose(connection: IDatabaseConnection, recovery?: () => Map): Promise { + return new Promise((resolve, reject) => { + connection.db.close(closeError => { + if (closeError) { + this.handleSQLiteError(connection, closeError, `[storage ${this.name}] close(): ${closeError}`); + } - // If the DB closed successfully and we are not running in-memory - // make a backup of the DB so that we can use it as fallback in - // case the actual DB becomes corrupt. - if (result.path !== SQLiteStorageDatabase.IN_MEMORY_PATH) { - return this.backup(result).then(resolve, error => { - this.logger.error(`[storage ${this.name}] backup(): ${error}`); + // Return early if this storage was created only in-memory + // e.g. when running tests we do not need to backup. + if (this.path === SQLiteStorageDatabase.IN_MEMORY_PATH) { + return resolve(); + } + + // If the DB closed successfully and we are not running in-memory + // and the DB did not get errors during runtime, make a backup + // of the DB so that we can use it as fallback in case the actual + // DB becomes corrupt in the future. + if (!connection.isErroneous && !connection.isInMemory) { + return this.backup().then(resolve, error => { + this.logger.error(`[storage ${this.name}] backup(): ${error}`); + + return resolve(); // ignore failing backup + }); + } + + // Recovery: if we detected errors while using the DB or we are using + // an inmemory DB (as a fallback to not being able to open the DB initially) + // and we have a recovery function provided, we recreate the DB with this + // data to recover all known data without loss if possible. + if (typeof recovery === 'function') { + + // Delete the existing DB. If the path does not exist or fails to + // be deleted, we do not try to recover anymore because we assume + // that the path is no longer writeable for us. + return unlink(this.path).then(() => { + + // Re-open the DB fresh + return this.doConnect(this.path).then(recoveryConnection => { + const closeRecoveryConnection = () => { + return this.doClose(recoveryConnection, undefined /* do not attempt to recover again */); + }; + + // Store items + return this.doUpdateItems(recoveryConnection, { insert: recovery() }).then(() => closeRecoveryConnection(), error => { + + // In case of an error updating items, still ensure to close the connection + // to prevent SQLITE_BUSY errors when the connection is restablished + closeRecoveryConnection(); - return resolve(); // ignore failing backup + return Promise.reject(error); + }); }); - } + }).then(resolve, reject); + } - return resolve(); - }); + // Finally without recovery we just reject + return reject(closeError || new Error('Database has errors or is in-memory without recovery option')); }); }); } - private backup(db: IOpenDatabaseResult): Promise { - if (db.path === SQLiteStorageDatabase.IN_MEMORY_PATH) { - return Promise.resolve(); // no backups when running in-memory - } + private backup(): Promise { + const backupPath = this.toBackupPath(this.path); - const backupPath = this.toBackupPath(db.path); - - return unlinkIgnoreError(backupPath).then(() => copy(db.path, backupPath)); + return copy(this.path, backupPath); } private toBackupPath(path: string): string { @@ -440,73 +535,70 @@ export class SQLiteStorageDatabase implements IStorageDatabase { checkIntegrity(full: boolean): Promise { this.logger.trace(`[storage ${this.name}] checkIntegrity(full: ${full})`); - return this.whenOpened.then(({ db }) => { - return this.get(db, full ? 'PRAGMA integrity_check' : 'PRAGMA quick_check').then(row => { - return full ? row['integrity_check'] : row['quick_check']; - }); - }); - } + return this.whenConnected.then(connection => { + return this.get(connection, full ? 'PRAGMA integrity_check' : 'PRAGMA quick_check').then(row => { + const integrity = full ? row['integrity_check'] : row['quick_check']; - private open(path: string): Promise { - this.logger.trace(`[storage ${this.name}] open()`); - - return new Promise((resolve, reject) => { - 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(SQLiteStorageDatabase.IN_MEMORY_PATH).then(resolve, reject); - }; - - this.doOpen(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') { - return this.handleSQLiteBusy(path).then(resolve, fallbackToInMemoryDatabase); + if (connection.isErroneous) { + return `${integrity} (last error: ${connection.lastError})`; } - // This error code indicates that even though the DB file exists, - // SQLite cannot open it and signals it is corrupt or not a DB. - if (error.code === 'SQLITE_CORRUPT' || error.code === 'SQLITE_NOTADB') { - return this.handleSQLiteCorrupt(path, error).then(resolve, fallbackToInMemoryDatabase); + if (connection.isInMemory) { + return `${integrity} (in-memory!)`; } - // Otherwise give up and fallback to in-memory DB - return fallbackToInMemoryDatabase(error); + return integrity; }); }); } - private handleSQLiteBusy(path: string): Promise { - this.logger.error(`[storage ${this.name}] open(): Retrying after ${SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT}ms due to SQLITE_BUSY`); + private connect(path: string, retryOnBusy: boolean = true): Promise { + this.logger.trace(`[storage ${this.name}] open(${path}, retryOnBusy: ${retryOnBusy})`); - // Retry after some time if the DB is busy - return timeout(SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT).then(() => this.doOpen(path)); - } + return this.doConnect(path).then(undefined, error => { + this.logger.error(`[storage ${this.name}] open(): Unable to open DB due to ${error}`); - private handleSQLiteCorrupt(path: string, error: any): Promise { - this.logger.error(`[storage ${this.name}] open(): Unable to open DB due to ${error.code}`); + // SQLITE_BUSY 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. + // + // In this case we simply wait for some time and retry once to establish the connection. + // + if (error.code === 'SQLITE_BUSY' && retryOnBusy) { + return timeout(SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT).then(() => this.connect(path, false /* not another retry */)); + } - // Move corrupt DB to a different filename and try to load from backup - // If that fails, a new empty DB is being created automatically - return rename(path, this.toCorruptPath(path)) - .then(() => renameIgnoreError(this.toBackupPath(path), path)) - .then(() => this.doOpen(path)); + // Otherwise, best we can do is to recover from a backup if that exists, as such we + // move the DB to a different filename and try to load from backup. If that fails, + // a new empty DB is being created automatically. + // + // The final fallback is to use an in-memory DB which should only happen if the target + // folder is really not writeable for us. + // + return unlink(path) + .then(() => renameIgnoreError(this.toBackupPath(path), path)) + .then(() => this.doConnect(path)) + .then(undefined, error => { + this.logger.error(`[storage ${this.name}] open(): Unable to use backup due to ${error}`); + + // 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. + return this.doConnect(SQLiteStorageDatabase.IN_MEMORY_PATH); + }); + }); } - private toCorruptPath(path: string): string { - const randomSuffix = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 4); + private handleSQLiteError(connection: IDatabaseConnection, error: Error & { code?: string }, msg: string): void { + connection.isErroneous = true; + connection.lastError = msg; - return `${path}.${randomSuffix}.corrupt`; + this.logger.error(msg); } - private doOpen(path: string): Promise { + private doConnect(path: string): Promise { + // TODO@Ben clean up performance markers return new Promise((resolve, reject) => { let measureRequireDuration = false; @@ -521,45 +613,48 @@ export class SQLiteStorageDatabase implements IStorageDatabase { mark('didRequireSQLite'); } - const db: Database = new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { - if (error) { - return db ? db.close(() => reject(error)) : reject(error); - } - - // 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(() => { - mark('didSetupSQLiteSchema'); - - return resolve({ path, db }); - }, error => { - mark('didSetupSQLiteSchema'); - - return db.close(() => reject(error)); - }); - }); + const connection: IDatabaseConnection = { + db: new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { + if (error) { + return connection.db ? connection.db.close(() => reject(error)) : reject(error); + } + + // 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'); + return this.exec(connection, [ + 'PRAGMA user_version = 1;', + 'CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)' + ].join('')).then(() => { + mark('didSetupSQLiteSchema'); + + return resolve(connection); + }, error => { + mark('didSetupSQLiteSchema'); + + return connection.db.close(() => reject(error)); + }); + }), + isInMemory: path === SQLiteStorageDatabase.IN_MEMORY_PATH + }; // Errors - db.on('error', error => this.logger.error(`[storage ${this.name}] Error (event): ${error}`)); + connection.db.on('error', error => this.handleSQLiteError(connection, error, `[storage ${this.name}] Error (event): ${error}`)); // Tracing if (this.logger.isTracing) { - db.on('trace', sql => this.logger.trace(`[storage ${this.name}] Trace (event): ${sql}`)); + connection.db.on('trace', sql => this.logger.trace(`[storage ${this.name}] Trace (event): ${sql}`)); } - }); + }, reject); }); } - private exec(db: Database, sql: string): Promise { + private exec(connection: IDatabaseConnection, sql: string): Promise { return new Promise((resolve, reject) => { - db.exec(sql, error => { + connection.db.exec(sql, error => { if (error) { - this.logger.error(`[storage ${this.name}] exec(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] exec(): ${error}`); return reject(error); } @@ -569,11 +664,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private get(db: Database, sql: string): Promise { + private get(connection: IDatabaseConnection, sql: string): Promise { return new Promise((resolve, reject) => { - db.get(sql, (error, row) => { + connection.db.get(sql, (error, row) => { if (error) { - this.logger.error(`[storage ${this.name}] get(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] get(): ${error}`); return reject(error); } @@ -583,11 +678,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private all(db: Database, sql: string): Promise<{ key: string, value: string }[]> { + private all(connection: IDatabaseConnection, sql: string): Promise<{ key: string, value: string }[]> { return new Promise((resolve, reject) => { - db.all(sql, (error, rows) => { + connection.db.all(sql, (error, rows) => { if (error) { - this.logger.error(`[storage ${this.name}] all(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] all(): ${error}`); return reject(error); } @@ -597,16 +692,16 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private transaction(db: Database, transactions: () => void): Promise { + private transaction(connection: IDatabaseConnection, transactions: () => void): Promise { return new Promise((resolve, reject) => { - db.serialize(() => { - db.run('BEGIN TRANSACTION'); + connection.db.serialize(() => { + connection.db.run('BEGIN TRANSACTION'); transactions(); - db.run('END TRANSACTION', error => { + connection.db.run('END TRANSACTION', error => { if (error) { - this.logger.error(`[storage ${this.name}] transaction(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] transaction(): ${error}`); return reject(error); } @@ -617,11 +712,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private prepare(db: Database, sql: string, runCallback: (stmt: Statement) => void): void { - const stmt = db.prepare(sql); + private prepare(connection: IDatabaseConnection, sql: string, runCallback: (stmt: Statement) => void, errorDetails: () => string): void { + const stmt = connection.db.prepare(sql); const statementErrorListener = error => { - this.logger.error(`[storage ${this.name}] prepare(): ${error} (${sql})`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] prepare(): ${error} (${sql}). Details: ${errorDetails()}`); }; stmt.on('error', statementErrorListener); @@ -675,11 +770,11 @@ export class InMemoryStorageDatabase implements IStorageDatabase { private items = new Map(); - getItems(): Thenable> { + getItems(): Promise> { return Promise.resolve(this.items); } - updateItems(request: IUpdateRequest): Thenable { + updateItems(request: IUpdateRequest): Promise { if (request.insert) { request.insert.forEach((value, key) => this.items.set(key, value)); } @@ -691,11 +786,11 @@ export class InMemoryStorageDatabase implements IStorageDatabase { return Promise.resolve(); } - close(): Thenable { + close(): Promise { return Promise.resolve(); } - checkIntegrity(full: boolean): Thenable { + checkIntegrity(full: boolean): Promise { return Promise.resolve('ok'); } } \ No newline at end of file diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts index 85024dea6dd1..916d4380a9a4 100644 --- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -12,9 +12,9 @@ export function registerContextMenuListener(): void { menu.popup({ window: BrowserWindow.fromWebContents(event.sender), - x: options ? options.x : void 0, - y: options ? options.y : void 0, - positioningItem: options ? options.positioningItem : void 0, + x: options ? options.x : undefined, + y: options ? options.y : undefined, + positioningItem: options ? options.positioningItem : undefined, callback: () => { event.sender.send(CONTEXT_MENU_CLOSE_CHANNEL, contextMenuId); } diff --git a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts b/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts index 6437745faef0..46dbed6a447c 100644 --- a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts +++ b/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { fromNodeEventEmitter } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IPCClient } from 'vs/base/parts/ipc/node/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; import { ipcRenderer } from 'electron'; @@ -14,7 +14,7 @@ export class Client extends IPCClient implements IDisposable { private protocol: Protocol; private static createProtocol(): Protocol { - const onMessage = fromNodeEventEmitter(ipcRenderer, 'ipc:message', (_, message: string) => message); + const onMessage = Event.fromNodeEventEmitter(ipcRenderer, 'ipc:message', (_, message: string) => message); ipcRenderer.send('ipc:hello'); return new Protocol(ipcRenderer, onMessage); } 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 a45d7468fa32..fed2c6164781 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 @@ -3,10 +3,11 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, filterEvent, mapEvent, fromNodeEventEmitter, signalEvent } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/node/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; import { ipcMain } from 'electron'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; interface IIPCEvent { event: { sender: Electron.WebContents; }; @@ -14,19 +15,31 @@ interface IIPCEvent { } function createScopedOnMessageEvent(senderId: number, eventName: string): Event { - const onMessage = fromNodeEventEmitter(ipcMain, eventName, (event, message: string) => ({ event, message })); - const onMessageFromSender = filterEvent(onMessage, ({ event }) => event.sender.id === senderId); - return mapEvent(onMessageFromSender, ({ message }) => message); + const onMessage = Event.fromNodeEventEmitter(ipcMain, eventName, (event, message: string) => ({ event, message })); + const onMessageFromSender = Event.filter(onMessage, ({ event }) => event.sender.id === senderId); + return Event.map(onMessageFromSender, ({ message }) => message); } export class Server extends IPCServer { + private static Clients = new Map(); + private static getOnDidClientConnect(): Event { - const onHello = fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); + const onHello = Event.fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); + + return Event.map(onHello, webContents => { + const id = webContents.id; + const client = Server.Clients.get(id); + + if (client) { + client.dispose(); + } + + const onDidClientReconnect = new Emitter(); + Server.Clients.set(id, toDisposable(() => onDidClientReconnect.fire())); - return mapEvent(onHello, webContents => { - const onMessage = createScopedOnMessageEvent(webContents.id, 'ipc:message'); - const onDidClientDisconnect = signalEvent(createScopedOnMessageEvent(webContents.id, 'ipc:disconnect')); + const onMessage = createScopedOnMessageEvent(id, 'ipc:message'); + const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'ipc:disconnect')), onDidClientReconnect.event); const protocol = new Protocol(webContents, onMessage); return { protocol, onDidClientDisconnect }; diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 82130d9063f6..60f848ff0b6a 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -7,7 +7,7 @@ import { ChildProcess, fork, ForkOptions } from 'child_process'; import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; import { Delayer, always, createCancelablePromise } from 'vs/base/common/async'; import { deepClone, assign } from 'vs/base/common/objects'; -import { Emitter, fromNodeEventEmitter, Event } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { createQueuedSender } from 'vs/base/node/processes'; import { ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient, IChannel } from 'vs/base/parts/ipc/node/ipc'; import { isRemoteConsoleLog, log } from 'vs/base/node/console'; @@ -29,7 +29,7 @@ export class Server extends IPCServer { } } catch (e) { /* not much to do */ } }, - onMessage: fromNodeEventEmitter(process, 'message', msg => Buffer.from(msg, 'base64')) + onMessage: Event.fromNodeEventEmitter(process, 'message', msg => Buffer.from(msg, 'base64')) }, ctx); process.once('disconnect', () => this.dispose()); @@ -105,7 +105,7 @@ export class Client implements IChannelClient, IDisposable { const that = this; return { - call(command: string, arg?: any, cancellationToken?: CancellationToken): Thenable { + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { return that.requestPromise(channelName, command, arg, cancellationToken); }, listen(event: string, arg?: any) { @@ -114,7 +114,7 @@ export class Client implements IChannelClient, IDisposable { } as T; } - protected requestPromise(channelName: string, name: string, arg?: any, cancellationToken = CancellationToken.None): Thenable { + protected requestPromise(channelName: string, name: string, arg?: any, cancellationToken = CancellationToken.None): Promise { if (!this.disposeDelayer) { return Promise.reject(new Error('disposed')); } @@ -199,7 +199,7 @@ export class Client implements IChannelClient, IDisposable { this.child = fork(this.modulePath, args, forkOpts); const onMessageEmitter = new Emitter(); - const onRawMessage = fromNodeEventEmitter(this.child, 'message', msg => msg); + const onRawMessage = Event.fromNodeEventEmitter(this.child, 'message', msg => msg); onRawMessage(msg => { diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 264a47da1ddc..2bfe8e281f7c 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Socket, Server as NetServer, createConnection, createServer } from 'net'; -import { Event, Emitter, once, mapEvent, fromNodeEventEmitter } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { IMessagePassingProtocol, ClientConnectionEvent, IPCServer, IPCClient } from 'vs/base/parts/ipc/node/ipc'; import { join } from 'path'; import { tmpdir } from 'os'; @@ -207,11 +207,11 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { export class Server extends IPCServer { private static toClientConnectionEvent(server: NetServer): Event { - const onConnection = fromNodeEventEmitter(server, 'connection'); + const onConnection = Event.fromNodeEventEmitter(server, 'connection'); - return mapEvent(onConnection, socket => ({ + return Event.map(onConnection, socket => ({ protocol: new Protocol(socket), - onDidClientDisconnect: once(fromNodeEventEmitter(socket, 'close')) + onDidClientDisconnect: Event.once(Event.fromNodeEventEmitter(socket, 'close')) })); } @@ -239,7 +239,7 @@ export class Client extends IPCClient { get onClose(): Event { return this.protocol.onClose; } - constructor(private protocol: Protocol, id: TContext) { + constructor(private protocol: Protocol | BufferedProtocol, id: TContext) { super(protocol, id); } @@ -249,9 +249,9 @@ export class Client extends IPCClient { } } -export function serve(port: number): Thenable; -export function serve(namedPipe: string): Thenable; -export function serve(hook: any): Thenable { +export function serve(port: number): Promise; +export function serve(namedPipe: string): Promise; +export function serve(hook: any): Promise { return new Promise((c, e) => { const server = createServer(); @@ -263,10 +263,10 @@ export function serve(hook: any): Thenable { }); } -export function connect(options: { host: string, port: number }, clientId: string): Thenable; -export function connect(port: number, clientId: string): Thenable; -export function connect(namedPipe: string, clientId: string): Thenable; -export function connect(hook: any, clientId: string): Thenable { +export function connect(options: { host: string, port: number }, clientId: string): Promise; +export function connect(port: number, clientId: string): Promise; +export function connect(namedPipe: string, clientId: string): Promise; +export function connect(hook: any, clientId: string): Promise { return new Promise((c, e) => { const socket = createConnection(hook, () => { socket.removeListener('error', e); @@ -276,3 +276,68 @@ export function connect(hook: any, clientId: string): Thenable { socket.once('error', e); }); } + +/** + * Will ensure no messages are lost if there are no event listeners. + */ +function createBufferedEvent(source: Event): Event { + let emitter: Emitter; + let hasListeners = false; + let isDeliveringMessages = false; + let bufferedMessages: T[] = []; + + const deliverMessages = () => { + if (isDeliveringMessages) { + return; + } + isDeliveringMessages = true; + while (hasListeners && bufferedMessages.length > 0) { + emitter.fire(bufferedMessages.shift()!); + } + isDeliveringMessages = false; + }; + + source((e: T) => { + bufferedMessages.push(e); + deliverMessages(); + }); + + emitter = new Emitter({ + onFirstListenerAdd: () => { + hasListeners = true; + // it is important to deliver these messages after this call, but before + // other messages have a chance to be received (to guarantee in order delivery) + // that's why we're using here nextTick and not other types of timeouts + process.nextTick(deliverMessages); + }, + onLastListenerRemove: () => { + hasListeners = false; + } + }); + + return emitter.event; +} + +/** + * Will ensure no messages are lost if there are no event listeners. + */ +export class BufferedProtocol implements IMessagePassingProtocol { + + private readonly _actual: Protocol; + public readonly onMessage: Event; + public readonly onClose: Event; + + constructor(actual: Protocol) { + this._actual = actual; + this.onMessage = createBufferedEvent(this._actual.onMessage); + this.onClose = createBufferedEvent(this._actual.onClose); + } + + public send(buffer: Buffer): void { + this._actual.send(buffer); + } + + public end(): void { + this._actual.end(); + } +} diff --git a/src/vs/base/parts/ipc/node/ipc.ts b/src/vs/base/parts/ipc/node/ipc.ts index 502e43072000..04b59f52a85a 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, toPromise, Relay } from 'vs/base/common/event'; +import { Event, Emitter, 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'; @@ -58,7 +58,7 @@ enum State { * with at most one single return value. */ export interface IChannel { - call(command: string, arg?: any, cancellationToken?: CancellationToken): Thenable; + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise; listen(event: string, arg?: any): Event; } @@ -68,7 +68,7 @@ export interface IChannel { * if you'd like to handle remote promises or events. */ export interface IServerChannel { - call(ctx: TContext, command: string, arg?: any, cancellationToken?: CancellationToken): Thenable; + call(ctx: TContext, command: string, arg?: any, cancellationToken?: CancellationToken): Promise; listen(ctx: TContext, event: string, arg?: any): Event; } @@ -103,8 +103,8 @@ export interface IConnectionHub { * channels (each from a separate client) to pick from. */ export interface IClientRouter { - routeCall(hub: IConnectionHub, command: string, arg?: any, cancellationToken?: CancellationToken): Thenable>; - routeEvent(hub: IConnectionHub, event: string, arg?: any): Thenable>; + routeCall(hub: IConnectionHub, command: string, arg?: any, cancellationToken?: CancellationToken): Promise>; + routeEvent(hub: IConnectionHub, event: string, arg?: any): Promise>; } /** @@ -289,8 +289,11 @@ export class ChannelServer implements IChannelServer; + let promise: Promise; try { promise = channel.call(this.ctx, request.name, request.arg, cancellationTokenSource.token); @@ -309,7 +312,7 @@ export class ChannelServer implements IChannelServer implements IChannelServer { + private requestPromise(channelName: string, name: string, arg?: any, cancellationToken = CancellationToken.None): Promise { const id = this.lastRequestId++; const type = RequestType.Promise; const request: IRawRequest = { id, type, channelName, name, arg }; @@ -539,11 +545,11 @@ export class ChannelClient implements IChannelClient, IDisposable { } } - private whenInitialized(): Thenable { + private whenInitialized(): Promise { if (this.state === State.Idle) { return Promise.resolve(); } else { - return toPromise(this.onDidInitialize); + return Event.toPromise(this.onDidInitialize); } } @@ -590,7 +596,7 @@ export class IPCServer implements IChannelServer, I constructor(onDidClientConnect: Event) { onDidClientConnect(({ protocol, onDidClientDisconnect }) => { - const onFirstMessage = once(protocol.onMessage); + const onFirstMessage = Event.once(protocol.onMessage); onFirstMessage(msg => { const reader = new BufferReader(msg); @@ -681,9 +687,9 @@ export class IPCClient implements IChannelClient, IChannelSer } } -export function getDelayedChannel(promise: Thenable): T { +export function getDelayedChannel(promise: Promise): T { return { - call(command: string, arg?: any, cancellationToken?: CancellationToken): Thenable { + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { return promise.then(c => c.call(command, arg, cancellationToken)); }, @@ -699,7 +705,7 @@ export function getNextTickChannel(channel: T): T { let didTick = false; return { - call(command: string, arg?: any, cancellationToken?: CancellationToken): Thenable { + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { if (didTick) { return channel.call(command, arg, cancellationToken); } @@ -726,13 +732,13 @@ export function getNextTickChannel(channel: T): T { export class StaticRouter implements IClientRouter { - constructor(private fn: (ctx: TContext) => boolean | Thenable) { } + constructor(private fn: (ctx: TContext) => boolean | Promise) { } - routeCall(hub: IConnectionHub): Thenable> { + routeCall(hub: IConnectionHub): Promise> { return this.route(hub); } - routeEvent(hub: IConnectionHub): Thenable> { + routeEvent(hub: IConnectionHub): Promise> { return this.route(hub); } @@ -743,7 +749,7 @@ export class StaticRouter implements IClientRouter } } - await toPromise(hub.onDidChangeConnections); + await Event.toPromise(hub.onDidChangeConnections); return await this.route(hub); } } \ No newline at end of file diff --git a/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts b/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts index 95373c4c4afe..98f2d75f55f5 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts @@ -39,7 +39,7 @@ suite('IPC, Child Process', () => { service.onMarco(({ answer }) => { try { assert.equal(answer, 'polo'); - c(null); + c(undefined); } catch (err) { e(err); } diff --git a/src/vs/base/parts/ipc/test/node/ipc.net.test.ts b/src/vs/base/parts/ipc/test/node/ipc.net.test.ts index 5eae46b727dd..3b2e0bf45325 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.net.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.net.test.ts @@ -47,7 +47,7 @@ suite('IPC, Socket Protocol', () => { const sub = b.onMessage(data => { sub.dispose(); assert.equal(data.toString(), 'foobarfarboo'); - resolve(null); + resolve(undefined); }); a.send(Buffer.from('foobarfarboo')); }); @@ -55,7 +55,7 @@ suite('IPC, Socket Protocol', () => { const sub_1 = b.onMessage(data => { sub_1.dispose(); assert.equal(data.readInt8(0), 123); - resolve(null); + resolve(undefined); }); const buffer = Buffer.allocUnsafe(1); buffer.writeInt8(123, 0); @@ -81,7 +81,7 @@ suite('IPC, Socket Protocol', () => { return new Promise(resolve => { b.onMessage(msg => { assert.deepEqual(JSON.parse(msg.toString()), data); - resolve(null); + resolve(undefined); }); }); }); @@ -109,7 +109,7 @@ suite('IPC, Socket Protocol', () => { const receiver2 = new Protocol(stream, buffer); receiver2.onMessage((msg) => { assert.equal(JSON.parse(msg.toString()).value, 2); - resolve(void 0); + resolve(undefined); }); }); 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 b612eac71a5b..4a1faa6b892c 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, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; -import { Emitter, toPromise, Event } from 'vs/base/common/event'; +import { Emitter, 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'; @@ -95,11 +95,11 @@ class TestIPCServer extends IPCServer { const TestChannelId = 'testchannel'; interface ITestService { - marco(): Thenable; - error(message: string): Thenable; - neverComplete(): Thenable; - neverCompleteCT(cancellationToken: CancellationToken): Thenable; - buffersLength(buffers: Buffer[]): Thenable; + marco(): Promise; + error(message: string): Promise; + neverComplete(): Promise; + neverCompleteCT(cancellationToken: CancellationToken): Promise; + buffersLength(buffers: Buffer[]): Promise; pong: Event; } @@ -109,19 +109,19 @@ class TestService implements ITestService { private _pong = new Emitter(); readonly pong = this._pong.event; - marco(): Thenable { + marco(): Promise { return Promise.resolve('polo'); } - error(message: string): Thenable { + error(message: string): Promise { return Promise.reject(new Error(message)); } - neverComplete(): Thenable { + neverComplete(): Promise { return new Promise(_ => { }); } - neverCompleteCT(cancellationToken: CancellationToken): Thenable { + neverCompleteCT(cancellationToken: CancellationToken): Promise { if (cancellationToken.isCancellationRequested) { return Promise.reject(canceled()); } @@ -129,7 +129,7 @@ class TestService implements ITestService { return new Promise((_, e) => cancellationToken.onCancellationRequested(() => e(canceled()))); } - buffersLength(buffers: Buffer[]): Thenable { + buffersLength(buffers: Buffer[]): Promise { return Promise.resolve(buffers.reduce((r, b) => r + b.length, 0)); } @@ -142,7 +142,7 @@ class TestChannel implements IServerChannel { constructor(private service: ITestService) { } - call(_, command: string, arg?: any, cancellationToken?: CancellationToken): Thenable { + call(_, command: string, arg: any, cancellationToken: CancellationToken): Promise { switch (command) { case 'marco': return this.service.marco(); case 'error': return this.service.error(arg); @@ -169,23 +169,23 @@ class TestChannelClient implements ITestService { constructor(private channel: IChannel) { } - marco(): Thenable { + marco(): Promise { return this.channel.call('marco'); } - error(message: string): Thenable { + error(message: string): Promise { return this.channel.call('error', message); } - neverComplete(): Thenable { + neverComplete(): Promise { return this.channel.call('neverComplete'); } - neverCompleteCT(cancellationToken: CancellationToken): Thenable { + neverCompleteCT(cancellationToken: CancellationToken): Promise { return this.channel.call('neverCompleteCT', undefined, cancellationToken); } - buffersLength(buffers: Buffer[]): Thenable { + buffersLength(buffers: Buffer[]): Promise { return this.channel.call('buffersLength', buffers); } } @@ -201,8 +201,8 @@ suite('Base IPC', function () { const b3 = Buffer.alloc(0); serverProtocol.send(b3); - const b2 = await toPromise(serverProtocol.onMessage); - const b4 = await toPromise(clientProtocol.onMessage); + const b2 = await Event.toPromise(serverProtocol.onMessage); + const b4 = await Event.toPromise(clientProtocol.onMessage); assert.strictEqual(b1, b2); assert.strictEqual(b3, b4); diff --git a/src/vs/base/parts/ipc/test/node/testService.ts b/src/vs/base/parts/ipc/test/node/testService.ts index c6771f955841..420053bdc68d 100644 --- a/src/vs/base/parts/ipc/test/node/testService.ts +++ b/src/vs/base/parts/ipc/test/node/testService.ts @@ -13,9 +13,9 @@ export interface IMarcoPoloEvent { export interface ITestService { onMarco: Event; - marco(): Thenable; - pong(ping: string): Thenable<{ incoming: string, outgoing: string }>; - cancelMe(): Thenable; + marco(): Promise; + pong(ping: string): Promise<{ incoming: string, outgoing: string }>; + cancelMe(): Promise; } export class TestService implements ITestService { @@ -23,16 +23,16 @@ export class TestService implements ITestService { private _onMarco = new Emitter(); onMarco: Event = this._onMarco.event; - marco(): Thenable { + marco(): Promise { this._onMarco.fire({ answer: 'polo' }); return Promise.resolve('polo'); } - pong(ping: string): Thenable<{ incoming: string, outgoing: string }> { + pong(ping: string): Promise<{ incoming: string, outgoing: string }> { return Promise.resolve({ incoming: ping, outgoing: 'pong' }); } - cancelMe(): Thenable { + cancelMe(): Promise { return Promise.resolve(timeout(100)).then(() => true); } } @@ -49,7 +49,7 @@ export class TestChannel implements IServerChannel { throw new Error('Event not found'); } - call(_, command: string, ...args: any[]): Thenable { + call(_, command: string, ...args: any[]): Promise { switch (command) { case 'pong': return this.testService.pong(args[0]); case 'cancelMe': return this.testService.cancelMe(); @@ -65,15 +65,15 @@ export class TestServiceClient implements ITestService { constructor(private channel: IChannel) { } - marco(): Thenable { + marco(): Promise { return this.channel.call('marco'); } - pong(ping: string): Thenable<{ incoming: string, outgoing: string }> { + pong(ping: string): Promise<{ incoming: string, outgoing: string }> { return this.channel.call('pong', ping); } - cancelMe(): Thenable { + cancelMe(): Promise { return this.channel.call('cancelMe'); } } \ No newline at end of file diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 092cdcb05e53..6b58504ced80 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -19,6 +19,7 @@ import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLa import { OS } from 'vs/base/common/platform'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { IItemAccessor } from 'vs/base/parts/quickopen/common/quickOpenScorer'; +import { coalesce } from 'vs/base/common/arrays'; export interface IContext { event: any; @@ -45,7 +46,7 @@ export class QuickOpenItemAccessorClass implements IItemAccessor getItemPath(entry: QuickOpenEntry): string { const resource = entry.getResource(); - return resource ? resource.fsPath : void 0; + return resource ? resource.fsPath : undefined; } } @@ -89,8 +90,7 @@ export class QuickOpenEntry { * The label of the entry to use when a screen reader wants to read about the entry */ getAriaLabel(): string { - return [this.getLabel(), this.getDescription(), this.getDetail()] - .filter(s => !!s) + return coalesce([this.getLabel(), this.getDescription(), this.getDetail()]) .join(', '); } @@ -468,13 +468,13 @@ class Renderer implements IRenderer { options.title = entry.getTooltip(); options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow options.descriptionMatches = descriptionHighlights || []; - data.label.setValue(entry.getLabel(), entry.getDescription(), options); + data.label.setLabel(entry.getLabel(), entry.getDescription(), options); // Meta data.detail.set(entry.getDetail(), detailHighlights); // Keybinding - data.keybinding.set(entry.getKeybinding(), null); + data.keybinding.set(entry.getKeybinding()); } } @@ -484,9 +484,7 @@ class Renderer implements IRenderer { data.actionBar = null; data.container = null; data.entry = null; - data.keybinding.dispose(); data.keybinding = null; - data.detail.dispose(); data.detail = null; data.group = null; data.icon = null; diff --git a/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts b/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts index 933f9f4c13ed..d80e2a4f5b4c 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { isFunction } from 'vs/base/common/types'; import { ITree, IRenderer, IFilter, IDataSource, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree'; import { IModel } from 'vs/base/parts/quickopen/common/quickOpen'; @@ -37,13 +36,13 @@ export class DataSource implements IDataSource { return model && model === element && model.entries.length > 0; } - getChildren(tree: ITree, element: any): TPromise { + getChildren(tree: ITree, element: any): Promise { const model = this.modelProvider.getModel(); - return TPromise.as(model === element ? model.entries : []); + return Promise.resolve(model === element ? model.entries : []); } - getParent(tree: ITree, element: any): TPromise { - return TPromise.as(null); + getParent(tree: ITree, element: any): Promise { + return Promise.resolve(null); } } @@ -140,4 +139,4 @@ export class Renderer implements IRenderer { const model = this.modelProvider.getModel(); model.renderer.disposeTemplate(templateId, templateData); } -} \ No newline at end of file +} diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index b6febe7c64e2..c862671c6c6c 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -277,7 +277,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { this._register(this.tree.onDidChangeSelection(event => { if (event.selection && event.selection.length > 0) { - const mouseEvent: StandardMouseEvent = event.payload && event.payload.originalEvent instanceof StandardMouseEvent ? event.payload.originalEvent : void 0; + const mouseEvent: StandardMouseEvent = event.payload && event.payload.originalEvent instanceof StandardMouseEvent ? event.payload.originalEvent : undefined; const shouldOpenInBackground = mouseEvent ? this.shouldOpenInBackground(mouseEvent) : false; this.elementSelected(event.selection[0], event, shouldOpenInBackground ? Mode.OPEN_IN_BACKGROUND : Mode.OPEN); @@ -574,7 +574,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { show(param: any, options?: IShowOptions): void { this.visible = true; this.isLoosingFocus = false; - this.quickNavigateConfiguration = options ? options.quickNavigateConfiguration : void 0; + this.quickNavigateConfiguration = options ? options.quickNavigateConfiguration : undefined; // Adjust UI for quick navigate mode if (this.quickNavigateConfiguration) { @@ -675,8 +675,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { let caseInsensitiveMatch: any; const prefix = autoFocus.autoFocusPrefixMatch; const lowerCasePrefix = prefix.toLowerCase(); - for (let i = 0; i < entries.length; i++) { - const entry = entries[i]; + for (const entry of entries) { const label = input.dataSource.getLabel(entry); if (!caseSensitiveMatch && label.indexOf(prefix) === 0) { @@ -773,7 +772,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { let preferredItemsHeight: number; if (this.layoutDimensions && this.layoutDimensions.height) { - preferredItemsHeight = (this.layoutDimensions.height - 50 /* subtract height of input field (30px) and some spacing (drop shadow) to fit */) * 0.40 /* max 40% of screen */; + preferredItemsHeight = (this.layoutDimensions.height - 50 /* subtract height of input field (30px) and some spacing (drop shadow) to fit */) * 0.4 /* max 40% of screen */; } if (!preferredItemsHeight || preferredItemsHeight > QuickOpenWidget.MAX_ITEMS_HEIGHT) { diff --git a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts index 73e8c9105ce3..f755cc4bf71a 100644 --- a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts +++ b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { compareAnything } from 'vs/base/common/comparers'; -import { matchesPrefix, IMatch, createMatches, matchesCamelCase, isUpper } from 'vs/base/common/filters'; +import { matchesPrefix, IMatch, matchesCamelCase, isUpper } from 'vs/base/common/filters'; import { nativeSep } from 'vs/base/common/paths'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings'; @@ -60,7 +60,7 @@ export function score(target: string, query: string, queryLower: string, fuzzy: return res; } -function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): [number, number[]] { +function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): Score { const scores: number[] = []; const matches: number[] = []; @@ -80,15 +80,25 @@ function doScore(query: string, queryLower: string, queryLength: number, target: // y // for (let queryIndex = 0; queryIndex < queryLength; queryIndex++) { + const queryIndexOffset = queryIndex * targetLength; + const queryIndexPreviousOffset = queryIndexOffset - targetLength; + + const queryIndexGtNull = queryIndex > 0; + + const queryCharAtIndex = query[queryIndex]; + const queryLowerCharAtIndex = queryLower[queryIndex]; + for (let targetIndex = 0; targetIndex < targetLength; targetIndex++) { - const currentIndex = queryIndex * targetLength + targetIndex; + const targetIndexGtNull = targetIndex > 0; + + const currentIndex = queryIndexOffset + targetIndex; const leftIndex = currentIndex - 1; - const diagIndex = (queryIndex - 1) * targetLength + targetIndex - 1; + const diagIndex = queryIndexPreviousOffset + targetIndex - 1; - const leftScore: number = targetIndex > 0 ? scores[leftIndex] : 0; - const diagScore: number = queryIndex > 0 && targetIndex > 0 ? scores[diagIndex] : 0; + const leftScore = targetIndexGtNull ? scores[leftIndex] : 0; + const diagScore = queryIndexGtNull && targetIndexGtNull ? scores[diagIndex] : 0; - const matchesSequenceLength: number = queryIndex > 0 && targetIndex > 0 ? matches[diagIndex] : 0; + const matchesSequenceLength = queryIndexGtNull && targetIndexGtNull ? matches[diagIndex] : 0; // If we are not matching on the first query character any more, we only produce a // score if we had a score previously for the last query index (by looking at the diagScore). @@ -96,10 +106,10 @@ function doScore(query: string, queryLower: string, queryLength: number, target: // given a target of "ede" and a query of "de", we would otherwise produce a wrong high score // for query[1] ("e") matching on target[0] ("e") because of the "beginning of word" boost. let score: number; - if (!diagScore && queryIndex > 0) { + if (!diagScore && queryIndexGtNull) { score = 0; } else { - score = computeCharScore(query, queryLower, queryIndex, target, targetLower, targetIndex, matchesSequenceLength); + score = computeCharScore(queryCharAtIndex, queryLowerCharAtIndex, target, targetLower, targetIndex, matchesSequenceLength); } // We have a score and its equal or larger than the left score @@ -146,10 +156,10 @@ function doScore(query: string, queryLower: string, queryLength: number, target: return [scores[queryLength * targetLength - 1], positions.reverse()]; } -function computeCharScore(query: string, queryLower: string, queryIndex: number, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number { +function computeCharScore(queryCharAtIndex, queryLowerCharAtIndex, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number { let score = 0; - if (queryLower[queryIndex] !== targetLower[targetIndex]) { + if (queryLowerCharAtIndex !== targetLower[targetIndex]) { return score; // no match of characters } @@ -170,7 +180,7 @@ function computeCharScore(query: string, queryLower: string, queryIndex: number, } // Same case bonus - if (query[queryIndex] === target[targetIndex]) { + if (queryCharAtIndex === target[targetIndex]) { score += 1; // if (DEBUG) { @@ -349,11 +359,28 @@ export function scoreItem(item: T, query: IPreparedQuery, fuzzy: boolean, acc return itemScore; } +function createMatches(offsets: undefined | number[]): IMatch[] { + let ret: IMatch[] = []; + if (!offsets) { + return ret; + } + let last: IMatch | undefined; + for (const pos of offsets) { + if (last && last.end === pos) { + last.end += 1; + } else { + last = { start: pos, end: pos + 1 }; + ret.push(last); + } + } + return ret; +} + function doScoreItem(label: string, description: string, path: string, query: IPreparedQuery, fuzzy: boolean): IItemScore { // 1.) treat identity matches on full path highest if (path && isLinux ? query.original === path : equalsIgnoreCase(query.original, path)) { - return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : void 0 }; + return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : undefined }; } // We only consider label matches if the query is not including file path separators @@ -605,4 +632,4 @@ export function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, ac // equal return 0; -} \ No newline at end of file +} diff --git a/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts b/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts index 5233d5b52b49..39d574d2d12a 100644 --- a/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts +++ b/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts @@ -38,11 +38,11 @@ suite('QuickOpen', () => { model.addEntries([entry1, entry2, entry3]); const ds = new DataSource(model); - assert.equal(entry1.getId(), ds.getId(null, entry1)); - assert.equal(true, ds.hasChildren(null, model)); - assert.equal(false, ds.hasChildren(null, entry1)); + assert.equal(entry1.getId(), ds.getId(null!, entry1)); + assert.equal(true, ds.hasChildren(null!, model)); + assert.equal(false, ds.hasChildren(null!, entry1)); - ds.getChildren(null, model).then((children: any[]) => { + ds.getChildren(null!, model).then((children: any[]) => { assert.equal(3, children.length); }); }); diff --git a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts index 446aeb25b0c1..b5cfc6234915 100644 --- a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts +++ b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts @@ -29,15 +29,15 @@ const ResourceAccessor = new ResourceAccessorClass(); class NullAccessorClass implements scorer.IItemAccessor { getItemLabel(resource: URI): string { - return void 0; + return undefined!; } getItemDescription(resource: URI): string { - return void 0; + return undefined!; } getItemPath(resource: URI): string { - return void 0; + return undefined!; } } @@ -120,52 +120,52 @@ suite('Quick Open Scorer', () => { // Path Identity const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache); assert.ok(identityRes.score); - assert.equal(identityRes.descriptionMatch.length, 1); - assert.equal(identityRes.labelMatch.length, 1); - assert.equal(identityRes.descriptionMatch[0].start, 0); - assert.equal(identityRes.descriptionMatch[0].end, ResourceAccessor.getItemDescription(resource).length); - assert.equal(identityRes.labelMatch[0].start, 0); - assert.equal(identityRes.labelMatch[0].end, ResourceAccessor.getItemLabel(resource).length); + assert.equal(identityRes.descriptionMatch!.length, 1); + assert.equal(identityRes.labelMatch!.length, 1); + assert.equal(identityRes.descriptionMatch![0].start, 0); + assert.equal(identityRes.descriptionMatch![0].end, ResourceAccessor.getItemDescription(resource).length); + assert.equal(identityRes.labelMatch![0].start, 0); + assert.equal(identityRes.labelMatch![0].end, ResourceAccessor.getItemLabel(resource).length); // Basename Prefix const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor, cache); assert.ok(basenamePrefixRes.score); assert.ok(!basenamePrefixRes.descriptionMatch); - assert.equal(basenamePrefixRes.labelMatch.length, 1); - assert.equal(basenamePrefixRes.labelMatch[0].start, 0); - assert.equal(basenamePrefixRes.labelMatch[0].end, 'som'.length); + assert.equal(basenamePrefixRes.labelMatch!.length, 1); + assert.equal(basenamePrefixRes.labelMatch![0].start, 0); + assert.equal(basenamePrefixRes.labelMatch![0].end, 'som'.length); // Basename Camelcase const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor, cache); assert.ok(basenameCamelcaseRes.score); assert.ok(!basenameCamelcaseRes.descriptionMatch); - assert.equal(basenameCamelcaseRes.labelMatch.length, 2); - assert.equal(basenameCamelcaseRes.labelMatch[0].start, 0); - assert.equal(basenameCamelcaseRes.labelMatch[0].end, 1); - assert.equal(basenameCamelcaseRes.labelMatch[1].start, 4); - assert.equal(basenameCamelcaseRes.labelMatch[1].end, 5); + assert.equal(basenameCamelcaseRes.labelMatch!.length, 2); + assert.equal(basenameCamelcaseRes.labelMatch![0].start, 0); + assert.equal(basenameCamelcaseRes.labelMatch![0].end, 1); + assert.equal(basenameCamelcaseRes.labelMatch![1].start, 4); + assert.equal(basenameCamelcaseRes.labelMatch![1].end, 5); // Basename Match const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor, cache); assert.ok(basenameRes.score); assert.ok(!basenameRes.descriptionMatch); - assert.equal(basenameRes.labelMatch.length, 2); - assert.equal(basenameRes.labelMatch[0].start, 1); - assert.equal(basenameRes.labelMatch[0].end, 2); - assert.equal(basenameRes.labelMatch[1].start, 4); - assert.equal(basenameRes.labelMatch[1].end, 5); + assert.equal(basenameRes.labelMatch!.length, 2); + assert.equal(basenameRes.labelMatch![0].start, 1); + assert.equal(basenameRes.labelMatch![0].end, 2); + assert.equal(basenameRes.labelMatch![1].start, 4); + assert.equal(basenameRes.labelMatch![1].end, 5); // Path Match const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor, cache); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch.length, 1); - assert.equal(pathRes.labelMatch[0].start, 8); - assert.equal(pathRes.labelMatch[0].end, 11); - assert.equal(pathRes.descriptionMatch.length, 1); - assert.equal(pathRes.descriptionMatch[0].start, 1); - assert.equal(pathRes.descriptionMatch[0].end, 4); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 8); + assert.equal(pathRes.labelMatch![0].end, 11); + assert.equal(pathRes.descriptionMatch!.length, 1); + assert.equal(pathRes.descriptionMatch![0].start, 1); + assert.equal(pathRes.descriptionMatch![0].end, 4); // No Match const noRes = scoreItem(resource, '987', true, ResourceAccessor, cache); @@ -182,7 +182,7 @@ suite('Quick Open Scorer', () => { test('scoreItem - invalid input', function () { - let res = scoreItem(null, null, true, ResourceAccessor, cache); + let res = scoreItem(null, null!, true, ResourceAccessor, cache); assert.equal(res.score, 0); res = scoreItem(null, 'null', true, ResourceAccessor, cache); @@ -199,12 +199,12 @@ suite('Quick Open Scorer', () => { assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch.length, 1); - assert.equal(pathRes.labelMatch[0].start, 0); - assert.equal(pathRes.labelMatch[0].end, 7); - assert.equal(pathRes.descriptionMatch.length, 1); - assert.equal(pathRes.descriptionMatch[0].start, 23); - assert.equal(pathRes.descriptionMatch[0].end, 26); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 0); + assert.equal(pathRes.labelMatch![0].end, 7); + assert.equal(pathRes.descriptionMatch!.length, 1); + assert.equal(pathRes.descriptionMatch![0].start, 23); + assert.equal(pathRes.descriptionMatch![0].end, 26); }); test('scoreItem - avoid match scattering (bug #36119)', function () { @@ -214,9 +214,9 @@ suite('Quick Open Scorer', () => { assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch.length, 1); - assert.equal(pathRes.labelMatch[0].start, 0); - assert.equal(pathRes.labelMatch[0].end, 9); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 0); + assert.equal(pathRes.labelMatch![0].end, 9); }); test('scoreItem - prefers more compact matches', function () { @@ -227,12 +227,12 @@ suite('Quick Open Scorer', () => { const res = scoreItem(resource, 'ad', true, ResourceAccessor, cache); assert.ok(res.score); assert.ok(res.descriptionMatch); - assert.ok(!res.labelMatch.length); - assert.equal(res.descriptionMatch.length, 2); - assert.equal(res.descriptionMatch[0].start, 11); - assert.equal(res.descriptionMatch[0].end, 12); - assert.equal(res.descriptionMatch[1].start, 13); - assert.equal(res.descriptionMatch[1].end, 14); + assert.ok(!res.labelMatch!.length); + assert.equal(res.descriptionMatch!.length, 2); + assert.equal(res.descriptionMatch![0].start, 11); + assert.equal(res.descriptionMatch![0].end, 12); + assert.equal(res.descriptionMatch![1].start, 13); + assert.equal(res.descriptionMatch![1].end, 14); }); test('scoreItem - proper target offset', function () { @@ -247,9 +247,9 @@ suite('Quick Open Scorer', () => { const res = scoreItem(resource, 'de', true, ResourceAccessor, cache); - assert.equal(res.labelMatch.length, 1); - assert.equal(res.labelMatch[0].start, 1); - assert.equal(res.labelMatch[0].end, 3); + assert.equal(res.labelMatch!.length, 1); + assert.equal(res.labelMatch![0].start, 1); + assert.equal(res.labelMatch![0].end, 3); }); test('scoreItem - proper target offset #3', function () { @@ -257,19 +257,19 @@ suite('Quick Open Scorer', () => { const res = scoreItem(resource, 'debug', true, ResourceAccessor, cache); - assert.equal(res.descriptionMatch.length, 3); - assert.equal(res.descriptionMatch[0].start, 9); - assert.equal(res.descriptionMatch[0].end, 10); - assert.equal(res.descriptionMatch[1].start, 36); - assert.equal(res.descriptionMatch[1].end, 37); - assert.equal(res.descriptionMatch[2].start, 40); - assert.equal(res.descriptionMatch[2].end, 41); - - assert.equal(res.labelMatch.length, 2); - assert.equal(res.labelMatch[0].start, 9); - assert.equal(res.labelMatch[0].end, 10); - assert.equal(res.labelMatch[1].start, 20); - assert.equal(res.labelMatch[1].end, 21); + assert.equal(res.descriptionMatch!.length, 3); + assert.equal(res.descriptionMatch![0].start, 9); + assert.equal(res.descriptionMatch![0].end, 10); + assert.equal(res.descriptionMatch![1].start, 36); + assert.equal(res.descriptionMatch![1].end, 37); + assert.equal(res.descriptionMatch![2].start, 40); + assert.equal(res.descriptionMatch![2].end, 41); + + assert.equal(res.labelMatch!.length, 2); + assert.equal(res.labelMatch![0].start, 9); + assert.equal(res.labelMatch![0].end, 10); + assert.equal(res.labelMatch![1].start, 20); + assert.equal(res.labelMatch![1].end, 21); }); test('scoreItem - no match unless query contained in sequence', function () { diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index ca9ebe1b84d0..63388531d67c 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as WinJS from 'vs/base/common/winjs.base'; import * as Touch from 'vs/base/browser/touch'; import * as Mouse from 'vs/base/browser/mouseEvent'; import * as Keyboard from 'vs/base/browser/keyboardEvent'; @@ -13,6 +12,7 @@ import { Event } from 'vs/base/common/event'; import { IAction, IActionItem } from 'vs/base/common/actions'; import { Color } from 'vs/base/common/color'; import { IItemCollapseEvent, IItemExpandEvent } from 'vs/base/parts/tree/browser/treeModel'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; export interface ITree { @@ -50,7 +50,7 @@ export interface ITree { /** * Sets the input of the tree. */ - setInput(element: any): WinJS.Promise; + setInput(element: any): Promise; /** * Returns the tree's input. @@ -76,47 +76,42 @@ export interface ITree { * Refreshes an element. * Provide no arguments and it will refresh the input element. */ - refresh(element?: any, recursive?: boolean): WinJS.Promise; - - /** - * Updates an element's width. - */ - updateWidth(element: any): void; + refresh(element?: any, recursive?: boolean): Promise; /** * Expands an element. * The returned promise returns a boolean for whether the element was expanded or not. */ - expand(element: any): WinJS.Promise; + expand(element: any): Promise; /** * Expands several elements. * The returned promise returns a boolean array for whether the elements were expanded or not. */ - expandAll(elements?: any[]): WinJS.Promise; + expandAll(elements?: any[]): Promise; /** * Collapses an element. * The returned promise returns a boolean for whether the element was collapsed or not. */ - collapse(element: any, recursive?: boolean): WinJS.Promise; + collapse(element: any, recursive?: boolean): Promise; /** * Collapses several elements. * Provide no arguments and it will recursively collapse all elements in the tree * The returned promise returns a boolean for whether the elements were collapsed or not. */ - collapseAll(elements?: any[], recursive?: boolean): WinJS.Promise; + collapseAll(elements?: any[], recursive?: boolean): Promise; /** * Toggles an element's expansion state. */ - toggleExpansion(element: any, recursive?: boolean): WinJS.Promise; + toggleExpansion(element: any, recursive?: boolean): Promise; /** * Toggles several element's expansion state. */ - toggleExpansionAll(elements: any[]): WinJS.Promise; + toggleExpansionAll(elements: any[]): Promise; /** * Returns whether an element is expanded or not. @@ -132,7 +127,7 @@ export interface ITree { * Reveals an element in the tree. The relativeTop is a value between 0 and 1. The closer to 0 the more the * element will scroll up to the top. */ - reveal(element: any, relativeTop?: number): WinJS.Promise; + reveal(element: any, relativeTop?: number): Promise; /** * Returns the relative top position of any given element, if visible. @@ -378,12 +373,12 @@ export interface IDataSource { /** * Returns the element's children as an array in a promise. */ - getChildren(tree: ITree, element: any): WinJS.Promise; + getChildren(tree: ITree, element: any): Promise; /** * Returns the element's parent in a promise. */ - getParent(tree: ITree, element: any): WinJS.Promise; + getParent(tree: ITree, element: any): Promise; /** * Returns whether an element should be expanded when first added to the tree. @@ -443,7 +438,7 @@ export interface IAccessibilityProvider { * * See also: https://www.w3.org/TR/wai-aria/states_and_properties#aria-label */ - getAriaLabel(tree: ITree, element: any): string; + getAriaLabel(tree: ITree, element: any): string | null; /** * Given an element in the tree return its aria-posinset. Should be between 1 and aria-setsize @@ -594,18 +589,13 @@ export const DRAG_OVER_ACCEPT_BUBBLE_DOWN = (autoExpand = false) => ({ accept: t export const DRAG_OVER_ACCEPT_BUBBLE_UP_COPY: IDragOverReaction = { accept: true, bubble: DragOverBubble.BUBBLE_UP, effect: DragOverEffect.COPY }; export const DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY = (autoExpand = false) => ({ accept: true, bubble: DragOverBubble.BUBBLE_DOWN, effect: DragOverEffect.COPY, autoExpand }); -export interface IDragAndDropData { - update(event: Mouse.DragMouseEvent): void; - getData(): any; -} - export interface IDragAndDrop { /** * Returns a uri if the given element should be allowed to drag. * Returns null, otherwise. */ - getDragURI(tree: ITree, element: any): string; + getDragURI(tree: ITree, element: any): string | null; /** * Returns a label to display when dragging the element. @@ -621,7 +611,7 @@ export interface IDragAndDrop { * Returns a DragOverReaction indicating whether sources can be * dropped into target or some parent of the target. */ - onDragOver(tree: ITree, data: IDragAndDropData, targetElement: any, originalEvent: Mouse.DragMouseEvent): IDragOverReaction; + onDragOver(tree: ITree, data: IDragAndDropData, targetElement: any, originalEvent: Mouse.DragMouseEvent): IDragOverReaction | null; /** * Handles the action of dropping sources into target. @@ -754,5 +744,5 @@ export interface IActionProvider { /** * Returns an action item to render an action. */ - getActionItem(tree: ITree, element: any, action: IAction): IActionItem; + getActionItem(tree: ITree, element: any, action: IAction): IActionItem | null; } diff --git a/src/vs/base/parts/tree/browser/treeDefaults.ts b/src/vs/base/parts/tree/browser/treeDefaults.ts index bac3d93962e3..0228661b8515 100644 --- a/src/vs/base/parts/tree/browser/treeDefaults.ts +++ b/src/vs/base/parts/tree/browser/treeDefaults.ts @@ -12,7 +12,8 @@ import * as dom from 'vs/base/browser/dom'; import * as mouse from 'vs/base/browser/mouseEvent'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import * as _ from 'vs/base/parts/tree/browser/tree'; -import { KeyCode, KeyMod, Keybinding, createKeybinding, SimpleKeybinding, createSimpleKeybinding } from 'vs/base/common/keyCodes'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { KeyCode, KeyMod, Keybinding, SimpleKeybinding, createSimpleKeybinding } from 'vs/base/common/keyCodes'; export interface IKeyBindingCallback { (tree: _.ITree, event: IKeyboardEvent): void; @@ -70,14 +71,14 @@ export class KeybindingDispatcher { return false; } - public set(keybinding: KeyCode, callback: IKeyBindingCallback) { + public set(keybinding: number, callback: IKeyBindingCallback) { this._arr.push({ - keybinding: createKeybinding(keybinding, platform.OS), + keybinding: createSimpleKeybinding(keybinding, platform.OS), callback: callback }); } - public dispatch(keybinding: SimpleKeybinding): IKeyBindingCallback { + public dispatch(keybinding: SimpleKeybinding): IKeyBindingCallback | null { // Loop from the last to the first to handle overwrites for (let i = this._arr.length - 1; i >= 0; i--) { let item = this._arr[i]; @@ -189,9 +190,9 @@ export class DefaultController implements _.IController { if (this.shouldToggleExpansion(element, event, origin)) { if (tree.isExpanded(element)) { - tree.collapse(element).then(null, errors.onUnexpectedError); + tree.collapse(element).then(undefined, errors.onUnexpectedError); } else { - tree.expand(element).then(null, errors.onUnexpectedError); + tree.expand(element).then(undefined, errors.onUnexpectedError); } } } @@ -225,7 +226,7 @@ export class DefaultController implements _.IController { return false; } - const twistieWidth = parseInt(twistieStyle.width) + parseInt(twistieStyle.paddingRight); + const twistieWidth = parseInt(twistieStyle.width!) + parseInt(twistieStyle.paddingRight!); return event.browserEvent.offsetX <= twistieWidth; } @@ -281,7 +282,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusPrevious(1, payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -293,7 +294,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusPreviousPage(payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -305,7 +306,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusNext(1, payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -317,7 +318,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusNextPage(payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -329,7 +330,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusFirst(payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -341,7 +342,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusLast(payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -359,7 +360,7 @@ export class DefaultController implements _.IController { return tree.reveal(tree.getFocus()); } return undefined; - }).then(null, errors.onUnexpectedError); + }).then(undefined, errors.onUnexpectedError); } return true; } @@ -377,7 +378,7 @@ export class DefaultController implements _.IController { return tree.reveal(tree.getFocus()); } return undefined; - }).then(null, errors.onUnexpectedError); + }).then(undefined, errors.onUnexpectedError); } return true; } @@ -430,24 +431,24 @@ export class DefaultController implements _.IController { export class DefaultDragAndDrop implements _.IDragAndDrop { - public getDragURI(tree: _.ITree, element: any): string { + public getDragURI(tree: _.ITree, element: any): string | null { return null; } - public onDragStart(tree: _.ITree, data: _.IDragAndDropData, originalEvent: mouse.DragMouseEvent): void { + public onDragStart(tree: _.ITree, data: IDragAndDropData, originalEvent: mouse.DragMouseEvent): void { return; } - public onDragOver(tree: _.ITree, data: _.IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): _.IDragOverReaction { + public onDragOver(tree: _.ITree, data: IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): _.IDragOverReaction | null { return null; } - public drop(tree: _.ITree, data: _.IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): void { + public drop(tree: _.ITree, data: IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): void { return; } // {{SQL CARBON EDIT}} - public dropAbort(tree: _.ITree, data: _.IDragAndDropData): void { } + public dropAbort(tree: _.ITree, data: IDragAndDropData): void { } } export class DefaultFilter implements _.IFilter { @@ -466,7 +467,7 @@ export class DefaultSorter implements _.ISorter { export class DefaultAccessibilityProvider implements _.IAccessibilityProvider { - getAriaLabel(tree: _.ITree, element: any): string { + getAriaLabel(tree: _.ITree, element: any): string | null { return null; } } @@ -558,7 +559,7 @@ export class CollapseAllAction extends Action { super('vs.tree.collapse', nls.localize('collapse', "Collapse"), 'monaco-tree-action collapse-all', enabled); } - public run(context?: any): Thenable { + public run(context?: any): Promise { if (this.viewer.getHighlight()) { return Promise.resolve(); // Global action disabled if user is in edit mode from another action } diff --git a/src/vs/base/parts/tree/browser/treeDnd.ts b/src/vs/base/parts/tree/browser/treeDnd.ts index f0af2c70dad0..e1baf124ac06 100644 --- a/src/vs/base/parts/tree/browser/treeDnd.ts +++ b/src/vs/base/parts/tree/browser/treeDnd.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as _ from 'vs/base/parts/tree/browser/tree'; -import * as Mouse from 'vs/base/browser/mouseEvent'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; -export class ElementsDragAndDropData implements _.IDragAndDropData { +export class ElementsDragAndDropData implements IDragAndDropData { private elements: any[]; @@ -14,7 +14,7 @@ export class ElementsDragAndDropData implements _.IDragAndDropData { this.elements = elements; } - public update(event: Mouse.DragMouseEvent): void { + public update(dataTransfer: DataTransfer): void { // no-op } @@ -23,7 +23,7 @@ export class ElementsDragAndDropData implements _.IDragAndDropData { } } -export class ExternalElementsDragAndDropData implements _.IDragAndDropData { +export class ExternalElementsDragAndDropData implements IDragAndDropData { private elements: any[]; @@ -31,7 +31,7 @@ export class ExternalElementsDragAndDropData implements _.IDragAndDropData { this.elements = elements; } - public update(event: Mouse.DragMouseEvent): void { + public update(dataTransfer: DataTransfer): void { // no-op } @@ -40,7 +40,7 @@ export class ExternalElementsDragAndDropData implements _.IDragAndDropData { } } -export class DesktopDragAndDropData implements _.IDragAndDropData { +export class DesktopDragAndDropData implements IDragAndDropData { private types: any[]; private files: any[]; @@ -50,15 +50,15 @@ export class DesktopDragAndDropData implements _.IDragAndDropData { this.files = []; } - public update(event: Mouse.DragMouseEvent): void { - if (event.dataTransfer.types) { + public update(dataTransfer: DataTransfer): void { + if (dataTransfer.types) { this.types = []; - Array.prototype.push.apply(this.types, event.dataTransfer.types); + Array.prototype.push.apply(this.types, dataTransfer.types as any); } - if (event.dataTransfer.files) { + if (dataTransfer.files) { this.files = []; - Array.prototype.push.apply(this.files, event.dataTransfer.files); + Array.prototype.push.apply(this.files, dataTransfer.files as any); this.files = this.files.filter(f => f.size || f.type); } diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index a07e9f1b88f9..c2482813ac32 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./tree'; -import * as WinJS from 'vs/base/common/winjs.base'; import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults'; import * as Model from 'vs/base/parts/tree/browser/treeModel'; import * as View from './treeView'; @@ -21,13 +20,13 @@ export class TreeContext implements _.ITreeContext { public options: _.ITreeOptions; public dataSource: _.IDataSource; - public renderer: _.IRenderer; + public renderer?: _.IRenderer; public controller: _.IController; public dnd: _.IDragAndDrop; public filter: _.IFilter; - public sorter: _.ISorter; + public sorter?: _.ISorter; public accessibilityProvider: _.IAccessibilityProvider; - public styler: _.ITreeStyler; + public styler?: _.ITreeStyler; constructor(tree: _.ITree, configuration: _.ITreeConfiguration, options: _.ITreeOptions = {}) { this.tree = tree; @@ -43,9 +42,9 @@ export class TreeContext implements _.ITreeContext { this.controller = configuration.controller || new TreeDefaults.DefaultController({ clickBehavior: TreeDefaults.ClickBehavior.ON_MOUSE_UP, keyboardSupport: typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport }); this.dnd = configuration.dnd || new TreeDefaults.DefaultDragAndDrop(); this.filter = configuration.filter || new TreeDefaults.DefaultFilter(); - this.sorter = configuration.sorter || null; + this.sorter = configuration.sorter; this.accessibilityProvider = configuration.accessibilityProvider || new TreeDefaults.DefaultAccessibilityProvider(); - this.styler = configuration.styler || null; + this.styler = configuration.styler; } } @@ -150,7 +149,7 @@ export class Tree implements _.ITree { this.view.onHidden(); } - public setInput(element: any): WinJS.Promise { + public setInput(element: any): Promise { return this.model.setInput(element); } @@ -158,36 +157,31 @@ export class Tree implements _.ITree { return this.model.getInput(); } - public refresh(element: any = null, recursive = true): WinJS.Promise { + public refresh(element: any = null, recursive = true): Promise { return this.model.refresh(element, recursive); } - public updateWidth(element: any): void { - let item = this.model.getItem(element); - return this.view.updateWidth(item); - } - - public expand(element: any): WinJS.Promise { + public expand(element: any): Promise { return this.model.expand(element); } - public expandAll(elements: any[]): WinJS.Promise { + public expandAll(elements: any[]): Promise { return this.model.expandAll(elements); } - public collapse(element: any, recursive: boolean = false): WinJS.Promise { + public collapse(element: any, recursive: boolean = false): Promise { return this.model.collapse(element, recursive); } - public collapseAll(elements: any[] | null = null, recursive: boolean = false): WinJS.Promise { + public collapseAll(elements: any[] | null = null, recursive: boolean = false): Promise { return this.model.collapseAll(elements, recursive); } - public toggleExpansion(element: any, recursive: boolean = false): WinJS.Promise { + public toggleExpansion(element: any, recursive: boolean = false): Promise { return this.model.toggleExpansion(element, recursive); } - public toggleExpansionAll(elements: any[]): WinJS.Promise { + public toggleExpansionAll(elements: any[]): Promise { return this.model.toggleExpansionAll(elements); } @@ -199,13 +193,13 @@ export class Tree implements _.ITree { return this.model.getExpandedElements(); } - public reveal(element: any, relativeTop: number | null = null): WinJS.Promise { + public reveal(element: any, relativeTop: number | null = null): Promise { return this.model.reveal(element, relativeTop); } public getRelativeTop(element: any): number { - let item = this.model.getItem(element); - return this.view.getRelativeTop(item); + const item = this.model.getItem(element); + return item ? this.view.getRelativeTop(item) : 0; } public getFirstVisibleElement(): any { @@ -378,11 +372,11 @@ export class Tree implements _.ITree { if (this.model !== null) { this.model.dispose(); - this.model = null; + this.model = null!; // StrictNullOverride Nulling out ok in dispose } if (this.view !== null) { this.view.dispose(); - this.view = null; + this.view = null!; // StrictNullOverride Nulling out ok in dispose } this._onDidChangeFocus.dispose(); diff --git a/src/vs/base/parts/tree/browser/treeModel.ts b/src/vs/base/parts/tree/browser/treeModel.ts index 8202d980d773..5226fd60be37 100644 --- a/src/vs/base/parts/tree/browser/treeModel.ts +++ b/src/vs/base/parts/tree/browser/treeModel.ts @@ -9,7 +9,7 @@ import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import * as arrays from 'vs/base/common/arrays'; import { INavigator } from 'vs/base/common/iterator'; import * as _ from './tree'; -import { Event, Emitter, once, EventMultiplexer, Relay } from 'vs/base/common/event'; +import { Event, Emitter, EventMultiplexer, Relay } from 'vs/base/common/event'; // {{SQL CARBON EDIT}} import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode'; @@ -20,8 +20,8 @@ interface ITraitMap extends IMap { } export class LockData { private _item: Item; - private _onDispose = new Emitter(); - readonly onDispose: Event = this._onDispose.event; + private _onDispose?= new Emitter(); + readonly onDispose: Event = this._onDispose!.event; constructor(item: Item) { this._item = item; @@ -35,7 +35,7 @@ export class LockData { if (this._onDispose) { this._onDispose.fire(); this._onDispose.dispose(); - this._onDispose = null; + this._onDispose = undefined; } } } @@ -80,18 +80,18 @@ export class Lock { return !!this.locks[item.id]; } - public run(item: Item, fn: () => Thenable): Thenable { - var lock = this.getLock(item); + public run(item: Item, fn: () => Promise): Promise { + const lock = this.getLock(item); if (lock) { return new Promise((c, e) => { - once(lock.onDispose)(() => { + Event.once(lock.onDispose)(() => { return this.run(item, fn).then(c, e); }); }); } - var result: Thenable; + let result: Promise; return new Promise((c, e) => { @@ -99,7 +99,7 @@ export class Lock { return e(new Error('Item is disposed.')); } - var lock = this.locks[item.id] = new LockData(item); + let lock = this.locks[item.id] = new LockData(item); result = fn().then((r) => { delete this.locks[item.id]; @@ -112,11 +112,11 @@ export class Lock { }); } - private getLock(item: Item): LockData { - var key: string; + private getLock(item: Item): LockData | null { + let key: string; for (key in this.locks) { - var lock = this.locks[key]; + let lock = this.locks[key]; if (item.intersects(lock.item)) { return lock; @@ -189,13 +189,13 @@ export class ItemRegistry { return this.items.hasOwnProperty(id); } - public getItem(id: string): Item { + public getItem(id: string): Item | null { const result = this.items[id]; return result ? result.item : null; } public dispose(): void { - this.items = null; + this.items = null!; // StrictNullOverride: nulling out ok in dispose this._onDidRevealItem.dispose(); this._onExpandItem.dispose(); @@ -229,7 +229,7 @@ export interface IItemTraitEvent extends IBaseItemEvent { } export interface IItemRevealEvent extends IBaseItemEvent { - relativeTop: number; + relativeTop: number | null; } export interface IItemChildrenRefreshEvent extends IBaseItemEvent { @@ -248,11 +248,11 @@ export class Item { private needsChildrenRefresh: boolean; private doesHaveChildren: boolean; - public parent: Item; - public previous: Item; - public next: Item; - public firstChild: Item; - public lastChild: Item; + public parent: Item | null; + public previous: Item | null; + public next: Item | null; + public firstChild: Item | null; + public lastChild: Item | null; private height: number; private depth: number; @@ -309,7 +309,7 @@ export class Item { this.traits = {}; this.depth = 0; - this.expanded = this.context.dataSource.shouldAutoexpand && this.context.dataSource.shouldAutoexpand(this.context.tree, element); + this.expanded = !!(this.context.dataSource.shouldAutoexpand && this.context.dataSource.shouldAutoexpand(this.context.tree, element)); this._onDidCreate.fire(this); @@ -348,22 +348,22 @@ export class Item { } public reveal(relativeTop: number | null = null): void { - var eventData: IItemRevealEvent = { item: this, relativeTop: relativeTop }; + let eventData: IItemRevealEvent = { item: this, relativeTop: relativeTop }; this._onDidReveal.fire(eventData); } - public expand(): Thenable { + public expand(): Promise { if (this.isExpanded() || !this.doesHaveChildren || this.lock.isLocked(this)) { return Promise.resolve(false); } - var result = this.lock.run(this, () => { + let result = this.lock.run(this, () => { if (this.isExpanded() || !this.doesHaveChildren) { return Promise.resolve(false); } - var eventData: IItemExpandEvent = { item: this }; - var result: Thenable; + let eventData: IItemExpandEvent = { item: this }; + let result: Promise; this._onExpand.fire(eventData); if (this.needsChildrenRefresh) { @@ -400,9 +400,9 @@ export class Item { }); } - public collapse(recursive: boolean = false): Thenable { + public collapse(recursive: boolean = false): Promise { if (recursive) { - var collapseChildrenPromise = Promise.resolve(null); + let collapseChildrenPromise = Promise.resolve(null); this.forEachChild((child) => { collapseChildrenPromise = collapseChildrenPromise.then(() => child.collapse(true)); }); @@ -415,7 +415,7 @@ export class Item { } return this.lock.run(this, () => { - var eventData: IItemCollapseEvent = { item: this }; + let eventData: IItemCollapseEvent = { item: this }; this._onCollapse.fire(eventData); this._setExpanded(false); this._onDidCollapse.fire(eventData); @@ -426,13 +426,13 @@ export class Item { } public addTrait(trait: string): void { - var eventData: IItemTraitEvent = { item: this, trait: trait }; + let eventData: IItemTraitEvent = { item: this, trait: trait }; this.traits[trait] = true; this._onDidAddTrait.fire(eventData); } public removeTrait(trait: string): void { - var eventData: IItemTraitEvent = { item: this, trait: trait }; + let eventData: IItemTraitEvent = { item: this, trait: trait }; delete this.traits[trait]; this._onDidRemoveTrait.fire(eventData); } @@ -442,8 +442,8 @@ export class Item { } public getAllTraits(): string[] { - var result: string[] = []; - var trait: string; + let result: string[] = []; + let trait: string; for (trait in this.traits) { if (this.traits.hasOwnProperty(trait) && this.traits[trait]) { result.push(trait); @@ -456,7 +456,7 @@ export class Item { return this.height; } - private refreshChildren(recursive: boolean, safe: boolean = false, force: boolean = false): Thenable { + private refreshChildren(recursive: boolean, safe: boolean = false, force: boolean = false): Promise { if (!force && !this.isExpanded()) { const setNeedsChildrenRefresh = (item: Item) => { item.needsChildrenRefresh = true; @@ -470,11 +470,11 @@ export class Item { this.needsChildrenRefresh = false; - var doRefresh = () => { - var eventData: IItemChildrenRefreshEvent = { item: this, isNested: safe }; + let doRefresh = () => { + let eventData: IItemChildrenRefreshEvent = { item: this, isNested: safe }; this._onRefreshChildren.fire(eventData); - var childrenPromise: Thenable; + let childrenPromise: Promise; if (this.doesHaveChildren) { childrenPromise = this.context.dataSource.getChildren(this.context.tree, this.element); } else { @@ -493,16 +493,16 @@ export class Item { elements = !elements ? [] : elements.slice(0); elements = this.sort(elements); - var staleItems: IItemMap = {}; + let staleItems: IItemMap = {}; while (this.firstChild !== null) { staleItems[this.firstChild.id] = this.firstChild; this.removeChild(this.firstChild); } - for (var i = 0, len = elements.length; i < len; i++) { - var element = elements[i]; - var id = this.context.dataSource.getId(this.context.tree, element); - var item = staleItems[id] || new Item(id, this.registry, this.context, this.lock, element); + for (let i = 0, len = elements.length; i < len; i++) { + let element = elements[i]; + let id = this.context.dataSource.getId(this.context.tree, element); + let item = staleItems[id] || new Item(id, this.registry, this.context, this.lock, element); item.element = element; if (recursive) { item.needsChildrenRefresh = recursive; @@ -511,7 +511,7 @@ export class Item { this.addChild(item); } - for (var staleItemId in staleItems) { + for (let staleItemId in staleItems) { if (staleItems.hasOwnProperty(staleItemId)) { staleItems[staleItemId].dispose(); } @@ -534,14 +534,14 @@ export class Item { }); return result - .then(null, onUnexpectedError) + .then(undefined, onUnexpectedError) .then(() => this._onDidRefreshChildren.fire(eventData)); }; return safe ? doRefresh() : this.lock.run(this, doRefresh); } - private doRefresh(recursive: boolean, safe: boolean = false): Thenable { + private doRefresh(recursive: boolean, safe: boolean = false): Promise { this.doesHaveChildren = this.context.dataSource.hasChildren(this.context.tree, this.element); this.height = this._getHeight(); this.updateVisibility(); @@ -555,7 +555,7 @@ export class Item { this.setVisible(this._isVisible()); } - public refresh(recursive: boolean): Thenable { + public refresh(recursive: boolean): Promise { return this.doRefresh(recursive); } @@ -568,8 +568,8 @@ export class Item { } public getHierarchy(): Item[] { - var result: Item[] = []; - var node: Item = this; + let result: Item[] = []; + let node: Item | null = this; do { result.push(node); @@ -580,7 +580,8 @@ export class Item { return result; } - private isAncestorOf(item: Item): boolean { + private isAncestorOf(startItem: Item): boolean { + let item: Item | null = startItem; while (item) { if (item.id === this.id) { return true; @@ -590,27 +591,39 @@ export class Item { return false; } - private addChild(item: Item, afterItem: Item = this.lastChild): void { - var isEmpty = this.firstChild === null; - var atHead = afterItem === null; - var atTail = afterItem === this.lastChild; + private addChild(item: Item, afterItem: Item | null = this.lastChild): void { + let isEmpty = this.firstChild === null; + let atHead = afterItem === null; + let atTail = afterItem === this.lastChild; if (isEmpty) { this.firstChild = this.lastChild = item; item.next = item.previous = null; } else if (atHead) { + if (!this.firstChild) { + throw new Error('Invalid tree state'); + } this.firstChild.previous = item; item.next = this.firstChild; item.previous = null; this.firstChild = item; } else if (atTail) { + if (!this.lastChild) { + throw new Error('Invalid tree state'); + } this.lastChild.next = item; item.next = null; item.previous = this.lastChild; this.lastChild = item; } else { item.previous = afterItem; + if (!afterItem) { + throw new Error('Invalid tree state'); + } item.next = afterItem.next; + if (!afterItem.next) { + throw new Error('Invalid tree state'); + } afterItem.next.previous = item; afterItem.next = item; } @@ -620,28 +633,41 @@ export class Item { } private removeChild(item: Item): void { - var isFirstChild = this.firstChild === item; - var isLastChild = this.lastChild === item; + let isFirstChild = this.firstChild === item; + let isLastChild = this.lastChild === item; if (isFirstChild && isLastChild) { this.firstChild = this.lastChild = null; } else if (isFirstChild) { + if (!item.next) { + throw new Error('Invalid tree state'); + } item.next.previous = null; this.firstChild = item.next; } else if (isLastChild) { + if (!item.previous) { + throw new Error('Invalid tree state'); + } item.previous.next = null; this.lastChild = item.previous; } else { + if (!item.next) { + throw new Error('Invalid tree state'); + } item.next.previous = item.previous; + if (!item.previous) { + throw new Error('Invalid tree state'); + } item.previous.next = item.next; } item.parent = null; - item.depth = null; + item.depth = NaN; } private forEachChild(fn: (child: Item) => void): void { - var child = this.firstChild, next: Item; + let child = this.firstChild; + let next: Item | null; while (child) { next = child.next; fn(child); @@ -650,7 +676,7 @@ export class Item { } private mapEachChild(fn: (child: Item) => T): T[] { - var result: T[] = []; + let result: T[] = []; this.forEachChild((child) => { result.push(fn(child)); }); @@ -658,9 +684,10 @@ export class Item { } private sort(elements: any[]): any[] { - if (this.context.sorter) { + const sorter = this.context.sorter; + if (sorter) { return elements.sort((element, otherElement) => { - return this.context.sorter.compare(this.context.tree, element, otherElement); + return sorter.compare(this.context.tree, element, otherElement); }); } @@ -668,10 +695,16 @@ export class Item { } /* protected */ public _getHeight(): number { + if (!this.context.renderer) { + return 0; + } return this.context.renderer.getHeight(this.context.tree, this.element); } /* protected */ public _isVisible(): boolean { + if (!this.context.filter) { + return false; + } return this.context.filter.isVisible(this.context.tree, this.element); } @@ -746,10 +779,10 @@ class RootItem extends Item { export class TreeNavigator implements INavigator { - private start: Item; - private item: Item; + private start: Item | null; + private item: Item | null; - static lastDescendantOf(item: Item): Item { + static lastDescendantOf(item: Item | null): Item | null { if (!item) { return null; } @@ -769,16 +802,16 @@ export class TreeNavigator implements INavigator { return TreeNavigator.lastDescendantOf(item.lastChild); } - constructor(item: Item, subTreeOnly: boolean = true) { + constructor(item: Item | null, subTreeOnly: boolean = true) { this.item = item; this.start = subTreeOnly ? item : null; } - public current(): Item { + public current(): Item | null { return this.item || null; } - public next(): Item { + public next(): Item | null { if (this.item) { do { if ((this.item instanceof RootItem || (this.item.isVisible() && this.item.isExpanded())) && this.item.firstChild) { @@ -800,10 +833,10 @@ export class TreeNavigator implements INavigator { return this.item || null; } - public previous(): Item { + public previous(): Item | null { if (this.item) { do { - var previous = TreeNavigator.lastDescendantOf(this.item.previous); + let previous = TreeNavigator.lastDescendantOf(this.item.previous); if (previous) { this.item = previous; } else if (this.item.parent && this.item.parent !== this.start && this.item.parent.isVisible()) { @@ -816,9 +849,9 @@ export class TreeNavigator implements INavigator { return this.item || null; } - public parent(): Item { + public parent(): Item | null { if (this.item) { - var parent = this.item.parent; + let parent = this.item.parent; if (parent && parent !== this.start && parent.isVisible()) { this.item = parent; } else { @@ -828,29 +861,29 @@ export class TreeNavigator implements INavigator { return this.item || null; } - public first(): Item { + public first(): Item | null { this.item = this.start; this.next(); return this.item || null; } - public last(): Item { + public last(): Item | null { return TreeNavigator.lastDescendantOf(this.start); } } function getRange(one: Item, other: Item): Item[] { - var oneHierarchy = one.getHierarchy(); - var otherHierarchy = other.getHierarchy(); - var length = arrays.commonPrefixLength(oneHierarchy, otherHierarchy); - var item = oneHierarchy[length - 1]; - var nav = item.getNavigator(); + let oneHierarchy = one.getHierarchy(); + let otherHierarchy = other.getHierarchy(); + let length = arrays.commonPrefixLength(oneHierarchy, otherHierarchy); + let item: Item | null = oneHierarchy[length - 1]; + let nav = item.getNavigator(); - var oneIndex: number | null = null; - var otherIndex: number | null = null; + let oneIndex: number | null = null; + let otherIndex: number | null = null; - var index = 0; - var result: Item[] = []; + let index = 0; + let result: Item[] = []; while (item && (oneIndex === null || otherIndex === null)) { result.push(item); @@ -870,13 +903,13 @@ function getRange(one: Item, other: Item): Item[] { return []; } - var min = Math.min(oneIndex, otherIndex); - var max = Math.max(oneIndex, otherIndex); + let min = Math.min(oneIndex, otherIndex); + let max = Math.max(oneIndex, otherIndex); return result.slice(min, max + 1); } export interface IBaseEvent { - item: Item; + item: Item | null; } export interface IInputEvent extends IBaseEvent { } @@ -889,7 +922,7 @@ export class TreeModel { private context: _.ITreeContext; private lock: Lock; - private input: Item; + private input: Item | null; private registry: ItemRegistry; private registryDisposable: IDisposable; private traitsToItems: ITraitMap; @@ -938,8 +971,8 @@ export class TreeModel { this.traitsToItems = {}; } - public setInput(element: any): Thenable { - var eventData: IInputEvent = { item: this.input }; + public setInput(element: any): Promise { + let eventData: IInputEvent = { item: this.input }; this._onSetInput.fire(eventData); this.setSelection([]); @@ -974,7 +1007,7 @@ export class TreeModel { this.registryDisposable = this.registry .onDidDisposeItem(item => item.getAllTraits().forEach(trait => delete this.traitsToItems[trait][item.id])); - var id = this.context.dataSource.getId(this.context.tree, element); + let id = this.context.dataSource.getId(this.context.tree, element); this.input = new RootItem(id, this.registry, this.context, this.lock, element); eventData = { item: this.input }; this._onDidSetInput.fire(eventData); @@ -985,22 +1018,22 @@ export class TreeModel { return this.input ? this.input.getElement() : null; } - public refresh(element: any = null, recursive: boolean = true): Thenable { - var item = this.getItem(element); + public refresh(element: any = null, recursive: boolean = true): Promise { + let item = this.getItem(element); if (!item) { return Promise.resolve(null); } - var eventData: IRefreshEvent = { item: item, recursive: recursive }; + let eventData: IRefreshEvent = { item: item, recursive: recursive }; this._onRefresh.fire(eventData); return item.refresh(recursive).then(() => { this._onDidRefresh.fire(eventData); }); } - public expand(element: any): Thenable { - var item = this.getItem(element); + public expand(element: any): Promise { + let item = this.getItem(element); if (!item) { return Promise.resolve(false); @@ -1009,12 +1042,12 @@ export class TreeModel { return item.expand(); } - public expandAll(elements?: any[]): Thenable { + public expandAll(elements?: any[]): Promise { if (!elements) { elements = []; - var item: Item; - var nav = this.getNavigator(); + let item: Item | null; + let nav = this.getNavigator(); while (item = nav.next()) { elements.push(item); @@ -1024,7 +1057,7 @@ export class TreeModel { return this._expandAll(elements); } - private _expandAll(elements: any[]): Thenable { + private _expandAll(elements: any[]): Promise { if (elements.length === 0) { return Promise.resolve(null); } @@ -1033,7 +1066,7 @@ export class TreeModel { const elementsToDelay: any[] = []; for (const element of elements) { - var item = this.getItem(element); + let item = this.getItem(element); if (item) { elementsToExpand.push(element); @@ -1050,16 +1083,16 @@ export class TreeModel { .then(() => this._expandAll(elementsToDelay)); } - private __expandAll(elements: any[]): Thenable { - var promises = []; - for (var i = 0, len = elements.length; i < len; i++) { + private __expandAll(elements: any[]): Promise { + const promises: Array> = []; + for (let i = 0, len = elements.length; i < len; i++) { promises.push(this.expand(elements[i])); } return Promise.all(promises); } - public collapse(element: any, recursive: boolean = false): Thenable { - var item = this.getItem(element); + public collapse(element: any, recursive: boolean = false): Promise { + const item = this.getItem(element); if (!item) { return Promise.resolve(false); @@ -1068,32 +1101,32 @@ export class TreeModel { return item.collapse(recursive); } - public collapseAll(elements: any[] | null = null, recursive: boolean = false): Thenable { + public collapseAll(elements: any[] | null = null, recursive: boolean = false): Promise { if (!elements) { elements = [this.input]; recursive = true; } - var promises = []; - for (var i = 0, len = elements.length; i < len; i++) { + let promises: Array> = []; + for (let i = 0, len = elements.length; i < len; i++) { promises.push(this.collapse(elements[i], recursive)); } return Promise.all(promises); } - public toggleExpansion(element: any, recursive: boolean = false): Thenable { + public toggleExpansion(element: any, recursive: boolean = false): Promise { return this.isExpanded(element) ? this.collapse(element, recursive) : this.expand(element); } - public toggleExpansionAll(elements: any[]): Thenable { - var promises = []; - for (var i = 0, len = elements.length; i < len; i++) { + public toggleExpansionAll(elements: any[]): Promise { + let promises: Array> = []; + for (let i = 0, len = elements.length; i < len; i++) { promises.push(this.toggleExpansion(elements[i])); } return Promise.all(promises); } public isExpanded(element: any): boolean { - var item = this.getItem(element); + let item = this.getItem(element); if (!item) { return false; @@ -1103,9 +1136,9 @@ export class TreeModel { } public getExpandedElements(): any[] { - var result: any[] = []; - var item: Item; - var nav = this.getNavigator(); + let result: any[] = []; + let item: Item | null; + let nav = this.getNavigator(); while (item = nav.next()) { if (item.isExpanded()) { @@ -1116,9 +1149,9 @@ export class TreeModel { return result; } - public reveal(element: any, relativeTop: number | null = null): Thenable { + public reveal(element: any, relativeTop: number | null = null): Promise { return this.resolveUnknownParentChain(element).then((chain: any[]) => { - var result = Promise.resolve(null); + let result = Promise.resolve(null); chain.forEach((e) => { result = result.then(() => this.expand(e)); @@ -1126,7 +1159,7 @@ export class TreeModel { return result; }).then(() => { - var item = this.getItem(element); + let item = this.getItem(element); if (item) { return item.reveal(relativeTop); @@ -1134,7 +1167,7 @@ export class TreeModel { }); } - private resolveUnknownParentChain(element: any): Thenable { + private resolveUnknownParentChain(element: any): Promise { return this.context.dataSource.getParent(this.context.tree, element).then((parent) => { if (!parent) { return Promise.resolve([]); @@ -1149,17 +1182,17 @@ export class TreeModel { public setHighlight(element?: any, eventPayload?: any): void { this.setTraits('highlighted', element ? [element] : []); - var eventData: _.IHighlightEvent = { highlight: this.getHighlight(), payload: eventPayload }; + let eventData: _.IHighlightEvent = { highlight: this.getHighlight(), payload: eventPayload }; this._onDidHighlight.fire(eventData); } - public getHighlight(includeHidden?: boolean): any { - var result = this.getElementsWithTrait('highlighted', includeHidden); + public getHighlight(includeHidden: boolean = false): any { + let result = this.getElementsWithTrait('highlighted', includeHidden); return result.length === 0 ? null : result[0]; } public isHighlighted(element: any): boolean { - var item = this.getItem(element); + let item = this.getItem(element); if (!item) { return false; @@ -1173,8 +1206,8 @@ export class TreeModel { } public selectRange(fromElement: any, toElement: any, eventPayload?: any): void { - var fromItem = this.getItem(fromElement); - var toItem = this.getItem(toElement); + let fromItem = this.getItem(fromElement); + let toItem = this.getItem(toElement); if (!fromItem || !toItem) { return; @@ -1184,8 +1217,8 @@ export class TreeModel { } public deselectRange(fromElement: any, toElement: any, eventPayload?: any): void { - var fromItem = this.getItem(fromElement); - var toItem = this.getItem(toElement); + let fromItem = this.getItem(fromElement); + let toItem = this.getItem(toElement); if (!fromItem || !toItem) { return; @@ -1196,7 +1229,7 @@ export class TreeModel { public selectAll(elements: any[], eventPayload?: any): void { this.addTraits('selected', elements); - var eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; + let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; this._onDidSelect.fire(eventData); } @@ -1206,24 +1239,24 @@ export class TreeModel { public deselectAll(elements: any[], eventPayload?: any): void { this.removeTraits('selected', elements); - var eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; + let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; this._onDidSelect.fire(eventData); } public setSelection(elements: any[], eventPayload?: any): void { this.setTraits('selected', elements); - var eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; + let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; this._onDidSelect.fire(eventData); } public toggleSelection(element: any, eventPayload?: any): void { this.toggleTrait('selected', element); - var eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; + let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; this._onDidSelect.fire(eventData); } public isSelected(element: any): boolean { - var item = this.getItem(element); + let item = this.getItem(element); if (!item) { return false; @@ -1232,17 +1265,17 @@ export class TreeModel { return item.hasTrait('selected'); } - public getSelection(includeHidden?: boolean): any[] { + public getSelection(includeHidden: boolean = false): any[] { return this.getElementsWithTrait('selected', includeHidden); } public selectNext(count: number = 1, clearSelection: boolean = true, eventPayload?: any): void { - var selection = this.getSelection(); - var item: Item = selection.length > 0 ? selection[0] : this.input; - var nextItem: Item; - var nav = this.getNavigator(item, false); + let selection = this.getSelection(); + let item: Item = selection.length > 0 ? selection[0] : this.input; + let nextItem: Item | null; + let nav = this.getNavigator(item, false); - for (var i = 0; i < count; i++) { + for (let i = 0; i < count; i++) { nextItem = nav.next(); if (!nextItem) { break; @@ -1258,7 +1291,7 @@ export class TreeModel { } public selectPrevious(count: number = 1, clearSelection: boolean = true, eventPayload?: any): void { - var selection = this.getSelection(), + let selection = this.getSelection(), item: Item | null = null, previousItem: Item | null = null; @@ -1275,7 +1308,7 @@ export class TreeModel { item = selection[0]; let nav = this.getNavigator(item, false); - for (var i = 0; i < count; i++) { + for (let i = 0; i < count; i++) { previousItem = nav.previous(); if (!previousItem) { break; @@ -1292,10 +1325,10 @@ export class TreeModel { } public selectParent(eventPayload?: any, clearSelection: boolean = true): void { - var selection = this.getSelection(); - var item: Item = selection.length > 0 ? selection[0] : this.input; - var nav = this.getNavigator(item, false); - var parent = nav.parent(); + let selection = this.getSelection(); + let item: Item = selection.length > 0 ? selection[0] : this.input; + let nav = this.getNavigator(item, false); + let parent = nav.parent(); if (parent) { if (clearSelection) { @@ -1308,12 +1341,12 @@ export class TreeModel { public setFocus(element?: any, eventPayload?: any): void { this.setTraits('focused', element ? [element] : []); - var eventData: _.IFocusEvent = { focus: this.getFocus(), payload: eventPayload }; + let eventData: _.IFocusEvent = { focus: this.getFocus(), payload: eventPayload }; this._onDidFocus.fire(eventData); } public isFocused(element: any): boolean { - var item = this.getItem(element); + let item = this.getItem(element); if (!item) { return false; @@ -1322,17 +1355,17 @@ export class TreeModel { return item.hasTrait('focused'); } - public getFocus(includeHidden?: boolean): any { - var result = this.getElementsWithTrait('focused', includeHidden); + public getFocus(includeHidden: boolean = false): any { + let result = this.getElementsWithTrait('focused', includeHidden); return result.length === 0 ? null : result[0]; } public focusNext(count: number = 1, eventPayload?: any): void { - var item: Item = this.getFocus() || this.input; - var nextItem: Item; - var nav = this.getNavigator(item, false); + let item: Item = this.getFocus() || this.input; + let nextItem: Item | null; + let nav = this.getNavigator(item, false); - for (var i = 0; i < count; i++) { + for (let i = 0; i < count; i++) { nextItem = nav.next(); if (!nextItem) { break; @@ -1344,11 +1377,11 @@ export class TreeModel { } public focusPrevious(count: number = 1, eventPayload?: any): void { - var item: Item = this.getFocus() || this.input; - var previousItem: Item; - var nav = this.getNavigator(item, false); + let item: Item = this.getFocus() || this.input; + let previousItem: Item | null; + let nav = this.getNavigator(item, false); - for (var i = 0; i < count; i++) { + for (let i = 0; i < count; i++) { previousItem = nav.previous(); if (!previousItem) { break; @@ -1360,9 +1393,9 @@ export class TreeModel { } public focusParent(eventPayload?: any): void { - var item: Item = this.getFocus() || this.input; - var nav = this.getNavigator(item, false); - var parent = nav.parent(); + let item: Item = this.getFocus() || this.input; + let nav = this.getNavigator(item, false); + let parent = nav.parent(); if (parent) { this.setFocus(parent, eventPayload); @@ -1385,10 +1418,10 @@ export class TreeModel { } public focusNth(index: number, eventPayload?: any, from?: any): void { - var navItem = this.getParent(from); - var nav = this.getNavigator(navItem); - var item = nav.first(); - for (var i = 0; i < index; i++) { + let navItem = this.getParent(from); + let nav = this.getNavigator(navItem); + let item = nav.first(); + for (let i = 0; i < index; i++) { item = nav.next(); } @@ -1398,12 +1431,12 @@ export class TreeModel { } public focusLast(eventPayload?: any, from?: any): void { - var navItem = this.getParent(from); - var item: Item; - if (from) { + const navItem = this.getParent(from); + let item: Item | null; + if (from && navItem) { item = navItem.lastChild; } else { - var nav = this.getNavigator(navItem); + const nav = this.getNavigator(navItem); item = nav.last(); } @@ -1412,9 +1445,9 @@ export class TreeModel { } } - private getParent(from?: any): Item { + private getParent(from?: any): Item | null { if (from) { - var fromItem = this.getItem(from); + const fromItem = this.getItem(from); if (fromItem && fromItem.parent) { return fromItem.parent; } @@ -1427,7 +1460,7 @@ export class TreeModel { return new TreeNavigator(this.getItem(element), subTreeOnly); } - public getItem(element: any = null): Item { + public getItem(element: any = null): Item | null { if (element === null) { return this.input; } else if (element instanceof Item) { @@ -1440,9 +1473,9 @@ export class TreeModel { } public addTraits(trait: string, elements: any[]): void { - var items: IItemMap = this.traitsToItems[trait] || {}; - var item: Item; - for (var i = 0, len = elements.length; i < len; i++) { + let items: IItemMap = this.traitsToItems[trait] || {}; + let item: Item | null; + for (let i = 0, len = elements.length; i < len; i++) { item = this.getItem(elements[i]); if (item) { @@ -1454,9 +1487,9 @@ export class TreeModel { } public removeTraits(trait: string, elements: any[]): void { - var items: IItemMap = this.traitsToItems[trait] || {}; - var item: Item; - var id: string; + let items: IItemMap = this.traitsToItems[trait] || {}; + let item: Item | null; + let id: string; if (elements.length === 0) { for (id in items) { @@ -1469,7 +1502,7 @@ export class TreeModel { delete this.traitsToItems[trait]; } else { - for (var i = 0, len = elements.length; i < len; i++) { + for (let i = 0, len = elements.length; i < len; i++) { item = this.getItem(elements[i]); if (item) { @@ -1481,12 +1514,12 @@ export class TreeModel { } public hasTrait(trait: string, element: any): boolean { - var item = this.getItem(element); - return item && item.hasTrait(trait); + const item = this.getItem(element); + return !!(item && item.hasTrait(trait)); } private toggleTrait(trait: string, element: any): void { - var item = this.getItem(element); + let item = this.getItem(element); if (!item) { return; @@ -1503,8 +1536,8 @@ export class TreeModel { if (elements.length === 0) { this.removeTraits(trait, elements); } else { - var items: { [id: string]: Item; } = {}; - var item: Item; + let items: { [id: string]: Item; } = {}; + let item: Item | null; for (let i = 0, len = elements.length; i < len; i++) { item = this.getItem(elements[i]); @@ -1514,9 +1547,9 @@ export class TreeModel { } } - var traitItems: IItemMap = this.traitsToItems[trait] || {}; - var itemsToRemoveTrait: Item[] = []; - var id: string; + let traitItems: IItemMap = this.traitsToItems[trait] || {}; + let itemsToRemoveTrait: Item[] = []; + let id: string; for (id in traitItems) { if (traitItems.hasOwnProperty(id)) { @@ -1547,9 +1580,9 @@ export class TreeModel { } private getElementsWithTrait(trait: string, includeHidden: boolean): any[] { - var elements = []; - var items = this.traitsToItems[trait] || {}; - var id: string; + let elements: any[] = []; + let items = this.traitsToItems[trait] || {}; + let id: string; for (id in items) { if (items.hasOwnProperty(id) && (items[id].isVisible() || includeHidden)) { elements.push(items[id].getElement()); @@ -1561,7 +1594,7 @@ export class TreeModel { public dispose(): void { if (this.registry) { this.registry.dispose(); - this.registry = null; + this.registry = null!; // StrictNullOverride: nulling out ok in dispose } this._onSetInput.dispose(); diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 75b862ee9109..60cc6c932490 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -21,19 +21,19 @@ import { HeightMap, IViewItem } from 'vs/base/parts/tree/browser/treeViewModel'; import * as _ from 'vs/base/parts/tree/browser/tree'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Event, Emitter } from 'vs/base/common/event'; -import { DataTransfers } from 'vs/base/browser/dnd'; +import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; import { DefaultTreestyler } from './treeDefaults'; import { Delayer, timeout } from 'vs/base/common/async'; export interface IRow { - element: HTMLElement; + element: HTMLElement | null; templateId: string; templateData: any; } function removeFromParent(element: HTMLElement): void { try { - element.parentElement.removeChild(element); + element.parentElement!.removeChild(element); } catch (e) { // this will throw if this happens due to a blur event, nasty business } @@ -41,26 +41,26 @@ function removeFromParent(element: HTMLElement): void { export class RowCache implements Lifecycle.IDisposable { - private _cache: { [templateId: string]: IRow[]; }; + private _cache: { [templateId: string]: IRow[]; } | null; constructor(private context: _.ITreeContext) { this._cache = { '': [] }; } public alloc(templateId: string): IRow { - var result = this.cache(templateId).pop(); + let result = this.cache(templateId).pop(); if (!result) { - var content = document.createElement('div'); + let content = document.createElement('div'); content.className = 'content'; - var row = document.createElement('div'); + let row = document.createElement('div'); row.appendChild(content); let templateData: any = null; try { - templateData = this.context.renderer.renderTemplate(this.context.tree, templateId, content); + templateData = this.context.renderer!.renderTemplate(this.context.tree, templateId, content); } catch (err) { console.error('Tree usage error: exception while rendering template'); console.error(err); @@ -77,24 +77,24 @@ export class RowCache implements Lifecycle.IDisposable { } public release(templateId: string, row: IRow): void { - removeFromParent(row.element); + removeFromParent(row.element!); this.cache(templateId).push(row); } private cache(templateId: string): IRow[] { - return this._cache[templateId] || (this._cache[templateId] = []); + return this._cache![templateId] || (this._cache![templateId] = []); } public garbageCollect(): void { if (this._cache) { Object.keys(this._cache).forEach(templateId => { - this._cache[templateId].forEach(cachedRow => { - this.context.renderer.disposeTemplate(this.context.tree, templateId, cachedRow.templateData); + this._cache![templateId].forEach(cachedRow => { + this.context.renderer!.disposeTemplate(this.context.tree, templateId, cachedRow.templateData); cachedRow.element = null; cachedRow.templateData = null; }); - delete this._cache[templateId]; + delete this._cache![templateId]; }); } } @@ -102,7 +102,6 @@ export class RowCache implements Lifecycle.IDisposable { public dispose(): void { this.garbageCollect(); this._cache = null; - this.context = null; } } @@ -117,7 +116,7 @@ export class ViewItem implements IViewItem { public model: Model.Item; public id: string; - protected row: IRow; + protected row: IRow | null; public top: number; public height: number; @@ -125,8 +124,8 @@ export class ViewItem implements IViewItem { public onDragStart: (e: DragEvent) => void; public needsRender: boolean; - public uri: string; - public unbindDragStart: Lifecycle.IDisposable; + public uri: string | null; + public unbindDragStart: Lifecycle.IDisposable = Lifecycle.Disposable.None; public loadingTimer: any; public _styles: any; @@ -172,12 +171,12 @@ export class ViewItem implements IViewItem { } public get element(): HTMLElement { - return this.row && this.row.element; + return (this.row && this.row.element)!; } private _templateId: string; private get templateId(): string { - return this._templateId || (this._templateId = (this.context.renderer.getTemplateId && this.context.renderer.getTemplateId(this.context.tree, this.model.getElement()))); + return this._templateId || (this._templateId = (this.context.renderer!.getTemplateId && this.context.renderer!.getTemplateId(this.context.tree, this.model.getElement()))); } public addClass(name: string): void { @@ -195,7 +194,7 @@ export class ViewItem implements IViewItem { return; } - var classes = ['monaco-tree-row']; + let classes = ['monaco-tree-row']; classes.push.apply(classes, Object.keys(this._styles)); if (this.model.hasChildren()) { @@ -208,7 +207,7 @@ export class ViewItem implements IViewItem { // ARIA this.element.setAttribute('role', 'treeitem'); - const accessibility = this.context.accessibilityProvider; + const accessibility = this.context.accessibilityProvider!; const ariaLabel = accessibility.getAriaLabel(this.context.tree, this.model.getElement()); if (ariaLabel) { this.element.setAttribute('aria-label', ariaLabel); @@ -234,18 +233,17 @@ export class ViewItem implements IViewItem { this.element.setAttribute('aria-level', String(this.model.getDepth())); if (this.context.options.paddingOnRow) { - this.element.style.paddingLeft = this.context.options.twistiePixels + ((this.model.getDepth() - 1) * this.context.options.indentPixels) + 'px'; + this.element.style.paddingLeft = this.context.options.twistiePixels! + ((this.model.getDepth() - 1) * this.context.options.indentPixels!) + 'px'; } else { - this.element.style.paddingLeft = ((this.model.getDepth() - 1) * this.context.options.indentPixels) + 'px'; - (this.row.element.firstElementChild).style.paddingLeft = this.context.options.twistiePixels + 'px'; + this.element.style.paddingLeft = ((this.model.getDepth() - 1) * this.context.options.indentPixels!) + 'px'; + (this.row!.element!.firstElementChild).style.paddingLeft = this.context.options.twistiePixels + 'px'; } - var uri = this.context.dnd.getDragURI(this.context.tree, this.model.getElement()); + let uri = this.context.dnd!.getDragURI(this.context.tree, this.model.getElement()); if (uri !== this.uri) { if (this.unbindDragStart) { this.unbindDragStart.dispose(); - this.unbindDragStart = null; } if (uri) { @@ -260,15 +258,18 @@ export class ViewItem implements IViewItem { } if (!skipUserRender && this.element) { - const style = window.getComputedStyle(this.element); - const paddingLeft = parseFloat(style.paddingLeft); + let paddingLeft: number = 0; + if (this.context.horizontalScrolling) { + const style = window.getComputedStyle(this.element); + paddingLeft = parseFloat(style.paddingLeft!); + } if (this.context.horizontalScrolling) { this.element.style.width = 'fit-content'; } try { - this.context.renderer.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row.templateData); + this.context.renderer!.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row!.templateData); } catch (err) { console.error('Tree usage error: exception while rendering element'); console.error(err); @@ -287,13 +288,13 @@ export class ViewItem implements IViewItem { } const style = window.getComputedStyle(this.element); - const paddingLeft = parseFloat(style.paddingLeft); + const paddingLeft = parseFloat(style.paddingLeft!); this.element.style.width = 'fit-content'; this.width = DOM.getContentWidth(this.element) + paddingLeft; this.element.style.width = ''; } - public insertInDOM(container: HTMLElement, afterElement: HTMLElement): void { + public insertInDOM(container: HTMLElement, afterElement: HTMLElement | null): void { if (!this.row) { this.row = this.context.cache.alloc(this.templateId); @@ -324,10 +325,7 @@ export class ViewItem implements IViewItem { return; } - if (this.unbindDragStart) { - this.unbindDragStart.dispose(); - this.unbindDragStart = null; - } + this.unbindDragStart.dispose(); this.uri = null; @@ -338,7 +336,6 @@ export class ViewItem implements IViewItem { public dispose(): void { this.row = null; - this.model = null; } } @@ -350,7 +347,7 @@ class RootViewItem extends ViewItem { this.row = { element: wrapper, templateData: null, - templateId: null + templateId: null! }; } @@ -359,7 +356,7 @@ class RootViewItem extends ViewItem { return; } - var classes = ['monaco-tree-wrapper']; + let classes = ['monaco-tree-wrapper']; classes.push.apply(classes, Object.keys(this._styles)); if (this.model.hasChildren()) { @@ -383,7 +380,7 @@ interface IThrottledGestureEvent { translationY: number; } -function reactionEquals(one: _.IDragOverReaction, other: _.IDragOverReaction): boolean { +function reactionEquals(one: _.IDragOverReaction, other: _.IDragOverReaction | null): boolean { if (!one && !other) { return true; } else if (!one || !other) { @@ -407,11 +404,9 @@ export class TreeView extends HeightMap { private static counter: number = 0; private instance: number; - private static currentExternalDragAndDropData: _.IDragAndDropData = null; - private context: IViewContext; private modelListeners: Lifecycle.IDisposable[]; - private model: Model.TreeModel; + private model: Model.TreeModel | null = null; private viewListeners: Lifecycle.IDisposable[]; private domNode: HTMLElement; @@ -435,29 +430,29 @@ export class TreeView extends HeightMap { private isRefreshing = false; private refreshingPreviousChildrenIds: { [id: string]: string[] } = {}; - private currentDragAndDropData: _.IDragAndDropData; + private currentDragAndDropData: IDragAndDropData | null = null; private currentDropElement: any; private currentDropElementReaction: _.IDragOverReaction; - private currentDropTarget: ViewItem; + private currentDropTarget: ViewItem | null = null; private shouldInvalidateDropReaction: boolean; - private currentDropTargets: ViewItem[]; + private currentDropTargets: ViewItem[] | null = null; private currentDropDisposable: Lifecycle.IDisposable = Lifecycle.Disposable.None; - private dragAndDropScrollInterval: number; - private dragAndDropScrollTimeout: number; - private dragAndDropMouseY: number; + private dragAndDropScrollInterval: number | null = null; + private dragAndDropScrollTimeout: number | null = null; + private dragAndDropMouseY: number | null = null; private didJustPressContextMenuKey: boolean; private highlightedItemWasDraggable: boolean; - private onHiddenScrollTop: number; + private onHiddenScrollTop: number | null = null; - private readonly _onDOMFocus: Emitter = new Emitter(); + private readonly _onDOMFocus = new Emitter(); get onDOMFocus(): Event { return this._onDOMFocus.event; } - private readonly _onDOMBlur: Emitter = new Emitter(); + private readonly _onDOMBlur = new Emitter(); get onDOMBlur(): Event { return this._onDOMBlur.event; } - private readonly _onDidScroll: Emitter = new Emitter(); + private readonly _onDidScroll = new Emitter(); get onDidScroll(): Event { return this._onDidScroll.event; } constructor(context: _.ITreeContext, container: HTMLElement) { @@ -486,7 +481,6 @@ export class TreeView extends HeightMap { this.modelListeners = []; this.viewListeners = []; - this.model = null; this.items = {}; this.domNode = document.createElement('div'); @@ -496,10 +490,7 @@ export class TreeView extends HeightMap { this.styleElement = DOM.createStyleSheet(this.domNode); - this.treeStyler = context.styler; - if (!this.treeStyler) { - this.treeStyler = new DefaultTreestyler(this.styleElement, `monaco-tree-instance-${this.instance}`); - } + this.treeStyler = context.styler || new DefaultTreestyler(this.styleElement, `monaco-tree-instance-${this.instance}`); // ARIA this.domNode.setAttribute('role', 'tree'); @@ -541,7 +532,7 @@ export class TreeView extends HeightMap { this.rowsContainer.className += ' show-twisties'; } - var focusTracker = DOM.trackFocus(this.domNode); + let focusTracker = DOM.trackFocus(this.domNode); this.viewListeners.push(focusTracker.onDidFocus(() => this.onFocus())); this.viewListeners.push(focusTracker.onDidBlur(() => this.onBlur())); this.viewListeners.push(focusTracker); @@ -569,7 +560,7 @@ export class TreeView extends HeightMap { event.stopPropagation(); event.preventDefault(); - var result = { translationY: event.translationY, translationX: event.translationX }; + let result = { translationY: event.translationY, translationX: event.translationX }; if (lastEvent) { result.translationY += lastEvent.translationY; @@ -601,8 +592,6 @@ export class TreeView extends HeightMap { this.dragAndDropScrollInterval = null; this.dragAndDropScrollTimeout = null; - this.onHiddenScrollTop = null; - this.onRowsChanged(); this.layout(); @@ -636,7 +625,7 @@ export class TreeView extends HeightMap { } public onVisible(): void { - this.scrollTop = this.onHiddenScrollTop; + this.scrollTop = this.onHiddenScrollTop!; this.onHiddenScrollTop = null; this.setupMSGesture(); } @@ -691,12 +680,12 @@ export class TreeView extends HeightMap { } private render(scrollTop: number, viewHeight: number, scrollLeft: number, viewWidth: number, scrollWidth: number): void { - var i: number; - var stop: number; + let i: number; + let stop: number; - var renderTop = scrollTop; - var renderBottom = scrollTop + viewHeight; - var thisRenderBottom = this.lastRenderTop + this.lastRenderHeight; + let renderTop = scrollTop; + let renderBottom = scrollTop + viewHeight; + let thisRenderBottom = this.lastRenderTop + this.lastRenderHeight; // when view scrolls down, start rendering from the renderBottom for (i = this.indexAfter(renderBottom) - 1, stop = this.indexAt(Math.max(thisRenderBottom, renderTop)); i >= stop; i--) { @@ -718,7 +707,7 @@ export class TreeView extends HeightMap { this.removeItemFromDOM(this.itemAtIndex(i)); } - var topItem = this.itemAtIndex(this.indexAt(renderTop)); + let topItem = this.itemAtIndex(this.indexAt(renderTop)); if (topItem) { this.rowsContainer.style.top = (topItem.top - renderTop) + 'px'; @@ -790,15 +779,15 @@ export class TreeView extends HeightMap { } public focusNextPage(eventPayload?: any): void { - var lastPageIndex = this.indexAt(this.scrollTop + this.viewHeight); + let lastPageIndex = this.indexAt(this.scrollTop + this.viewHeight); lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1; - var lastPageElement = this.itemAtIndex(lastPageIndex).model.getElement(); - var currentlyFocusedElement = this.model.getFocus(); + let lastPageElement = this.itemAtIndex(lastPageIndex).model.getElement(); + let currentlyFocusedElement = this.model!.getFocus(); if (currentlyFocusedElement !== lastPageElement) { - this.model.setFocus(lastPageElement, eventPayload); + this.model!.setFocus(lastPageElement, eventPayload); } else { - var previousScrollTop = this.scrollTop; + let previousScrollTop = this.scrollTop; this.scrollTop += this.viewHeight; if (this.scrollTop !== previousScrollTop) { @@ -812,7 +801,7 @@ export class TreeView extends HeightMap { } public focusPreviousPage(eventPayload?: any): void { - var firstPageIndex: number; + let firstPageIndex: number; if (this.scrollTop === 0) { firstPageIndex = this.indexAt(this.scrollTop); @@ -820,13 +809,13 @@ export class TreeView extends HeightMap { firstPageIndex = this.indexAfter(this.scrollTop - 1); } - var firstPageElement = this.itemAtIndex(firstPageIndex).model.getElement(); - var currentlyFocusedElement = this.model.getFocus(); + let firstPageElement = this.itemAtIndex(firstPageIndex).model.getElement(); + let currentlyFocusedElement = this.model!.getFocus(); if (currentlyFocusedElement !== firstPageElement) { - this.model.setFocus(firstPageElement, eventPayload); + this.model!.setFocus(firstPageElement, eventPayload); } else { - var previousScrollTop = this.scrollTop; + let previousScrollTop = this.scrollTop; this.scrollTop -= this.viewHeight; if (this.scrollTop !== previousScrollTop) { @@ -890,7 +879,7 @@ export class TreeView extends HeightMap { // Events private onClearingInput(e: Model.IInputEvent): void { - var item = e.item; + let item = e.item; if (item) { this.onRemoveItems(new MappedIterator(item.getNavigator(), item => item && item.id)); this.onRowsChanged(); @@ -903,8 +892,8 @@ export class TreeView extends HeightMap { } private onItemChildrenRefreshing(e: Model.IItemChildrenRefreshEvent): void { - var item = e.item; - var viewItem = this.items[item.id]; + let item = e.item; + let viewItem = this.items[item.id]; if (viewItem && this.context.options.showLoading) { viewItem.loadingTimer = setTimeout(() => { @@ -914,9 +903,9 @@ export class TreeView extends HeightMap { } if (!e.isNested) { - var childrenIds: string[] = []; - var navigator = item.getNavigator(); - var childItem: Model.Item; + let childrenIds: string[] = []; + let navigator = item.getNavigator(); + let childItem: Model.Item | null; while (childItem = navigator.next()) { childrenIds.push(childItem.id); @@ -927,8 +916,8 @@ export class TreeView extends HeightMap { } private onItemChildrenRefreshed(e: Model.IItemChildrenRefreshEvent): void { - var item = e.item; - var viewItem = this.items[item.id]; + let item = e.item; + let viewItem = this.items[item.id]; if (viewItem) { if (viewItem.loadingTimer) { @@ -940,18 +929,18 @@ export class TreeView extends HeightMap { } if (!e.isNested) { - var previousChildrenIds = this.refreshingPreviousChildrenIds[item.id]; - var afterModelItems: Model.Item[] = []; - var navigator = item.getNavigator(); - var childItem: Model.Item; + let previousChildrenIds = this.refreshingPreviousChildrenIds[item.id]; + let afterModelItems: Model.Item[] = []; + let navigator = item.getNavigator(); + let childItem: Model.Item | null; while (childItem = navigator.next()) { afterModelItems.push(childItem); } let skipDiff = Math.abs(previousChildrenIds.length - afterModelItems.length) > 1000; - let diff: Diff.IDiffChange[]; - let doToInsertItemsAlreadyExist: boolean; + let diff: Diff.IDiffChange[] = []; + let doToInsertItemsAlreadyExist: boolean = false; if (!skipDiff) { const lcs = new Diff.LcsDiff( @@ -973,7 +962,7 @@ export class TreeView extends HeightMap { // of the elements has changed doToInsertItemsAlreadyExist = diff.some(d => { if (d.modifiedLength > 0) { - for (var i = d.modifiedStart, len = d.modifiedStart + d.modifiedLength; i < len; i++) { + for (let i = d.modifiedStart, len = d.modifiedStart + d.modifiedLength; i < len; i++) { if (this.items.hasOwnProperty(afterModelItems[i].id)) { return true; } @@ -986,15 +975,14 @@ export class TreeView extends HeightMap { // 50 is an optimization number, at some point we're better off // just replacing everything if (!skipDiff && !doToInsertItemsAlreadyExist && diff.length < 50) { - for (let i = 0, len = diff.length; i < len; i++) { - const diffChange = diff[i]; + for (const diffChange of diff) { if (diffChange.originalLength > 0) { this.onRemoveItems(new ArrayIterator(previousChildrenIds, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength)); } if (diffChange.modifiedLength > 0) { - let beforeItem = afterModelItems[diffChange.modifiedStart - 1] || item; + let beforeItem: Model.Item | null = afterModelItems[diffChange.modifiedStart - 1] || item; beforeItem = beforeItem.getDepth() > 0 ? beforeItem : null; this.onInsertItems(new ArrayIterator(afterModelItems, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength), beforeItem ? beforeItem.id : null); @@ -1022,20 +1010,20 @@ export class TreeView extends HeightMap { } private onItemExpanding(e: Model.IItemExpandEvent): void { - var viewItem = this.items[e.item.id]; + let viewItem = this.items[e.item.id]; if (viewItem) { viewItem.expanded = true; } } private onItemExpanded(e: Model.IItemExpandEvent): void { - var item = e.item; - var viewItem = this.items[item.id]; + let item = e.item; + let viewItem = this.items[item.id]; if (viewItem) { viewItem.expanded = true; - var height = this.onInsertItems(item.getNavigator(), item.id); - var scrollTop = this.scrollTop; + let height = this.onInsertItems(item.getNavigator(), item.id) || 0; + let scrollTop = this.scrollTop; if (viewItem.top + viewItem.height <= this.scrollTop) { scrollTop += height; @@ -1046,8 +1034,8 @@ export class TreeView extends HeightMap { } private onItemCollapsing(e: Model.IItemCollapseEvent): void { - var item = e.item; - var viewItem = this.items[item.id]; + let item = e.item; + let viewItem = this.items[item.id]; if (viewItem) { viewItem.expanded = false; this.onRemoveItems(new MappedIterator(item.getNavigator(), item => item && item.id)); @@ -1055,24 +1043,9 @@ export class TreeView extends HeightMap { } } - public updateWidth(item: Model.Item): void { - if (!item || !item.isVisible()) { - return; - } - - const viewItem = this.items[item.id]; - - if (!viewItem) { - return; - } - - viewItem.updateWidth(); - this.updateScrollWidth(); - } - public getRelativeTop(item: Model.Item): number { if (item && item.isVisible()) { - var viewItem = this.items[item.id]; + let viewItem = this.items[item.id]; if (viewItem) { return (viewItem.top - this.scrollTop) / (this.viewHeight - viewItem.height); } @@ -1081,20 +1054,20 @@ export class TreeView extends HeightMap { } private onItemReveal(e: Model.IItemRevealEvent): void { - var item = e.item; - var relativeTop = e.relativeTop; - var viewItem = this.items[item.id]; + let item = e.item; + let relativeTop = e.relativeTop; + let viewItem = this.items[item.id]; if (viewItem) { if (relativeTop !== null) { relativeTop = relativeTop < 0 ? 0 : relativeTop; relativeTop = relativeTop > 1 ? 1 : relativeTop; // y = mx + b - var m = viewItem.height - this.viewHeight; + let m = viewItem.height - this.viewHeight; this.scrollTop = m * relativeTop + viewItem.top; } else { - var viewItemBottom = viewItem.top + viewItem.height; - var wrapperBottom = this.scrollTop + this.viewHeight; + let viewItemBottom = viewItem.top + viewItem.height; + let wrapperBottom = this.scrollTop + this.viewHeight; if (viewItem.top < this.scrollTop) { this.scrollTop = viewItem.top; @@ -1106,9 +1079,9 @@ export class TreeView extends HeightMap { } private onItemAddTrait(e: Model.IItemTraitEvent): void { - var item = e.item; - var trait = e.trait; - var viewItem = this.items[item.id]; + let item = e.item; + let trait = e.trait; + let viewItem = this.items[item.id]; if (viewItem) { viewItem.addClass(trait); } @@ -1126,9 +1099,9 @@ export class TreeView extends HeightMap { } private onItemRemoveTrait(e: Model.IItemTraitEvent): void { - var item = e.item; - var trait = e.trait; - var viewItem = this.items[item.id]; + let item = e.item; + let trait = e.trait; + let viewItem = this.items[item.id]; if (viewItem) { viewItem.removeClass(trait); } @@ -1195,8 +1168,8 @@ export class TreeView extends HeightMap { return; } - var event = new Mouse.StandardMouseEvent(e); - var item = this.getItemAround(event.target); + let event = new Mouse.StandardMouseEvent(e); + let item = this.getItemAround(event.target); if (!item) { return; @@ -1211,27 +1184,27 @@ export class TreeView extends HeightMap { } this.lastClickTimeStamp = Date.now(); - this.context.controller.onClick(this.context.tree, item.model.getElement(), event); + this.context.controller!.onClick(this.context.tree, item.model.getElement(), event); } private onMouseMiddleClick(e: MouseEvent): void { - if (!this.context.controller.onMouseMiddleClick) { + if (!this.context.controller!.onMouseMiddleClick!) { return; } - var event = new Mouse.StandardMouseEvent(e); - var item = this.getItemAround(event.target); + let event = new Mouse.StandardMouseEvent(e); + let item = this.getItemAround(event.target); if (!item) { return; } - this.context.controller.onMouseMiddleClick(this.context.tree, item.model.getElement(), event); + this.context.controller!.onMouseMiddleClick!(this.context.tree, item.model.getElement(), event); } private onMouseDown(e: MouseEvent): void { this.didJustPressContextMenuKey = false; - if (!this.context.controller.onMouseDown) { + if (!this.context.controller!.onMouseDown!) { return; } @@ -1239,23 +1212,23 @@ export class TreeView extends HeightMap { return; } - var event = new Mouse.StandardMouseEvent(e); + let event = new Mouse.StandardMouseEvent(e); if (event.ctrlKey && Platform.isNative && Platform.isMacintosh) { return; } - var item = this.getItemAround(event.target); + let item = this.getItemAround(event.target); if (!item) { return; } - this.context.controller.onMouseDown(this.context.tree, item.model.getElement(), event); + this.context.controller!.onMouseDown!(this.context.tree, item.model.getElement(), event); } private onMouseUp(e: MouseEvent): void { - if (!this.context.controller.onMouseUp) { + if (!this.context.controller!.onMouseUp!) { return; } @@ -1263,29 +1236,29 @@ export class TreeView extends HeightMap { return; } - var event = new Mouse.StandardMouseEvent(e); + let event = new Mouse.StandardMouseEvent(e); if (event.ctrlKey && Platform.isNative && Platform.isMacintosh) { return; } - var item = this.getItemAround(event.target); + let item = this.getItemAround(event.target); if (!item) { return; } - this.context.controller.onMouseUp(this.context.tree, item.model.getElement(), event); + this.context.controller!.onMouseUp!(this.context.tree, item.model.getElement(), event); } private onTap(e: Touch.GestureEvent): void { - var item = this.getItemAround(e.initialTarget); + let item = this.getItemAround(e.initialTarget); if (!item) { return; } - this.context.controller.onTap(this.context.tree, item.model.getElement(), e); + this.context.controller!.onTap(this.context.tree, item.model.getElement(), e); } private onTouchChange(event: Touch.GestureEvent): void { @@ -1298,31 +1271,31 @@ export class TreeView extends HeightMap { private onContextMenu(keyboardEvent: KeyboardEvent): void; private onContextMenu(mouseEvent: MouseEvent): void; private onContextMenu(event: KeyboardEvent | MouseEvent): void { - var resultEvent: _.ContextMenuEvent; - var element: any; + let resultEvent: _.ContextMenuEvent; + let element: any; if (event instanceof KeyboardEvent || this.didJustPressContextMenuKey) { this.didJustPressContextMenuKey = false; - var keyboardEvent = new Keyboard.StandardKeyboardEvent(event); - element = this.model.getFocus(); + let keyboardEvent = new Keyboard.StandardKeyboardEvent(event); + element = this.model!.getFocus(); - var position: DOM.IDomNodePagePosition; + let position: DOM.IDomNodePagePosition; if (!element) { - element = this.model.getInput(); + element = this.model!.getInput(); position = DOM.getDomNodePagePosition(this.inputItem.element); } else { - var id = this.context.dataSource.getId(this.context.tree, element); - var viewItem = this.items[id]; + let id = this.context.dataSource.getId(this.context.tree, element); + let viewItem = this.items[id]; position = DOM.getDomNodePagePosition(viewItem.element); } resultEvent = new _.KeyboardContextMenuEvent(position.left + position.width, position.top, keyboardEvent); } else { - var mouseEvent = new Mouse.StandardMouseEvent(event); - var item = this.getItemAround(mouseEvent.target); + let mouseEvent = new Mouse.StandardMouseEvent(event); + let item = this.getItemAround(mouseEvent.target); if (!item) { return; @@ -1332,11 +1305,11 @@ export class TreeView extends HeightMap { resultEvent = new _.MouseContextMenuEvent(mouseEvent); } - this.context.controller.onContextMenu(this.context.tree, element, resultEvent); + this.context.controller!.onContextMenu(this.context.tree, element, resultEvent); } private onKeyDown(e: KeyboardEvent): void { - var event = new Keyboard.StandardKeyboardEvent(e); + let event = new Keyboard.StandardKeyboardEvent(e); this.didJustPressContextMenuKey = event.keyCode === KeyCode.ContextMenu || (event.shiftKey && event.keyCode === KeyCode.F10); @@ -1349,7 +1322,7 @@ export class TreeView extends HeightMap { event.stopPropagation(); } - this.context.controller.onKeyDown(this.context.tree, event); + this.context.controller!.onKeyDown(this.context.tree, event); } private onKeyUp(e: KeyboardEvent): void { @@ -1358,17 +1331,17 @@ export class TreeView extends HeightMap { } this.didJustPressContextMenuKey = false; - this.context.controller.onKeyUp(this.context.tree, new Keyboard.StandardKeyboardEvent(e)); + this.context.controller!.onKeyUp(this.context.tree, new Keyboard.StandardKeyboardEvent(e)); } private onDragStart(item: ViewItem, e: any): void { - if (this.model.getHighlight()) { + if (this.model!.getHighlight()) { return; } - var element = item.model.getElement(); - var selection = this.model.getSelection(); - var elements: any[]; + let element = item.model.getElement(); + let selection = this.model!.getSelection(); + let elements: any[]; if (selection.indexOf(element) > -1) { elements = selection; @@ -1381,8 +1354,8 @@ export class TreeView extends HeightMap { if (e.dataTransfer.setDragImage) { let label: string; - if (this.context.dnd.getDragLabel) { - label = this.context.dnd.getDragLabel(this.context.tree, elements); + if (this.context.dnd!.getDragLabel) { + label = this.context.dnd!.getDragLabel!(this.context.tree, elements); } else { label = String(elements.length); } @@ -1396,23 +1369,23 @@ export class TreeView extends HeightMap { } this.currentDragAndDropData = new dnd.ElementsDragAndDropData(elements); - TreeView.currentExternalDragAndDropData = new dnd.ExternalElementsDragAndDropData(elements); + StaticDND.CurrentDragAndDropData = new dnd.ExternalElementsDragAndDropData(elements); - this.context.dnd.onDragStart(this.context.tree, this.currentDragAndDropData, new Mouse.DragMouseEvent(e)); + this.context.dnd!.onDragStart(this.context.tree, this.currentDragAndDropData, new Mouse.DragMouseEvent(e)); } private setupDragAndDropScrollInterval(): void { - var viewTop = DOM.getTopLeftOffset(this.wrapper).top; + let viewTop = DOM.getTopLeftOffset(this.wrapper).top; if (!this.dragAndDropScrollInterval) { this.dragAndDropScrollInterval = window.setInterval(() => { - if (this.dragAndDropMouseY === undefined) { + if (this.dragAndDropMouseY === null) { return; } - var diff = this.dragAndDropMouseY - viewTop; - var scrollDiff = 0; - var upperLimit = this.viewHeight - 35; + let diff = this.dragAndDropMouseY - viewTop; + let scrollDiff = 0; + let upperLimit = this.viewHeight - 35; if (diff < 35) { scrollDiff = Math.max(-14, 0.2 * (diff - 35)); @@ -1449,9 +1422,9 @@ export class TreeView extends HeightMap { } private onDragOver(e: DragEvent): boolean { - var event = new Mouse.DragMouseEvent(e); + let event = new Mouse.DragMouseEvent(e); - var viewItem = this.getItemAround(event.target); + let viewItem = this.getItemAround(event.target); if (!viewItem || (event.posx === 0 && event.posy === 0 && event.browserEvent.type === DOM.EventType.DRAG_LEAVE)) { // dragging outside of tree @@ -1459,7 +1432,7 @@ export class TreeView extends HeightMap { if (this.currentDropTarget) { // clear previously hovered element feedback - this.currentDropTargets.forEach(i => i.dropTarget = false); + this.currentDropTargets!.forEach(i => i.dropTarget = false); this.currentDropTargets = []; this.currentDropDisposable.dispose(); } @@ -1479,8 +1452,8 @@ export class TreeView extends HeightMap { if (!this.currentDragAndDropData) { // just started dragging - if (TreeView.currentExternalDragAndDropData) { - this.currentDragAndDropData = TreeView.currentExternalDragAndDropData; + if (StaticDND.CurrentDragAndDropData) { + this.currentDragAndDropData = StaticDND.CurrentDragAndDropData; } else { if (!event.dataTransfer.types) { return false; @@ -1490,16 +1463,16 @@ export class TreeView extends HeightMap { } } - this.currentDragAndDropData.update(event); + this.currentDragAndDropData.update((event.browserEvent as DragEvent).dataTransfer!); - var element: any; - var item: Model.Item = viewItem.model; - var reaction: _.IDragOverReaction; + let element: any; + let item: Model.Item | null = viewItem.model; + let reaction: _.IDragOverReaction | null; // check the bubble up behavior do { - element = item ? item.getElement() : this.model.getInput(); - reaction = this.context.dnd.onDragOver(this.context.tree, this.currentDragAndDropData, element, event); + element = item ? item.getElement() : this.model!.getInput(); + reaction = this.context.dnd!.onDragOver(this.context.tree, this.currentDragAndDropData, element, event); if (!reaction || reaction.bubble !== _.DragOverBubble.BUBBLE_UP) { break; @@ -1513,12 +1486,12 @@ export class TreeView extends HeightMap { return false; } - var canDrop = reaction && reaction.accept; + let canDrop = reaction && reaction.accept; if (canDrop) { this.currentDropElement = item.getElement(); event.preventDefault(); - event.dataTransfer.dropEffect = reaction.effect === _.DragOverEffect.COPY ? 'copy' : 'move'; + event.dataTransfer.dropEffect = reaction!.effect === _.DragOverEffect.COPY ? 'copy' : 'move'; } else { this.currentDropElement = null; } @@ -1526,41 +1499,41 @@ export class TreeView extends HeightMap { // item is the model item where drop() should be called // can be null - var currentDropTarget = item.id === this.inputItem.id ? this.inputItem : this.items[item.id]; + let currentDropTarget = item.id === this.inputItem.id ? this.inputItem : this.items[item.id]; if (this.shouldInvalidateDropReaction || this.currentDropTarget !== currentDropTarget || !reactionEquals(this.currentDropElementReaction, reaction)) { this.shouldInvalidateDropReaction = false; if (this.currentDropTarget) { - this.currentDropTargets.forEach(i => i.dropTarget = false); + this.currentDropTargets!.forEach(i => i.dropTarget = false); this.currentDropTargets = []; this.currentDropDisposable.dispose(); } this.currentDropTarget = currentDropTarget; - this.currentDropElementReaction = reaction; + this.currentDropElementReaction = reaction!; if (canDrop) { // setup hover feedback for drop target if (this.currentDropTarget) { this.currentDropTarget.dropTarget = true; - this.currentDropTargets.push(this.currentDropTarget); + this.currentDropTargets!.push(this.currentDropTarget); } - if (reaction.bubble === _.DragOverBubble.BUBBLE_DOWN) { - var nav = item.getNavigator(); - var child: Model.Item; + if (reaction!.bubble === _.DragOverBubble.BUBBLE_DOWN) { + let nav = item.getNavigator(); + let child: Model.Item | null; while (child = nav.next()) { viewItem = this.items[child.id]; if (viewItem) { viewItem.dropTarget = true; - this.currentDropTargets.push(viewItem); + this.currentDropTargets!.push(viewItem); } } } - if (reaction.autoExpand) { + if (reaction!.autoExpand) { const timeoutPromise = timeout(500); this.currentDropDisposable = Lifecycle.toDisposable(() => timeoutPromise.cancel()); @@ -1576,10 +1549,10 @@ export class TreeView extends HeightMap { private onDrop(e: DragEvent): void { if (this.currentDropElement) { - var event = new Mouse.DragMouseEvent(e); + let event = new Mouse.DragMouseEvent(e); event.preventDefault(); - this.currentDragAndDropData.update(event); - this.context.dnd.drop(this.context.tree, this.currentDragAndDropData, this.currentDropElement, event); + this.currentDragAndDropData!.update((event.browserEvent as DragEvent).dataTransfer!); + this.context.dnd!.drop(this.context.tree, this.currentDragAndDropData!, this.currentDropElement, event); this.onDragEnd(e); } else { // {{SQL CARBON EDIT}} @@ -1590,7 +1563,7 @@ export class TreeView extends HeightMap { private onDragEnd(e: DragEvent): void { if (this.currentDropTarget) { - this.currentDropTargets.forEach(i => i.dropTarget = false); + this.currentDropTargets!.forEach(i => i.dropTarget = false); this.currentDropTargets = []; } else { // {{SQL CARBON EDIT}} @@ -1601,7 +1574,7 @@ export class TreeView extends HeightMap { this.cancelDragAndDropScrollInterval(); this.currentDragAndDropData = null; - TreeView.currentExternalDragAndDropData = null; + StaticDND.CurrentDragAndDropData = undefined; this.currentDropElement = null; this.currentDropTarget = null; this.dragAndDropMouseY = null; @@ -1633,7 +1606,7 @@ export class TreeView extends HeightMap { } // Circumvent IE11 breaking change in e.pointerType & TypeScript's stale definitions - var pointerType = event.pointerType; + let pointerType = event.pointerType; if (pointerType === ((event).MSPOINTER_TYPE_MOUSE || 'mouse')) { this.lastPointerType = 'mouse'; return; @@ -1661,8 +1634,8 @@ export class TreeView extends HeightMap { // DOM changes private insertItemInDOM(item: ViewItem): void { - var elementAfter: HTMLElement | null = null; - var itemAfter = this.itemAfter(item); + let elementAfter: HTMLElement | null = null; + let itemAfter = this.itemAfter(item); if (itemAfter && itemAfter.element) { elementAfter = itemAfter.element; @@ -1685,21 +1658,24 @@ export class TreeView extends HeightMap { return item.top < this.lastRenderTop + this.lastRenderHeight && item.top + item.height > this.lastRenderTop; } - private getItemAround(element: HTMLElement): ViewItem { - var candidate: ViewItem = this.inputItem; + private getItemAround(element: HTMLElement): ViewItem | undefined { + let candidate: ViewItem = this.inputItem; + let el: HTMLElement | null = element; + do { - if ((element)[TreeView.BINDING]) { - candidate = (element)[TreeView.BINDING]; + if ((el)[TreeView.BINDING]) { + candidate = (el)[TreeView.BINDING]; } - if (element === this.wrapper || element === this.domNode) { + if (el === this.wrapper || el === this.domNode) { return candidate; } - if (element === this.scrollableElement.getDomNode() || element === document.body) { - return null; + if (el === this.scrollableElement.getDomNode() || el === document.body) { + return undefined; } - } while (element = element.parentElement); + } while (el = el.parentElement); + return undefined; } @@ -1717,7 +1693,6 @@ export class TreeView extends HeightMap { this.scrollableElement.dispose(); this.releaseModel(); - this.modelListeners = null; this.viewListeners = Lifecycle.dispose(this.viewListeners); @@ -1727,16 +1702,13 @@ export class TreeView extends HeightMap { if (this.domNode.parentNode) { this.domNode.parentNode.removeChild(this.domNode); } - this.domNode = null; if (this.items) { Object.keys(this.items).forEach(key => this.items[key].removeFromDOM()); - this.items = null; } if (this.context.cache) { this.context.cache.dispose(); - this.context.cache = null; } super.dispose(); diff --git a/src/vs/base/parts/tree/browser/treeViewModel.ts b/src/vs/base/parts/tree/browser/treeViewModel.ts index 7445dc928138..ed252c987973 100644 --- a/src/vs/base/parts/tree/browser/treeViewModel.ts +++ b/src/vs/base/parts/tree/browser/treeViewModel.ts @@ -15,25 +15,20 @@ export interface IViewItem { export class HeightMap { - private heightMap: IViewItem[]; - private indexes: { [item: string]: number; }; + private heightMap: IViewItem[] = []; + private indexes: { [item: string]: number; } = {}; - constructor() { - this.heightMap = []; - this.indexes = {}; - } - - public getContentHeight(): number { - var last = this.heightMap[this.heightMap.length - 1]; + getContentHeight(): number { + let last = this.heightMap[this.heightMap.length - 1]; return !last ? 0 : last.top + last.height; } - public onInsertItems(iterator: INextIterator, afterItemId: string | null = null): number { - var item: Item; - var viewItem: IViewItem; - var i: number, j: number; - var totalSize: number; - var sizeDiff = 0; + onInsertItems(iterator: INextIterator, afterItemId: string | null = null): number | undefined { + let item: Item | null = null; + let viewItem: IViewItem; + let i: number, j: number; + let totalSize: number; + let sizeDiff = 0; if (afterItemId === null) { i = 0; @@ -50,9 +45,9 @@ export class HeightMap { totalSize = viewItem.top + viewItem.height; } - var boundSplice = this.heightMap.splice.bind(this.heightMap, i, 0); + let boundSplice = this.heightMap.splice.bind(this.heightMap, i, 0); - var itemsToInsert: IViewItem[] = []; + let itemsToInsert: IViewItem[] = []; while (item = iterator.next()) { viewItem = this.createViewItem(item); @@ -82,17 +77,17 @@ export class HeightMap { return sizeDiff; } - public onInsertItem(item: IViewItem): void { + onInsertItem(item: IViewItem): void { // noop } // Contiguous items - public onRemoveItems(iterator: INextIterator): void { - var itemId: string; - var viewItem: IViewItem; - var startIndex: number | null = null; - var i: number; - var sizeDiff = 0; + onRemoveItems(iterator: INextIterator): void { + let itemId: string | null = null; + let viewItem: IViewItem; + let startIndex: number | null = null; + let i = 0; + let sizeDiff = 0; while (itemId = iterator.next()) { i = this.indexes[itemId]; @@ -112,7 +107,7 @@ export class HeightMap { } } - if (sizeDiff === 0) { + if (sizeDiff === 0 || startIndex === null) { return; } @@ -126,22 +121,22 @@ export class HeightMap { } } - public onRemoveItem(item: IViewItem): void { + onRemoveItem(item: IViewItem): void { // noop } - public onRefreshItemSet(items: Item[]): void { - var sortedItems = items.sort((a, b) => this.indexes[a.id] - this.indexes[b.id]); + onRefreshItemSet(items: Item[]): void { + let sortedItems = items.sort((a, b) => this.indexes[a.id] - this.indexes[b.id]); this.onRefreshItems(new ArrayIterator(sortedItems)); } // Ordered, but not necessarily contiguous items - public onRefreshItems(iterator: INextIterator): void { - var item: Item; - var viewItem: IViewItem; - var newHeight: number; - var i: number, j: number | null = null; - var cummDiff = 0; + onRefreshItems(iterator: INextIterator): void { + let item: Item | null = null; + let viewItem: IViewItem; + let newHeight: number; + let i: number, j: number | null = null; + let cummDiff = 0; while (item = iterator.next()) { i = this.indexes[item.id]; @@ -171,31 +166,31 @@ export class HeightMap { } } - public onRefreshItem(item: IViewItem, needsRender: boolean = false): void { + onRefreshItem(item: IViewItem, needsRender: boolean = false): void { // noop } - public itemsCount(): number { + itemsCount(): number { return this.heightMap.length; } - public itemAt(position: number): string { + itemAt(position: number): string { return this.heightMap[this.indexAt(position)].model.id; } - public withItemsInRange(start: number, end: number, fn: (item: string) => void): void { + withItemsInRange(start: number, end: number, fn: (item: string) => void): void { start = this.indexAt(start); end = this.indexAt(end); - for (var i = start; i <= end; i++) { + for (let i = start; i <= end; i++) { fn(this.heightMap[i].model.id); } } - public indexAt(position: number): number { - var left = 0; - var right = this.heightMap.length; - var center: number; - var item: IViewItem; + indexAt(position: number): number { + let left = 0; + let right = this.heightMap.length; + let center: number; + let item: IViewItem; // Binary search while (left < right) { @@ -217,15 +212,15 @@ export class HeightMap { return this.heightMap.length; } - public indexAfter(position: number): number { + indexAfter(position: number): number { return Math.min(this.indexAt(position) + 1, this.heightMap.length); } - public itemAtIndex(index: number): IViewItem { + itemAtIndex(index: number): IViewItem { return this.heightMap[index]; } - public itemAfter(item: IViewItem): IViewItem { + itemAfter(item: IViewItem): IViewItem { return this.heightMap[this.indexes[item.model.id] + 1] || null; } @@ -233,8 +228,8 @@ export class HeightMap { throw new Error('not implemented'); } - public dispose(): void { - this.heightMap = null; - this.indexes = null; + dispose(): void { + this.heightMap = []; + this.indexes = {}; } } diff --git a/src/vs/base/parts/tree/test/browser/treeModel.test.ts b/src/vs/base/parts/tree/test/browser/treeModel.test.ts index 75858fa440bd..24bdd29cfa10 100644 --- a/src/vs/base/parts/tree/test/browser/treeModel.test.ts +++ b/src/vs/base/parts/tree/test/browser/treeModel.test.ts @@ -36,12 +36,12 @@ export class FakeRenderer { class TreeContext implements _.ITreeContext { - public tree: _.ITree = null; + public tree: _.ITree = null!; public options: _.ITreeOptions = { autoExpandSingleChildren: true }; public dataSource: _.IDataSource; public renderer: _.IRenderer; - public controller: _.IController; - public dnd: _.IDragAndDrop; + public controller?: _.IController; + public dnd?: _.IDragAndDrop; public filter: _.IFilter; public sorter: _.ISorter; @@ -72,7 +72,7 @@ class EventCounter { this._count = 0; } - public listen(event: Event, fn: (e: T) => void = null): () => void { + public listen(event: Event, fn: ((e: T) => void) | null = null): () => void { let r = event(data => { this._count++; if (fn) { @@ -105,7 +105,7 @@ class EventCounter { } } -var SAMPLE: any = { +const SAMPLE: any = { ONE: { id: 'one' }, AB: { @@ -169,18 +169,18 @@ class TestDataSource implements _.IDataSource { return !!element.children; } - public getChildren(tree, element): Thenable { + public getChildren(tree, element): Promise { return Promise.resolve(element.children); } - public getParent(tree, element): Thenable { + public getParent(tree, element): Promise { throw new Error('Not implemented'); } } suite('TreeModel', () => { - var model: model.TreeModel; - var counter: EventCounter; + let model: model.TreeModel; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -313,7 +313,7 @@ suite('TreeModel', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll(['a', 'c']).then(() => { // going internals - var r = (model).registry; + const r = (model).registry; assert(r.getItem('a').intersects(r.getItem('a'))); assert(r.getItem('a').intersects(r.getItem('aa'))); @@ -331,8 +331,8 @@ suite('TreeModel', () => { }); suite('TreeModel - TreeNavigator', () => { - var model: model.TreeModel; - var counter: EventCounter; + let model: model.TreeModel; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -348,24 +348,24 @@ suite('TreeModel - TreeNavigator', () => { test('next()', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); }); }); test('previous()', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(); + const nav = model.getNavigator(); nav.next(); nav.next(); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.previous().id, 'b'); - assert.equal(nav.previous().id, 'a'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); @@ -373,22 +373,22 @@ suite('TreeModel - TreeNavigator', () => { test('parent()', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - var nav = model.getNavigator(); + const nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.parent().id, 'a'); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.parent()!.id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.parent().id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.parent()!.id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.next().id, 'ca'); - assert.equal(nav.parent().id, 'c'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.next()!.id, 'ca'); + assert.equal(nav.parent()!.id, 'c'); assert.equal(nav.parent() && false, null); }); @@ -397,10 +397,10 @@ suite('TreeModel - TreeNavigator', () => { test('next() - scoped', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0]); + const nav = model.getNavigator(SAMPLE.AB.children[0]); return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); assert.equal(nav.next() && false, null); }); }); @@ -408,11 +408,11 @@ suite('TreeModel - TreeNavigator', () => { test('previous() - scoped', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0]); + const nav = model.getNavigator(SAMPLE.AB.children[0]); return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.previous().id, 'aa'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.previous()!.id, 'aa'); assert.equal(nav.previous() && false, null); }); }); @@ -421,10 +421,10 @@ suite('TreeModel - TreeNavigator', () => { test('parent() - scoped', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0]); + const nav = model.getNavigator(SAMPLE.AB.children[0]); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); assert.equal(nav.parent() && false, null); }); }); @@ -432,12 +432,12 @@ suite('TreeModel - TreeNavigator', () => { test('next() - non sub tree only', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0], false); + const nav = model.getNavigator(SAMPLE.AB.children[0], false); return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); }); }); @@ -445,16 +445,16 @@ suite('TreeModel - TreeNavigator', () => { test('previous() - non sub tree only', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0], false); + const nav = model.getNavigator(SAMPLE.AB.children[0], false); return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.previous().id, 'b'); - assert.equal(nav.previous().id, 'ab'); - assert.equal(nav.previous().id, 'aa'); - assert.equal(nav.previous().id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); + assert.equal(nav.previous()!.id, 'ab'); + assert.equal(nav.previous()!.id, 'aa'); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); @@ -463,11 +463,11 @@ suite('TreeModel - TreeNavigator', () => { test('parent() - non sub tree only', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0], false); + const nav = model.getNavigator(SAMPLE.AB.children[0], false); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.parent().id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.parent()!.id, 'a'); assert.equal(nav.parent() && false, null); }); }); @@ -477,9 +477,9 @@ suite('TreeModel - TreeNavigator', () => { return model.setInput(SAMPLE.DEEP).then(() => { return model.expand(SAMPLE.DEEP.children[0]).then(() => { return model.expand(SAMPLE.DEEP.children[0].children[0]).then(() => { - var nav = model.getNavigator(SAMPLE.DEEP.children[0].children[0]); - assert.equal(nav.next().id, 'xa'); - assert.equal(nav.next().id, 'xb'); + const nav = model.getNavigator(SAMPLE.DEEP.children[0].children[0]); + assert.equal(nav.next()!.id, 'xa'); + assert.equal(nav.next()!.id, 'xb'); assert.equal(nav.next() && false, null); }); }); @@ -490,10 +490,10 @@ suite('TreeModel - TreeNavigator', () => { return model.setInput(SAMPLE.DEEP).then(() => { return model.expand(SAMPLE.DEEP.children[0]).then(() => { return model.expand(SAMPLE.DEEP.children[0].children[0]).then(() => { - var nav = model.getNavigator(SAMPLE.DEEP.children[0].children[0]); - assert.equal(nav.next().id, 'xa'); - assert.equal(nav.next().id, 'xb'); - assert.equal(nav.previous().id, 'xa'); + const nav = model.getNavigator(SAMPLE.DEEP.children[0].children[0]); + assert.equal(nav.next()!.id, 'xa'); + assert.equal(nav.next()!.id, 'xb'); + assert.equal(nav.previous()!.id, 'xa'); assert.equal(nav.previous() && false, null); }); }); @@ -504,15 +504,15 @@ suite('TreeModel - TreeNavigator', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { const nav = model.getNavigator(); - assert.equal(nav.last().id, 'cb'); + assert.equal(nav.last()!.id, 'cb'); }); }); }); }); suite('TreeModel - Expansion', () => { - var model: model.TreeModel; - var counter: EventCounter; + let model: model.TreeModel; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -530,24 +530,24 @@ suite('TreeModel - Expansion', () => { return model.setInput(SAMPLE.AB).then(() => { counter.listen(model.onExpandItem, (e) => { assert.equal(e.item.id, 'a'); - var nav = model.getNavigator(e.item); + const nav = model.getNavigator(e.item); assert.equal(nav.next() && false, null); }); counter.listen(model.onDidExpandItem, (e) => { assert.equal(e.item.id, 'a'); - var nav = model.getNavigator(e.item); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); + const nav = model.getNavigator(e.item); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); assert.equal(nav.next() && false, null); }); assert(!model.isExpanded(SAMPLE.AB.children[0])); - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); assert.equal(model.getExpandedElements().length, 0); @@ -556,14 +556,14 @@ suite('TreeModel - Expansion', () => { assert(model.isExpanded(SAMPLE.AB.children[0])); nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); - var expandedElements = model.getExpandedElements(); + const expandedElements = model.getExpandedElements(); assert.equal(expandedElements.length, 1); assert.equal(expandedElements[0].id, 'a'); @@ -626,18 +626,18 @@ suite('TreeModel - Expansion', () => { assert(!model.isExpanded(SAMPLE.AB.children[0])); - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); - var f: () => void = counter.listen(model.onRefreshItemChildren, (e) => { + const f: () => void = counter.listen(model.onRefreshItemChildren, (e) => { assert.equal(e.item.id, 'a'); f(); }); - var g: () => void = counter.listen(model.onDidRefreshItemChildren, (e) => { + const g: () => void = counter.listen(model.onDidRefreshItemChildren, (e) => { assert.equal(e.item.id, 'a'); g(); }); @@ -646,11 +646,11 @@ suite('TreeModel - Expansion', () => { assert(model.isExpanded(SAMPLE.AB.children[0])); nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); assert.equal(counter.count, 2); @@ -661,12 +661,12 @@ suite('TreeModel - Expansion', () => { test('top level collapsed', () => { return model.setInput(SAMPLE.AB).then(() => { return model.collapseAll([{ id: 'a' }, { id: 'b' }, { id: 'c' }]).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.previous().id, 'b'); - assert.equal(nav.previous().id, 'a'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); @@ -683,7 +683,7 @@ suite('TreeModel - Expansion', () => { if (e === 'b') { return Promise.resolve(['b1']); } return Promise.resolve([]); }, - getParent: (_, e): Thenable => { throw new Error('not implemented'); }, + getParent: (_, e): Promise => { throw new Error('not implemented'); }, shouldAutoexpand: (_, e) => e === 'b' } }); @@ -712,9 +712,9 @@ class TestFilter implements _.IFilter { } suite('TreeModel - Filter', () => { - var model: model.TreeModel; - var counter: EventCounter; - var filter: TestFilter; + let model: model.TreeModel; + let counter: EventCounter; + let filter: TestFilter; setup(() => { counter = new EventCounter(); @@ -734,21 +734,21 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.next().id, 'ca'); - assert.equal(nav.next().id, 'cb'); - - assert.equal(nav.previous().id, 'ca'); - assert.equal(nav.previous().id, 'c'); - assert.equal(nav.previous().id, 'b'); - assert.equal(nav.previous().id, 'ab'); - assert.equal(nav.previous().id, 'aa'); - assert.equal(nav.previous().id, 'a'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.next()!.id, 'ca'); + assert.equal(nav.next()!.id, 'cb'); + + assert.equal(nav.previous()!.id, 'ca'); + assert.equal(nav.previous()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); + assert.equal(nav.previous()!.id, 'ab'); + assert.equal(nav.previous()!.id, 'aa'); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); @@ -759,7 +759,7 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.refresh().then(() => { - var nav = model.getNavigator(); + const nav = model.getNavigator(); assert.equal(nav.next() && false, null); }); }); @@ -772,12 +772,12 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expand({ id: 'a' }).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.previous().id, 'aa'); - assert.equal(nav.previous().id, 'a'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.previous()!.id, 'aa'); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); @@ -789,11 +789,11 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expand({ id: 'a' }).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); }); }); @@ -806,14 +806,14 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expand({ id: 'c' }).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.next().id, 'ca'); - assert.equal(nav.next().id, 'cb'); - assert.equal(nav.previous().id, 'ca'); - assert.equal(nav.previous().id, 'c'); - assert.equal(nav.previous().id, 'b'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.next()!.id, 'ca'); + assert.equal(nav.next()!.id, 'cb'); + assert.equal(nav.previous()!.id, 'ca'); + assert.equal(nav.previous()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); assert.equal(nav.previous() && false, null); }); }); @@ -826,14 +826,14 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expand({ id: 'c' }).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.next().id, 'ca'); - assert.equal(nav.next().id, 'cb'); - assert.equal(nav.previous().id, 'ca'); - assert.equal(nav.previous().id, 'c'); - assert.equal(nav.previous().id, 'b'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.next()!.id, 'ca'); + assert.equal(nav.next()!.id, 'cb'); + assert.equal(nav.previous()!.id, 'ca'); + assert.equal(nav.previous()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); assert.equal(nav.previous() && false, null); }); }); @@ -844,16 +844,16 @@ suite('TreeModel - Filter', () => { filter.fn = (e) => e.id !== 'b'; return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator({ id: 'c' }, false); - assert.equal(nav.previous().id, 'a'); + const nav = model.getNavigator({ id: 'c' }, false); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); }); suite('TreeModel - Traits', () => { - var model: model.TreeModel; - var counter: EventCounter; + let model: model.TreeModel; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -1079,7 +1079,7 @@ suite('TreeModel - Traits', () => { class DynamicModel implements _.IDataSource { private data: any; - public promiseFactory: { (): Thenable; }; + public promiseFactory: { (): Promise; } | null; private _onGetChildren = new Emitter(); readonly onGetChildren: Event = this._onGetChildren.event; @@ -1124,24 +1124,24 @@ class DynamicModel implements _.IDataSource { return !!this.data[element]; } - public getChildren(tree, element): Thenable { + public getChildren(tree, element): Promise { this._onGetChildren.fire(element); - var result = this.promiseFactory ? this.promiseFactory() : Promise.resolve(null); + const result = this.promiseFactory ? this.promiseFactory() : Promise.resolve(null); return result.then(() => { this._onDidGetChildren.fire(element); return Promise.resolve(this.data[element]); }); } - public getParent(tree, element): Thenable { + public getParent(tree, element): Promise { throw new Error('Not implemented'); } } suite('TreeModel - Dynamic data model', () => { - var model: model.TreeModel; - var dataModel: DynamicModel; - var counter: EventCounter; + let model: model.TreeModel; + let dataModel: DynamicModel; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -1167,8 +1167,8 @@ suite('TreeModel - Dynamic data model', () => { return model.expandAll(['grandfather', 'father', 'son']).then(() => { dataModel.removeChild('grandfather', 'father'); - var items = ['baby', 'son', 'daughter', 'father']; - var times = 0; + const items = ['baby', 'son', 'daughter', 'father']; + let times = 0; counter.listen(model.onDidDisposeItem, item => { assert.equal(items[times++], item.id); }); @@ -1187,17 +1187,17 @@ suite('TreeModel - Dynamic data model', () => { dataModel.addChild('root', 'mega'); return model.setInput('root').then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'hyper'); - assert.equal(nav.next().id, 'mega'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'hyper'); + assert.equal(nav.next()!.id, 'mega'); assert.equal(nav.next() && false, null); dataModel.removeChild('root', 'hyper'); return model.refresh().then(() => { nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'mega'); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'mega'); assert.equal(nav.next() && false, null); dataModel.addChild('mega', 'micro'); @@ -1207,17 +1207,17 @@ suite('TreeModel - Dynamic data model', () => { return model.refresh().then(() => { return model.expand('mega').then(() => { nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'mega'); - assert.equal(nav.next().id, 'micro'); - assert.equal(nav.next().id, 'nano'); - assert.equal(nav.next().id, 'pico'); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'mega'); + assert.equal(nav.next()!.id, 'micro'); + assert.equal(nav.next()!.id, 'nano'); + assert.equal(nav.next()!.id, 'pico'); assert.equal(nav.next() && false, null); model.collapse('mega'); nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'mega'); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'mega'); assert.equal(nav.next() && false, null); }); }); @@ -1237,13 +1237,13 @@ suite('TreeModel - Dynamic data model', () => { return model.expand('super').then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'apples'); - assert.equal(nav.next().id, 'bananas'); - assert.equal(nav.next().id, 'pears'); - assert.equal(nav.next().id, 'hyper'); - assert.equal(nav.next().id, 'mega'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'apples'); + assert.equal(nav.next()!.id, 'bananas'); + assert.equal(nav.next()!.id, 'pears'); + assert.equal(nav.next()!.id, 'hyper'); + assert.equal(nav.next()!.id, 'mega'); assert.equal(nav.next() && false, null); dataModel.move('bananas', 'super', 'hyper'); @@ -1253,12 +1253,12 @@ suite('TreeModel - Dynamic data model', () => { return model.expandAll(['hyper', 'mega']).then(() => { nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'pears'); - assert.equal(nav.next().id, 'hyper'); - assert.equal(nav.next().id, 'bananas'); - assert.equal(nav.next().id, 'mega'); - assert.equal(nav.next().id, 'apples'); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'pears'); + assert.equal(nav.next()!.id, 'hyper'); + assert.equal(nav.next()!.id, 'bananas'); + assert.equal(nav.next()!.id, 'mega'); + assert.equal(nav.next()!.id, 'apples'); assert.equal(nav.next() && false, null); }); }); @@ -1274,8 +1274,8 @@ suite('TreeModel - Dynamic data model', () => { return model.setInput('root').then(() => { return model.expand('grandfather').then(() => { return model.collapse('father').then(() => { - var times = 0; - var listener = dataModel.onGetChildren((element) => { + let times = 0; + let listener = dataModel.onGetChildren((element) => { times++; assert.equal(element, 'grandfather'); }); @@ -1309,11 +1309,11 @@ suite('TreeModel - Dynamic data model', () => { return model.expand('father').then(() => { return model.expand('mother').then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); - assert.equal(nav.next().id, 'mother'); - assert.equal(nav.next().id, 'daughter'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'father'); + assert.equal(nav.next()!.id, 'son'); + assert.equal(nav.next()!.id, 'mother'); + assert.equal(nav.next()!.id, 'daughter'); assert.equal(nav.next() && false, null); dataModel.removeChild('father', 'son'); @@ -1323,16 +1323,16 @@ suite('TreeModel - Dynamic data model', () => { dataModel.promiseFactory = () => { return timeout(0); }; - var getTimes = 0; - var gotTimes = 0; - var getListener = dataModel.onGetChildren((element) => { getTimes++; }); - var gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); + let getTimes = 0; + let gotTimes = 0; + const getListener = dataModel.onGetChildren((element) => { getTimes++; }); + const gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); - var p1 = model.refresh('father'); + const p1 = model.refresh('father'); assert.equal(getTimes, 1); assert.equal(gotTimes, 0); - var p2 = model.refresh('mother'); + const p2 = model.refresh('mother'); assert.equal(getTimes, 2); assert.equal(gotTimes, 0); @@ -1341,10 +1341,10 @@ suite('TreeModel - Dynamic data model', () => { assert.equal(gotTimes, 2); nav = model.getNavigator(); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'brother'); - assert.equal(nav.next().id, 'mother'); - assert.equal(nav.next().id, 'sister'); + assert.equal(nav.next()!.id, 'father'); + assert.equal(nav.next()!.id, 'brother'); + assert.equal(nav.next()!.id, 'mother'); + assert.equal(nav.next()!.id, 'sister'); assert.equal(nav.next() && false, null); getListener.dispose(); @@ -1363,22 +1363,22 @@ suite('TreeModel - Dynamic data model', () => { return model.setInput('root').then(() => { return model.expand('grandfather').then(() => { return model.expand('father').then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'grandfather'); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'grandfather'); + assert.equal(nav.next()!.id, 'father'); + assert.equal(nav.next()!.id, 'son'); assert.equal(nav.next() && false, null); - var refreshTimes = 0; + let refreshTimes = 0; counter.listen(model.onDidRefreshItem, (e) => { refreshTimes++; }); - var getTimes = 0; - var getListener = dataModel.onGetChildren((element) => { getTimes++; }); + let getTimes = 0; + const getListener = dataModel.onGetChildren((element) => { getTimes++; }); - var gotTimes = 0; - var gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); + let gotTimes = 0; + const gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); - var p1Completes = []; + const p1Completes: Array<(value?: any) => void> = []; dataModel.promiseFactory = () => { return new Promise((c) => { p1Completes.push(c); }); }; model.refresh('grandfather').then(() => { @@ -1388,16 +1388,16 @@ suite('TreeModel - Dynamic data model', () => { assert.equal(gotTimes, 0); // unblock the first get - p1Completes.shift()(); + p1Completes.shift()!(); // once the first get is unblocked, the second get should appear assert.equal(refreshTimes, 2); // (+1) first father refresh assert.equal(getTimes, 2); assert.equal(gotTimes, 1); - var p2Complete; + let p2Complete; dataModel.promiseFactory = () => { return new Promise((c) => { p2Complete = c; }); }; - var p2 = model.refresh('father'); + const p2 = model.refresh('father'); // same situation still assert.equal(refreshTimes, 3); // (+1) second father refresh @@ -1405,7 +1405,7 @@ suite('TreeModel - Dynamic data model', () => { assert.equal(gotTimes, 1); // unblock the second get - p1Completes.shift()(); + p1Completes.shift()!(); // the third get should have appeared, it should've been waiting for the second one assert.equal(refreshTimes, 4); // (+1) first son request @@ -1421,9 +1421,9 @@ suite('TreeModel - Dynamic data model', () => { return p2.then(() => { nav = model.getNavigator(); - assert.equal(nav.next().id, 'grandfather'); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); + assert.equal(nav.next()!.id, 'grandfather'); + assert.equal(nav.next()!.id, 'father'); + assert.equal(nav.next()!.id, 'son'); assert.equal(nav.next() && false, null); getListener.dispose(); @@ -1529,7 +1529,7 @@ suite('TreeModel - Dynamic data model', () => { // delay expansions and refreshes dataModel.promiseFactory = () => { return timeout(0); }; - var promises: Thenable[] = []; + const promises: Promise[] = []; promises.push(model.expand('father')); dataModel.removeChild('root', 'father'); @@ -1549,7 +1549,7 @@ suite('TreeModel - Dynamic data model', () => { }); suite('TreeModel - bugs', () => { - var counter: EventCounter; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -1573,17 +1573,17 @@ suite('TreeModel - bugs', () => { if (e === 'bart') { return getBartChildren(); } return Promise.resolve([]); }, - getParent: (_, e): Thenable => { throw new Error('not implemented'); }, + getParent: (_, e): Promise => { throw new Error('not implemented'); }, } }); let listeners = []; // helpers - var getGetRootChildren = (children: string[], millis = 0) => () => timeout(millis).then(() => children); - var getRootChildren = getGetRootChildren(['homer', 'bart', 'lisa', 'marge', 'maggie'], 0); - var getGetBartChildren = (millis = 0) => () => timeout(millis).then(() => ['milhouse', 'nelson']); - var getBartChildren = getGetBartChildren(0); + const getGetRootChildren = (children: string[], millis = 0) => () => timeout(millis).then(() => children); + let getRootChildren = getGetRootChildren(['homer', 'bart', 'lisa', 'marge', 'maggie'], 0); + const getGetBartChildren = (millis = 0) => () => timeout(millis).then(() => ['milhouse', 'nelson']); + const getBartChildren = getGetBartChildren(0); // item expanding should not exist! counter.listen(model.onExpandItem, () => { assert(false, 'should never receive item:expanding event'); }); @@ -1595,14 +1595,14 @@ suite('TreeModel - bugs', () => { getRootChildren = getGetRootChildren(['homer', 'lisa', 'marge', 'maggie'], 10); // refresh root - var p1 = model.refresh('root', true).then(() => { + const p1 = model.refresh('root', true).then(() => { assert(true); }, () => { assert(false, 'should never reach this'); }); // at the same time, try to expand bart! - var p2 = model.expand('bart').then(() => { + const p2 = model.expand('bart').then(() => { assert(false, 'should never reach this'); }, () => { assert(true, 'bart should fail to expand since he was removed meanwhile'); @@ -1617,7 +1617,6 @@ suite('TreeModel - bugs', () => { while (listeners.length > 0) { listeners.pop()(); } listeners = null; model.dispose(); - model = null; assert.equal(counter.count, 0); }); @@ -1643,8 +1642,8 @@ suite('TreeModel - bugs', () => { await model.expand('father'); let nav = model.getNavigator(); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); + assert.equal(nav.next()!.id, 'father'); + assert.equal(nav.next()!.id, 'son'); assert.equal(nav.next(), null); await model.collapse('father'); @@ -1654,7 +1653,7 @@ suite('TreeModel - bugs', () => { await model.expand('father'); nav = model.getNavigator(); - assert.equal(nav.next().id, 'father'); + assert.equal(nav.next()!.id, 'father'); assert.equal(nav.next(), null); counter.dispose(); diff --git a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts index a39a46cc54b5..8b6d96f24ff2 100644 --- a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts +++ b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts @@ -17,9 +17,9 @@ function makeItem(id, height): any { } function makeItems(...args: any[]) { - var r = []; + let r: any[] = []; - for (var i = 0; i < args.length; i += 2) { + for (let i = 0; i < args.length; i += 2) { r.push(makeItem(args[i], args[i + 1])); } @@ -27,8 +27,8 @@ function makeItems(...args: any[]) { } function makeNavigator(...args: any[]): any { - var items = makeItems.apply(null, args); - var i = 0; + let items = makeItems.apply(null, args); + let i = 0; return { next: function () { @@ -50,7 +50,7 @@ class TestHeightMap extends HeightMap { } suite('TreeView - HeightMap', () => { - var rangeMap: HeightMap; + let rangeMap: HeightMap; setup(() => { rangeMap = new TestHeightMap(); @@ -59,7 +59,7 @@ suite('TreeView - HeightMap', () => { teardown(() => { rangeMap.dispose(); - rangeMap = null; + rangeMap = null!; }); test('simple', () => { @@ -76,7 +76,7 @@ suite('TreeView - HeightMap', () => { }); test('onInsertItems at beginning', () => { - var navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); + let navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); rangeMap.onInsertItems(navigator); assert.equal(rangeMap.itemAt(0), 'x'); @@ -97,7 +97,7 @@ suite('TreeView - HeightMap', () => { }); test('onInsertItems in middle', () => { - var navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); + let navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); rangeMap.onInsertItems(navigator, 'a'); assert.equal(rangeMap.itemAt(0), 'a'); @@ -118,7 +118,7 @@ suite('TreeView - HeightMap', () => { }); test('onInsertItems at end', () => { - var navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); + let navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); rangeMap.onInsertItems(navigator, 'd'); assert.equal(rangeMap.itemAt(0), 'a'); @@ -171,7 +171,7 @@ suite('TreeView - HeightMap', () => { }); test('onRefreshItems at beginning', () => { - var navigator = makeNavigator('a', 1, 'b', 1); + let navigator = makeNavigator('a', 1, 'b', 1); rangeMap.onRefreshItems(navigator); assert.equal(rangeMap.itemAt(0), 'a'); @@ -184,7 +184,7 @@ suite('TreeView - HeightMap', () => { }); test('onRefreshItems in middle', () => { - var navigator = makeNavigator('b', 40, 'c', 4); + let navigator = makeNavigator('b', 40, 'c', 4); rangeMap.onRefreshItems(navigator); assert.equal(rangeMap.itemAt(0), 'a'); @@ -199,7 +199,7 @@ suite('TreeView - HeightMap', () => { }); test('onRefreshItems at end', () => { - var navigator = makeNavigator('d', 22); + let navigator = makeNavigator('d', 22); rangeMap.onRefreshItems(navigator); assert.equal(rangeMap.itemAt(0), 'a'); @@ -214,8 +214,8 @@ suite('TreeView - HeightMap', () => { }); test('withItemsInRange', () => { - var i = 0; - var itemsInRange = ['a', 'b']; + let i = 0; + let itemsInRange = ['a', 'b']; rangeMap.withItemsInRange(2, 27, function (item) { assert.equal(item, itemsInRange[i++]); }); assert.equal(i, itemsInRange.length); diff --git a/src/vs/base/test/browser/dom.test.ts b/src/vs/base/test/browser/dom.test.ts index 02685ff98b32..c19a78547667 100644 --- a/src/vs/base/test/browser/dom.test.ts +++ b/src/vs/base/test/browser/dom.test.ts @@ -25,18 +25,19 @@ suite('dom', () => { let div = $('div', { class: 'test' }); assert.equal(div.className, 'test'); - div = $('div', null); + div = $('div', undefined); assert.equal(div.className, ''); }); test('should build nodes with children', () => { - let div = $('div', null, $('span', { id: 'demospan' })); + let div = $('div', undefined, $('span', { id: 'demospan' })); let firstChild = div.firstChild as HTMLElement; assert.equal(firstChild.tagName, 'SPAN'); assert.equal(firstChild.id, 'demospan'); - div = $('div', null, 'hello'); - assert.equal(div.firstChild.textContent, 'hello'); + div = $('div', undefined, 'hello'); + + assert.equal(div.firstChild && div.firstChild.textContent, 'hello'); }); }); }); diff --git a/src/vs/base/test/browser/highlightedLabel.test.ts b/src/vs/base/test/browser/highlightedLabel.test.ts index e9019ca173e1..57bf808cd099 100644 --- a/src/vs/base/test/browser/highlightedLabel.test.ts +++ b/src/vs/base/test/browser/highlightedLabel.test.ts @@ -12,11 +12,6 @@ suite('HighlightedLabel', () => { label = new HighlightedLabel(document.createElement('div'), true); }); - teardown(() => { - label.dispose(); - label = null; - }); - test('empty label', function () { assert.equal(label.element.innerHTML, ''); }); diff --git a/src/vs/base/test/browser/htmlContent.test.ts b/src/vs/base/test/browser/htmlContent.test.ts index efafdb2aa1ee..8febe04dff06 100644 --- a/src/vs/base/test/browser/htmlContent.test.ts +++ b/src/vs/base/test/browser/htmlContent.test.ts @@ -8,7 +8,7 @@ import { renderMarkdown, renderText, renderFormattedText } from 'vs/base/browser suite('HtmlContent', () => { test('render simple element', () => { - var result: HTMLElement = renderText('testing'); + let result: HTMLElement = renderText('testing'); assert.strictEqual(result.nodeType, document.ELEMENT_NODE); assert.strictEqual(result.textContent, 'testing'); @@ -16,7 +16,7 @@ suite('HtmlContent', () => { }); test('render element with class', () => { - var result: HTMLElement = renderText('testing', { + let result: HTMLElement = renderText('testing', { className: 'testClass' }); assert.strictEqual(result.nodeType, document.ELEMENT_NODE); @@ -24,9 +24,9 @@ suite('HtmlContent', () => { }); test('simple formatting', () => { - var result: HTMLElement = renderFormattedText('**bold**'); + let result: HTMLElement = renderFormattedText('**bold**'); assert.strictEqual(result.children.length, 1); - assert.strictEqual(result.firstChild.textContent, 'bold'); + assert.strictEqual(result.firstChild!.textContent, 'bold'); assert.strictEqual((result.firstChild).tagName, 'B'); assert.strictEqual(result.innerHTML, 'bold'); @@ -38,18 +38,18 @@ suite('HtmlContent', () => { }); test('no formatting', () => { - var result: HTMLElement = renderFormattedText('this is just a string'); + let result: HTMLElement = renderFormattedText('this is just a string'); assert.strictEqual(result.innerHTML, 'this is just a string'); }); test('preserve newlines', () => { - var result: HTMLElement = renderFormattedText('line one\nline two'); + let result: HTMLElement = renderFormattedText('line one\nline two'); assert.strictEqual(result.innerHTML, 'line one
line two'); }); test('action', () => { - var callbackCalled = false; - var result: HTMLElement = renderFormattedText('[[action]]', { + let callbackCalled = false; + let result: HTMLElement = renderFormattedText('[[action]]', { actionHandler: { callback(content) { assert.strictEqual(content, '0'); @@ -60,15 +60,15 @@ suite('HtmlContent', () => { }); assert.strictEqual(result.innerHTML, 'action'); - var event: MouseEvent = document.createEvent('MouseEvent'); + let event: MouseEvent = document.createEvent('MouseEvent'); event.initEvent('click', true, true); - result.firstChild.dispatchEvent(event); + result.firstChild!.dispatchEvent(event); assert.strictEqual(callbackCalled, true); }); test('fancy action', () => { - var callbackCalled = false; - var result: HTMLElement = renderFormattedText('__**[[action]]**__', { + let callbackCalled = false; + let result: HTMLElement = renderFormattedText('__**[[action]]**__', { actionHandler: { callback(content) { assert.strictEqual(content, '0'); @@ -79,14 +79,14 @@ suite('HtmlContent', () => { }); assert.strictEqual(result.innerHTML, 'action'); - var event: MouseEvent = document.createEvent('MouseEvent'); + let event: MouseEvent = document.createEvent('MouseEvent'); event.initEvent('click', true, true); - result.firstChild.firstChild.firstChild.dispatchEvent(event); + result.firstChild!.firstChild!.firstChild!.dispatchEvent(event); assert.strictEqual(callbackCalled, true); }); test('escaped formatting', () => { - var result: HTMLElement = renderFormattedText('\\*\\*bold\\*\\*'); + let result: HTMLElement = renderFormattedText('\\*\\*bold\\*\\*'); assert.strictEqual(result.children.length, 0); assert.strictEqual(result.innerHTML, '**bold**'); }); @@ -111,15 +111,15 @@ suite('HtmlContent', () => { assert.strictEqual(result.innerHTML, imageFromMarked); }); test('image width from title params', () => { - var result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); assert.strictEqual(result.innerHTML, `

image

`); }); test('image height from title params', () => { - var result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); assert.strictEqual(result.innerHTML, `

image

`); }); test('image width and height from title params', () => { - var result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); assert.strictEqual(result.innerHTML, `

image

`); }); }); diff --git a/src/vs/base/test/browser/ui/grid/gridview.test.ts b/src/vs/base/test/browser/ui/grid/gridview.test.ts index e23aea246f4f..eec930b38391 100644 --- a/src/vs/base/test/browser/ui/grid/gridview.test.ts +++ b/src/vs/base/test/browser/ui/grid/gridview.test.ts @@ -21,10 +21,6 @@ suite('Gridview', function () { container.appendChild(gridview.element); }); - teardown(function () { - gridview = null; - }); - test('empty gridview is empty', function () { assert.deepEqual(nodesToArrays(gridview.getViews()), []); gridview.dispose(); diff --git a/src/vs/base/test/browser/ui/grid/util.ts b/src/vs/base/test/browser/ui/grid/util.ts index 44154ad89b36..15da109dcd39 100644 --- a/src/vs/base/test/browser/ui/grid/util.ts +++ b/src/vs/base/test/browser/ui/grid/util.ts @@ -9,20 +9,20 @@ import { IView, GridNode, isGridBranchNode, } from 'vs/base/browser/ui/grid/grid export class TestView implements IView { - private _onDidChange = new Emitter<{ width: number; height: number; }>(); + private _onDidChange = new Emitter<{ width: number; height: number; } | undefined>(); readonly onDidChange = this._onDidChange.event; get minimumWidth(): number { return this._minimumWidth; } - set minimumWidth(size: number) { this._minimumWidth = size; this._onDidChange.fire(); } + set minimumWidth(size: number) { this._minimumWidth = size; this._onDidChange.fire(undefined); } get maximumWidth(): number { return this._maximumWidth; } - set maximumWidth(size: number) { this._maximumWidth = size; this._onDidChange.fire(); } + set maximumWidth(size: number) { this._maximumWidth = size; this._onDidChange.fire(undefined); } get minimumHeight(): number { return this._minimumHeight; } - set minimumHeight(size: number) { this._minimumHeight = size; this._onDidChange.fire(); } + set minimumHeight(size: number) { this._minimumHeight = size; this._onDidChange.fire(undefined); } get maximumHeight(): number { return this._maximumHeight; } - set maximumHeight(size: number) { this._maximumHeight = size; this._onDidChange.fire(); } + set maximumHeight(size: number) { this._maximumHeight = size; this._onDidChange.fire(undefined); } private _element: HTMLElement = document.createElement('div'); get element(): HTMLElement { this._onDidGetElement.fire(); return this._element; } diff --git a/src/vs/base/test/browser/ui/list/listView.test.ts b/src/vs/base/test/browser/ui/list/listView.test.ts index 9332862f451b..ea1572f2eef1 100644 --- a/src/vs/base/test/browser/ui/list/listView.test.ts +++ b/src/vs/base/test/browser/ui/list/listView.test.ts @@ -25,7 +25,6 @@ suite('ListView', function () { templateId: 'template', renderTemplate() { templatesCount++; }, renderElement() { }, - disposeElement() { }, disposeTemplate() { templatesCount--; } }; 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 e2ee6d3757b5..af6ffc17ce0a 100644 --- a/src/vs/base/test/browser/ui/list/rangeMap.test.ts +++ b/src/vs/base/test/browser/ui/list/rangeMap.test.ts @@ -8,7 +8,7 @@ import { RangeMap, groupIntersect, consolidate } from 'vs/base/browser/ui/list/r import { Range } from 'vs/base/common/range'; suite('RangeMap', () => { - var rangeMap: RangeMap; + let rangeMap: RangeMap; setup(() => { rangeMap = new RangeMap(); 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 f482871d3ae4..fc44ff1e339d 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -14,10 +14,10 @@ class TestView implements IView { readonly onDidChange = this._onDidChange.event; get minimumSize(): number { return this._minimumSize; } - set minimumSize(size: number) { this._minimumSize = size; this._onDidChange.fire(); } + set minimumSize(size: number) { this._minimumSize = size; this._onDidChange.fire(undefined); } get maximumSize(): number { return this._maximumSize; } - set maximumSize(size: number) { this._maximumSize = size; this._onDidChange.fire(); } + set maximumSize(size: number) { this._maximumSize = size; this._onDidChange.fire(undefined); } private _element: HTMLElement = document.createElement('div'); get element(): HTMLElement { this._onDidGetElement.fire(); return this._element; } @@ -72,13 +72,9 @@ suite('Splitview', () => { container.style.height = `${200}px`; }); - teardown(() => { - container = null; - }); - test('empty splitview has empty DOM', () => { const splitview = new SplitView(container); - assert.equal(container.firstElementChild.firstElementChild.childElementCount, 0, 'split view should be empty'); + assert.equal(container.firstElementChild!.firstElementChild!.childElementCount, 0, 'split view should be empty'); splitview.dispose(); }); @@ -135,7 +131,7 @@ suite('Splitview', () => { let didLayout = false; const layoutDisposable = view.onDidLayout(() => didLayout = true); - const renderDisposable = view.onDidGetElement(() => void 0); + const renderDisposable = view.onDidGetElement(() => undefined); splitview.addView(view, 20); diff --git a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts index 2193746e6a51..fe0b88353107 100644 --- a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; -import { AsyncDataTree, IDataSource } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { ITreeNode, ITreeRenderer, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; +import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { hasClass } from 'vs/base/browser/dom'; @@ -46,23 +46,16 @@ suite('AsyncDataTree', function () { renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { templateData.textContent = element.element.id; } - disposeElement(element: ITreeNode, index: number, templateData: HTMLElement): void { - // noop - } disposeTemplate(templateData: HTMLElement): void { // noop } }; - const dataSource = new class implements IDataSource { - hasChildren(element: Element | null): boolean { - return !element || (element.children && element.children.length > 0); + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; } - getChildren(element: Element | null): Thenable { - if (!element) { - return Promise.resolve(root.children); - } - + getChildren(element: Element): Promise { return Promise.resolve(element.children || []); } }; @@ -82,11 +75,11 @@ suite('AsyncDataTree', function () { const _: (id: string) => Element = find.bind(null, root.children); - const tree = new AsyncDataTree(container, delegate, [renderer], dataSource, { identityProvider }); + const tree = new AsyncDataTree(container, delegate, [renderer], dataSource, { identityProvider }); tree.layout(200); assert.equal(container.querySelectorAll('.monaco-list-row').length, 0); - await tree.refresh(null); + await tree.setInput(root); assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; assert(!hasClass(twistie, 'collapsible')); @@ -98,14 +91,14 @@ suite('AsyncDataTree', function () { { id: 'ac' } ]; - await tree.refresh(null); + await tree.updateChildren(root); assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); await tree.expand(_('a')); assert.equal(container.querySelectorAll('.monaco-list-row').length, 4); _('a').children = []; - await tree.refresh(null); + await tree.updateChildren(root); assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); }); }); \ No newline at end of file diff --git a/src/vs/base/test/browser/ui/tree/dataTree.test.ts b/src/vs/base/test/browser/ui/tree/dataTree.test.ts new file mode 100644 index 000000000000..98b4912d1ee5 --- /dev/null +++ b/src/vs/base/test/browser/ui/tree/dataTree.test.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ITreeNode, ITreeRenderer, IDataSource } from 'vs/base/browser/ui/tree/tree'; +import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { DataTree } from 'vs/base/browser/ui/tree/dataTree'; + +interface E { + value: number; + children?: E[]; +} + +suite('DataTree', function () { + let tree: DataTree; + + const root: E = { + value: -1, + children: [ + { value: 0, children: [{ value: 10 }, { value: 11 }, { value: 12 }] }, + { value: 1 }, + { value: 2 }, + ] + }; + + const empty: E = { + value: -1, + children: [] + }; + + setup(() => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = `${element.element.value}`; + } + disposeTemplate(): void { } + }; + + const dataSource = new class implements IDataSource { + getChildren(element: E): E[] { + return element.children || []; + } + }; + + const identityProvider = new class implements IIdentityProvider { + getId(element: E): { toString(): string; } { + return `${element.value}`; + } + }; + + tree = new DataTree(container, delegate, [renderer], dataSource, { + identityProvider + }); + tree.layout(200); + }); + + teardown(() => { + tree.dispose(); + }); + + test('view state is lost implicitly', () => { + tree.setInput(root); + + let navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 10); + assert.equal(navigator.next()!.value, 11); + assert.equal(navigator.next()!.value, 12); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + tree.collapse(root.children![0]); + navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + tree.setSelection([root.children![1]]); + tree.setFocus([root.children![2]]); + + tree.setInput(empty); + tree.setInput(root); + navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 10); + assert.equal(navigator.next()!.value, 11); + assert.equal(navigator.next()!.value, 12); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + assert.deepEqual(tree.getSelection(), []); + assert.deepEqual(tree.getFocus(), []); + }); + + test('view state can be preserved', () => { + tree.setInput(root); + + let navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 10); + assert.equal(navigator.next()!.value, 11); + assert.equal(navigator.next()!.value, 12); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + tree.collapse(root.children![0]); + navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + tree.setSelection([root.children![1]]); + tree.setFocus([root.children![2]]); + + const viewState = tree.getViewState(); + + tree.setInput(empty); + tree.setInput(root, viewState); + navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + assert.deepEqual(tree.getSelection(), [root.children![1]]); + assert.deepEqual(tree.getFocus(), [root.children![2]]); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/browser/ui/tree/objectTree.test.ts b/src/vs/base/test/browser/ui/tree/objectTree.test.ts new file mode 100644 index 000000000000..9160946a9b4d --- /dev/null +++ b/src/vs/base/test/browser/ui/tree/objectTree.test.ts @@ -0,0 +1,189 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; +import { Iterator } from 'vs/base/common/iterator'; + +suite('ObjectTree', function () { + suite('TreeNavigator', function () { + let tree: ObjectTree; + let filter = (_: number) => true; + + setup(() => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = `${element.element}`; + } + disposeTemplate(): void { } + }; + + tree = new ObjectTree(container, delegate, [renderer], { filter: { filter: (el) => filter(el) } }); + tree.layout(200); + }); + + teardown(() => { + tree.dispose(); + filter = (_: number) => true; + }); + + test('should be able to navigate', () => { + tree.setChildren(null, Iterator.fromArray([ + { + element: 0, children: Iterator.fromArray([ + { element: 10 }, + { element: 11 }, + { element: 12 }, + ]) + }, + { element: 1 }, + { element: 2 } + ])); + + const navigator = tree.navigate(); + + assert.equal(navigator.current(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.current(), 0); + assert.equal(navigator.next(), 10); + assert.equal(navigator.current(), 10); + assert.equal(navigator.next(), 11); + assert.equal(navigator.current(), 11); + assert.equal(navigator.next(), 12); + assert.equal(navigator.current(), 12); + assert.equal(navigator.next(), 1); + assert.equal(navigator.current(), 1); + assert.equal(navigator.next(), 2); + assert.equal(navigator.current(), 2); + assert.equal(navigator.previous(), 1); + assert.equal(navigator.current(), 1); + assert.equal(navigator.previous(), 12); + assert.equal(navigator.previous(), 11); + assert.equal(navigator.previous(), 10); + assert.equal(navigator.previous(), 0); + assert.equal(navigator.previous(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.next(), 10); + assert.equal(navigator.parent(), 0); + assert.equal(navigator.parent(), null); + assert.equal(navigator.first(), 0); + assert.equal(navigator.last(), 2); + }); + + test('should skip collapsed nodes', () => { + tree.setChildren(null, Iterator.fromArray([ + { + element: 0, collapsed: true, children: Iterator.fromArray([ + { element: 10 }, + { element: 11 }, + { element: 12 }, + ]) + }, + { element: 1 }, + { element: 2 } + ])); + + const navigator = tree.navigate(); + + assert.equal(navigator.current(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.next(), 1); + assert.equal(navigator.next(), 2); + assert.equal(navigator.next(), null); + assert.equal(navigator.previous(), 2); + assert.equal(navigator.previous(), 1); + assert.equal(navigator.previous(), 0); + assert.equal(navigator.previous(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.parent(), null); + assert.equal(navigator.first(), 0); + assert.equal(navigator.last(), 2); + }); + + test('should skip filtered elements', () => { + filter = el => el % 2 === 0; + + tree.setChildren(null, Iterator.fromArray([ + { + element: 0, children: Iterator.fromArray([ + { element: 10 }, + { element: 11 }, + { element: 12 }, + ]) + }, + { element: 1 }, + { element: 2 } + ])); + + const navigator = tree.navigate(); + + assert.equal(navigator.current(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.next(), 10); + assert.equal(navigator.next(), 12); + assert.equal(navigator.next(), 2); + assert.equal(navigator.next(), null); + assert.equal(navigator.previous(), 2); + assert.equal(navigator.previous(), 12); + assert.equal(navigator.previous(), 10); + assert.equal(navigator.previous(), 0); + assert.equal(navigator.previous(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.next(), 10); + assert.equal(navigator.parent(), 0); + assert.equal(navigator.parent(), null); + assert.equal(navigator.first(), 0); + assert.equal(navigator.last(), 2); + }); + + test('should be able to start from node', () => { + tree.setChildren(null, Iterator.fromArray([ + { + element: 0, children: Iterator.fromArray([ + { element: 10 }, + { element: 11 }, + { element: 12 }, + ]) + }, + { element: 1 }, + { element: 2 } + ])); + + const navigator = tree.navigate(1); + + assert.equal(navigator.current(), 1); + assert.equal(navigator.next(), 2); + assert.equal(navigator.current(), 2); + assert.equal(navigator.previous(), 1); + assert.equal(navigator.current(), 1); + assert.equal(navigator.previous(), 12); + assert.equal(navigator.previous(), 11); + assert.equal(navigator.previous(), 10); + assert.equal(navigator.previous(), 0); + assert.equal(navigator.previous(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.next(), 10); + assert.equal(navigator.parent(), 0); + assert.equal(navigator.parent(), null); + assert.equal(navigator.first(), 0); + assert.equal(navigator.last(), 2); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index 4cdc75f955c5..44551bf171bc 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -32,8 +32,16 @@ suite('Arrays', () => { }); test('stableSort', () => { + function fill(num: number, valueFn: () => T, arr: T[] = []): T[] { + for (let i = 0; i < num; i++) { + arr[i] = valueFn(); + } + + return arr; + } + let counter = 0; - let data = arrays.fill(10000, () => ({ n: 1, m: counter++ })); + let data = fill(10000, () => ({ n: 1, m: counter++ })); arrays.mergeSort(data, (a, b) => a.n - b.n); @@ -262,13 +270,13 @@ suite('Arrays', () => { } test('coalesce', () => { - let a = arrays.coalesce([null, 1, null, 2, 3]); + let a: Array = arrays.coalesce([null, 1, null, 2, 3]); assert.equal(a.length, 3); assert.equal(a[0], 1); assert.equal(a[1], 2); assert.equal(a[2], 3); - arrays.coalesce([null, 1, null, void 0, undefined, 2, 3]); + arrays.coalesce([null, 1, null, undefined, undefined, 2, 3]); assert.equal(a.length, 3); assert.equal(a[0], 1); assert.equal(a[1], 2); @@ -298,14 +306,14 @@ suite('Arrays', () => { }); test('coalesce - inplace', function () { - let a = [null, 1, null, 2, 3]; + let a: Array = [null, 1, null, 2, 3]; arrays.coalesceInPlace(a); assert.equal(a.length, 3); assert.equal(a[0], 1); assert.equal(a[1], 2); assert.equal(a[2], 3); - a = [null, 1, null, void 0, undefined, 2, 3]; + a = [null, 1, null, undefined!, undefined!, 2, 3]; arrays.coalesceInPlace(a); assert.equal(a.length, 3); assert.equal(a[0], 1); diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 40a6c75d5254..5a1c47538c74 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -39,17 +39,6 @@ suite('Async', () => { return result; }); - // test('Cancel callback behaviour', async function () { - // let withCancelCallback = new WinJsPromise(() => { }, () => { }); - // let withoutCancelCallback = new TPromise(() => { }); - - // withCancelCallback.cancel(); - // (withoutCancelCallback as WinJsPromise).cancel(); - - // await withCancelCallback.then(undefined, err => { assert.ok(isPromiseCanceledError(err)); }); - // await withoutCancelCallback.then(undefined, err => { assert.ok(isPromiseCanceledError(err)); }); - // }); - // 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 () { @@ -64,7 +53,7 @@ suite('Async', () => { order.push('afterCreate'); const promise = cancellablePromise - .then(null, err => null) + .then(undefined, err => null) .then(() => order.push('finally')); cancellablePromise.cancel(); @@ -86,7 +75,7 @@ suite('Async', () => { order.push('afterCreate'); const promise = cancellablePromise - .then(null, err => null) + .then(undefined, err => null) .then(() => order.push('finally')); cancellablePromise.cancel(); @@ -95,50 +84,6 @@ suite('Async', () => { return promise.then(() => assert.deepEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally'])); }); - // // Cancelling a sync tpromise will NOT cancel the promise, since it has resolved already. - // // Every `then` callback runs sync in the same execution frame, thus `finally` executes - // // before `afterCancel`. - // test('TPromise execution order (sync)', function () { - // const order = []; - // let promise = new WinJsPromise(resolve => { - // order.push('in executor'); - // resolve(1234); - // }, () => order.push('cancelled')); - - // order.push('afterCreate'); - - // promise = promise - // .then(null, err => null) - // .then(() => order.push('finally')); - - // promise.cancel(); - // order.push('afterCancel'); - - // return promise.then(() => assert.deepEqual(order, ['in executor', 'afterCreate', 'finally', 'afterCancel'])); - // }); - - // // Cancelling an async tpromise will cancel the promise. - // // Every `then` callback runs sync on the same execution frame as the `cancel` call, - // // so finally still executes before `afterCancel`. - // test('TPromise execution order (async)', function () { - // const order = []; - // let promise = new WinJsPromise(resolve => { - // order.push('in executor'); - // setTimeout(() => resolve(1234)); - // }, () => order.push('cancelled')); - - // order.push('afterCreate'); - - // promise = promise - // .then(null, err => null) - // .then(() => order.push('finally')); - - // promise.cancel(); - // order.push('afterCancel'); - - // return promise.then(() => assert.deepEqual(order, ['in executor', 'afterCreate', 'cancelled', 'finally', 'afterCancel'])); - // }); - test('cancelablePromise - get inner result', async function () { let promise = async.createCancelablePromise(token => { return async.timeout(12).then(_ => 1234); @@ -195,7 +140,7 @@ suite('Async', () => { let throttler = new async.Throttler(); - let promises: Thenable[] = []; + let promises: Promise[] = []; promises.push(throttler.queue(factoryFactory(1)).then((n) => { assert.equal(n, 1); })); promises.push(throttler.queue(factoryFactory(2)).then((n) => { assert.equal(n, 3); })); @@ -211,7 +156,7 @@ suite('Async', () => { }; let delayer = new async.Delayer(0); - let promises: Thenable[] = []; + let promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -259,17 +204,17 @@ suite('Async', () => { }; let delayer = new async.Delayer(0); - let promises: Thenable[] = []; + let promises: Promise[] = []; assert(!delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled'); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled'); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled'); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); delayer.cancel(); @@ -286,7 +231,7 @@ suite('Async', () => { }; let delayer = new async.Delayer(0); - let promises: Thenable[] = []; + let promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -294,10 +239,10 @@ suite('Async', () => { assert.equal(result, 1); assert(!delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled'); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled'); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); delayer.cancel(); @@ -336,7 +281,7 @@ suite('Async', () => { }; let delayer = new async.Delayer(0); - let promises: Thenable[] = []; + let promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -381,7 +326,7 @@ suite('Async', () => { let limiter = new async.Limiter(1); - let promises: Thenable[] = []; + let promises: Promise[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); return Promise.all(promises).then((res) => { @@ -402,7 +347,7 @@ suite('Async', () => { let factoryFactory = (n: number) => () => async.timeout(0).then(() => n); let limiter = new async.Limiter(1); - let promises: Thenable[] = []; + let promises: Promise[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); return Promise.all(promises).then((res) => { @@ -429,7 +374,7 @@ suite('Async', () => { let limiter = new async.Limiter(5); - let promises: Thenable[] = []; + let promises: Promise[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); return Promise.all(promises).then((res) => { @@ -499,7 +444,7 @@ suite('Async', () => { queue.queue(f1); queue.queue(f2); - queue.queue(f3).then(null, () => error = true); + queue.queue(f3).then(undefined, () => error = true); queue.queue(f4); return queue.queue(f5).then(() => { assert.equal(res[0], 1); @@ -577,7 +522,7 @@ suite('Async', () => { assert.ok(r2Queue); assert.equal(r1Queue, queue.queueFor(URI.file('/some/path'))); // same queue returned - let syncPromiseFactory = () => Promise.resolve(null); + let syncPromiseFactory = () => Promise.resolve(undefined); r1Queue.queue(syncPromiseFactory); diff --git a/src/vs/base/test/common/cache.test.ts b/src/vs/base/test/common/cache.test.ts index fb651bcc2e44..cff3c03f3bdf 100644 --- a/src/vs/base/test/common/cache.test.ts +++ b/src/vs/base/test/common/cache.test.ts @@ -42,7 +42,7 @@ suite('Cache', () => { let result = cache.get(); assert.equal(counter1, 1); assert.equal(counter2, 0); - result.promise.then(null, () => assert(true)); + result.promise.then(undefined, () => assert(true)); result.dispose(); assert.equal(counter1, 1); assert.equal(counter2, 0); diff --git a/src/vs/base/test/common/color.test.ts b/src/vs/base/test/common/color.test.ts index 16eaaf1b9fa2..406ef624a666 100644 --- a/src/vs/base/test/common/color.test.ts +++ b/src/vs/base/test/common/color.test.ts @@ -186,7 +186,7 @@ suite('Color', () => { }); test('bug#36240', () => { - assert.deepEqual(HSVA.fromRGBA(new RGBA(92, 106, 196, 1)), new HSVA(232, .531, .769, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(92, 106, 196, 1)), new HSVA(232, 0.531, 0.769, 1)); assert.deepEqual(HSVA.toRGBA(HSVA.fromRGBA(new RGBA(92, 106, 196, 1))), new RGBA(92, 106, 196, 1)); }); }); @@ -196,50 +196,50 @@ suite('Color', () => { test('parseHex', () => { // invalid - assert.deepEqual(Color.Format.CSS.parseHex(null), null); + assert.deepEqual(Color.Format.CSS.parseHex(null!), null); assert.deepEqual(Color.Format.CSS.parseHex(''), null); assert.deepEqual(Color.Format.CSS.parseHex('#'), null); assert.deepEqual(Color.Format.CSS.parseHex('#0102030'), null); // somewhat valid - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFG0').rgba, new RGBA(255, 255, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFg0').rgba, new RGBA(255, 255, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#-FFF00').rgba, new RGBA(15, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFG0')!.rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFg0')!.rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#-FFF00')!.rgba, new RGBA(15, 255, 0, 1)); // valid - assert.deepEqual(Color.Format.CSS.parseHex('#000000').rgba, new RGBA(0, 0, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFFF').rgba, new RGBA(255, 255, 255, 1)); - - assert.deepEqual(Color.Format.CSS.parseHex('#FF0000').rgba, new RGBA(255, 0, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#00FF00').rgba, new RGBA(0, 255, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0000FF').rgba, new RGBA(0, 0, 255, 1)); - - assert.deepEqual(Color.Format.CSS.parseHex('#FFFF00').rgba, new RGBA(255, 255, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#00FFFF').rgba, new RGBA(0, 255, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FF00FF').rgba, new RGBA(255, 0, 255, 1)); - - assert.deepEqual(Color.Format.CSS.parseHex('#C0C0C0').rgba, new RGBA(192, 192, 192, 1)); - - assert.deepEqual(Color.Format.CSS.parseHex('#808080').rgba, new RGBA(128, 128, 128, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#800000').rgba, new RGBA(128, 0, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#808000').rgba, new RGBA(128, 128, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#008000').rgba, new RGBA(0, 128, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#800080').rgba, new RGBA(128, 0, 128, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#008080').rgba, new RGBA(0, 128, 128, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#000080').rgba, new RGBA(0, 0, 128, 1)); - - assert.deepEqual(Color.Format.CSS.parseHex('#010203').rgba, new RGBA(1, 2, 3, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#040506').rgba, new RGBA(4, 5, 6, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#070809').rgba, new RGBA(7, 8, 9, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0a0A0a').rgba, new RGBA(10, 10, 10, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0b0B0b').rgba, new RGBA(11, 11, 11, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0c0C0c').rgba, new RGBA(12, 12, 12, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0d0D0d').rgba, new RGBA(13, 13, 13, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0e0E0e').rgba, new RGBA(14, 14, 14, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0f0F0f').rgba, new RGBA(15, 15, 15, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#a0A0a0').rgba, new RGBA(160, 160, 160, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#CFA').rgba, new RGBA(204, 255, 170, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#CFA8').rgba, new RGBA(204, 255, 170, 0.533)); + assert.deepEqual(Color.Format.CSS.parseHex('#000000')!.rgba, new RGBA(0, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFFF')!.rgba, new RGBA(255, 255, 255, 1)); + + assert.deepEqual(Color.Format.CSS.parseHex('#FF0000')!.rgba, new RGBA(255, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#00FF00')!.rgba, new RGBA(0, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0000FF')!.rgba, new RGBA(0, 0, 255, 1)); + + assert.deepEqual(Color.Format.CSS.parseHex('#FFFF00')!.rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#00FFFF')!.rgba, new RGBA(0, 255, 255, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FF00FF')!.rgba, new RGBA(255, 0, 255, 1)); + + assert.deepEqual(Color.Format.CSS.parseHex('#C0C0C0')!.rgba, new RGBA(192, 192, 192, 1)); + + assert.deepEqual(Color.Format.CSS.parseHex('#808080')!.rgba, new RGBA(128, 128, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#800000')!.rgba, new RGBA(128, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#808000')!.rgba, new RGBA(128, 128, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#008000')!.rgba, new RGBA(0, 128, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#800080')!.rgba, new RGBA(128, 0, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#008080')!.rgba, new RGBA(0, 128, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#000080')!.rgba, new RGBA(0, 0, 128, 1)); + + assert.deepEqual(Color.Format.CSS.parseHex('#010203')!.rgba, new RGBA(1, 2, 3, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#040506')!.rgba, new RGBA(4, 5, 6, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#070809')!.rgba, new RGBA(7, 8, 9, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0a0A0a')!.rgba, new RGBA(10, 10, 10, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0b0B0b')!.rgba, new RGBA(11, 11, 11, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0c0C0c')!.rgba, new RGBA(12, 12, 12, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0d0D0d')!.rgba, new RGBA(13, 13, 13, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0e0E0e')!.rgba, new RGBA(14, 14, 14, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0f0F0f')!.rgba, new RGBA(15, 15, 15, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#a0A0a0')!.rgba, new RGBA(160, 160, 160, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#CFA')!.rgba, new RGBA(204, 255, 170, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#CFA8')!.rgba, new RGBA(204, 255, 170, 0.533)); }); }); }); diff --git a/src/vs/base/test/common/decorators.test.ts b/src/vs/base/test/common/decorators.test.ts index 57eeae1b6038..d05fb508666c 100644 --- a/src/vs/base/test/common/decorators.test.ts +++ b/src/vs/base/test/common/decorators.test.ts @@ -11,7 +11,7 @@ suite('Decorators', () => { class Foo { count = 0; - constructor(private _answer: number) { } + constructor(private _answer: number | null | undefined) { } @memoize answer() { @@ -56,7 +56,7 @@ suite('Decorators', () => { class Foo { count = 0; - constructor(private _answer: number) { } + constructor(private _answer: number | null | undefined) { } @memoize get answer() { diff --git a/src/vs/base/test/common/diff/diff.test.ts b/src/vs/base/test/common/diff/diff.test.ts index d5b53b94bad9..568af915e8ac 100644 --- a/src/vs/base/test/common/diff/diff.test.ts +++ b/src/vs/base/test/common/diff/diff.test.ts @@ -21,16 +21,16 @@ class StringDiffSequence implements ISequence { } function createArray(length: number, value: T): T[] { - var r = []; - for (var i = 0; i < length; i++) { + const r: T[] = []; + for (let i = 0; i < length; i++) { r[i] = value; } return r; } function maskBasedSubstring(str: string, mask: boolean[]): string { - var r = ''; - for (var i = 0; i < str.length; i++) { + let r = ''; + for (let i = 0; i < str.length; i++) { if (mask[i]) { r += str.charAt(i); } @@ -39,10 +39,10 @@ function maskBasedSubstring(str: string, mask: boolean[]): string { } function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffChange[], answerStr: string, onlyLength: boolean = false): void { - var originalMask = createArray(originalStr.length, true); - var modifiedMask = createArray(modifiedStr.length, true); + let originalMask = createArray(originalStr.length, true); + let modifiedMask = createArray(modifiedStr.length, true); - var i, j, change; + let i, j, change; for (i = 0; i < changes.length; i++) { change = changes[i]; @@ -59,8 +59,8 @@ function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffCh } } - var originalAnswer = maskBasedSubstring(originalStr, originalMask); - var modifiedAnswer = maskBasedSubstring(modifiedStr, modifiedMask); + let originalAnswer = maskBasedSubstring(originalStr, originalMask); + let modifiedAnswer = maskBasedSubstring(modifiedStr, modifiedMask); if (onlyLength) { assert.equal(originalAnswer.length, answerStr.length); @@ -72,14 +72,14 @@ function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffCh } function lcsInnerTest(Algorithm: any, originalStr: string, modifiedStr: string, answerStr: string, onlyLength: boolean = false): void { - var diff = new Algorithm(new StringDiffSequence(originalStr), new StringDiffSequence(modifiedStr)); - var changes = diff.ComputeDiff(); + let diff = new Algorithm(new StringDiffSequence(originalStr), new StringDiffSequence(modifiedStr)); + let changes = diff.ComputeDiff(); assertAnswer(originalStr, modifiedStr, changes, answerStr, onlyLength); } function stringPower(str: string, power: number): string { - var r = str; - for (var i = 0; i < power; i++) { + let r = str; + for (let i = 0; i < power; i++) { r += r; } return r; @@ -87,7 +87,7 @@ function stringPower(str: string, power: number): string { function lcsTest(Algorithm: any, originalStr: string, modifiedStr: string, answerStr: string) { lcsInnerTest(Algorithm, originalStr, modifiedStr, answerStr); - for (var i = 2; i <= 5; i++) { + for (let i = 2; i <= 5; i++) { lcsInnerTest(Algorithm, stringPower(originalStr, i), stringPower(modifiedStr, i), stringPower(answerStr, i), true); } } @@ -116,14 +116,14 @@ suite('Diff', () => { suite('Diff - Ported from VS', () => { test('using continue processing predicate to quit early', function () { - var left = 'abcdef'; - var right = 'abxxcyyydzzzzezzzzzzzzzzzzzzzzzzzzf'; + let left = 'abcdef'; + let right = 'abxxcyyydzzzzezzzzzzzzzzzzzzzzzzzzf'; // We use a long non-matching portion at the end of the right-side string, so the backwards tracking logic // doesn't get there first. - var predicateCallCount = 0; + let predicateCallCount = 0; - var diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { + let diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { assert.equal(predicateCallCount, 0); predicateCallCount++; @@ -134,7 +134,7 @@ suite('Diff - Ported from VS', () => { // cancel processing return false; }); - var changes = diff.ComputeDiff(true); + let changes = diff.ComputeDiff(true); assert.equal(predicateCallCount, 1); @@ -170,11 +170,11 @@ suite('Diff - Ported from VS', () => { // Cancel *one iteration* after the second match ('d') - var hitSecondMatch = false; + let hitSecondMatch = false; diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { assert(longestMatchSoFar <= 2); // We never see a match of length > 2 - var hitYet = hitSecondMatch; + let hitYet = hitSecondMatch; hitSecondMatch = longestMatchSoFar > 1; // Continue processing as long as there hasn't been a match made. return !hitYet; diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 89eb04844f66..e22bb371e7d3 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 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 { Event, Emitter, EventBufferer, EventMultiplexer, AsyncEmitter, IWaitUntil } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as Errors from 'vs/base/common/errors'; import { timeout } from 'vs/base/common/async'; @@ -71,7 +71,7 @@ suite('Event', function () { // unhook listener while (bucket.length) { - bucket.pop().dispose(); + bucket.pop()!.dispose(); } // noop @@ -111,7 +111,7 @@ suite('Event', function () { Errors.setUnexpectedErrorHandler(() => null); try { - let a = new Emitter(); + let a = new Emitter(); let hit = false; a.event(function () { throw 9; @@ -134,26 +134,26 @@ suite('Event', function () { } const context = {}; - let emitter = new Emitter(); + let emitter = new Emitter(); let reg1 = emitter.event(listener, context); let reg2 = emitter.event(listener, context); - emitter.fire(); + emitter.fire(undefined); assert.equal(counter, 2); reg1.dispose(); - emitter.fire(); + emitter.fire(undefined); assert.equal(counter, 3); reg2.dispose(); - emitter.fire(); + emitter.fire(undefined); assert.equal(counter, 3); }); test('Debounce Event', function (done: () => void) { let doc = new Samples.Document3(); - let onDocDidChange = debounceEvent(doc.onDidChange, (prev: string[], cur) => { + let onDocDidChange = Event.debounce(doc.onDidChange, (prev: string[], cur) => { if (!prev) { prev = [cur]; } else if (prev.indexOf(cur) < 0) { @@ -183,7 +183,7 @@ suite('Event', function () { test('Debounce Event - leading', async function () { const emitter = new Emitter(); - let debounced = debounceEvent(emitter.event, (l, e) => e, 0, /*leading=*/true); + let debounced = Event.debounce(emitter.event, (l, e) => e, 0, /*leading=*/true); let calls = 0; debounced(() => { @@ -199,7 +199,7 @@ suite('Event', function () { test('Debounce Event - leading', async function () { const emitter = new Emitter(); - let debounced = debounceEvent(emitter.event, (l, e) => e, 0, /*leading=*/true); + let debounced = Event.debounce(emitter.event, (l, e) => e, 0, /*leading=*/true); let calls = 0; debounced(() => { @@ -254,7 +254,7 @@ suite('AsyncEmitter', function () { emitter.fireAsync(thenables => ({ foo: true, bar: 1, - waitUntil(t: Thenable) { thenables.push(t); } + waitUntil(t: Promise) { thenables.push(t); } })); emitter.dispose(); }); @@ -384,8 +384,8 @@ suite('Event utils', () => { let counter1 = 0, counter2 = 0, counter3 = 0; const listener1 = emitter.event(() => counter1++); - const listener2 = once(emitter.event)(() => counter2++); - const listener3 = once(emitter.event)(() => counter3++); + const listener2 = Event.once(emitter.event)(() => counter2++); + const listener3 = Event.once(emitter.event)(() => counter3++); assert.equal(counter1, 0); assert.equal(counter2, 0); @@ -412,7 +412,7 @@ suite('Event utils', () => { test('should emit when done', async () => { let count = 0; - const event = fromPromise(Promise.resolve(null)); + const event = Event.fromPromise(Promise.resolve(null)); event(() => count++); assert.equal(count, 0); @@ -425,7 +425,7 @@ suite('Event utils', () => { let count = 0; const promise = timeout(5); - const event = fromPromise(promise); + const event = Event.fromPromise(promise); event(() => count++); assert.equal(count, 0); @@ -438,7 +438,7 @@ suite('Event utils', () => { test('should emit', () => { const emitter = new Emitter(); - const event = stopwatch(emitter.event); + const event = Event.stopwatch(emitter.event); return new Promise((c, e) => { event(duration => { @@ -448,7 +448,7 @@ suite('Event utils', () => { e(err); } - c(null); + c(undefined); }); setTimeout(() => emitter.fire(), 10); @@ -462,7 +462,7 @@ suite('Event utils', () => { const result: number[] = []; const emitter = new Emitter(); const event = emitter.event; - const bufferedEvent = buffer(event); + const bufferedEvent = Event.buffer(event); emitter.fire(1); emitter.fire(2); @@ -484,7 +484,7 @@ suite('Event utils', () => { const result: number[] = []; const emitter = new Emitter(); const event = emitter.event; - const bufferedEvent = buffer(event, true); + const bufferedEvent = Event.buffer(event, true); emitter.fire(1); emitter.fire(2); @@ -506,7 +506,7 @@ suite('Event utils', () => { const result: number[] = []; const emitter = new Emitter(); const event = emitter.event; - const bufferedEvent = buffer(event, false, [-2, -1, 0]); + const bufferedEvent = Event.buffer(event, false, [-2, -1, 0]); emitter.fire(1); emitter.fire(2); @@ -524,7 +524,7 @@ suite('Event utils', () => { const result: number[] = []; const emitter = new Emitter(); const event = emitter.event; - const echoEvent = echo(event); + const echoEvent = Event.echo(event); emitter.fire(1); emitter.fire(2); @@ -547,7 +547,7 @@ suite('Event utils', () => { const result2: number[] = []; const emitter = new Emitter(); const event = emitter.event; - const echoEvent = echo(event); + const echoEvent = Event.echo(event); emitter.fire(1); emitter.fire(2); @@ -744,7 +744,7 @@ suite('Event utils', () => { test('latch', () => { const emitter = new Emitter(); - const event = latch(emitter.event); + const event = Event.latch(emitter.event); const result: number[] = []; const listener = event(num => result.push(num)); diff --git a/src/vs/base/test/common/filters.perf.test.ts b/src/vs/base/test/common/filters.perf.test.ts index 63d550f41ede..cd21e58ec02a 100644 --- a/src/vs/base/test/common/filters.perf.test.ts +++ b/src/vs/base/test/common/filters.perf.test.ts @@ -5,7 +5,7 @@ import * as filters from 'vs/base/common/filters'; import { data } from './filters.perf.data'; -const patterns = ['cci', 'ida', 'pos', 'CCI', 'enbled', 'callback', 'gGame', 'cons']; +const patterns = ['cci', 'ida', 'pos', 'CCI', 'enbled', 'callback', 'gGame', 'cons', 'zyx', 'aBc']; const _enablePerf = false; @@ -24,22 +24,49 @@ perfSuite('Performance - fuzzyMatch', function () { const t1 = Date.now(); let count = 0; - for (const pattern of patterns) { - const patternLow = pattern.toLowerCase(); - for (const item of data) { - count += 1; - match(pattern, patternLow, 0, item, item.toLowerCase(), 0, false); + for (let i = 0; i < 2; i++) { + for (const pattern of patterns) { + const patternLow = pattern.toLowerCase(); + for (const item of data) { + count += 1; + match(pattern, patternLow, 0, item, item.toLowerCase(), 0, false); + } } } const d = Date.now() - t1; - console.log(name, `${d}ms, ${Math.round(count / d) * 15}ops/15ms`); + console.log(name, `${d}ms, ${Math.round(count / d) * 15}/15ms, ${Math.round(count / d)}/1ms`); }); } - // perfTest('matchesFuzzy', filters.matchesFuzzy); - // perfTest('fuzzyContiguousFilter', filters.fuzzyContiguousFilter); perfTest('fuzzyScore', filters.fuzzyScore); perfTest('fuzzyScoreGraceful', filters.fuzzyScoreGraceful); perfTest('fuzzyScoreGracefulAggressive', filters.fuzzyScoreGracefulAggressive); }); + +perfSuite('Performance - IFilter', function () { + + function perfTest(name: string, match: filters.IFilter) { + test(name, () => { + + const t1 = Date.now(); + let count = 0; + for (let i = 0; i < 2; i++) { + for (const pattern of patterns) { + for (const item of data) { + count += 1; + match(pattern, item); + } + } + } + const d = Date.now() - t1; + console.log(name, `${d}ms, ${Math.round(count / d) * 15}/15ms, ${Math.round(count / d)}/1ms`); + }); + } + + perfTest('matchesFuzzy', filters.matchesFuzzy); + perfTest('matchesFuzzy2', filters.matchesFuzzy2); + perfTest('matchesPrefix', filters.matchesPrefix); + perfTest('matchesContiguousSubString', filters.matchesContiguousSubString); + perfTest('matchesCamelCase', filters.matchesCamelCase); +}); diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 1517643681dc..867f536038bf 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, FuzzyScorer } from 'vs/base/common/filters'; +import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, FuzzyScorer, createMatches } from 'vs/base/common/filters'; function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number; }[]) { let r = filter(word, wordToMatchAgainst); @@ -204,18 +204,20 @@ suite('Filters', () => { assert.deepEqual(matchesWords('pu', 'Category: Git: Pull', true), [{ start: 15, end: 17 }]); }); - function assertMatches(pattern: string, word: string, decoratedWord: string, filter: FuzzyScorer, opts: { patternPos?: number, wordPos?: number, firstMatchCanBeWeak?: boolean } = {}) { + function assertMatches(pattern: string, word: string, decoratedWord: string | undefined, filter: FuzzyScorer, opts: { patternPos?: number, wordPos?: number, firstMatchCanBeWeak?: boolean } = {}) { let r = filter(pattern, pattern.toLowerCase(), opts.patternPos || 0, word, word.toLowerCase(), opts.wordPos || 0, opts.firstMatchCanBeWeak || false); - assert.ok(!decoratedWord === (!r || r[1].length === 0)); + assert.ok(!decoratedWord === !r); if (r) { - const [, matches] = r; + let matches = createMatches(r); + let actualWord = ''; let pos = 0; - for (let i = 0; i < matches.length; i++) { - let actual = matches[i]; - let expected = decoratedWord.indexOf('^', pos) - i; - assert.equal(actual, expected); - pos = expected + 1 + i; + for (const match of matches) { + actualWord += word.substring(pos, match.start); + actualWord += '^' + word.substring(match.start, match.end).split('').join('^'); + pos = match.end; } + actualWord += word.substring(pos); + assert.equal(actualWord, decoratedWord); } } @@ -448,4 +450,8 @@ suite('Filters', () => { assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGraceful); assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGracefulAggressive); }); + + test('List highlight filter: Not all characters from match are highlighterd #66923', () => { + assertMatches('foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_^f^o^o', fuzzyScore); + }); }); diff --git a/src/vs/base/test/common/hash.test.ts b/src/vs/base/test/common/hash.test.ts index 7a1815153c0e..b028bb901e0c 100644 --- a/src/vs/base/test/common/hash.test.ts +++ b/src/vs/base/test/common/hash.test.ts @@ -17,7 +17,7 @@ suite('Hash', () => { }); test('number', () => { - assert.equal(hash(1), hash(1.0)); + assert.equal(hash(1), hash(1)); assert.notEqual(hash(0), hash(1)); assert.notEqual(hash(1), hash(-1)); assert.notEqual(hash(0x12345678), hash(0x123456789)); @@ -39,7 +39,7 @@ suite('Hash', () => { test('object', () => { assert.equal(hash({}), hash({})); assert.equal(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar' })); - assert.equal(hash({ 'foo': 'bar', 'foo2': void 0 }), hash({ 'foo2': void 0, 'foo': 'bar' })); + assert.equal(hash({ 'foo': 'bar', 'foo2': undefined }), hash({ 'foo2': undefined, 'foo': 'bar' })); assert.notEqual(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar2' })); assert.notEqual(hash({}), hash([])); }); diff --git a/src/vs/base/test/common/history.test.ts b/src/vs/base/test/common/history.test.ts index b9dd4c000538..20c19068ab42 100644 --- a/src/vs/base/test/common/history.test.ts +++ b/src/vs/base/test/common/history.test.ts @@ -113,12 +113,12 @@ suite('History Navigator', () => { assert.equal(testObject.current(), undefined); }); - function toArray(historyNavigator: HistoryNavigator): string[] { - let result: string[] = []; + function toArray(historyNavigator: HistoryNavigator): Array { + let result: Array = []; historyNavigator.first(); if (historyNavigator.current()) { do { - result.push(historyNavigator.current()); + result.push(historyNavigator.current()!); } while (historyNavigator.next()); } return result; diff --git a/src/vs/base/test/common/json.test.ts b/src/vs/base/test/common/json.test.ts index fe20a2d1886e..8ff798b8c3ed 100644 --- a/src/vs/base/test/common/json.test.ts +++ b/src/vs/base/test/common/json.test.ts @@ -9,23 +9,23 @@ import { import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; function assertKinds(text: string, ...kinds: SyntaxKind[]): void { - var scanner = createScanner(text); - var kind: SyntaxKind; + let scanner = createScanner(text); + let kind: SyntaxKind; while ((kind = scanner.scan()) !== SyntaxKind.EOF) { assert.equal(kind, kinds.shift()); } assert.equal(kinds.length, 0); } function assertScanError(text: string, expectedKind: SyntaxKind, scanError: ScanError): void { - var scanner = createScanner(text); + let scanner = createScanner(text); scanner.scan(); assert.equal(scanner.getToken(), expectedKind); assert.equal(scanner.getTokenError(), scanError); } function assertValidParse(input: string, expected: any, options?: ParseOptions): void { - var errors: ParseError[] = []; - var actual = parse(input, errors, options); + let errors: ParseError[] = []; + let actual = parse(input, errors, options); if (errors.length !== 0) { assert(false, getParseErrorMessage(errors[0].error)); @@ -34,16 +34,16 @@ function assertValidParse(input: string, expected: any, options?: ParseOptions): } function assertInvalidParse(input: string, expected: any, options?: ParseOptions): void { - var errors: ParseError[] = []; - var actual = parse(input, errors, options); + let errors: ParseError[] = []; + let actual = parse(input, errors, options); assert(errors.length > 0); assert.deepEqual(actual, expected); } function assertTree(input: string, expected: any, expectedErrors: number[] = [], options?: ParseOptions): void { - var errors: ParseError[] = []; - var actual = parseTree(input, errors, options); + let errors: ParseError[] = []; + let actual = parseTree(input, errors, options); assert.deepEqual(errors.map(e => e.error, expected), expectedErrors); let checkParent = (node: Node) => { diff --git a/src/vs/base/test/common/jsonEdit.test.ts b/src/vs/base/test/common/jsonEdit.test.ts index e4a8b57d8741..7ace5de2fb7a 100644 --- a/src/vs/base/test/common/jsonEdit.test.ts +++ b/src/vs/base/test/common/jsonEdit.test.ts @@ -132,31 +132,31 @@ suite('JSON - edits', () => { test('remove item in array with one item', () => { let content = '[\n 1\n]'; - let edits = setProperty(content, [0], void 0, formatterOptions); + let edits = setProperty(content, [0], undefined, formatterOptions); assertEdit(content, edits, '[]'); }); test('remove item in the middle of the array', () => { let content = '[\n 1,\n 2,\n 3\n]'; - let edits = setProperty(content, [1], void 0, formatterOptions); + let edits = setProperty(content, [1], undefined, formatterOptions); assertEdit(content, edits, '[\n 1,\n 3\n]'); }); test('remove last item in the array', () => { let content = '[\n 1,\n 2,\n "bar"\n]'; - let edits = setProperty(content, [2], void 0, formatterOptions); + let edits = setProperty(content, [2], undefined, formatterOptions); assertEdit(content, edits, '[\n 1,\n 2\n]'); }); test('remove last item in the array if ends with comma', () => { let content = '[\n 1,\n "foo",\n "bar",\n]'; - let edits = setProperty(content, [2], void 0, formatterOptions); + let edits = setProperty(content, [2], undefined, formatterOptions); assertEdit(content, edits, '[\n 1,\n "foo"\n]'); }); test('remove last item in the array if there is a comment in the beginning', () => { let content = '// This is a comment\n[\n 1,\n "foo",\n "bar"\n]'; - let edits = setProperty(content, [2], void 0, formatterOptions); + let edits = setProperty(content, [2], undefined, formatterOptions); assertEdit(content, edits, '// This is a comment\n[\n 1,\n "foo"\n]'); }); diff --git a/src/vs/base/test/common/jsonFormatter.test.ts b/src/vs/base/test/common/jsonFormatter.test.ts index 7ac469a37ac6..a105c4791f18 100644 --- a/src/vs/base/test/common/jsonFormatter.test.ts +++ b/src/vs/base/test/common/jsonFormatter.test.ts @@ -8,15 +8,15 @@ import * as assert from 'assert'; suite('JSON - formatter', () => { function format(content: string, expected: string, insertSpaces = true) { - let range: Formatter.Range | undefined = void 0; - var rangeStart = content.indexOf('|'); - var rangeEnd = content.lastIndexOf('|'); + let range: Formatter.Range | undefined = undefined; + const rangeStart = content.indexOf('|'); + const rangeEnd = content.lastIndexOf('|'); if (rangeStart !== -1 && rangeEnd !== -1) { content = content.substring(0, rangeStart) + content.substring(rangeStart + 1, rangeEnd) + content.substring(rangeEnd + 1); range = { offset: rangeStart, length: rangeEnd - rangeStart }; } - var edits = Formatter.format(content, range, { tabSize: 2, insertSpaces: insertSpaces, eol: '\n' }); + const edits = Formatter.format(content, range, { tabSize: 2, insertSpaces: insertSpaces, eol: '\n' }); let lastEditOffset = content.length; for (let i = edits.length - 1; i >= 0; i--) { @@ -32,11 +32,11 @@ suite('JSON - formatter', () => { } test('object - single property', () => { - var content = [ + const content = [ '{"x" : 1}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "x": 1', '}' @@ -45,11 +45,11 @@ suite('JSON - formatter', () => { format(content, expected); }); test('object - multiple properties', () => { - var content = [ + const content = [ '{"x" : 1, "y" : "foo", "z" : true}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "x": 1,', ' "y": "foo",', @@ -60,11 +60,11 @@ suite('JSON - formatter', () => { format(content, expected); }); test('object - no properties ', () => { - var content = [ + const content = [ '{"x" : { }, "y" : {}}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "x": {},', ' "y": {}', @@ -74,11 +74,11 @@ suite('JSON - formatter', () => { format(content, expected); }); test('object - nesting', () => { - var content = [ + const content = [ '{"x" : { "y" : { "z" : { }}, "a": true}}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "x": {', ' "y": {', @@ -93,11 +93,11 @@ suite('JSON - formatter', () => { }); test('array - single items', () => { - var content = [ + const content = [ '["[]"]' ].join('\n'); - var expected = [ + const expected = [ '[', ' "[]"', ']' @@ -107,11 +107,11 @@ suite('JSON - formatter', () => { }); test('array - multiple items', () => { - var content = [ + const content = [ '[true,null,1.2]' ].join('\n'); - var expected = [ + const expected = [ '[', ' true,', ' null,', @@ -123,11 +123,11 @@ suite('JSON - formatter', () => { }); test('array - no items', () => { - var content = [ + const content = [ '[ ]' ].join('\n'); - var expected = [ + const expected = [ '[]' ].join('\n'); @@ -135,11 +135,11 @@ suite('JSON - formatter', () => { }); test('array - nesting', () => { - var content = [ + const content = [ '[ [], [ [ {} ], "a" ] ]' ].join('\n'); - var expected = [ + const expected = [ '[', ' [],', ' [', @@ -155,11 +155,11 @@ suite('JSON - formatter', () => { }); test('syntax errors', () => { - var content = [ + const content = [ '[ null 1.2 ]' ].join('\n'); - var expected = [ + const expected = [ '[', ' null 1.2', ']', @@ -169,7 +169,7 @@ suite('JSON - formatter', () => { }); test('empty lines', () => { - var content = [ + const content = [ '{', '"a": true,', '', @@ -177,7 +177,7 @@ suite('JSON - formatter', () => { '}', ].join('\n'); - var expected = [ + const expected = [ '{', '\t"a": true,', '\t"b": true', @@ -187,14 +187,14 @@ suite('JSON - formatter', () => { format(content, expected, false); }); test('single line comment', () => { - var content = [ + const content = [ '[ ', '//comment', '"foo", "bar"', '] ' ].join('\n'); - var expected = [ + const expected = [ '[', ' //comment', ' "foo",', @@ -205,14 +205,14 @@ suite('JSON - formatter', () => { format(content, expected); }); test('block line comment', () => { - var content = [ + const content = [ '[{', ' /*comment*/ ', '"foo" : true', '}] ' ].join('\n'); - var expected = [ + const expected = [ '[', ' {', ' /*comment*/', @@ -224,13 +224,13 @@ suite('JSON - formatter', () => { format(content, expected); }); test('single line comment on same line', () => { - var content = [ + const content = [ ' { ', ' "a": {}// comment ', ' } ' ].join('\n'); - var expected = [ + const expected = [ '{', ' "a": {} // comment ', '}', @@ -239,12 +239,12 @@ suite('JSON - formatter', () => { format(content, expected); }); test('single line comment on same line 2', () => { - var content = [ + const content = [ '{ //comment', '}' ].join('\n'); - var expected = [ + const expected = [ '{ //comment', '}' ].join('\n'); @@ -252,13 +252,13 @@ suite('JSON - formatter', () => { format(content, expected); }); test('block comment on same line', () => { - var content = [ + const content = [ '{ "a": {}, /*comment*/ ', ' /*comment*/ "b": {}, ', ' "c": {/*comment*/} } ', ].join('\n'); - var expected = [ + const expected = [ '{', ' "a": {}, /*comment*/', ' /*comment*/ "b": {},', @@ -270,14 +270,14 @@ suite('JSON - formatter', () => { }); test('block comment on same line advanced', () => { - var content = [ + const content = [ ' { "d": [', ' null', ' ] /*comment*/', ' ,"e": /*comment*/ [null] }', ].join('\n'); - var expected = [ + const expected = [ '{', ' "d": [', ' null', @@ -292,12 +292,12 @@ suite('JSON - formatter', () => { }); test('multiple block comments on same line', () => { - var content = [ + const content = [ '{ "a": {} /*comment*/, /*comment*/ ', ' /*comment*/ "b": {} /*comment*/ } ' ].join('\n'); - var expected = [ + const expected = [ '{', ' "a": {} /*comment*/, /*comment*/', ' /*comment*/ "b": {} /*comment*/', @@ -307,12 +307,12 @@ suite('JSON - formatter', () => { format(content, expected); }); test('multiple mixed comments on same line', () => { - var content = [ + const content = [ '[ /*comment*/ /*comment*/ // comment ', ']' ].join('\n'); - var expected = [ + const expected = [ '[ /*comment*/ /*comment*/ // comment ', ']' ].join('\n'); @@ -321,13 +321,13 @@ suite('JSON - formatter', () => { }); test('range', () => { - var content = [ + const content = [ '{ "a": {},', '|"b": [null, null]|', '} ' ].join('\n'); - var expected = [ + const expected = [ '{ "a": {},', '"b": [', ' null,', @@ -340,14 +340,14 @@ suite('JSON - formatter', () => { }); test('range with existing indent', () => { - var content = [ + const content = [ '{ "a": {},', ' |"b": [null],', '"c": {}', '}|' ].join('\n'); - var expected = [ + const expected = [ '{ "a": {},', ' "b": [', ' null', @@ -360,14 +360,14 @@ suite('JSON - formatter', () => { }); test('range with existing indent - tabs', () => { - var content = [ + const content = [ '{ "a": {},', '| "b": [null], ', '"c": {}', '} | ' ].join('\n'); - var expected = [ + const expected = [ '{ "a": {},', '\t"b": [', '\t\tnull', @@ -381,7 +381,7 @@ suite('JSON - formatter', () => { test('block comment none-line breaking symbols', () => { - var content = [ + const content = [ '{ "a": [ 1', '/* comment */', ', 2', @@ -394,7 +394,7 @@ suite('JSON - formatter', () => { '}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "a": [', ' 1', @@ -413,7 +413,7 @@ suite('JSON - formatter', () => { format(content, expected); }); test('line comment after none-line breaking symbols', () => { - var content = [ + const content = [ '{ "a":', '// comment', 'null,', @@ -424,7 +424,7 @@ suite('JSON - formatter', () => { '}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "a":', ' // comment', diff --git a/src/vs/base/test/common/keyCodes.test.ts b/src/vs/base/test/common/keyCodes.test.ts index 937932ddba66..672c6c9cee1d 100644 --- a/src/vs/base/test/common/keyCodes.test.ts +++ b/src/vs/base/test/common/keyCodes.test.ts @@ -9,13 +9,13 @@ import { OperatingSystem } from 'vs/base/common/platform'; suite('keyCodes', () => { - function testBinaryEncoding(expected: Keybinding, k: number, OS: OperatingSystem): void { + function testBinaryEncoding(expected: Keybinding | null, k: number, OS: OperatingSystem): void { assert.deepEqual(createKeybinding(k, OS), expected); } test('MAC binary encoding', () => { - function test(expected: Keybinding, k: number): void { + function test(expected: Keybinding | null, k: number): void { testBinaryEncoding(expected, k, OperatingSystem.Macintosh); } @@ -57,7 +57,7 @@ suite('keyCodes', () => { [OperatingSystem.Linux, OperatingSystem.Windows].forEach((OS) => { - function test(expected: Keybinding, k: number): void { + function test(expected: Keybinding | null, k: number): void { testBinaryEncoding(expected, k, OS); } diff --git a/src/vs/base/test/common/labels.test.ts b/src/vs/base/test/common/labels.test.ts index 62bb9f979095..d30c1fab6e0e 100644 --- a/src/vs/base/test/common/labels.test.ts +++ b/src/vs/base/test/common/labels.test.ts @@ -55,7 +55,7 @@ suite('Labels', () => { assert.deepEqual(labels.shorten(['a\\b\\c', 'd\\b\\C']), ['…\\c', '…\\C']); // empty or null - assert.deepEqual(labels.shorten(['', null]), ['.\\', null]); + assert.deepEqual(labels.shorten(['', null!]), ['.\\', null]); assert.deepEqual(labels.shorten(['a', 'a\\b', 'a\\b\\c', 'd\\b\\c', 'd\\b']), ['a', 'a\\b', 'a\\b\\c', 'd\\b\\c', 'd\\b']); assert.deepEqual(labels.shorten(['a', 'a\\b', 'b']), ['a', 'a\\b', 'b']); @@ -103,7 +103,7 @@ suite('Labels', () => { assert.deepEqual(labels.shorten(['a/b/c', 'd/b/C']), ['…/c', '…/C']); // empty or null - assert.deepEqual(labels.shorten(['', null]), ['./', null]); + assert.deepEqual(labels.shorten(['', null!]), ['./', null]); assert.deepEqual(labels.shorten(['a', 'a/b', 'a/b/c', 'd/b/c', 'd/b']), ['a', 'a/b', 'a/b/c', 'd/b/c', 'd/b']); assert.deepEqual(labels.shorten(['a', 'a/b', 'b']), ['a', 'a/b', 'b']); @@ -164,4 +164,19 @@ suite('Labels', () => { assert.equal(labels.getBaseLabel('c:\\some\\folder\\file.txt'), 'file.txt'); assert.equal(labels.getBaseLabel('c:\\some\\folder'), 'folder'); }); + + test('mnemonicButtonLabel', () => { + assert.equal(labels.mnemonicButtonLabel('Hello World'), 'Hello World'); + assert.equal(labels.mnemonicButtonLabel(''), ''); + if (platform.isWindows) { + assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello && World'); + assert.equal(labels.mnemonicButtonLabel('Do &¬ Save & Continue'), 'Do ¬ Save && Continue'); + } else if (platform.isMacintosh) { + assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello & World'); + assert.equal(labels.mnemonicButtonLabel('Do &¬ Save & Continue'), 'Do not Save & Continue'); + } else { + assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello & World'); + assert.equal(labels.mnemonicButtonLabel('Do &¬ Save & Continue'), 'Do _not Save & Continue'); + } + }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index 31da426ea881..c083c3bdb89c 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.test.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IDisposable, dispose, ReferenceCollection } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, ReferenceCollection, Disposable as DisposableBase, toDisposable } from 'vs/base/common/lifecycle'; class Disposable implements IDisposable { isDisposed = false; @@ -49,6 +49,38 @@ suite('Lifecycle', () => { }); }); +suite('DisposableBase', () => { + test('register should not leak if object has already been disposed', () => { + let aCount = 0; + let bCount = 0; + + const disposable = new class extends DisposableBase { + register(other: IDisposable) { + this._register(other); + } + }; + + disposable.register(toDisposable(() => ++aCount)); + + assert.strictEqual(aCount, 0); + assert.strictEqual(bCount, 0); + + disposable.dispose(); + assert.strictEqual(aCount, 1); + assert.strictEqual(bCount, 0); + + // Any newly added disposables should be disposed of immediately + disposable.register(toDisposable(() => ++bCount)); + assert.strictEqual(aCount, 1); + assert.strictEqual(bCount, 1); + + // Further dispose calls should have no effect + disposable.dispose(); + assert.strictEqual(aCount, 1); + assert.strictEqual(bCount, 1); + }); +}); + suite('Reference Collection', () => { class Collection extends ReferenceCollection { private _count = 0; diff --git a/src/vs/base/test/common/linkedList.test.ts b/src/vs/base/test/common/linkedList.test.ts index d1178af42385..efaa2ac3dda2 100644 --- a/src/vs/base/test/common/linkedList.test.ts +++ b/src/vs/base/test/common/linkedList.test.ts @@ -61,15 +61,7 @@ suite('LinkedList', function () { list.push('far'); list.push('boo'); - assert.deepEqual( - list.toArray(), - [ - 'foo', - 'bar', - 'far', - 'boo', - ] - ); + assertElements(list, 'foo', 'bar', 'far', 'boo'); }); test('unshift/Iter', () => { @@ -109,15 +101,26 @@ suite('LinkedList', function () { list.unshift('bar'); list.unshift('far'); list.unshift('boo'); + assertElements(list, 'boo', 'far', 'bar', 'foo'); + }); + + test('pop/unshift', function () { + let list = new LinkedList(); + list.push('a'); + list.push('b'); + + assertElements(list, 'a', 'b'); + + let a = list.shift(); + assert.equal(a, 'a'); + assertElements(list, 'b'); + + list.unshift('a'); + assertElements(list, 'a', 'b'); + + let b = list.pop(); + assert.equal(b, 'b'); + assertElements(list, 'a'); - assert.deepEqual( - list.toArray(), - [ - 'boo', - 'far', - 'bar', - 'foo', - ] - ); }); }); diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index c87a62532c1c..6251373842c1 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -140,7 +140,7 @@ suite('Map', () => { assert.strictEqual(cache.size, 5); assert.deepStrictEqual(cache.keys(), [3, 4, 5, 6, 7]); let values: number[] = []; - [3, 4, 5, 6, 7].forEach(key => values.push(cache.get(key))); + [3, 4, 5, 6, 7].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [3, 4, 5, 6, 7]); }); @@ -155,7 +155,7 @@ suite('Map', () => { cache.peek(4); assert.deepStrictEqual(cache.keys(), [1, 2, 4, 5, 3]); let values: number[] = []; - [1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key))); + [1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [1, 2, 3, 4, 5]); }); @@ -177,7 +177,7 @@ suite('Map', () => { assert.deepEqual(cache.size, 15); let values: number[] = []; for (let i = 6; i <= 20; i++) { - values.push(cache.get(i)); + values.push(cache.get(i)!); assert.strictEqual(cache.get(i), i); } assert.deepStrictEqual(cache.values(), values); @@ -194,7 +194,7 @@ suite('Map', () => { assert.strictEqual(cache.size, 5); assert.deepStrictEqual(cache.keys(), [7, 8, 9, 10, 11]); let values: number[] = []; - cache.keys().forEach(key => values.push(cache.get(key))); + cache.keys().forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [7, 8, 9, 10, 11]); assert.deepStrictEqual(cache.values(), values); }); @@ -420,25 +420,25 @@ suite('Map', () => { let item: IteratorResult; let iter = map.findSuperstr('/user'); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, 2); assert.equal(item.done, false); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, 1); assert.equal(item.done, false); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, 3); assert.equal(item.done, false); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, undefined); assert.equal(item.done, true); iter = map.findSuperstr('/usr'); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, 4); assert.equal(item.done, false); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, undefined); assert.equal(item.done, true); @@ -588,7 +588,7 @@ suite('Map', () => { test('mapToSerializable / serializableToMap', function () { const map = new Map(); map.set('1', 'foo'); - map.set('2', null); + map.set('2', null!); map.set('3', 'bar'); const map2 = serializableToMap(mapToSerializable(map)); diff --git a/src/vs/base/test/common/marshalling.test.ts b/src/vs/base/test/common/marshalling.test.ts index 5dad4b796989..4f6934aec018 100644 --- a/src/vs/base/test/common/marshalling.test.ts +++ b/src/vs/base/test/common/marshalling.test.ts @@ -20,9 +20,9 @@ suite('Marshalling', () => { }); test('URI', () => { - let value = URI.from({ scheme: 'file', authority: 'server', path: '/shares/c#files', query: 'q', fragment: 'f' }); - let raw = stringify(value); - let clone = parse(raw); + const value = URI.from({ scheme: 'file', authority: 'server', path: '/shares/c#files', query: 'q', fragment: 'f' }); + const raw = stringify(value); + const clone = parse(raw); assert.equal(value.scheme, clone.scheme); assert.equal(value.authority, clone.authority); @@ -32,7 +32,7 @@ suite('Marshalling', () => { }); test('Bug 16793:# in folder name => mirror models get out of sync', () => { - var uri1 = URI.file('C:\\C#\\file.txt'); + const uri1 = URI.file('C:\\C#\\file.txt'); assert.equal(parse(stringify(uri1)).toString(), uri1.toString()); }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/objects.test.ts b/src/vs/base/test/common/objects.test.ts index 33d3a4382973..dacc9940e14c 100644 --- a/src/vs/base/test/common/objects.test.ts +++ b/src/vs/base/test/common/objects.test.ts @@ -193,13 +193,13 @@ suite('Objects', () => { three: { 3: true }, - four: void 0 + four: undefined }; diff = objects.distinct(base, obj); assert.deepEqual(diff, { one: null, - four: void 0 + four: undefined }); obj = { diff --git a/src/vs/base/test/common/octicon.test.ts b/src/vs/base/test/common/octicon.test.ts index 32f239984971..b25532f353bb 100644 --- a/src/vs/base/test/common/octicon.test.ts +++ b/src/vs/base/test/common/octicon.test.ts @@ -8,7 +8,7 @@ import { matchesFuzzyOcticonAware, parseOcticons } from 'vs/base/common/octicon' export interface IOcticonFilter { // Returns null if word doesn't match. - (query: string, target: { text: string, octiconOffsets?: number[] }): IMatch[]; + (query: string, target: { text: string, octiconOffsets?: number[] }): IMatch[] | null; } function filterOk(filter: IOcticonFilter, word: string, target: { text: string, octiconOffsets?: number[] }, highlights?: { start: number; end: number; }[]) { diff --git a/src/vs/base/test/common/paging.test.ts b/src/vs/base/test/common/paging.test.ts index f3d763b0b07d..0fe96e79836c 100644 --- a/src/vs/base/test/common/paging.test.ts +++ b/src/vs/base/test/common/paging.test.ts @@ -8,7 +8,7 @@ import { IPager, PagedModel } from 'vs/base/common/paging'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { isPromiseCanceledError, canceled } from 'vs/base/common/errors'; -function getPage(pageIndex: number, cancellationToken: CancellationToken): Thenable { +function getPage(pageIndex: number, cancellationToken: CancellationToken): Promise { if (cancellationToken.isCancellationRequested) { return Promise.reject(canceled()); } @@ -21,9 +21,9 @@ class TestPager implements IPager { readonly firstPage = [0, 1, 2, 3, 4]; readonly pageSize = 5; readonly total = 100; - readonly getPage: (pageIndex: number, cancellationToken: CancellationToken) => Thenable; + readonly getPage: (pageIndex: number, cancellationToken: CancellationToken) => Promise; - constructor(getPageFn?: (pageIndex: number, cancellationToken: CancellationToken) => Thenable) { + constructor(getPageFn?: (pageIndex: number, cancellationToken: CancellationToken) => Promise) { this.getPage = getPageFn || getPage; } } diff --git a/src/vs/base/test/common/paths.test.ts b/src/vs/base/test/common/paths.test.ts index 384cf4aeb771..0cbe7273a9f3 100644 --- a/src/vs/base/test/common/paths.test.ts +++ b/src/vs/base/test/common/paths.test.ts @@ -8,21 +8,34 @@ import * as platform from 'vs/base/common/platform'; suite('Paths', () => { - test('dirname', () => { - assert.equal(paths.dirname('foo/bar'), 'foo'); - assert.equal(paths.dirname('foo\\bar'), 'foo'); - assert.equal(paths.dirname('/foo/bar'), '/foo'); - assert.equal(paths.dirname('\\foo\\bar'), '\\foo'); - assert.equal(paths.dirname('/foo'), '/'); - assert.equal(paths.dirname('\\foo'), '\\'); - assert.equal(paths.dirname('/'), '/'); - assert.equal(paths.dirname('\\'), '\\'); - assert.equal(paths.dirname('foo'), '.'); - assert.equal(paths.dirname('/folder/'), '/'); - if (platform.isWindows) { - assert.equal(paths.dirname('c:\\some\\file.txt'), 'c:\\some'); - assert.equal(paths.dirname('c:\\some'), 'c:\\'); + function assertDirname(path: string, expected: string, win = false) { + const actual = paths.dirname(path, win ? '\\' : '/'); + + if (actual !== expected) { + assert.fail(`${path}: expected: ${expected}, ours: ${actual}`); } + } + + test('dirname', () => { + assertDirname('foo/bar', 'foo'); + assertDirname('foo\\bar', 'foo', true); + assertDirname('/foo/bar', '/foo'); + assertDirname('\\foo\\bar', '\\foo', true); + assertDirname('/foo', '/'); + assertDirname('\\foo', '\\', true); + assertDirname('/', '/'); + assertDirname('\\', '\\', true); + assertDirname('foo', '.'); + assertDirname('f', '.'); + assertDirname('f/', '.'); + assertDirname('/folder/', '/'); + assertDirname('c:\\some\\file.txt', 'c:\\some', true); + assertDirname('c:\\some', 'c:\\', true); + assertDirname('c:\\', 'c:\\', true); + assertDirname('c:', 'c:', true); + assertDirname('\\\\server\\share\\some\\path', '\\\\server\\share\\some', true); + assertDirname('\\\\server\\share\\some', '\\\\server\\share\\', true); + assertDirname('\\\\server\\share\\', '\\\\server\\share\\', true); }); test('normalize', () => { diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 7430408ded71..1236356ecf6f 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -42,20 +42,23 @@ suite('Resources', () => { test('dirname', () => { if (isWindows) { - 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/'); + 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/'); + assert.equal(dirname(URI.file('c:\\'))!.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'); - assert.equal(dirname(URI.file('/some/file')).toString(), 'file:///some'); + assert.equal(dirname(URI.file('/some/file/test.txt'))!.toString(), 'file:///some/file'); + assert.equal(dirname(URI.file('/some/file/'))!.toString(), 'file:///some'); + assert.equal(dirname(URI.file('/some/file'))!.toString(), 'file:///some'); } - assert.equal(dirname(URI.parse('foo://a/some/file/test.txt')).toString(), 'foo://a/some/file'); - assert.equal(dirname(URI.parse('foo://a/some/file/')).toString(), 'foo://a/some'); - assert.equal(dirname(URI.parse('foo://a/some/file')).toString(), 'foo://a/some'); - assert.equal(dirname(URI.parse('foo://a/some')).toString(), 'foo://a/'); + assert.equal(dirname(URI.parse('foo://a/some/file/test.txt'))!.toString(), 'foo://a/some/file'); + assert.equal(dirname(URI.parse('foo://a/some/file/'))!.toString(), 'foo://a/some'); + assert.equal(dirname(URI.parse('foo://a/some/file'))!.toString(), 'foo://a/some'); + assert.equal(dirname(URI.parse('foo://a/some'))!.toString(), 'foo://a/'); + assert.equal(dirname(URI.parse('foo://a/'))!.toString(), 'foo://a/'); + assert.equal(dirname(URI.parse('foo://a'))!.toString(), 'foo://a'); // does not explode (https://github.com/Microsoft/vscode/issues/41987) dirname(URI.from({ scheme: 'file', authority: '/users/someone/portal.h' })); @@ -135,6 +138,7 @@ suite('Resources', () => { assert.equal(normalizePath(URI.file('/foo/foo/../../bar')).toString(), 'file:///bar'); assert.equal(normalizePath(URI.file('/foo/foo/./../../bar')).toString(), 'file:///bar'); assert.equal(normalizePath(URI.file('/foo/foo/./../some/../bar')).toString(), 'file:///foo/bar'); + assert.equal(normalizePath(URI.file('/f')).toString(), 'file:///f'); } assert.equal(normalizePath(URI.parse('foo://a/foo/./bar')).toString(), 'foo://a/foo/bar'); assert.equal(normalizePath(URI.parse('foo://a/foo/.')).toString(), 'foo://a/foo'); @@ -145,6 +149,8 @@ suite('Resources', () => { assert.equal(normalizePath(URI.parse('foo://a/foo/foo/../../bar')).toString(), 'foo://a/bar'); assert.equal(normalizePath(URI.parse('foo://a/foo/foo/./../../bar')).toString(), 'foo://a/bar'); assert.equal(normalizePath(URI.parse('foo://a/foo/foo/./../some/../bar')).toString(), 'foo://a/foo/bar'); + assert.equal(normalizePath(URI.parse('foo://a')).toString(), 'foo://a'); + assert.equal(normalizePath(URI.parse('foo://a/')).toString(), 'foo://a/'); }); test('isAbsolute', () => { @@ -205,7 +211,7 @@ suite('Resources', () => { assert.equal(isEqualOrParent(fileURI5, fileURI5, true), true, '16'); }); - function assertMalformedFileUri(path: string, expected: string) { + function assertMalformedFileUri(path: string, expected: string | undefined) { const old = setUriThrowOnMissingScheme(false); const newURI = isMalformedFileUri(URI.parse(path)); assert.equal(newURI && newURI.toString(), expected); @@ -221,10 +227,10 @@ suite('Resources', () => { } assertMalformedFileUri('/foo/bar', 'file:///foo/bar'); - assertMalformedFileUri('file:///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); + assertMalformedFileUri('file:///foo/bar', undefined); + assertMalformedFileUri('file:///c%3A/foo/bar', undefined); + assertMalformedFileUri('file://localhost/c$/devel/test', undefined); + assertMalformedFileUri('foo://dadie/foo/bar', undefined); + assertMalformedFileUri('foo:///dadie/foo/bar', undefined); }); }); diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index ff1146b0c6c9..f548561a0eae 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -320,7 +320,7 @@ suite('Strings', () => { }); test('fuzzyContains', () => { - assert.ok(!strings.fuzzyContains(void 0, null)); + assert.ok(!strings.fuzzyContains((undefined)!, null!)); assert.ok(strings.fuzzyContains('hello world', 'h')); assert.ok(!strings.fuzzyContains('hello world', 'q')); assert.ok(strings.fuzzyContains('hello world', 'hw')); diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index aab7049b3c88..15d6b6be01f8 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -73,15 +73,15 @@ suite('URI', () => { assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: '', fragment: 'test=true' }).toString(true), 'http://a-test-site.com/#test=true'); assert.equal(URI.from({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(true), 'http:/api/files/test.me?t=1234'); - var value = URI.parse('file://shares/pröjects/c%23/#l12'); + const value = URI.parse('file://shares/pröjects/c%23/#l12'); assert.equal(value.authority, 'shares'); assert.equal(value.path, '/pröjects/c#/'); assert.equal(value.fragment, 'l12'); assert.equal(value.toString(), 'file://shares/pr%C3%B6jects/c%23/#l12'); assert.equal(value.toString(true), 'file://shares/pröjects/c%23/#l12'); - var uri2 = URI.parse(value.toString(true)); - var uri3 = URI.parse(value.toString()); + const uri2 = URI.parse(value.toString(true)); + const uri3 = URI.parse(value.toString()); assert.equal(uri2.authority, uri3.authority); assert.equal(uri2.path, uri3.path); assert.equal(uri2.query, uri3.query); @@ -91,9 +91,9 @@ suite('URI', () => { test('with, identity', () => { let uri = URI.parse('foo:bar/path'); - let uri2 = uri.with(null); + let uri2 = uri.with(null!); assert.ok(uri === uri2); - uri2 = uri.with(undefined); + uri2 = uri.with(undefined!); assert.ok(uri === uri2); uri2 = uri.with({}); assert.ok(uri === uri2); @@ -130,7 +130,7 @@ suite('URI', () => { }); test('parse', () => { - var value = URI.parse('http:/api/files/test.me?t=1234'); + let value = URI.parse('http:/api/files/test.me?t=1234'); assert.equal(value.scheme, 'http'); assert.equal(value.authority, ''); assert.equal(value.path, '/api/files/test.me'); @@ -238,7 +238,7 @@ suite('URI', () => { test('URI#file, win-speciale', () => { if (isWindows) { - var value = URI.file('c:\\test\\drive'); + let value = URI.file('c:\\test\\drive'); assert.equal(value.path, '/c:/test/drive'); assert.equal(value.toString(), 'file:///c%3A/test/drive'); @@ -298,7 +298,7 @@ suite('URI', () => { test('URI#file, always slash', () => { - var value = URI.file('a.file'); + let value = URI.file('a.file'); assert.equal(value.scheme, 'file'); assert.equal(value.authority, ''); assert.equal(value.path, '/a.file'); @@ -312,12 +312,12 @@ suite('URI', () => { }); test('URI.toString, only scheme and query', () => { - var value = URI.parse('stuff:?qüery'); + const value = URI.parse('stuff:?qüery'); assert.equal(value.toString(), 'stuff:?q%C3%BCery'); }); test('URI#toString, upper-case percent espaces', () => { - var value = URI.parse('file://sh%c3%a4res/path'); + const value = URI.parse('file://sh%c3%a4res/path'); assert.equal(value.toString(), 'file://sh%C3%A4res/path'); }); @@ -328,12 +328,12 @@ suite('URI', () => { test('URI#toString, escape all the bits', () => { - var value = URI.file('/Users/jrieken/Code/_samples/18500/Mödel + Other Thîngß/model.js'); + const value = URI.file('/Users/jrieken/Code/_samples/18500/Mödel + Other Thîngß/model.js'); assert.equal(value.toString(), 'file:///Users/jrieken/Code/_samples/18500/M%C3%B6del%20%2B%20Other%20Th%C3%AEng%C3%9F/model.js'); }); test('URI#toString, don\'t encode port', () => { - var value = URI.parse('http://localhost:8080/far'); + let value = URI.parse('http://localhost:8080/far'); assert.equal(value.toString(), 'http://localhost:8080/far'); value = URI.from({ scheme: 'http', authority: 'löcalhost:8080', path: '/far', query: undefined, fragment: undefined }); @@ -341,7 +341,7 @@ suite('URI', () => { }); test('URI#toString, user information in authority', () => { - var value = URI.parse('http://foo:bar@localhost/far'); + let value = URI.parse('http://foo:bar@localhost/far'); assert.equal(value.toString(), 'http://foo:bar@localhost/far'); value = URI.parse('http://foo@localhost/far'); @@ -359,11 +359,11 @@ suite('URI', () => { test('correctFileUriToFilePath2', () => { - var test = (input: string, expected: string) => { + const test = (input: string, expected: string) => { expected = normalize(expected, true); - var value = URI.parse(input); + const value = URI.parse(input); assert.equal(value.fsPath, expected, 'Result for ' + input); - var value2 = URI.file(value.fsPath); + const value2 = URI.file(value.fsPath); assert.equal(value2.fsPath, expected, 'Result for ' + input); assert.equal(value.toString(), value2.toString()); }; @@ -431,7 +431,7 @@ suite('URI', () => { test('URI - (de)serialize', function () { - var values = [ + const values = [ URI.parse('http://localhost:8080/far'), URI.file('c:\\test with %25\\c#code'), URI.file('\\\\shäres\\path\\c#\\plugin.json'), diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index abd5090501d9..aa441f7e9b6a 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -7,7 +7,7 @@ import * as paths from 'vs/base/common/paths'; import { URI } from 'vs/base/common/uri'; import { canceled } from 'vs/base/common/errors'; -export type ValueCallback = (value: T | Thenable) => void; +export type ValueCallback = (value: T | Promise) => void; export class DeferredPromise { diff --git a/src/vs/base/test/common/uuid.test.ts b/src/vs/base/test/common/uuid.test.ts index f776a951278e..37b7f832b3da 100644 --- a/src/vs/base/test/common/uuid.test.ts +++ b/src/vs/base/test/common/uuid.test.ts @@ -7,16 +7,16 @@ import * as uuid from 'vs/base/common/uuid'; suite('UUID', () => { test('generation', () => { - var asHex = uuid.v4().asHex(); + const asHex = uuid.v4().asHex(); assert.equal(asHex.length, 36); assert.equal(asHex[14], '4'); assert.ok(asHex[19] === '8' || asHex[19] === '9' || asHex[19] === 'a' || asHex[19] === 'b'); }); test('parse', () => { - var id = uuid.v4(); - var asHext = id.asHex(); - var id2 = uuid.parse(asHext); + const id = uuid.v4(); + const asHext = id.asHex(); + const id2 = uuid.parse(asHext); assert.equal(id.asHex(), id2.asHex()); }); }); diff --git a/src/vs/base/test/common/winjs.polyfill.promise.test.ts b/src/vs/base/test/common/winjs.polyfill.promise.test.ts deleted file mode 100644 index 0032b7e9dfe5..000000000000 --- a/src/vs/base/test/common/winjs.polyfill.promise.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; -import { Promise as WinJSPromise } from 'vs/base/common/winjs.base'; -import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; - -suite('Polyfill Promise', function () { - - test('sync-resolve, NativePromise', function () { - // native promise behaviour - const actual: string[] = []; - const promise = new Promise(resolve => { - actual.push('inCtor'); - resolve(null); - }).then(() => actual.push('inThen')); - actual.push('afterCtor'); - return promise.then(() => { - assert.deepEqual(actual, ['inCtor', 'afterCtor', 'inThen']); - }); - }); - - test('sync-resolve, WinJSPromise', function () { - - // winjs promise behaviour - const actual: string[] = []; - const promise = new WinJSPromise(resolve => { - actual.push('inCtor'); - resolve(null); - }).then(() => actual.push('inThen')); - actual.push('afterCtor'); - return promise.then(() => { - assert.deepEqual(actual, ['inCtor', 'inThen', 'afterCtor']); - }); - }); - - test('sync-resolve, PolyfillPromise', function () { - - // winjs promise behaviour - const actual: string[] = []; - const promise = new PolyfillPromise(resolve => { - actual.push('inCtor'); - resolve(null); - }).then(() => actual.push('inThen')); - actual.push('afterCtor'); - return promise.then(() => { - assert.deepEqual(actual, ['inCtor', 'afterCtor', 'inThen']); - }); - }); - - test('sync-then, NativePromise', function () { - const actual: string[] = []; - const promise = Promise.resolve(123).then(() => actual.push('inThen')); - actual.push('afterThen'); - return promise.then(() => { - assert.deepEqual(actual, ['afterThen', 'inThen']); - }); - }); - - test('sync-then, WinJSPromise', function () { - const actual: string[] = []; - const promise = WinJSPromise.as(123).then(() => actual.push('inThen')); - actual.push('afterThen'); - return promise.then(() => { - assert.deepEqual(actual, ['inThen', 'afterThen']); - }); - }); - - test('sync-then, PolyfillPromise', function () { - const actual: string[] = []; - const promise = PolyfillPromise.resolve(123).then(() => actual.push('inThen')); - actual.push('afterThen'); - return promise.then(() => { - assert.deepEqual(actual, ['afterThen', 'inThen']); - }); - }); - - test('PolyfillPromise, executor has two params', function () { - return new PolyfillPromise(function () { - assert.equal(arguments.length, 2); - assert.equal(typeof arguments[0], 'function'); - assert.equal(typeof arguments[1], 'function'); - - arguments[0](); - }); - }); - - test('Promises polyfill does not support chaining then and catch #57722', function () { - return PolyfillPromise.resolve(1).then(function (x) { return x + 1; }).then(function (x) { - assert.equal(x, 2); - }); - }); - - // run the same tests for the native and polyfill promise - ([Promise, PolyfillPromise]).forEach(PromiseCtor => { - - test(PromiseCtor.name + ', resolved value', function () { - return new PromiseCtor((resolve: Function) => resolve(1)).then((value: number) => assert.equal(value, 1)); - }); - - test(PromiseCtor.name + ', rejected value', function () { - return new PromiseCtor((_: Function, reject: Function) => reject(1)).then(null, (value: number) => assert.equal(value, 1)); - }); - - test(PromiseCtor.name + ', catch', function () { - return new PromiseCtor((_: Function, reject: Function) => reject(1)).catch((value: number) => assert.equal(value, 1)); - }); - - test(PromiseCtor.name + ', static-resolve', function () { - return PromiseCtor.resolve(42).then((value: number) => assert.equal(value, 42)); - }); - - test(PromiseCtor.name + ', static-reject', function () { - return PromiseCtor.reject(42).then(null, (value: number) => assert.equal(value, 42)); - }); - - test(PromiseCtor.name + ', static-all, 1', function () { - return PromiseCtor.all([ - PromiseCtor.resolve(1), - PromiseCtor.resolve(2) - ]).then((values: number[]) => { - assert.deepEqual(values, [1, 2]); - }); - }); - - test(PromiseCtor.name + ', static-all, 2', function () { - return PromiseCtor.all([ - PromiseCtor.resolve(1), - 3, - PromiseCtor.resolve(2) - ]).then((values: number[]) => { - assert.deepEqual(values, [1, 3, 2]); - }); - }); - - test(PromiseCtor.name + ', static-all, 3', function () { - return PromiseCtor.all([ - PromiseCtor.resolve(1), - PromiseCtor.reject(13), - PromiseCtor.reject(12), - ]).catch((values: number) => { - assert.deepEqual(values, 13); - }); - }); - - test(PromiseCtor.name + ', static-race, 1', function () { - return PromiseCtor.race([ - PromiseCtor.resolve(1), - PromiseCtor.resolve(2), - ]).then((value: number) => { - assert.deepEqual(value, 1); - }); - }); - - test(PromiseCtor.name + ', static-race, 2', function () { - return PromiseCtor.race([ - PromiseCtor.reject(-1), - PromiseCtor.resolve(2), - ]).catch((value: number) => { - assert.deepEqual(value, -1); - }); - }); - - test(PromiseCtor.name + ', static-race, 3', function () { - return PromiseCtor.race([ - PromiseCtor.resolve(1), - PromiseCtor.reject(2), - ]).then((value: number) => { - assert.deepEqual(value, 1); - }); - }); - - test(PromiseCtor.name + ', throw in ctor', function () { - return new PromiseCtor(() => { - throw new Error('sooo bad'); - }).catch((err: Error) => { - assert.equal(err.message, 'sooo bad'); - }); - }); - - }); -}); diff --git a/src/vs/base/test/common/winjs.promise.test.ts b/src/vs/base/test/common/winjs.promise.test.ts deleted file mode 100644 index fd0244390ca1..000000000000 --- a/src/vs/base/test/common/winjs.promise.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; -import * as winjs from 'vs/base/common/winjs.base'; - -suite('WinJS and ES6 Promises', function () { - - test('Promise.resolve', () => { - let resolveTPromise; - const tPromise = new winjs.Promise((c, e) => { - resolveTPromise = c; - }); - - const es6Promise = Promise.resolve(tPromise); - - const done = es6Promise.then(function (result) { - assert.equal(result, 'passed'); - }); - - resolveTPromise('passed'); - - return done; - }); - - test('new Promise', function () { - let resolveTPromise; - const tPromise = new winjs.Promise((c, e) => { - resolveTPromise = c; - }); - - const es6Promise = new Promise(function (c, e) { - c(tPromise); - }); - - const done = es6Promise.then(function (result) { - assert.equal(result, 'passed'); - }); - - resolveTPromise('passed'); - - return done; - }); - - test('1. Uncaught TypeError: this._state.then is not a function', () => { - let p1 = winjs.Promise.wrap(new Promise(function (c, e) { c(1); })); - Promise.all([p1]); - }); - - test('2. Uncaught TypeError: this._state.then is not a function', () => { - let p1 = winjs.Promise.wrap(new Promise(function (c, e) { c(1); })); - let thenFunc = p1.then.bind(p1); - setTimeout(() => { - thenFunc(() => { }); - }, 0); - }); - - test('3. Uncaught TypeError: this._state.then is not a function', () => { - let c; - let p1 = new winjs.Promise(function (_c, e) { c = _c; }); - let thenFunc = p1.then.bind(p1); - setTimeout(() => { - c(1); - thenFunc(() => { }); - }, 0); - }); -}); diff --git a/src/vs/base/test/node/config.test.ts b/src/vs/base/test/node/config.test.ts index 293645bff845..41267d64eeab 100644 --- a/src/vs/base/test/node/config.test.ts +++ b/src/vs/base/test/node/config.test.ts @@ -141,7 +141,7 @@ suite('Config', () => { testFile('config', 'config.json').then(res => { fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }'); - let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile, { changeBufferDelay: 100, onError: console.error, defaultConfig: void 0 }); + let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile, { changeBufferDelay: 100, onError: console.error, defaultConfig: { foo: 'bar' } }); watcher.getConfig(); // ensure we are in sync fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "changed" }'); diff --git a/src/vs/base/test/node/console.test.ts b/src/vs/base/test/node/console.test.ts index 8e3753afc0b6..71ad0ca80a6a 100644 --- a/src/vs/base/test/node/console.test.ts +++ b/src/vs/base/test/node/console.test.ts @@ -11,39 +11,38 @@ suite('Console', () => { test('getFirstFrame', () => { let stack = 'at vscode.commands.registerCommand (/Users/someone/Desktop/test-ts/out/src/extension.js:18:17)'; - let frame = getFirstFrame(stack); + let frame = getFirstFrame(stack)!; assert.equal(frame.uri.fsPath, normalize('/Users/someone/Desktop/test-ts/out/src/extension.js')); assert.equal(frame.line, 18); assert.equal(frame.column, 17); stack = 'at /Users/someone/Desktop/test-ts/out/src/extension.js:18:17'; - frame = getFirstFrame(stack); + frame = getFirstFrame(stack)!; assert.equal(frame.uri.fsPath, normalize('/Users/someone/Desktop/test-ts/out/src/extension.js')); assert.equal(frame.line, 18); assert.equal(frame.column, 17); stack = 'at c:\\Users\\someone\\Desktop\\end-js\\extension.js:18:17'; - frame = getFirstFrame(stack); + frame = getFirstFrame(stack)!; assert.equal(frame.uri.fsPath, 'c:\\Users\\someone\\Desktop\\end-js\\extension.js'); assert.equal(frame.line, 18); assert.equal(frame.column, 17); stack = 'at e.$executeContributedCommand(c:\\Users\\someone\\Desktop\\end-js\\extension.js:18:17)'; - frame = getFirstFrame(stack); + frame = getFirstFrame(stack)!; assert.equal(frame.uri.fsPath, 'c:\\Users\\someone\\Desktop\\end-js\\extension.js'); assert.equal(frame.line, 18); assert.equal(frame.column, 17); stack = 'at /Users/someone/Desktop/test-ts/out/src/extension.js:18:17\nat /Users/someone/Desktop/test-ts/out/src/other.js:28:27\nat /Users/someone/Desktop/test-ts/out/src/more.js:38:37'; - frame = getFirstFrame(stack); + frame = getFirstFrame(stack)!; assert.equal(frame.uri.fsPath, normalize('/Users/someone/Desktop/test-ts/out/src/extension.js')); assert.equal(frame.line, 18); assert.equal(frame.column, 17); - }); }); \ No newline at end of file diff --git a/src/vs/base/test/node/extfs/extfs.test.ts b/src/vs/base/test/node/extfs/extfs.test.ts index 07de8dc8efcf..76ed26a31532 100644 --- a/src/vs/base/test/node/extfs/extfs.test.ts +++ b/src/vs/base/test/node/extfs/extfs.test.ts @@ -38,7 +38,7 @@ function toReadable(value: string, throwError?: boolean): Readable { this.emit('error', new Error(readError)); } - let res: string; + let res!: string; let canPush = true; while (canPush && (res = stringChunks[counter++])) { canPush = this.push(res); @@ -96,14 +96,14 @@ suite('Extfs', () => { return done(error); } - assert.ok(!statAndIsLink.isSymbolicLink); + assert.ok(!statAndIsLink!.isSymbolicLink); extfs.statLink(symbolicLink, (error, statAndIsLink) => { if (error) { return done(error); } - assert.ok(statAndIsLink.isSymbolicLink); + assert.ok(statAndIsLink!.isSymbolicLink); extfs.delSync(directory); done(); }); @@ -258,7 +258,7 @@ suite('Extfs', () => { assert.ok(fs.existsSync(newDir)); - extfs.writeFileAndFlush(testFile, 'Hello World', null, error => { + extfs.writeFileAndFlush(testFile, 'Hello World', null!, error => { if (error) { return done(error); } @@ -267,7 +267,7 @@ suite('Extfs', () => { const largeString = (new Array(100 * 1024)).join('Large String\n'); - extfs.writeFileAndFlush(testFile, largeString, null, error => { + extfs.writeFileAndFlush(testFile, largeString, null!, error => { if (error) { return done(error); } @@ -293,7 +293,7 @@ suite('Extfs', () => { assert.ok(fs.existsSync(newDir)); - extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null, error => { + extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null!, error => { if (error) { return done(error); } @@ -302,7 +302,7 @@ suite('Extfs', () => { const largeString = (new Array(100 * 1024)).join('Large String\n'); - extfs.writeFileAndFlush(testFile, toReadable(largeString), null, error => { + extfs.writeFileAndFlush(testFile, toReadable(largeString), null!, error => { if (error) { return done(error); } @@ -329,7 +329,7 @@ suite('Extfs', () => { assert.ok(fs.existsSync(newDir)); - extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null, error => { + extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null!, error => { if (error) { return done(error); } @@ -356,7 +356,7 @@ suite('Extfs', () => { fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! - extfs.writeFileAndFlush(testFile, 'Hello World', null, error => { + extfs.writeFileAndFlush(testFile, 'Hello World', null!, error => { if (!error) { return done(new Error('Expected error for writing to readonly file')); } @@ -382,7 +382,7 @@ suite('Extfs', () => { fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! const readable = toReadable('Hello World'); - extfs.writeFileAndFlush(testFile, readable, null, error => { + extfs.writeFileAndFlush(testFile, readable, null!, error => { if (!error || (error).code !== 'EISDIR') { return done(new Error('Expected EISDIR error for writing to folder but got: ' + (error ? (error).code : 'no error'))); } @@ -408,7 +408,7 @@ suite('Extfs', () => { assert.ok(fs.existsSync(newDir)); - extfs.writeFileAndFlush(testFile, toReadable('Hello World', true /* throw error */), null, error => { + extfs.writeFileAndFlush(testFile, toReadable('Hello World', true /* throw error */), null!, error => { if (!error || error.message !== readError) { return done(new Error('Expected error for writing to folder')); } @@ -438,7 +438,7 @@ suite('Extfs', () => { fs.writeFileSync(testFile, ''); fs.chmodSync(testFile, 33060); // make readonly - extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null, error => { + extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null!, error => { if (!error || !((error).code !== 'EACCES' || (error).code !== 'EPERM')) { return done(new Error('Expected EACCES/EPERM error for writing to folder but got: ' + (error ? (error).code : 'no error'))); } @@ -464,7 +464,7 @@ suite('Extfs', () => { fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! - extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null, error => { + extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null!, error => { if (!error) { return done(new Error('Expected error for writing to folder')); } @@ -487,12 +487,12 @@ suite('Extfs', () => { assert.ok(fs.existsSync(newDir)); - extfs.writeFileAndFlushSync(testFile, 'Hello World', null); + extfs.writeFileAndFlushSync(testFile, 'Hello World', null!); assert.equal(fs.readFileSync(testFile), 'Hello World'); const largeString = (new Array(100 * 1024)).join('Large String\n'); - extfs.writeFileAndFlushSync(testFile, largeString, null); + extfs.writeFileAndFlushSync(testFile, largeString, null!); assert.equal(fs.readFileSync(testFile), largeString); extfs.del(parentDir, os.tmpdir(), done, ignore); @@ -551,13 +551,13 @@ suite('Extfs', () => { const newDir = path.join(parentDir, 'extfs', id); mkdirp(newDir, 493, error => { - let realpath: string; + let realpath!: string; try { realpath = extfs.realpathSync(newDir); } catch (error) { assert.ok(!error); } - assert.ok(realpath); + assert.ok(realpath!); extfs.del(parentDir, os.tmpdir(), done, ignore); }); diff --git a/src/vs/base/test/node/extfs/fixtures/examples/company.jxs b/src/vs/base/test/node/extfs/fixtures/examples/company.jxs index ca4a62bf248b..b65b52ade690 100644 --- a/src/vs/base/test/node/extfs/fixtures/examples/company.jxs +++ b/src/vs/base/test/node/extfs/fixtures/examples/company.jxs @@ -8,8 +8,8 @@ var Workforce; return Company; })(); (function (property, Workforce, IEmployee) { - if (property === void 0) { property = employees; } - if (IEmployee === void 0) { IEmployee = []; } + if (property === undefined) { property = employees; } + if (IEmployee === undefined) { IEmployee = []; } property; calculateMonthlyExpenses(); { diff --git a/src/vs/base/test/node/extfs/fixtures/examples/conway.jxs b/src/vs/base/test/node/extfs/fixtures/examples/conway.jxs index 306c906ce6f2..248f26be2053 100644 --- a/src/vs/base/test/node/extfs/fixtures/examples/conway.jxs +++ b/src/vs/base/test/node/extfs/fixtures/examples/conway.jxs @@ -7,9 +7,9 @@ var Conway; return Cell; })(); (function (property, number, property, number, property, boolean) { - if (property === void 0) { property = row; } - if (property === void 0) { property = col; } - if (property === void 0) { property = live; } + if (property === undefined) { property = row; } + if (property === undefined) { property = col; } + if (property === undefined) { property = live; } }); var GameOfLife = (function () { function GameOfLife() { diff --git a/src/vs/base/test/node/extfs/fixtures/examples/small.jxs b/src/vs/base/test/node/extfs/fixtures/examples/small.jxs index 5e57b4c9439a..2fb478319a52 100644 --- a/src/vs/base/test/node/extfs/fixtures/examples/small.jxs +++ b/src/vs/base/test/node/extfs/fixtures/examples/small.jxs @@ -7,10 +7,10 @@ var M; return C; })(); (function (x, property, number) { - if (property === void 0) { property = w; } + if (property === undefined) { property = w; } var local = 1; // unresolved symbol because x is local - //self.x++; + //self.x++; self.w--; // ok because w is a property property; f = function (y) { diff --git a/src/vs/base/test/node/flow.test.ts b/src/vs/base/test/node/flow.test.ts index f5e224b84343..14365d2cf20c 100644 --- a/src/vs/base/test/node/flow.test.ts +++ b/src/vs/base/test/node/flow.test.ts @@ -430,7 +430,7 @@ suite('Flow', () => { parallel(elements, function (element, callback) { sum += element; - callback(null, element * element); + callback(null!, element * element); }, function (errors, result) { assert.ok(!errors); @@ -449,7 +449,7 @@ suite('Flow', () => { parallel(elements, function (element, callback) { setTimeout(function () { sum += element; - callback(null, element * element); + callback(null!, element * element); }, timeouts.pop()); }, function (errors, result) { assert.ok(!errors); @@ -469,10 +469,10 @@ suite('Flow', () => { parallel(elements, function (element, callback) { setTimeout(function () { if (element === 4) { - callback(new Error('error!'), null); + callback(new Error('error!'), null!); } else { sum += element; - callback(null, element * element); + callback(null!, element * element); } }, timeouts.pop()); }, function (errors, result) { diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts index a4c56cac39de..d566de4a0ac5 100644 --- a/src/vs/base/test/node/glob.test.ts +++ b/src/vs/base/test/node/glob.test.ts @@ -738,24 +738,24 @@ suite('Glob', () => { }); test('falsy expression/pattern', function () { - assert.strictEqual(glob.match(null, 'foo'), false); + assert.strictEqual(glob.match(null!, 'foo'), false); assert.strictEqual(glob.match('', 'foo'), false); - assert.strictEqual(glob.parse(null)('foo'), false); + assert.strictEqual(glob.parse(null!)('foo'), false); assert.strictEqual(glob.parse('')('foo'), false); }); test('falsy path', function () { - assert.strictEqual(glob.parse('foo')(null), false); + assert.strictEqual(glob.parse('foo')(null!), false); assert.strictEqual(glob.parse('foo')(''), false); - assert.strictEqual(glob.parse('**/*.j?')(null), false); + assert.strictEqual(glob.parse('**/*.j?')(null!), false); assert.strictEqual(glob.parse('**/*.j?')(''), false); - assert.strictEqual(glob.parse('**/*.foo')(null), false); + assert.strictEqual(glob.parse('**/*.foo')(null!), false); assert.strictEqual(glob.parse('**/*.foo')(''), false); - assert.strictEqual(glob.parse('**/foo')(null), false); + assert.strictEqual(glob.parse('**/foo')(null!), false); assert.strictEqual(glob.parse('**/foo')(''), false); - assert.strictEqual(glob.parse('{**/baz,**/foo}')(null), false); + assert.strictEqual(glob.parse('{**/baz,**/foo}')(null!), false); assert.strictEqual(glob.parse('{**/baz,**/foo}')(''), false); - assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(null), false); + assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(null!), false); assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(''), false); }); @@ -808,7 +808,7 @@ suite('Glob', () => { }, ['foo', 'bar', 'baz'], [ ['bar/foo', '**/foo/**'], ['foo/bar', '{**/bar/**,**/baz/**}'], - ['bar/nope', null] + ['bar/nope', null!] ]); const siblings = ['baz', 'baz.zip', 'nope']; @@ -817,12 +817,12 @@ suite('Glob', () => { '**/foo/**': { when: '$(basename).zip' }, '**/bar/**': true }, ['bar'], [ - ['bar/foo', null], - ['bar/foo/baz', null], - ['bar/foo/nope', null], + ['bar/foo', null!], + ['bar/foo/baz', null!], + ['bar/foo/nope', null!], ['foo/bar', '**/bar/**'], ], [ - null, + null!, hasSibling, hasSibling ]); @@ -832,7 +832,7 @@ suite('Glob', () => { const parsed = glob.parse(pattern, { trimForExclusions: true }); assert.deepStrictEqual(glob.getBasenameTerms(parsed), basenameTerms); matches.forEach(([text, result], i) => { - assert.strictEqual(parsed(text, null, siblingsFns[i]), result); + assert.strictEqual(parsed(text, null!, siblingsFns[i]), result); }); } @@ -914,7 +914,7 @@ suite('Glob', () => { [nativeSep('bar/foo/bar'), '**/foo/bar/**'], // Not supported // [nativeSep('foo/bar/bar'), '{**/bar/bar/**,**/baz/bar/**}'], - [nativeSep('/foo/bar/nope'), null] + [nativeSep('/foo/bar/nope'), null!] ]); const siblings = ['baz', 'baz.zip', 'nope']; @@ -923,12 +923,12 @@ suite('Glob', () => { '**/foo/123/**': { when: '$(basename).zip' }, '**/bar/123/**': true }, ['*/bar/123'], [ - [nativeSep('bar/foo/123'), null], - [nativeSep('bar/foo/123/baz'), null], - [nativeSep('bar/foo/123/nope'), null], + [nativeSep('bar/foo/123'), null!], + [nativeSep('bar/foo/123/baz'), null!], + [nativeSep('bar/foo/123/nope'), null!], [nativeSep('foo/bar/123'), '**/bar/123/**'], ], [ - null, + null!, hasSibling, hasSibling ]); @@ -938,7 +938,7 @@ suite('Glob', () => { const parsed = glob.parse(pattern, { trimForExclusions: true }); assert.deepStrictEqual(glob.getPathTerms(parsed), pathTerms); matches.forEach(([text, result], i) => { - assert.strictEqual(parsed(text, null, siblingsFns[i]), result); + assert.strictEqual(parsed(text, null!, siblingsFns[i]), result); }); } diff --git a/src/vs/base/test/node/pfs.test.ts b/src/vs/base/test/node/pfs.test.ts index 44c703d8ebee..69c7860640ce 100644 --- a/src/vs/base/test/node/pfs.test.ts +++ b/src/vs/base/test/node/pfs.test.ts @@ -24,7 +24,7 @@ suite('PFS', () => { return pfs.mkdirp(newDir, 493).then(() => { assert.ok(fs.existsSync(newDir)); - return pfs.writeFile(testFile, 'Hello World', null).then(() => { + return pfs.writeFile(testFile, 'Hello World', null!).then(() => { assert.equal(fs.readFileSync(testFile), 'Hello World'); return pfs.del(parentDir, os.tmpdir()); @@ -46,11 +46,11 @@ suite('PFS', () => { assert.ok(fs.existsSync(newDir)); return Promise.all([ - pfs.writeFile(testFile1, 'Hello World 1', null), - pfs.writeFile(testFile2, 'Hello World 2', null), - pfs.writeFile(testFile3, 'Hello World 3', null), - pfs.writeFile(testFile4, 'Hello World 4', null), - pfs.writeFile(testFile5, 'Hello World 5', null) + pfs.writeFile(testFile1, 'Hello World 1', null!), + pfs.writeFile(testFile2, 'Hello World 2', null!), + pfs.writeFile(testFile3, 'Hello World 3', null!), + pfs.writeFile(testFile4, 'Hello World 4', null!), + pfs.writeFile(testFile5, 'Hello World 5', null!) ]).then(() => { assert.equal(fs.readFileSync(testFile1), 'Hello World 1'); assert.equal(fs.readFileSync(testFile2), 'Hello World 2'); @@ -73,11 +73,11 @@ suite('PFS', () => { assert.ok(fs.existsSync(newDir)); 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)), - pfs.writeFile(testFile, 'Hello World 4', null), - timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 5', null)) + pfs.writeFile(testFile, 'Hello World 1', undefined), + pfs.writeFile(testFile, 'Hello World 2', undefined), + timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 3', undefined)), + pfs.writeFile(testFile, 'Hello World 4', undefined), + timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 5', undefined)) ]).then(() => { assert.equal(fs.readFileSync(testFile), 'Hello World 5'); @@ -119,23 +119,6 @@ suite('PFS', () => { }); }); - test('unlinkIgnoreError', function () { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - - return pfs.mkdirp(newDir, 493).then(() => { - return pfs.unlinkIgnoreError(path.join(newDir, 'foo')).then(() => { - - return pfs.del(parentDir, os.tmpdir()); - }, error => { - assert.fail(error); - - return Promise.reject(error); - }); - }); - }); - test('moveIgnoreError', function () { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); diff --git a/src/vs/base/test/node/port.test.ts b/src/vs/base/test/node/port.test.ts index f22e2defc19d..c4ec1a439a08 100644 --- a/src/vs/base/test/node/port.test.ts +++ b/src/vs/base/test/node/port.test.ts @@ -21,7 +21,7 @@ suite('Ports', () => { // create a server to block this port const server = net.createServer(); - server.listen(initialPort, null, null, () => { + server.listen(initialPort, undefined, undefined, () => { // once listening, find another free port and assert that the port is different from the opened one ports.findFreePort(7000, 50, 300000).then(freePort => { diff --git a/src/vs/base/test/node/processes/processes.test.ts b/src/vs/base/test/node/processes/processes.test.ts index d32e2e54853e..5fcb781952ea 100644 --- a/src/vs/base/test/node/processes/processes.test.ts +++ b/src/vs/base/test/node/processes/processes.test.ts @@ -84,4 +84,29 @@ suite('Processes', () => { } }); }); + + + test('sanitizeProcessEnvironment', () => { + let env = { + FOO: 'bar', + ELECTRON_ENABLE_STACK_DUMPING: 'x', + ELECTRON_ENABLE_LOGGING: 'x', + ELECTRON_NO_ASAR: 'x', + ELECTRON_NO_ATTACH_CONSOLE: 'x', + ELECTRON_RUN_AS_NODE: 'x', + GOOGLE_API_KEY: 'x', + VSCODE_CLI: 'x', + VSCODE_DEV: 'x', + VSCODE_IPC_HOOK: 'x', + VSCODE_LOGS: 'x', + VSCODE_NLS_CONFIG: 'x', + VSCODE_PORTABLE: 'x', + VSCODE_PID: 'x', + VSCODE_NODE_CACHED_DATA_DIR: 'x', + VSCODE_NEW_VAR: 'x' + }; + processes.sanitizeProcessEnvironment(env); + assert.equal(env['FOO'], 'bar'); + assert.equal(Object.keys(env).length, 1); + }); }); diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/test/node/storage/storage.test.ts index 1e0702cc69b3..40867b460261 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/test/node/storage/storage.test.ts @@ -8,9 +8,10 @@ import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'path'; import { tmpdir } from 'os'; import { equal, ok } from 'assert'; -import { mkdirp, del, writeFile } from 'vs/base/node/pfs'; +import { mkdirp, del, writeFile, exists, unlink } from 'vs/base/node/pfs'; import { timeout } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; +import { isWindows } from 'vs/base/common/platform'; suite('Storage Library', () => { @@ -90,7 +91,6 @@ suite('Storage Library', () => { await Promise.all([delete1Promise, delete2Promise, delete3Promise]).then(() => deletePromiseResolved = true); equal(deletePromiseResolved, true); - storage.beforeClose(); await storage.close(); await del(storageDir, tmpdir()); }); @@ -100,7 +100,7 @@ suite('Storage Library', () => { await mkdirp(storageDir); class TestSQLiteStorageDatabase extends SQLiteStorageDatabase { - private _onDidChangeItemsExternal: Emitter = new Emitter(); + private _onDidChangeItemsExternal = new Emitter(); get onDidChangeItemsExternal(): Event { return this._onDidChangeItemsExternal.event; } fireDidChangeItemsExternal(event: IStorageItemsChangeEvent): void { @@ -136,18 +136,17 @@ suite('Storage Library', () => { changes.clear(); // Delete is accepted - change.set('foo', null); + change.set('foo', undefined); database.fireDidChangeItemsExternal({ items: change }); ok(changes.has('foo')); - equal(storage.get('foo', null), null); + equal(storage.get('foo', null!), null); changes.clear(); // Nothing happens if changing to same value - change.set('foo', null); + change.set('foo', undefined); database.fireDidChangeItemsExternal({ items: change }); equal(changes.size, 0); - storage.beforeClose(); await storage.close(); await del(storageDir, tmpdir()); }); @@ -168,7 +167,6 @@ suite('Storage Library', () => { let setPromiseResolved = false; Promise.all([set1Promise, set2Promise]).then(() => setPromiseResolved = true); - storage.beforeClose(); await storage.close(); equal(setPromiseResolved, true); @@ -179,7 +177,6 @@ suite('Storage Library', () => { equal(storage.get('foo'), 'bar'); equal(storage.get('bar'), 'foo'); - storage.beforeClose(); await storage.close(); storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); @@ -194,7 +191,6 @@ suite('Storage Library', () => { let deletePromiseResolved = false; Promise.all([delete1Promise, delete2Promise]).then(() => deletePromiseResolved = true); - storage.beforeClose(); await storage.close(); equal(deletePromiseResolved, true); @@ -205,7 +201,6 @@ suite('Storage Library', () => { ok(!storage.get('foo')); ok(!storage.get('bar')); - storage.beforeClose(); await storage.close(); await del(storageDir, tmpdir()); }); @@ -248,7 +243,36 @@ suite('Storage Library', () => { await Promise.all([set4Promise, delete1Promise]).then(() => setAndDeletePromiseResolved = true); ok(setAndDeletePromiseResolved); - storage.beforeClose(); + await storage.close(); + await del(storageDir, tmpdir()); + }); + + test('corrupt DB recovers', async () => { + const storageDir = uniqueStorageDir(); + await mkdirp(storageDir); + + const storageFile = join(storageDir, 'storage.db'); + + let storage = new Storage(new SQLiteStorageDatabase(storageFile)); + await storage.init(); + + await storage.set('bar', 'foo'); + + await writeFile(storageFile, 'This is a broken DB'); + + await storage.set('foo', 'bar'); + + equal(storage.get('bar'), 'foo'); + equal(storage.get('foo'), 'bar'); + + await storage.close(); + + storage = new Storage(new SQLiteStorageDatabase(storageFile)); + await storage.init(); + + equal(storage.get('bar'), 'foo'); + equal(storage.get('foo'), 'bar'); + await storage.close(); await del(storageDir, tmpdir()); }); @@ -270,7 +294,7 @@ suite('SQLite Storage Library', () => { } async function testDBBasics(path, logError?: (error) => void) { - let options: ISQLiteStorageDatabaseOptions; + let options!: ISQLiteStorageDatabaseOptions; if (logError) { options = { logging: { @@ -331,7 +355,14 @@ suite('SQLite Storage Library', () => { storedItems = await storage.getItems(); equal(storedItems.size, 0); - await storage.close(); + let recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return new Map(); + }); + + equal(recoveryCalled, false); } test('basics', async () => { @@ -339,7 +370,7 @@ suite('SQLite Storage Library', () => { await mkdirp(storageDir); - testDBBasics(join(storageDir, 'storage.db')); + await testDBBasics(join(storageDir, 'storage.db')); await del(storageDir, tmpdir()); }); @@ -399,7 +430,14 @@ suite('SQLite Storage Library', () => { equal(storedItems.get('some/foo/path'), 'some/bar/path'); equal(storedItems.get(JSON.stringify({ foo: 'bar' })), JSON.stringify({ bar: 'foo' })); - await storage.close(); + let recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return new Map(); + }); + + equal(recoveryCalled, false); await del(storageDir, tmpdir()); }); @@ -433,6 +471,74 @@ suite('SQLite Storage Library', () => { await del(storageDir, tmpdir()); }); + test('basics (DB that becomes corrupt during runtime stores all state from cache on close)', async () => { + if (isWindows) { + await Promise.resolve(); // Windows will fail to write to open DB due to locking + + return; + } + + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storagePath = join(storageDir, 'storage.db'); + let storage = new SQLiteStorageDatabase(storagePath); + + const items = new Map(); + items.set('foo', 'bar'); + items.set('some/foo/path', 'some/bar/path'); + items.set(JSON.stringify({ foo: 'bar' }), JSON.stringify({ bar: 'foo' })); + + await storage.updateItems({ insert: items }); + await storage.close(); + + const backupPath = `${storagePath}.backup`; + equal(await exists(backupPath), true); + + storage = new SQLiteStorageDatabase(storagePath); + await storage.getItems(); + + await writeFile(storagePath, 'This is now a broken DB'); + + // we still need to trigger a check to the DB so that we get to know that + // the DB is corrupt. We have no extra code on shutdown that checks for the + // health of the DB. This is an optimization to not perform too many tasks + // on shutdown. + await storage.checkIntegrity(true).then(null, error => { } /* error is expected here but we do not want to fail */); + + await unlink(backupPath); // also test that the recovery DB is backed up properly + + let recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return items; + }); + + equal(recoveryCalled, true); + equal(await exists(backupPath), true); + + storage = new SQLiteStorageDatabase(storagePath); + + const storedItems = await storage.getItems(); + equal(storedItems.size, items.size); + equal(storedItems.get('foo'), 'bar'); + equal(storedItems.get('some/foo/path'), 'some/bar/path'); + equal(storedItems.get(JSON.stringify({ foo: 'bar' })), JSON.stringify({ bar: 'foo' })); + + recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return new Map(); + }); + + equal(recoveryCalled, false); + + await del(storageDir, tmpdir()); + }); + test('real world example', async () => { const storageDir = uniqueStorageDir(); @@ -627,7 +733,70 @@ suite('SQLite Storage Library', () => { equal(items.get('foo3'), 'bar'); equal(items.get('some/foo3/path'), 'some/bar/path'); - storage.beforeClose(); + await storage.close(); + + await del(storageDir, tmpdir()); + }); + + test('lots of INSERT & DELETE (below inline max)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); + + const items = new Map(); + const keys: Set = new Set(); + for (let i = 0; i < 200; i++) { + const uuid = generateUuid(); + const key = `key: ${uuid}`; + + items.set(key, `value: ${uuid}`); + keys.add(key); + } + + await storage.updateItems({ insert: items }); + + let storedItems = await storage.getItems(); + equal(storedItems.size, items.size); + + await storage.updateItems({ delete: keys }); + + storedItems = await storage.getItems(); + equal(storedItems.size, 0); + + await storage.close(); + + await del(storageDir, tmpdir()); + }); + + test('lots of INSERT & DELETE (above inline max)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); + + const items = new Map(); + const keys: Set = new Set(); + for (let i = 0; i < 400; i++) { + const uuid = generateUuid(); + const key = `key: ${uuid}`; + + items.set(key, `value: ${uuid}`); + keys.add(key); + } + + await storage.updateItems({ insert: items }); + + let storedItems = await storage.getItems(); + equal(storedItems.size, items.size); + + await storage.updateItems({ delete: keys }); + + storedItems = await storage.getItems(); + equal(storedItems.size, 0); + await storage.close(); await del(storageDir, tmpdir()); diff --git a/src/vs/base/test/node/stream/stream.test.ts b/src/vs/base/test/node/stream/stream.test.ts index cc5b3c6e6075..9c05523961dc 100644 --- a/src/vs/base/test/node/stream/stream.test.ts +++ b/src/vs/base/test/node/stream/stream.test.ts @@ -14,7 +14,7 @@ suite('Stream', () => { return stream.readExactlyByFile(file, 10).then(({ buffer, bytesRead }) => { assert.equal(bytesRead, 10); - assert.equal(buffer.toString(), '/*--------'); + assert.equal(buffer!.toString(), '/*--------'); }); }); diff --git a/src/vs/base/test/node/utils.ts b/src/vs/base/test/node/utils.ts index 138cf1889e28..d2e92094ad68 100644 --- a/src/vs/base/test/node/utils.ts +++ b/src/vs/base/test/node/utils.ts @@ -10,10 +10,10 @@ import { mkdirp, del } from 'vs/base/node/pfs'; export interface ITestFileResult { testFile: string; - cleanUp: () => Thenable; + cleanUp: () => Promise; } -export function testFile(folder: string, file: string): Thenable { +export function testFile(folder: string, file: string): Promise { const id = generateUuid(); const parentDir = join(tmpdir(), 'vsctests', id); const newDir = join(parentDir, 'config', id); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 070c5b0c9595..fc486a48dc54 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -87,9 +87,12 @@ export class IssueReporter extends Disposable { extensionsDisabled: !!this.environmentService.disableExtensions, }); - this.previewButton = new Button(document.getElementById('issue-reporter')); + const issueReporterElement = this.getElementById('issue-reporter'); + if (issueReporterElement) { + this.previewButton = new Button(issueReporterElement); + } - ipcRenderer.on('vscode:issuePerformanceInfoResponse', (event, info) => { + ipcRenderer.on('vscode:issuePerformanceInfoResponse', (_, info) => { this.logService.trace('issueReporter: Received performance data'); this.issueReporterModel.update(info); this.receivedPerformanceInfo = true; @@ -100,7 +103,7 @@ export class IssueReporter extends Disposable { this.updatePreviewButtonState(); }); - ipcRenderer.on('vscode:issueSystemInfoResponse', (event, info) => { + ipcRenderer.on('vscode:issueSystemInfoResponse', (_, info) => { this.logService.trace('issueReporter: Received system data'); this.issueReporterModel.update({ systemInfo: info }); this.receivedSystemInfo = true; @@ -116,7 +119,7 @@ export class IssueReporter extends Disposable { this.logService.trace('issueReporter: Sent data requests'); if (window.document.documentElement.lang !== 'en') { - show(document.getElementById('english')); + show(this.getElementById('english')); } this.setUpTypes(); @@ -208,7 +211,7 @@ export class IssueReporter extends Disposable { styleTag.innerHTML = content.join('\n'); document.head.appendChild(styleTag); - document.body.style.color = styles.color; + document.body.style.color = styles.color || null; } private handleExtensionData(extensions: IssueReporterExtensionData[]) { @@ -221,7 +224,7 @@ export class IssueReporter extends Disposable { this.updateExtensionTable(nonThemes, numberOfThemeExtesions); if (this.environmentService.disableExtensions || extensions.length === 0) { - (document.getElementById('disableExtensions')).disabled = true; + (this.getElementById('disableExtensions')).disabled = true; } this.updateExtensionSelector(extensions); @@ -239,32 +242,33 @@ export class IssueReporter extends Disposable { private updateSettingsSearchDetails(data: ISettingsSearchIssueReporterData): void { const target = document.querySelector('.block-settingsSearchResults .block-info'); - - const details = ` + if (target) { + const details = `
Query: "${data.query}"
Literal match count: ${data.filterResultCount}
- `; + `; - let table = ` - - Setting - Extension - Score - `; + let table = ` + + Setting + Extension + Score + `; - data.actualSearchResults - .forEach(setting => { - table += ` - - ${setting.key} - ${setting.extensionId} - ${String(setting.score).slice(0, 5)} - `; - }); + data.actualSearchResults + .forEach(setting => { + table += ` + + ${setting.key} + ${setting.extensionId} + ${String(setting.score).slice(0, 5)} + `; + }); - target.innerHTML = `${details}${table}
`; + target.innerHTML = `${details}${table}
`; + } } private initServices(configuration: IWindowConfiguration): void { @@ -286,7 +290,7 @@ export class IssueReporter extends Disposable { if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)); - const commonProperties = resolveCommonProperties(product.commit, pkg.version, configuration.machineId, this.environmentService.installSourcePath); + const commonProperties = resolveCommonProperties(product.commit || 'Commit unknown', pkg.version, configuration.machineId, this.environmentService.installSourcePath); const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths }; @@ -320,27 +324,29 @@ export class IssueReporter extends Disposable { const showInfoElements = document.getElementsByClassName('showInfo'); for (let i = 0; i < showInfoElements.length; i++) { const showInfo = showInfoElements.item(i); - showInfo.addEventListener('click', (e) => { + showInfo!.addEventListener('click', (e) => { e.preventDefault(); const label = (e.target); - const containingElement = label.parentElement.parentElement; - const info = containingElement.lastElementChild; - if (info.classList.contains('hidden')) { - show(info); - label.textContent = localize('hide', "hide"); - } else { - hide(info); - label.textContent = localize('show', "show"); + if (label) { + const containingElement = label.parentElement && label.parentElement.parentElement; + const info = containingElement && containingElement.lastElementChild; + if (info && info.classList.contains('hidden')) { + show(info); + label.textContent = localize('hide', "hide"); + } else { + hide(info); + label.textContent = localize('show', "show"); + } } }); } - this.addEventListener('issue-source', 'change', (event: Event) => { - const fileOnExtension = JSON.parse((event.target).value); + this.addEventListener('issue-source', 'change', (e: Event) => { + const fileOnExtension = JSON.parse((e.target).value); this.issueReporterModel.update({ fileOnExtension: fileOnExtension, includeExtensions: !fileOnExtension }); this.render(); - const title = (document.getElementById('issue-title')).value; + const title = (this.getElementById('issue-title')).value; if (fileOnExtension) { this.searchExtensionIssues(title); } else { @@ -349,20 +355,20 @@ export class IssueReporter extends Disposable { } }); - this.addEventListener('description', 'input', (event: Event) => { - const issueDescription = (event.target).value; + this.addEventListener('description', 'input', (e: Event) => { + const issueDescription = (e.target).value; this.issueReporterModel.update({ issueDescription }); // Only search for extension issues on title change if (!this.issueReporterModel.fileOnExtension()) { - const title = (document.getElementById('issue-title')).value; + const title = (this.getElementById('issue-title')).value; this.searchVSCodeIssues(title, issueDescription); } }); - this.addEventListener('issue-title', 'input', (e) => { - const title = (event.target).value; - const lengthValidationMessage = document.getElementById('issue-title-length-validation-error'); + this.addEventListener('issue-title', 'input', (e: Event) => { + const title = (e.target).value; + const lengthValidationMessage = this.getElementById('issue-title-length-validation-error'); if (title && this.getIssueUrlWithTitle(title).length > MAX_URL_LENGTH) { show(lengthValidationMessage); } else { @@ -409,7 +415,7 @@ export class IssueReporter extends Disposable { e.stopPropagation(); e.preventDefault(); - const issueTitle = (document.getElementById('issue-title'))!.value; + const issueTitle = (this.getElementById('issue-title'))!.value; const { issueDescription } = this.issueReporterModel.getData(); if (!this.hasBeenSubmitted && (issueTitle || issueDescription)) { ipcRenderer.send('vscode:issueReporterConfirmClose'); @@ -471,17 +477,17 @@ export class IssueReporter extends Disposable { return false; } - private getExtensionRepositoryUrl(): string { + private getExtensionRepositoryUrl(): string | undefined { const selectedExtension = this.issueReporterModel.getData().selectedExtension; return selectedExtension && selectedExtension.repositoryUrl; } - private getExtensionBugsUrl(): string { + private getExtensionBugsUrl(): string | undefined { const selectedExtension = this.issueReporterModel.getData().selectedExtension; return selectedExtension && selectedExtension.bugsUrl; } - private searchVSCodeIssues(title: string, issueDescription: string): void { + private searchVSCodeIssues(title: string, issueDescription?: string): void { if (title) { this.searchDuplicates(title, issueDescription); } else { @@ -510,7 +516,7 @@ export class IssueReporter extends Disposable { } private clearSearchResults(): void { - const similarIssues = document.getElementById('similar-issues'); + const similarIssues = this.getElementById('similar-issues')!; similarIssues.innerHTML = ''; this.numberOfSearchResultsDisplayed = 0; } @@ -518,7 +524,7 @@ export class IssueReporter extends Disposable { @debounce(300) private searchGitHub(repo: string, title: string): void { const query = `is:issue+repo:${repo}+${title}`; - const similarIssues = document.getElementById('similar-issues'); + const similarIssues = this.getElementById('similar-issues')!; window.fetch(`https://api.github.com/search/issues?q=${query}`).then((response) => { response.json().then(result => { @@ -532,7 +538,7 @@ export class IssueReporter extends Disposable { similarIssues.appendChild(message); const resetTime = response.headers.get('X-RateLimit-Reset'); - const timeToWait = parseInt(resetTime) - Math.floor(Date.now() / 1000); + const timeToWait = resetTime ? parseInt(resetTime) - Math.floor(Date.now() / 1000) : 1; if (this.shouldQueueSearch) { this.shouldQueueSearch = false; setTimeout(() => { @@ -550,7 +556,7 @@ export class IssueReporter extends Disposable { } @debounce(300) - private searchDuplicates(title: string, body: string): void { + private searchDuplicates(title: string, body?: string): void { const url = 'https://vscode-probot.westus.cloudapp.azure.com:7890/duplicate_candidates'; const init = { method: 'POST', @@ -582,7 +588,7 @@ export class IssueReporter extends Disposable { } private displaySearchResults(results: SearchResult[]) { - const similarIssues = document.getElementById('similar-issues'); + const similarIssues = this.getElementById('similar-issues')!; if (results.length) { const issues = $('div.issues-container'); const issuesText = $('div.list-title'); @@ -598,6 +604,7 @@ export class IssueReporter extends Disposable { link.addEventListener('auxclick', (e) => this.openLink(e)); let issueState: HTMLElement; + let item: HTMLElement; if (issue.state) { issueState = $('span.issue-state'); @@ -611,9 +618,12 @@ export class IssueReporter extends Disposable { issueState.title = issue.state === 'open' ? localize('open', "Open") : localize('closed', "Closed"); issueState.appendChild(issueIcon); issueState.appendChild(issueStateLabel); + + item = $('div.issue', {}, issueState, link); + } else { + item = $('div.issue', {}, link); } - const item = $('div.issue', {}, issueState, link); issues.appendChild(item); } @@ -639,7 +649,7 @@ export class IssueReporter extends Disposable { private setUpTypes(): void { const makeOption = (issueType: IssueType, description: string) => ``; - const typeSelect = (document.getElementById('issue-type')); + const typeSelect = this.getElementById('issue-type')! as HTMLSelectElement; const { issueType } = this.issueReporterModel.getData(); if (issueType === IssueType.SettingsSearchIssue) { typeSelect.innerHTML = makeOption(IssueType.SettingsSearchIssue, localize('settingsSearchIssue', "Settings Search Issue")); @@ -658,7 +668,7 @@ export class IssueReporter extends Disposable { private renderBlocks(): void { // Depending on Issue Type, we render different blocks and text const { issueType, fileOnExtension } = this.issueReporterModel.getData(); - const blockContainer = document.getElementById('block-container'); + const blockContainer = this.getElementById('block-container'); const systemBlock = document.querySelector('.block-system'); const processBlock = document.querySelector('.block-process'); const workspaceBlock = document.querySelector('.block-workspace'); @@ -666,11 +676,11 @@ export class IssueReporter extends Disposable { const searchedExtensionsBlock = document.querySelector('.block-searchedExtensions'); const settingsSearchResultsBlock = document.querySelector('.block-settingsSearchResults'); - const problemSource = document.getElementById('problem-source'); - const problemSourceHelpText = document.getElementById('problem-source-help-text'); - const descriptionTitle = document.getElementById('issue-description-label'); - const descriptionSubtitle = document.getElementById('issue-description-subtitle'); - const extensionSelector = document.getElementById('extension-selection'); + const problemSource = this.getElementById('problem-source')!; + const problemSourceHelpText = this.getElementById('problem-source-help-text')!; + const descriptionTitle = this.getElementById('issue-description-label')!; + const descriptionSubtitle = this.getElementById('issue-description-subtitle')!; + const extensionSelector = this.getElementById('extension-selection')!; // Hide all by default hide(blockContainer); @@ -733,7 +743,7 @@ export class IssueReporter extends Disposable { } private validateInput(inputId: string): boolean { - const inputElement = (document.getElementById(inputId)); + const inputElement = (this.getElementById(inputId)); if (!inputElement.value) { inputElement.classList.add('invalid-input'); return false; @@ -765,16 +775,16 @@ export class IssueReporter extends Disposable { (invalidInput[0]).focus(); } - document.getElementById('issue-title').addEventListener('input', (event) => { + this.addEventListener('issue-title', 'input', _ => { this.validateInput('issue-title'); }); - document.getElementById('description').addEventListener('input', (event) => { + this.addEventListener('description', 'input', _ => { this.validateInput('description'); }); if (this.issueReporterModel.fileOnExtension()) { - document.getElementById('extension-selector').addEventListener('change', (event) => { + this.addEventListener('extension-selector', 'change', _ => { this.validateInput('extension-selector'); }); } @@ -791,7 +801,7 @@ export class IssueReporter extends Disposable { this.telemetryService.publicLog('issueReporterSubmit', { issueType: this.issueReporterModel.getData().issueType, numSimilarIssuesDisplayed: this.numberOfSearchResultsDisplayed }); this.hasBeenSubmitted = true; - const baseUrl = this.getIssueUrlWithTitle((document.getElementById('issue-title')).value); + const baseUrl = this.getIssueUrlWithTitle((this.getElementById('issue-title')).value); const issueBody = this.issueReporterModel.serialize(); let url = baseUrl + `&body=${encodeURIComponent(issueBody)}`; @@ -834,19 +844,21 @@ export class IssueReporter extends Disposable { private updateSystemInfo = (state) => { const target = document.querySelector('.block-system .block-info'); - let tableHtml = ''; - Object.keys(state.systemInfo).forEach(k => { - const data = typeof state.systemInfo[k] === 'object' - ? Object.keys(state.systemInfo[k]).map(key => `${key}: ${state.systemInfo[k][key]}`).join('
') - : state.systemInfo[k]; - - tableHtml += ` - - ${k} - ${data} - `; - }); - target.innerHTML = `${tableHtml}
`; + if (target) { + let tableHtml = ''; + Object.keys(state.systemInfo).forEach(k => { + const data = typeof state.systemInfo[k] === 'object' + ? Object.keys(state.systemInfo[k]).map(key => `${key}: ${state.systemInfo[k][key]}`).join('
') + : state.systemInfo[k]; + + tableHtml += ` + + ${k} + ${data} + `; + }); + target.innerHTML = `${tableHtml}
`; + } } private updateExtensionSelector(extensions: IssueReporterExtensionData[]): void { @@ -878,64 +890,70 @@ export class IssueReporter extends Disposable { }); const makeOption = (extension: IOption) => ``; - const extensionsSelector = document.getElementById('extension-selector'); - extensionsSelector.innerHTML = '' + extensionOptions.map(makeOption).join('\n'); - - this.addEventListener('extension-selector', 'change', (e: Event) => { - const selectedExtensionId = (e.target).value; - const extensions = this.issueReporterModel.getData().allExtensions; - const matches = extensions.filter(extension => extension.id === selectedExtensionId); - if (matches.length) { - this.issueReporterModel.update({ selectedExtension: matches[0] }); - - const title = (document.getElementById('issue-title')).value; - this.searchExtensionIssues(title); - } else { - this.issueReporterModel.update({ selectedExtension: null }); - this.clearSearchResults(); - } - }); + const extensionsSelector = this.getElementById('extension-selector'); + if (extensionsSelector) { + extensionsSelector.innerHTML = '' + extensionOptions.map(makeOption).join('\n'); + + this.addEventListener('extension-selector', 'change', (e: Event) => { + const selectedExtensionId = (e.target).value; + const extensions = this.issueReporterModel.getData().allExtensions; + const matches = extensions.filter(extension => extension.id === selectedExtensionId); + if (matches.length) { + this.issueReporterModel.update({ selectedExtension: matches[0] }); + + const title = (this.getElementById('issue-title')).value; + this.searchExtensionIssues(title); + } else { + this.issueReporterModel.update({ selectedExtension: undefined }); + this.clearSearchResults(); + } + }); + } } private updateProcessInfo = (state) => { const target = document.querySelector('.block-process .block-info'); - target.innerHTML = `${state.processInfo}`; + if (target) { + target.innerHTML = `${state.processInfo}`; + } } private updateWorkspaceInfo = (state) => { - document.querySelector('.block-workspace .block-info code').textContent = '\n' + state.workspaceInfo; + document.querySelector('.block-workspace .block-info code')!.textContent = '\n' + state.workspaceInfo; } private updateExtensionTable(extensions: IssueReporterExtensionData[], numThemeExtensions: number): void { const target = document.querySelector('.block-extensions .block-info'); + if (target) { + if (this.environmentService.disableExtensions) { + target.innerHTML = localize('disabledExtensions', "Extensions are disabled"); + return; + } - if (this.environmentService.disableExtensions) { - target.innerHTML = localize('disabledExtensions', "Extensions are disabled"); - return; - } + const themeExclusionStr = numThemeExtensions ? `\n(${numThemeExtensions} theme extensions excluded)` : ''; + extensions = extensions || []; - const themeExclusionStr = numThemeExtensions ? `\n(${numThemeExtensions} theme extensions excluded)` : ''; - extensions = extensions || []; + if (!extensions.length) { + target.innerHTML = 'Extensions: none' + themeExclusionStr; + return; + } - if (!extensions.length) { - target.innerHTML = 'Extensions: none' + themeExclusionStr; - return; + const table = this.getExtensionTableHtml(extensions); + target.innerHTML = `${table}
${themeExclusionStr}`; } - - const table = this.getExtensionTableHtml(extensions); - target.innerHTML = `${table}
${themeExclusionStr}`; } private updateSearchedExtensionTable(extensions: IssueReporterExtensionData[]): void { const target = document.querySelector('.block-searchedExtensions .block-info'); + if (target) { + if (!extensions.length) { + target.innerHTML = 'Extensions: none'; + return; + } - if (!extensions.length) { - target.innerHTML = 'Extensions: none'; - return; + const table = this.getExtensionTableHtml(extensions); + target.innerHTML = `${table}
`; } - - const table = this.getExtensionTableHtml(extensions); - target.innerHTML = `${table}
`; } private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): string { @@ -972,19 +990,28 @@ export class IssueReporter extends Disposable { } } - private addEventListener(elementId: string, eventType: string, handler: (event: Event) => void): void { + private getElementById(elementId: string): HTMLElement | undefined { const element = document.getElementById(elementId); if (element) { - element.addEventListener(eventType, handler); + return element; } else { const error = new Error(`${elementId} not found.`); this.logService.error(error); /* __GDPR__ - "issueReporterAddEventListenerError" : { + "issueReporterGetElementError" : { "message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" } } */ - this.telemetryService.publicLog('issueReporterAddEventListenerError', { message: error.message }); + this.telemetryService.publicLog('issueReporterGetElementError', { message: error.message }); + + return undefined; + } + } + + private addEventListener(elementId: string, eventType: string, handler: (event: Event) => void): void { + const element = this.getElementById(elementId); + if (element) { + element.addEventListener(eventType, handler); } } } diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-browser/issue/issueReporterModel.ts index 4b5060eeb724..389c0f6668d0 100644 --- a/src/vs/code/electron-browser/issue/issueReporterModel.ts +++ b/src/vs/code/electron-browser/issue/issueReporterModel.ts @@ -7,7 +7,7 @@ import { assign } from 'vs/base/common/objects'; import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; export interface IssueReporterData { - issueType?: IssueType; + issueType: IssueType; issueDescription?: string; versionInfo?: any; @@ -15,15 +15,15 @@ export interface IssueReporterData { processInfo?: any; workspaceInfo?: any; - includeSystemInfo?: boolean; - includeWorkspaceInfo?: boolean; - includeProcessInfo?: boolean; - includeExtensions?: boolean; - includeSearchedExtensions?: boolean; - includeSettingsSearchDetails?: boolean; + includeSystemInfo: boolean; + includeWorkspaceInfo: boolean; + includeProcessInfo: boolean; + includeExtensions: boolean; + includeSearchedExtensions: boolean; + includeSettingsSearchDetails: boolean; numberOfThemeExtesions?: number; - allExtensions?: IssueReporterExtensionData[]; + allExtensions: IssueReporterExtensionData[]; enabledNonThemeExtesions?: IssueReporterExtensionData[]; extensionsDisabled?: boolean; fileOnExtension?: boolean; @@ -36,14 +36,16 @@ export interface IssueReporterData { export class IssueReporterModel { private _data: IssueReporterData; - constructor(initialData?: IssueReporterData) { + constructor(initialData?: Partial) { const defaultData = { + issueType: IssueType.Bug, includeSystemInfo: true, includeWorkspaceInfo: true, includeProcessInfo: true, includeExtensions: true, includeSearchedExtensions: true, - includeSettingsSearchDetails: true + includeSettingsSearchDetails: true, + allExtensions: [] }; this._data = initialData ? assign(defaultData, initialData) : defaultData; @@ -53,7 +55,7 @@ export class IssueReporterModel { return this._data; } - update(newData: IssueReporterData): void { + update(newData: Partial): void { assign(this._data, newData); } @@ -103,7 +105,7 @@ ${this.getInfos()} let info = ''; if (this._data.issueType === IssueType.Bug || this._data.issueType === IssueType.PerformanceIssue) { - if (this._data.includeSystemInfo) { + if (this._data.includeSystemInfo && this._data.systemInfo) { info += this.generateSystemInfoMd(); } } diff --git a/src/vs/code/electron-browser/issue/issueReporterPage.ts b/src/vs/code/electron-browser/issue/issueReporterPage.ts index 78071ea772a8..ae48c9560a13 100644 --- a/src/vs/code/electron-browser/issue/issueReporterPage.ts +++ b/src/vs/code/electron-browser/issue/issueReporterPage.ts @@ -64,9 +64,9 @@ export default (): string => `
+ key: 'sendSystemInfo', + comment: ['{0} is either "show" or "hide" and is a button to toggle the visibililty of the system information'] + }, "Include my system information ({0})")).replace('{0}', `${escape(localize('show', "show"))}`)} @@ -74,9 +74,9 @@ export default (): string => `
+ key: 'sendProcessInfo', + comment: ['{0} is either "show" or "hide" and is a button to toggle the visibililty of the process info'] + }, "Include my currently running processes ({0})")).replace('{0}', `${escape(localize('show', "show"))}`)}