From ecb80f14f0d5a5502c2ba3af5eb4d1bc52545fae Mon Sep 17 00:00:00 2001 From: Anthony Dresser Date: Tue, 20 Aug 2019 21:07:47 -0700 Subject: [PATCH] Merge from vscode 6e530127a1bb8ffbd1bfb77dc680c321dc0d71f5 (#6844) --- build/gulpfile.hygiene.js | 52 +- build/gulpfile.vscode.web.js | 3 + build/lib/tslint/abstractGlobalsRule.js | 40 ++ build/lib/tslint/abstractGlobalsRule.ts | 51 ++ build/lib/tslint/noDOMGlobalsRule.js | 34 ++ build/lib/tslint/noDOMGlobalsRule.ts | 45 ++ build/lib/tslint/noNodeJSGlobalsRule.js | 40 ++ build/lib/tslint/noNodeJSGlobalsRule.ts | 51 ++ extensions/cms/src/cmsUtils.ts | 2 +- .../src/wizard/dataTierApplicationWizard.ts | 2 +- extensions/integration-tests/src/cms.test.ts | 2 +- .../integration-tests/src/dacpac.test.ts | 4 +- .../src/schemaCompare.test.ts | 8 +- .../syntaxes/markdown.tmLanguage.json | 34 +- .../markdown-language-features/media/index.js | 29 +- .../markdown-language-features/package.json | 5 +- .../preview-src/index.ts | 23 +- .../preview-src/scroll-sync.ts | 9 + .../src/commands/index.ts | 4 +- .../src/commands/renderDocument.ts | 20 + .../src/extension.ts | 4 +- .../src/features/preview.ts | 52 +- .../src/features/previewContentProvider.ts | 16 +- .../src/markdownEngine.ts | 31 +- .../src/test/engine.test.ts | 32 ++ .../src/util/resources.ts | 4 +- extensions/mssql/src/main.ts | 4 +- extensions/mssql/src/mssql.d.ts | 2 +- extensions/mssql/src/mssqlApiFactory.ts | 4 +- .../src/dialogs/schemaCompareOptionsDialog.ts | 2 +- .../src/schemaCompareMainWindow.ts | 2 +- package.json | 4 +- remote/package.json | 2 +- remote/yarn.lock | 12 +- resources/completions/zsh/_code | 2 +- src/buildfile.js | 8 +- .../modelComponents/webview.component.ts | 2 +- .../browser/parts/views/customView.ts | 4 +- .../contents/webviewContent.component.ts | 2 +- .../webview/webviewWidget.component.ts | 2 +- .../parts/webview/browser/webViewDialog.ts | 2 +- src/vs/base/browser/dom.ts | 1 + src/vs/base/browser/markdownRenderer.ts | 16 +- .../ui/octiconLabel/octicons/octicons2.css | 4 +- .../ui/octiconLabel/octicons/octicons2.svg | 49 +- .../ui/octiconLabel/octicons/octicons2.ttf | Bin 35276 -> 35564 bytes src/vs/base/browser/ui/splitview/splitview.ts | 3 +- src/vs/base/common/insane/cgmanifest.json | 17 + src/vs/base/common/insane/insane.d.ts | 20 + src/vs/base/common/insane/insane.js | 478 ++++++++++++++++++ src/vs/base/common/insane/insane.license.txt | 20 + src/vs/base/common/network.ts | 7 +- src/vs/base/common/platform.ts | 14 +- src/vs/base/common/search.ts | 23 + .../base/parts/quickopen/common/quickOpen.ts | 4 +- src/vs/code/browser/workbench/workbench.html | 2 +- .../electron-browser/workbench/workbench.html | 2 +- src/vs/code/electron-main/app.ts | 8 +- .../editor/browser/services/openerService.ts | 87 +++- src/vs/editor/common/controller/cursor.ts | 26 +- .../editor/common/controller/cursorCommon.ts | 29 +- .../controller/cursorDeleteOperations.ts | 12 +- .../common/controller/cursorTypeOperations.ts | 272 +++++----- .../common/modes/languageConfiguration.ts | 8 +- .../modes/languageConfigurationRegistry.ts | 25 +- .../common/modes/supports/characterPair.ts | 27 +- .../modes/supports/electricCharacter.ts | 68 +-- .../contrib/documentSymbols/outlineModel.ts | 48 +- src/vs/editor/contrib/find/replacePattern.ts | 15 +- .../contrib/find/test/replacePattern.test.ts | 24 + .../contrib/folding/indentRangeProvider.ts | 31 +- .../folding/test/indentRangeProvider.test.ts | 14 +- .../standalone/browser/standaloneEditor.ts | 11 +- .../common/monarch/monarchCompile.ts | 158 +++--- .../standalone/common/monarch/monarchLexer.ts | 53 +- .../test/browser/controller/cursor.test.ts | 47 +- .../browser/services/openerService.test.ts | 148 +++++- .../modes/supports/characterPair.test.ts | 16 +- .../modes/supports/electricCharacter.test.ts | 77 +-- src/vs/monaco.d.ts | 8 +- src/vs/nls.d.ts | 7 + .../common/configurationRegistry.ts | 4 + .../download/common/downloadService.ts | 2 +- .../common/extensionManagement.ts | 1 + .../common/extensionManagementIpc.ts | 7 +- .../node/extensionManagementService.ts | 6 + .../platform/history/common/historyStorage.ts | 2 +- .../lifecycle/browser/lifecycleService.ts | 12 +- .../electron-browser/menubarService.ts | 2 +- .../platform/menubar/electron-main/menubar.ts | 2 +- .../menubar/electron-main/menubarService.ts | 2 +- .../menubar/{common => node}/menubar.ts | 0 src/vs/platform/menubar/node/menubarIpc.ts | 2 +- src/vs/platform/opener/common/opener.ts | 10 +- .../storage/browser/storageService.ts | 27 +- src/vs/platform/storage/node/storageIpc.ts | 6 +- .../storage/node/storageMainService.ts | 10 + .../browser/workbenchCommonProperties.ts | 7 +- .../url/electron-browser/urlService.ts | 10 +- src/vs/platform/windows/common/windows.ts | 2 + .../workspacesMainService.test.ts | 5 +- src/vs/vscode.d.ts | 24 + src/vs/vscode.proposed.d.ts | 31 -- .../api/browser/mainThreadCodeInsets.ts | 2 +- .../api/browser/mainThreadDiagnostics.ts | 21 +- .../api/browser/mainThreadTreeViews.ts | 30 +- .../api/browser/mainThreadWebview.ts | 154 +++--- .../workbench/api/browser/mainThreadWindow.ts | 2 +- src/vs/workbench/api/common/apiCommands.ts | 7 +- .../api/common/configurationExtensionPoint.ts | 13 +- .../workbench/api/common/extHost.protocol.ts | 14 +- .../workbench/api/common/extHostCodeInsets.ts | 6 +- .../api/common/extHostDiagnostics.ts | 38 +- .../api/common/extHostRequireInterceptor.ts | 8 +- .../api/common/extHostTypeConverters.ts | 18 + src/vs/workbench/api/common/extHostWebview.ts | 45 +- src/vs/workbench/api/common/shared/webview.ts | 2 +- .../api/node/extHostExtensionService.ts | 6 +- .../api/node/extHostTerminalService.ts | 1 + .../api/worker/extHostExtensionService.ts | 2 +- src/vs/workbench/browser/dnd.ts | 2 +- src/vs/workbench/browser/labels.ts | 27 +- .../browser/parts/editor/breadcrumbsModel.ts | 37 +- .../parts/editor/media/breadcrumbscontrol.css | 4 + .../media/notificationsActions.css | 2 +- .../notifications/media/notificationsList.css | 3 +- .../browser/parts/titlebar/menubarControl.ts | 193 +------ .../browser/parts/titlebar/titlebarPart.ts | 25 +- .../browser/parts/views/customView.ts | 4 +- src/vs/workbench/browser/web.main.ts | 2 +- src/vs/workbench/browser/workbench.ts | 8 +- src/vs/workbench/common/views.ts | 2 - .../debug/browser/debugEditorActions.ts | 7 +- .../contrib/debug/browser/debugService.ts | 6 +- .../contrib/debug/browser/debugSession.ts | 21 +- .../contrib/debug/browser/linkDetector.ts | 2 +- .../contrib/debug/browser/rawDebugSession.ts | 6 +- .../workbench/contrib/debug/browser/repl.ts | 59 ++- .../contrib/debug/common/debugProtocol.d.ts | 38 +- .../debug/test/browser/debugModel.test.ts | 5 +- .../experiments/browser/experimentalPrompt.ts | 7 +- .../extensions/browser/extensionEditor.ts | 13 +- .../browser/extensionTipsService.ts | 16 +- .../electron-browser/extensionsSlowActions.ts | 7 +- .../runtimeExtensionsEditor.ts | 13 +- .../contrib/feedback/browser/feedback.ts | 8 +- .../feedback/browser/feedbackStatusbarItem.ts | 6 +- .../files/browser/editors/binaryFileEditor.ts | 2 +- .../contrib/files/browser/views/emptyView.ts | 1 - .../files/common/editors/fileEditorInput.ts | 5 +- .../contrib/outline/browser/outlinePanel.ts | 3 +- .../electron-browser/startupProfiler.ts | 4 +- .../preferences/browser/keybindingsEditor.ts | 2 +- .../preferences/browser/settingsTreeModels.ts | 14 +- .../contrib/preferences/common/preferences.ts | 1 - .../quickopen/browser/gotoLineHandler.ts | 10 +- .../search/browser/media/searchview.css | 14 + .../contrib/search/browser/searchActions.ts | 33 +- .../contrib/search/browser/searchView.ts | 16 +- .../contrib/search/browser/searchWidget.ts | 29 +- .../contrib/search/common/searchModel.ts | 15 +- .../languageSurveys.contribution.ts | 12 +- .../electron-browser/nps.contribution.ts | 9 +- .../tasks/browser/abstractTaskService.ts | 2 +- .../terminal/browser/terminal.contribution.ts | 5 + .../terminal/browser/terminalActions.ts | 4 +- .../terminal/browser/terminalInstance.ts | 58 ++- .../contrib/terminal/browser/terminalPanel.ts | 2 +- .../contrib/terminal/common/terminal.ts | 12 +- .../terminal/common/terminalEnvironment.ts | 2 +- .../contrib/terminal/node/terminalRemote.ts | 4 +- .../terminal/node/windowsShellHelper.ts | 6 +- .../contrib/url/common/url.contribution.ts | 103 +++- .../browser/dynamicWebviewEditorOverlay.ts | 2 +- .../contrib/webview/browser/pre/main.js | 5 + .../webview/browser/webview.contribution.ts | 2 +- .../webview/{common => browser}/webview.ts | 0 .../contrib/webview/browser/webviewEditor.ts | 2 +- .../webview/browser/webviewEditorInput.ts | 2 +- .../webview/browser/webviewEditorService.ts | 2 +- .../contrib/webview/browser/webviewElement.ts | 5 +- .../contrib/webview/browser/webviewService.ts | 2 +- .../electron-browser/webview.contribution.ts | 4 +- .../electron-browser/webviewCommands.ts | 4 +- .../electron-browser/webviewElement.ts | 27 +- .../electron-browser/webviewService.ts | 4 +- .../electron-browser/gettingStarted.ts | 7 +- .../welcome/page/browser/welcomePage.ts | 2 +- .../browser/editor/editorWalkThrough.ts | 2 +- .../browser/walkThrough.contribution.ts | 20 +- .../{common => browser}/walkThroughInput.ts | 0 .../walkThrough/browser/walkThroughPart.ts | 2 +- .../electron-browser/actions/helpActions.ts | 49 +- src/vs/workbench/electron-browser/window.ts | 255 +++++++++- .../configuration/common/configuration.ts | 8 +- .../environment/browser/environmentService.ts | 2 + .../common/extensionManagementService.ts | 37 +- .../node/extensionManagementService.ts | 32 +- .../extensions/browser/extensionService.ts | 49 +- .../node/extensionHostProcessSetup.ts | 1 + .../services/files/common/workspaceWatcher.ts | 8 +- .../integrity/node/integrityService.ts | 6 +- .../keybinding/browser/keybindingService.ts | 2 +- .../keybinding/browser/keymapService.ts | 2 +- .../{common => browser}/navigatorKeyboard.ts | 0 .../opener/electron-browser/openerService.ts | 40 -- .../preferences/common/preferencesModels.ts | 8 +- .../services/search/common/replace.ts | 13 +- .../textfile/browser/textFileService.ts | 6 +- .../textfile/common/textFileService.ts | 4 +- .../common/inMemoryUserDataProvider.ts | 5 +- .../api/extHostDiagnostics.test.ts | 36 ++ .../api/extHostWebview.test.ts | 24 +- .../api/mainThreadDiagnostics.test.ts | 12 +- src/vs/workbench/workbench.common.main.ts | 3 + src/vs/workbench/workbench.desktop.main.ts | 3 +- src/vs/workbench/workbench.web.main.ts | 3 - test/smoke/src/application.ts | 2 +- test/smoke/src/vscode/puppeteerDriver.ts | 5 +- tslint.json | 3 + yarn.lock | 16 +- 221 files changed, 3140 insertions(+), 1552 deletions(-) create mode 100644 build/lib/tslint/abstractGlobalsRule.js create mode 100644 build/lib/tslint/abstractGlobalsRule.ts create mode 100644 build/lib/tslint/noDOMGlobalsRule.js create mode 100644 build/lib/tslint/noDOMGlobalsRule.ts create mode 100644 build/lib/tslint/noNodeJSGlobalsRule.js create mode 100644 build/lib/tslint/noNodeJSGlobalsRule.ts create mode 100644 extensions/markdown-language-features/src/commands/renderDocument.ts create mode 100644 extensions/markdown-language-features/src/test/engine.test.ts create mode 100644 src/vs/base/common/insane/cgmanifest.json create mode 100644 src/vs/base/common/insane/insane.d.ts create mode 100644 src/vs/base/common/insane/insane.js create mode 100644 src/vs/base/common/insane/insane.license.txt create mode 100644 src/vs/base/common/search.ts rename src/vs/platform/menubar/{common => node}/menubar.ts (100%) rename src/vs/workbench/contrib/webview/{common => browser}/webview.ts (100%) rename src/vs/workbench/contrib/welcome/walkThrough/{common => browser}/walkThroughInput.ts (100%) rename src/vs/workbench/services/keybinding/{common => browser}/navigatorKeyboard.ts (100%) delete mode 100644 src/vs/workbench/services/opener/electron-browser/openerService.ts diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index f0b3295720..08a1d7f4be 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -50,6 +50,7 @@ const indentationFilter = [ '!src/vs/css.js', '!src/vs/css.build.js', '!src/vs/loader.js', + '!src/vs/base/common/insane/insane.js', '!src/vs/base/common/marked/marked.js', '!src/vs/base/node/terminateProcess.sh', '!src/vs/base/node/cpuUsage.sh', @@ -171,18 +172,16 @@ const eslintFilter = [ '!src/vs/nls.js', '!src/vs/css.build.js', '!src/vs/nls.build.js', + '!src/**/insane.js', '!src/**/marked.js', '!**/test/**' ]; -const tslintFilter = [ - 'src/**/*.ts', - 'test/**/*.ts', - 'extensions/**/*.ts', +const tslintBaseFilter = [ '!**/fixtures/**', '!**/typings/**', '!**/node_modules/**', - '!extensions/typescript/test/colorize-fixtures/**', + '!extensions/typescript-basics/test/colorize-fixtures/**', '!extensions/vscode-api-tests/testWorkspace/**', '!extensions/vscode-api-tests/testWorkspace2/**', '!extensions/**/*.test.ts', @@ -201,6 +200,29 @@ const sqlFilter = [ ]; // {{SQL CARBON EDIT}} + +const tslintCoreFilter = [ + 'src/**/*.ts', + 'test/**/*.ts', + '!extensions/**/*.ts', + '!test/smoke/**', + ...tslintBaseFilter +]; + +const tslintExtensionsFilter = [ + 'extensions/**/*.ts', + '!src/**/*.ts', + '!test/**/*.ts', + ...tslintBaseFilter +]; + +const tslintHygieneFilter = [ + 'src/**/*.ts', + 'test/**/*.ts', + 'extensions/**/*.ts', + ...tslintBaseFilter +]; + const copyrightHeaderLines = [ '/*---------------------------------------------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', @@ -217,12 +239,20 @@ gulp.task('eslint', () => { }); gulp.task('tslint', () => { - const options = { emitError: true }; + return es.merge([ - return vfs.src(all, { base: '.', follow: true, allowEmpty: true }) - .pipe(filter(tslintFilter)) - .pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint' })) - .pipe(gulptslint.default.report(options)); + // Core: include type information (required by certain rules like no-nodejs-globals) + vfs.src(all, { base: '.', follow: true, allowEmpty: true }) + .pipe(filter(tslintCoreFilter)) + .pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint', program: tslint.Linter.createProgram('src/tsconfig.json') })) + .pipe(gulptslint.default.report({ emitError: true })), + + // Exenstions: do not include type information + vfs.src(all, { base: '.', follow: true, allowEmpty: true }) + .pipe(filter(tslintExtensionsFilter)) + .pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint' })) + .pipe(gulptslint.default.report({ emitError: true })) + ]); }); function hygiene(some) { @@ -363,7 +393,7 @@ function hygiene(some) { .pipe(copyrights); const typescript = result - .pipe(filter(tslintFilter)) + .pipe(filter(tslintHygieneFilter)) .pipe(formatting) .pipe(tsl) // {{SQL CARBON EDIT}} diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index a40c461aa9..7978d3520e 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -41,6 +41,9 @@ const vscodeWebResources = [ // Webview 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', + // Extension Worker + 'out-build/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js', + // Excludes '!out-build/vs/**/{node,electron-browser,electron-main}/**', '!out-build/vs/editor/standalone/**', diff --git a/build/lib/tslint/abstractGlobalsRule.js b/build/lib/tslint/abstractGlobalsRule.js new file mode 100644 index 0000000000..c870e4d2d3 --- /dev/null +++ b/build/lib/tslint/abstractGlobalsRule.js @@ -0,0 +1,40 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const Lint = require("tslint"); +class AbstractGlobalsRuleWalker extends Lint.RuleWalker { + constructor(file, program, opts, _config) { + super(file, opts); + this.program = program; + this._config = _config; + } + visitIdentifier(node) { + if (this.getDisallowedGlobals().some(disallowedGlobal => disallowedGlobal === node.text)) { + if (this._config.allowed && this._config.allowed.some(allowed => allowed === node.text)) { + return; // override + } + const checker = this.program.getTypeChecker(); + const symbol = checker.getSymbolAtLocation(node); + if (symbol) { + const valueDeclaration = symbol.valueDeclaration; + if (valueDeclaration) { + const parent = valueDeclaration.parent; + if (parent) { + const sourceFile = parent.getSourceFile(); + if (sourceFile) { + const fileName = sourceFile.fileName; + if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) { + this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`); + } + } + } + } + } + } + super.visitIdentifier(node); + } +} +exports.AbstractGlobalsRuleWalker = AbstractGlobalsRuleWalker; diff --git a/build/lib/tslint/abstractGlobalsRule.ts b/build/lib/tslint/abstractGlobalsRule.ts new file mode 100644 index 0000000000..676a34d6db --- /dev/null +++ b/build/lib/tslint/abstractGlobalsRule.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; + +interface AbstractGlobalsRuleConfig { + target: string; + allowed: string[]; +} + +export abstract class AbstractGlobalsRuleWalker extends Lint.RuleWalker { + + constructor(file: ts.SourceFile, private program: ts.Program, opts: Lint.IOptions, private _config: AbstractGlobalsRuleConfig) { + super(file, opts); + } + + protected abstract getDisallowedGlobals(): string[]; + + protected abstract getDefinitionPattern(): string; + + visitIdentifier(node: ts.Identifier) { + if (this.getDisallowedGlobals().some(disallowedGlobal => disallowedGlobal === node.text)) { + if (this._config.allowed && this._config.allowed.some(allowed => allowed === node.text)) { + return; // override + } + + const checker = this.program.getTypeChecker(); + const symbol = checker.getSymbolAtLocation(node); + if (symbol) { + const valueDeclaration = symbol.valueDeclaration; + if (valueDeclaration) { + const parent = valueDeclaration.parent; + if (parent) { + const sourceFile = parent.getSourceFile(); + if (sourceFile) { + const fileName = sourceFile.fileName; + if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) { + this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`); + } + } + } + } + } + } + + super.visitIdentifier(node); + } +} diff --git a/build/lib/tslint/noDOMGlobalsRule.js b/build/lib/tslint/noDOMGlobalsRule.js new file mode 100644 index 0000000000..3b992a00e6 --- /dev/null +++ b/build/lib/tslint/noDOMGlobalsRule.js @@ -0,0 +1,34 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const Lint = require("tslint"); +const minimatch = require("minimatch"); +const abstractGlobalsRule_1 = require("./abstractGlobalsRule"); +class Rule extends Lint.Rules.TypedRule { + applyWithProgram(sourceFile, program) { + const configs = this.getOptions().ruleArguments; + for (const config of configs) { + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new NoDOMGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); + } + } + return []; + } +} +exports.Rule = Rule; +class NoDOMGlobalsRuleWalker extends abstractGlobalsRule_1.AbstractGlobalsRuleWalker { + getDefinitionPattern() { + return 'lib.dom.d.ts'; + } + getDisallowedGlobals() { + // intentionally not complete + return [ + "window", + "document", + "HTMLElement" + ]; + } +} diff --git a/build/lib/tslint/noDOMGlobalsRule.ts b/build/lib/tslint/noDOMGlobalsRule.ts new file mode 100644 index 0000000000..f3c493f228 --- /dev/null +++ b/build/lib/tslint/noDOMGlobalsRule.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; +import * as minimatch from 'minimatch'; +import { AbstractGlobalsRuleWalker } from './abstractGlobalsRule'; + +interface NoDOMGlobalsRuleConfig { + target: string; + allowed: string[]; +} + +export class Rule extends Lint.Rules.TypedRule { + + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + const configs = this.getOptions().ruleArguments; + + for (const config of configs) { + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new NoDOMGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); + } + } + + return []; + } +} + +class NoDOMGlobalsRuleWalker extends AbstractGlobalsRuleWalker { + + getDefinitionPattern(): string { + return 'lib.dom.d.ts'; + } + + getDisallowedGlobals(): string[] { + // intentionally not complete + return [ + "window", + "document", + "HTMLElement" + ]; + } +} diff --git a/build/lib/tslint/noNodeJSGlobalsRule.js b/build/lib/tslint/noNodeJSGlobalsRule.js new file mode 100644 index 0000000000..315bf38e54 --- /dev/null +++ b/build/lib/tslint/noNodeJSGlobalsRule.js @@ -0,0 +1,40 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const Lint = require("tslint"); +const minimatch = require("minimatch"); +const abstractGlobalsRule_1 = require("./abstractGlobalsRule"); +class Rule extends Lint.Rules.TypedRule { + applyWithProgram(sourceFile, program) { + const configs = this.getOptions().ruleArguments; + for (const config of configs) { + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new NoNodejsGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); + } + } + return []; + } +} +exports.Rule = Rule; +class NoNodejsGlobalsRuleWalker extends abstractGlobalsRule_1.AbstractGlobalsRuleWalker { + getDefinitionPattern() { + return '@types/node'; + } + getDisallowedGlobals() { + // https://nodejs.org/api/globals.html#globals_global_objects + return [ + "Buffer", + "__dirname", + "__filename", + "clearImmediate", + "exports", + "global", + "module", + "process", + "setImmediate" + ]; + } +} diff --git a/build/lib/tslint/noNodeJSGlobalsRule.ts b/build/lib/tslint/noNodeJSGlobalsRule.ts new file mode 100644 index 0000000000..be6798421d --- /dev/null +++ b/build/lib/tslint/noNodeJSGlobalsRule.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; +import * as minimatch from 'minimatch'; +import { AbstractGlobalsRuleWalker } from './abstractGlobalsRule'; + +interface NoNodejsGlobalsConfig { + target: string; + allowed: string[]; +} + +export class Rule extends Lint.Rules.TypedRule { + + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + const configs = this.getOptions().ruleArguments; + + for (const config of configs) { + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new NoNodejsGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); + } + } + + return []; + } +} + +class NoNodejsGlobalsRuleWalker extends AbstractGlobalsRuleWalker { + + getDefinitionPattern(): string { + return '@types/node'; + } + + getDisallowedGlobals(): string[] { + // https://nodejs.org/api/globals.html#globals_global_objects + return [ + "Buffer", + "__dirname", + "__filename", + "clearImmediate", + "exports", + "global", + "module", + "process", + "setImmediate" + ]; + } +} diff --git a/extensions/cms/src/cmsUtils.ts b/extensions/cms/src/cmsUtils.ts index 4cb6897439..ddc42246c4 100644 --- a/extensions/cms/src/cmsUtils.ts +++ b/extensions/cms/src/cmsUtils.ts @@ -84,7 +84,7 @@ export class CmsUtils { // CMS APIs public async getCmsService(): Promise { if (!this._cmsService) { - this._cmsService = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.mssql).cmsService; + this._cmsService = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.IExtension).cmsService; } return this._cmsService; } diff --git a/extensions/dacpac/src/wizard/dataTierApplicationWizard.ts b/extensions/dacpac/src/wizard/dataTierApplicationWizard.ts index 0b1e26e8fc..9373adb3ee 100644 --- a/extensions/dacpac/src/wizard/dataTierApplicationWizard.ts +++ b/extensions/dacpac/src/wizard/dataTierApplicationWizard.ts @@ -371,7 +371,7 @@ export class DataTierApplicationWizard { } private static async getService(providerName: string): Promise { - const service = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.mssql).dacFx; + const service = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.IExtension).dacFx; return service; } } diff --git a/extensions/integration-tests/src/cms.test.ts b/extensions/integration-tests/src/cms.test.ts index 7d061d0d95..765e7ac185 100644 --- a/extensions/integration-tests/src/cms.test.ts +++ b/extensions/integration-tests/src/cms.test.ts @@ -31,7 +31,7 @@ if (context.RunTest) { setup(async function () { // Set up CMS provider if (!cmsService) { - cmsService = ((await vscode.extensions.getExtension(mssql.extension.name).activate() as mssql.mssql)).cmsService; + cmsService = ((await vscode.extensions.getExtension(mssql.extension.name).activate() as mssql.IExtension)).cmsService; assert(cmsService !== undefined); } diff --git a/extensions/integration-tests/src/dacpac.test.ts b/extensions/integration-tests/src/dacpac.test.ts index 2ddc37fc14..bf56c0ea5b 100644 --- a/extensions/integration-tests/src/dacpac.test.ts +++ b/extensions/integration-tests/src/dacpac.test.ts @@ -36,7 +36,7 @@ if (context.RunTest) { const databaseName = 'ADS_deployDacpac_' + now.getTime().toString(); try { - const dacfxService = ((await vscode.extensions.getExtension(mssql.extension.name).activate() as mssql.mssql)).dacFx; + const dacfxService = ((await vscode.extensions.getExtension(mssql.extension.name).activate() as mssql.IExtension)).dacFx; assert(dacfxService, 'DacFx Service Provider is not available'); // Deploy dacpac @@ -72,7 +72,7 @@ if (context.RunTest) { const databaseName = 'ADS_importBacpac_' + now.getTime().toString(); try { - let dacfxService = ((await vscode.extensions.getExtension(mssql.extension.name).activate() as mssql.mssql)).dacFx; + let dacfxService = ((await vscode.extensions.getExtension(mssql.extension.name).activate() as mssql.IExtension)).dacFx; assert(dacfxService, 'DacFx Service Provider is not available'); // Import bacpac diff --git a/extensions/integration-tests/src/schemaCompare.test.ts b/extensions/integration-tests/src/schemaCompare.test.ts index bda635fb3a..d26be7fe50 100644 --- a/extensions/integration-tests/src/schemaCompare.test.ts +++ b/extensions/integration-tests/src/schemaCompare.test.ts @@ -31,7 +31,7 @@ if (context.RunTest) { suiteSetup(async function () { let attempts: number = 20; while (attempts > 0) { - schemaCompareService = ((await vscode.extensions.getExtension(mssql.extension.name).activate() as mssql.mssql)).schemaCompare; + schemaCompareService = ((await vscode.extensions.getExtension(mssql.extension.name).activate() as mssql.IExtension)).schemaCompare; if (schemaCompareService) { break; } @@ -120,7 +120,7 @@ class SchemaCompareTester { const targetDB: string = 'ads_schemaCompare_targetDB_' + now.getTime().toString(); try { - let dacfxService = ((await vscode.extensions.getExtension(mssql.extension.name).activate() as mssql.mssql)).dacFx; + let dacfxService = ((await vscode.extensions.getExtension(mssql.extension.name).activate() as mssql.IExtension)).dacFx; assert(dacfxService, 'DacFx Service Provider is not available'); let result1 = await dacfxService.deployDacpac(dacpac1, sourceDB, true, ownerUri, azdata.TaskExecutionMode.execute); let result2 = await dacfxService.deployDacpac(dacpac2, targetDB, true, ownerUri, azdata.TaskExecutionMode.execute); @@ -200,7 +200,7 @@ class SchemaCompareTester { const targetDB: string = 'ads_schemaCompare_targetDB_' + now.getTime().toString(); try { - let dacfxService = (vscode.extensions.getExtension('mssql').exports as mssql.mssql).dacFx; + let dacfxService = (vscode.extensions.getExtension('mssql').exports as mssql.IExtension).dacFx; assert(dacfxService, 'DacFx Service Provider is not available'); let result = await dacfxService.deployDacpac(path.join(__dirname, 'testData/Database2.dacpac'), targetDB, true, ownerUri, azdata.TaskExecutionMode.execute); @@ -258,7 +258,7 @@ class SchemaCompareTester { assert(schemaCompareResult.errorMessage === null, `Expected: there should be no error. Actual Error message: "${schemaCompareResult.errorMessage}"`); assert(schemaCompareResult.success === true, `Expected: success in schema compare, Actual: Failure`); assert(schemaCompareResult.differences.length === 4, `Expected: 4 differences. Actual differences: "${schemaCompareResult.differences.length}"`); - assert(schemaCompareResult.operationId === operationId, `Operation Id Expected to be same as passed. Expected : ${operationId}, Actual ${schemaCompareResult.operationId}`) + assert(schemaCompareResult.operationId === operationId, `Operation Id Expected to be same as passed. Expected : ${operationId}, Actual ${schemaCompareResult.operationId}`); } private async assertScriptGenerationResult(resultstatus: azdata.ResultStatus, server: string, database: string): Promise { diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 6fb25ae6ce..3fe94d8cf7 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.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-markdown-tm-grammar/commit/05ccfa3db6edbd357390431f9e316adb38ba41d8", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/eb3898715b50d7911377a650e383a768a3a21f25", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -1855,12 +1855,12 @@ "name": "markup.fenced_code.block.markdown" }, "heading": { - "match": "(?:^|\\G)[ ]{0,3}((#{1,6})\\s*(?=[\\S[^#]]).*?\\s*(#{1,6})?)$\\n?", + "match": "(?:^|\\G)[ ]{0,3}((#{1,6})\\s+(?=[\\S[^#]]).*?\\s*(#{1,6})?)$\\n?", "captures": { "1": { "patterns": [ { - "match": "(#{6})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{6})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.6.markdown", "captures": { "1": { @@ -1875,7 +1875,7 @@ } }, { - "match": "(#{5})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{5})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.5.markdown", "captures": { "1": { @@ -1890,7 +1890,7 @@ } }, { - "match": "(#{4})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{4})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.4.markdown", "captures": { "1": { @@ -1905,7 +1905,7 @@ } }, { - "match": "(#{3})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{3})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.3.markdown", "captures": { "1": { @@ -1920,7 +1920,7 @@ } }, { - "match": "(#{2})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{2})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.2.markdown", "captures": { "1": { @@ -1935,7 +1935,7 @@ } }, { - "match": "(#{1})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{1})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.1.markdown", "captures": { "1": { @@ -1993,7 +1993,7 @@ "1": { "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ] }, @@ -2015,7 +2015,7 @@ "begin": "(\\s*|$)", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ], "while": "(?i)^(?!.*)" @@ -2023,10 +2023,10 @@ ] }, { - "begin": "(?i)(^|\\G)\\s*(?=))", + "begin": "(?i)(^|\\G)\\s*(?=))", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ], "while": "^(?!\\s*$)" @@ -2035,7 +2035,7 @@ "begin": "(^|\\G)\\s*(?=(<[a-zA-Z0-9\\-](/?>|\\s.*?>)|)\\s*$)", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ], "while": "^(?!\\s*$)" @@ -2095,7 +2095,7 @@ "include": "#inline" }, { - "include": "text.html.basic" + "include": "text.html.derivative" }, { "include": "#heading-setext" @@ -2152,7 +2152,7 @@ "include": "#inline" }, { - "include": "text.html.basic" + "include": "text.html.derivative" }, { "include": "#heading-setext" @@ -2246,7 +2246,7 @@ "end": "(?<=>)", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ] }, @@ -2391,7 +2391,7 @@ "end": "(?<=>)", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ] }, diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 62d64e1539..31b478d7eb 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -659,10 +659,20 @@ window.onload = () => { events_1.onceDocumentLoaded(() => { if (settings.scrollPreviewWithEditor) { setTimeout(() => { - const initialLine = +settings.line; - if (!isNaN(initialLine)) { - scrollDisabled = true; - scroll_sync_1.scrollToRevealSourceLine(initialLine); + // Try to scroll to fragment if available + if (state.fragment) { + const element = scroll_sync_1.getLineElementForFragment(state.fragment); + if (element) { + scrollDisabled = true; + scroll_sync_1.scrollToRevealSourceLine(element.line); + } + } + else { + const initialLine = +settings.line; + if (!isNaN(initialLine)) { + scrollDisabled = true; + scroll_sync_1.scrollToRevealSourceLine(initialLine); + } } }, 0); } @@ -940,6 +950,15 @@ function getEditorLineNumberForPageOffset(offset) { return null; } exports.getEditorLineNumberForPageOffset = getEditorLineNumberForPageOffset; +/** + * Try to find the html element by using a fragment id + */ +function getLineElementForFragment(fragment) { + return getCodeLineElements().find((element) => { + return element.element.id === fragment; + }); +} +exports.getLineElementForFragment = getLineElementForFragment; /***/ }), @@ -986,4 +1005,4 @@ exports.getSettings = getSettings; /***/ }) /******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNB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SUFBSSxDQUFDLENBQUM7U0FDdkI7YUFDSTtZQUNKLE1BQU0scUJBQXFCLEdBQUcsa0JBQWtCLEdBQUcsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDM0UsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksR0FBRyxxQkFBcUIsQ0FBQztZQUNuRCxPQUFPLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN2QjtLQUNEO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDYixDQUFDO0FBakJELDRFQWlCQzs7Ozs7Ozs7Ozs7Ozs7QUN2SUQ7OztnR0FHZ0c7O0FBYWhHLElBQUksY0FBYyxHQUFnQyxTQUFTLENBQUM7QUFFNUQsU0FBZ0IsT0FBTyxDQUFTLEdBQVc7SUFDMUMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3hFLElBQUksT0FBTyxFQUFFO1FBQ1osTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxJQUFJLElBQUksRUFBRTtZQUNULE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN4QjtLQUNEO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsR0FBRyxFQUFFLENBQUMsQ0FBQztBQUNuRCxDQUFDO0FBVkQsMEJBVUM7QUFFRCxTQUFnQixXQUFXO0lBQzFCLElBQUksY0FBYyxFQUFFO1FBQ25CLE9BQU8sY0FBYyxDQUFDO0tBQ3RCO0lBRUQsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxJQUFJLGNBQWMsRUFBRTtRQUNuQixPQUFPLGNBQWMsQ0FBQztLQUN0QjtJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBWEQsa0NBV0MiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHtcbiBcdFx0XHRcdGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuIFx0XHRcdFx0Z2V0OiBnZXR0ZXJcbiBcdFx0XHR9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9wcmV2aWV3LXNyYy9pbmRleC50c1wiKTtcbiIsIi8qKlxuICogbG9kYXNoIChDdXN0b20gQnVpbGQpIDxodHRwczovL2xvZGFzaC5jb20vPlxuICogQnVpbGQ6IGBsb2Rhc2ggbW9kdWxhcml6ZSBleHBvcnRzPVwibnBtXCIgLW8gLi9gXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9ycyA8aHR0cHM6Ly9qcXVlcnkub3JnLz5cbiAqIFJlbGVhc2VkIHVuZGVyIE1JVCBsaWNlbnNlIDxodHRwczovL2xvZGFzaC5jb20vbGljZW5zZT5cbiAqIEJhc2VkIG9uIFVuZGVyc2NvcmUuanMgMS44LjMgPGh0dHA6Ly91bmRlcnNjb3JlanMub3JnL0xJQ0VOU0U+XG4gKiBDb3B5cmlnaHQgSmVyZW15IEFzaGtlbmFzLCBEb2N1bWVudENsb3VkIGFuZCBJbnZlc3RpZ2F0aXZlIFJlcG9ydGVycyAmIEVkaXRvcnNcbiAqL1xuXG4vKiogVXNlZCBhcyB0aGUgYFR5cGVFcnJvcmAgbWVzc2FnZSBmb3IgXCJGdW5jdGlvbnNcIiBtZXRob2RzLiAqL1xudmFyIEZVTkNfRVJST1JfVEVYVCA9ICdFeHBlY3RlZCBhIGZ1bmN0aW9uJztcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgdmFyaW91cyBgTnVtYmVyYCBjb25zdGFudHMuICovXG52YXIgTkFOID0gMCAvIDA7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBzeW1ib2xUYWcgPSAnW29iamVjdCBTeW1ib2xdJztcblxuLyoqIFVzZWQgdG8gbWF0Y2ggbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS4gKi9cbnZhciByZVRyaW0gPSAvXlxccyt8XFxzKyQvZztcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJhZCBzaWduZWQgaGV4YWRlY2ltYWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmFkSGV4ID0gL15bLStdMHhbMC05YS1mXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiaW5hcnkgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmluYXJ5ID0gL14wYlswMV0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3Qgb2N0YWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzT2N0YWwgPSAvXjBvWzAtN10rJC9pO1xuXG4vKiogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgd2l0aG91dCBhIGRlcGVuZGVuY3kgb24gYHJvb3RgLiAqL1xudmFyIGZyZWVQYXJzZUludCA9IHBhcnNlSW50O1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYGdsb2JhbGAgZnJvbSBOb2RlLmpzLiAqL1xudmFyIGZyZWVHbG9iYWwgPSB0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGdsb2JhbCAmJiBnbG9iYWwuT2JqZWN0ID09PSBPYmplY3QgJiYgZ2xvYmFsO1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYHNlbGZgLiAqL1xudmFyIGZyZWVTZWxmID0gdHlwZW9mIHNlbGYgPT0gJ29iamVjdCcgJiYgc2VsZiAmJiBzZWxmLk9iamVjdCA9PT0gT2JqZWN0ICYmIHNlbGY7XG5cbi8qKiBVc2VkIGFzIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb2JqZWN0LiAqL1xudmFyIHJvb3QgPSBmcmVlR2xvYmFsIHx8IGZyZWVTZWxmIHx8IEZ1bmN0aW9uKCdyZXR1cm4gdGhpcycpKCk7XG5cbi8qKiBVc2VkIGZvciBidWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZVxuICogW2B0b1N0cmluZ1RhZ2BdKGh0dHA6Ly9lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmplY3RUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXgsXG4gICAgbmF0aXZlTWluID0gTWF0aC5taW47XG5cbi8qKlxuICogR2V0cyB0aGUgdGltZXN0YW1wIG9mIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRoYXQgaGF2ZSBlbGFwc2VkIHNpbmNlXG4gKiB0aGUgVW5peCBlcG9jaCAoMSBKYW51YXJ5IDE5NzAgMDA6MDA6MDAgVVRDKS5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDIuNC4wXG4gKiBAY2F0ZWdvcnkgRGF0ZVxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgdGltZXN0YW1wLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmRlZmVyKGZ1bmN0aW9uKHN0YW1wKSB7XG4gKiAgIGNvbnNvbGUubG9nKF8ubm93KCkgLSBzdGFtcCk7XG4gKiB9LCBfLm5vdygpKTtcbiAqIC8vID0+IExvZ3MgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgaXQgdG9vayBmb3IgdGhlIGRlZmVycmVkIGludm9jYXRpb24uXG4gKi9cbnZhciBub3cgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHJvb3QuRGF0ZS5ub3coKTtcbn07XG5cbi8qKlxuICogQ3JlYXRlcyBhIGRlYm91bmNlZCBmdW5jdGlvbiB0aGF0IGRlbGF5cyBpbnZva2luZyBgZnVuY2AgdW50aWwgYWZ0ZXIgYHdhaXRgXG4gKiBtaWxsaXNlY29uZHMgaGF2ZSBlbGFwc2VkIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhlIGRlYm91bmNlZCBmdW5jdGlvbiB3YXNcbiAqIGludm9rZWQuIFRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgIG1ldGhvZCB0byBjYW5jZWxcbiAqIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLlxuICogUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2Agc2hvdWxkIGJlIGludm9rZWQgb24gdGhlXG4gKiBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGAgdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkXG4gKiB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50XG4gKiBjYWxscyB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHJldHVybiB0aGUgcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYFxuICogaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIGRlYm91bmNlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy5kZWJvdW5jZWAgYW5kIGBfLnRocm90dGxlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRlYm91bmNlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIGRlbGF5LlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9ZmFsc2VdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtudW1iZXJ9IFtvcHRpb25zLm1heFdhaXRdXG4gKiAgVGhlIG1heGltdW0gdGltZSBgZnVuY2AgaXMgYWxsb3dlZCB0byBiZSBkZWxheWVkIGJlZm9yZSBpdCdzIGludm9rZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGRlYm91bmNlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgY29zdGx5IGNhbGN1bGF0aW9ucyB3aGlsZSB0aGUgd2luZG93IHNpemUgaXMgaW4gZmx1eC5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdyZXNpemUnLCBfLmRlYm91bmNlKGNhbGN1bGF0ZUxheW91dCwgMTUwKSk7XG4gKlxuICogLy8gSW52b2tlIGBzZW5kTWFpbGAgd2hlbiBjbGlja2VkLCBkZWJvdW5jaW5nIHN1YnNlcXVlbnQgY2FsbHMuXG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgXy5kZWJvdW5jZShzZW5kTWFpbCwgMzAwLCB7XG4gKiAgICdsZWFkaW5nJzogdHJ1ZSxcbiAqICAgJ3RyYWlsaW5nJzogZmFsc2VcbiAqIH0pKTtcbiAqXG4gKiAvLyBFbnN1cmUgYGJhdGNoTG9nYCBpcyBpbnZva2VkIG9uY2UgYWZ0ZXIgMSBzZWNvbmQgb2YgZGVib3VuY2VkIGNhbGxzLlxuICogdmFyIGRlYm91bmNlZCA9IF8uZGVib3VuY2UoYmF0Y2hMb2csIDI1MCwgeyAnbWF4V2FpdCc6IDEwMDAgfSk7XG4gKiB2YXIgc291cmNlID0gbmV3IEV2ZW50U291cmNlKCcvc3RyZWFtJyk7XG4gKiBqUXVlcnkoc291cmNlKS5vbignbWVzc2FnZScsIGRlYm91bmNlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyBkZWJvdW5jZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIGRlYm91bmNlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsYXN0QXJncyxcbiAgICAgIGxhc3RUaGlzLFxuICAgICAgbWF4V2FpdCxcbiAgICAgIHJlc3VsdCxcbiAgICAgIHRpbWVySWQsXG4gICAgICBsYXN0Q2FsbFRpbWUsXG4gICAgICBsYXN0SW52b2tlVGltZSA9IDAsXG4gICAgICBsZWFkaW5nID0gZmFsc2UsXG4gICAgICBtYXhpbmcgPSBmYWxzZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICB3YWl0ID0gdG9OdW1iZXIod2FpdCkgfHwgMDtcbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICEhb3B0aW9ucy5sZWFkaW5nO1xuICAgIG1heGluZyA9ICdtYXhXYWl0JyBpbiBvcHRpb25zO1xuICAgIG1heFdhaXQgPSBtYXhpbmcgPyBuYXRpdmVNYXgodG9OdW1iZXIob3B0aW9ucy5tYXhXYWl0KSB8fCAwLCB3YWl0KSA6IG1heFdhaXQ7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIGludm9rZUZ1bmModGltZSkge1xuICAgIHZhciBhcmdzID0gbGFzdEFyZ3MsXG4gICAgICAgIHRoaXNBcmcgPSBsYXN0VGhpcztcblxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIHJlc3VsdCA9IGZ1bmMuYXBwbHkodGhpc0FyZywgYXJncyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxlYWRpbmdFZGdlKHRpbWUpIHtcbiAgICAvLyBSZXNldCBhbnkgYG1heFdhaXRgIHRpbWVyLlxuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICAvLyBTdGFydCB0aGUgdGltZXIgZm9yIHRoZSB0cmFpbGluZyBlZGdlLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgLy8gSW52b2tlIHRoZSBsZWFkaW5nIGVkZ2UuXG4gICAgcmV0dXJuIGxlYWRpbmcgPyBpbnZva2VGdW5jKHRpbWUpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtYWluaW5nV2FpdCh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZSxcbiAgICAgICAgcmVzdWx0ID0gd2FpdCAtIHRpbWVTaW5jZUxhc3RDYWxsO1xuXG4gICAgcmV0dXJuIG1heGluZyA/IG5hdGl2ZU1pbihyZXN1bHQsIG1heFdhaXQgLSB0aW1lU2luY2VMYXN0SW52b2tlKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHNob3VsZEludm9rZSh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZTtcblxuICAgIC8vIEVpdGhlciB0aGlzIGlzIHRoZSBmaXJzdCBjYWxsLCBhY3Rpdml0eSBoYXMgc3RvcHBlZCBhbmQgd2UncmUgYXQgdGhlXG4gICAgLy8gdHJhaWxpbmcgZWRnZSwgdGhlIHN5c3RlbSB0aW1lIGhhcyBnb25lIGJhY2t3YXJkcyBhbmQgd2UncmUgdHJlYXRpbmdcbiAgICAvLyBpdCBhcyB0aGUgdHJhaWxpbmcgZWRnZSwgb3Igd2UndmUgaGl0IHRoZSBgbWF4V2FpdGAgbGltaXQuXG4gICAgcmV0dXJuIChsYXN0Q2FsbFRpbWUgPT09IHVuZGVmaW5lZCB8fCAodGltZVNpbmNlTGFzdENhbGwgPj0gd2FpdCkgfHxcbiAgICAgICh0aW1lU2luY2VMYXN0Q2FsbCA8IDApIHx8IChtYXhpbmcgJiYgdGltZVNpbmNlTGFzdEludm9rZSA+PSBtYXhXYWl0KSk7XG4gIH1cblxuICBmdW5jdGlvbiB0aW1lckV4cGlyZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKTtcbiAgICBpZiAoc2hvdWxkSW52b2tlKHRpbWUpKSB7XG4gICAgICByZXR1cm4gdHJhaWxpbmdFZGdlKHRpbWUpO1xuICAgIH1cbiAgICAvLyBSZXN0YXJ0IHRoZSB0aW1lci5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHJlbWFpbmluZ1dhaXQodGltZSkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdHJhaWxpbmdFZGdlKHRpbWUpIHtcbiAgICB0aW1lcklkID0gdW5kZWZpbmVkO1xuXG4gICAgLy8gT25seSBpbnZva2UgaWYgd2UgaGF2ZSBgbGFzdEFyZ3NgIHdoaWNoIG1lYW5zIGBmdW5jYCBoYXMgYmVlblxuICAgIC8vIGRlYm91bmNlZCBhdCBsZWFzdCBvbmNlLlxuICAgIGlmICh0cmFpbGluZyAmJiBsYXN0QXJncykge1xuICAgICAgcmV0dXJuIGludm9rZUZ1bmModGltZSk7XG4gICAgfVxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhbmNlbCgpIHtcbiAgICBpZiAodGltZXJJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXJJZCk7XG4gICAgfVxuICAgIGxhc3RJbnZva2VUaW1lID0gMDtcbiAgICBsYXN0QXJncyA9IGxhc3RDYWxsVGltZSA9IGxhc3RUaGlzID0gdGltZXJJZCA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZsdXNoKCkge1xuICAgIHJldHVybiB0aW1lcklkID09PSB1bmRlZmluZWQgPyByZXN1bHQgOiB0cmFpbGluZ0VkZ2Uobm93KCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gZGVib3VuY2VkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCksXG4gICAgICAgIGlzSW52b2tpbmcgPSBzaG91bGRJbnZva2UodGltZSk7XG5cbiAgICBsYXN0QXJncyA9IGFyZ3VtZW50cztcbiAgICBsYXN0VGhpcyA9IHRoaXM7XG4gICAgbGFzdENhbGxUaW1lID0gdGltZTtcblxuICAgIGlmIChpc0ludm9raW5nKSB7XG4gICAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBsZWFkaW5nRWRnZShsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgICAgaWYgKG1heGluZykge1xuICAgICAgICAvLyBIYW5kbGUgaW52b2NhdGlvbnMgaW4gYSB0aWdodCBsb29wLlxuICAgICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgICAgICByZXR1cm4gaW52b2tlRnVuYyhsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGRlYm91bmNlZC5jYW5jZWwgPSBjYW5jZWw7XG4gIGRlYm91bmNlZC5mbHVzaCA9IGZsdXNoO1xuICByZXR1cm4gZGVib3VuY2VkO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSB0aHJvdHRsZWQgZnVuY3Rpb24gdGhhdCBvbmx5IGludm9rZXMgYGZ1bmNgIGF0IG1vc3Qgb25jZSBwZXJcbiAqIGV2ZXJ5IGB3YWl0YCBtaWxsaXNlY29uZHMuIFRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgXG4gKiBtZXRob2QgdG8gY2FuY2VsIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvXG4gKiBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS4gUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2BcbiAqIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZSBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGBcbiAqIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZCB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGVcbiAqIHRocm90dGxlZCBmdW5jdGlvbi4gU3Vic2VxdWVudCBjYWxscyB0byB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uIHJldHVybiB0aGVcbiAqIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2AgaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIHRocm90dGxlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy50aHJvdHRsZWAgYW5kIGBfLmRlYm91bmNlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIHRocm90dGxlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHRocm90dGxlIGludm9jYXRpb25zIHRvLlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IHRocm90dGxlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgZXhjZXNzaXZlbHkgdXBkYXRpbmcgdGhlIHBvc2l0aW9uIHdoaWxlIHNjcm9sbGluZy5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdzY3JvbGwnLCBfLnRocm90dGxlKHVwZGF0ZVBvc2l0aW9uLCAxMDApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHJlbmV3VG9rZW5gIHdoZW4gdGhlIGNsaWNrIGV2ZW50IGlzIGZpcmVkLCBidXQgbm90IG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IDUgbWludXRlcy5cbiAqIHZhciB0aHJvdHRsZWQgPSBfLnRocm90dGxlKHJlbmV3VG9rZW4sIDMwMDAwMCwgeyAndHJhaWxpbmcnOiBmYWxzZSB9KTtcbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCB0aHJvdHRsZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgdGhyb3R0bGVkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCB0aHJvdHRsZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gdGhyb3R0bGUoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGVhZGluZyA9IHRydWUsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICdsZWFkaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLmxlYWRpbmcgOiBsZWFkaW5nO1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cbiAgcmV0dXJuIGRlYm91bmNlKGZ1bmMsIHdhaXQsIHtcbiAgICAnbGVhZGluZyc6IGxlYWRpbmcsXG4gICAgJ21heFdhaXQnOiB3YWl0LFxuICAgICd0cmFpbGluZyc6IHRyYWlsaW5nXG4gIH0pO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIHRoZVxuICogW2xhbmd1YWdlIHR5cGVdKGh0dHA6Ly93d3cuZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1lY21hc2NyaXB0LWxhbmd1YWdlLXR5cGVzKVxuICogb2YgYE9iamVjdGAuIChlLmcuIGFycmF5cywgZnVuY3Rpb25zLCBvYmplY3RzLCByZWdleGVzLCBgbmV3IE51bWJlcigwKWAsIGFuZCBgbmV3IFN0cmluZygnJylgKVxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGFuIG9iamVjdCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0KHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChfLm5vb3ApO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdCh2YWx1ZSkge1xuICB2YXIgdHlwZSA9IHR5cGVvZiB2YWx1ZTtcbiAgcmV0dXJuICEhdmFsdWUgJiYgKHR5cGUgPT0gJ29iamVjdCcgfHwgdHlwZSA9PSAnZnVuY3Rpb24nKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZS4gQSB2YWx1ZSBpcyBvYmplY3QtbGlrZSBpZiBpdCdzIG5vdCBgbnVsbGBcbiAqIGFuZCBoYXMgYSBgdHlwZW9mYCByZXN1bHQgb2YgXCJvYmplY3RcIi5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZSwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZSh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShfLm5vb3ApO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0TGlrZSh2YWx1ZSkge1xuICByZXR1cm4gISF2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT0gJ29iamVjdCc7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhIGBTeW1ib2xgIHByaW1pdGl2ZSBvciBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYSBzeW1ib2wsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc1N5bWJvbChTeW1ib2wuaXRlcmF0b3IpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNTeW1ib2woJ2FiYycpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNTeW1ib2wodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnc3ltYm9sJyB8fFxuICAgIChpc09iamVjdExpa2UodmFsdWUpICYmIG9iamVjdFRvU3RyaW5nLmNhbGwodmFsdWUpID09IHN5bWJvbFRhZyk7XG59XG5cbi8qKlxuICogQ29udmVydHMgYHZhbHVlYCB0byBhIG51bWJlci5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcHJvY2Vzcy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIG51bWJlci5cbiAqIEBleGFtcGxlXG4gKlxuICogXy50b051bWJlcigzLjIpO1xuICogLy8gPT4gMy4yXG4gKlxuICogXy50b051bWJlcihOdW1iZXIuTUlOX1ZBTFVFKTtcbiAqIC8vID0+IDVlLTMyNFxuICpcbiAqIF8udG9OdW1iZXIoSW5maW5pdHkpO1xuICogLy8gPT4gSW5maW5pdHlcbiAqXG4gKiBfLnRvTnVtYmVyKCczLjInKTtcbiAqIC8vID0+IDMuMlxuICovXG5mdW5jdGlvbiB0b051bWJlcih2YWx1ZSkge1xuICBpZiAodHlwZW9mIHZhbHVlID09ICdudW1iZXInKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGlmIChpc1N5bWJvbCh2YWx1ZSkpIHtcbiAgICByZXR1cm4gTkFOO1xuICB9XG4gIGlmIChpc09iamVjdCh2YWx1ZSkpIHtcbiAgICB2YXIgb3RoZXIgPSB0eXBlb2YgdmFsdWUudmFsdWVPZiA9PSAnZnVuY3Rpb24nID8gdmFsdWUudmFsdWVPZigpIDogdmFsdWU7XG4gICAgdmFsdWUgPSBpc09iamVjdChvdGhlcikgPyAob3RoZXIgKyAnJykgOiBvdGhlcjtcbiAgfVxuICBpZiAodHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlID09PSAwID8gdmFsdWUgOiArdmFsdWU7XG4gIH1cbiAgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKHJlVHJpbSwgJycpO1xuICB2YXIgaXNCaW5hcnkgPSByZUlzQmluYXJ5LnRlc3QodmFsdWUpO1xuICByZXR1cm4gKGlzQmluYXJ5IHx8IHJlSXNPY3RhbC50ZXN0KHZhbHVlKSlcbiAgICA/IGZyZWVQYXJzZUludCh2YWx1ZS5zbGljZSgyKSwgaXNCaW5hcnkgPyAyIDogOClcbiAgICA6IChyZUlzQmFkSGV4LnRlc3QodmFsdWUpID8gTkFOIDogK3ZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aHJvdHRsZTtcbiIsInZhciBnO1xyXG5cclxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcclxuZyA9IChmdW5jdGlvbigpIHtcclxuXHRyZXR1cm4gdGhpcztcclxufSkoKTtcclxuXHJcbnRyeSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXHJcblx0ZyA9IGcgfHwgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpIHx8ICgxLCBldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2ggKGUpIHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxyXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xyXG59XHJcblxyXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXHJcbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXHJcbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gZztcclxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5pbXBvcnQgeyBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcblxuZXhwb3J0IGNsYXNzIEFjdGl2ZUxpbmVNYXJrZXIge1xuXHRwcml2YXRlIF9jdXJyZW50OiBhbnk7XG5cblx0b25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmU6IG51bWJlcikge1xuXHRcdGNvbnN0IHsgcHJldmlvdXMgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0XHR0aGlzLl91cGRhdGUocHJldmlvdXMgJiYgcHJldmlvdXMuZWxlbWVudCk7XG5cdH1cblxuXHRfdXBkYXRlKGJlZm9yZTogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHR0aGlzLl91bm1hcmtBY3RpdmVFbGVtZW50KHRoaXMuX2N1cnJlbnQpO1xuXHRcdHRoaXMuX21hcmtBY3RpdmVFbGVtZW50KGJlZm9yZSk7XG5cdFx0dGhpcy5fY3VycmVudCA9IGJlZm9yZTtcblx0fVxuXG5cdF91bm1hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQ6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkKSB7XG5cdFx0aWYgKCFlbGVtZW50KSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGVsZW1lbnQuY2xhc3NOYW1lID0gZWxlbWVudC5jbGFzc05hbWUucmVwbGFjZSgvXFxiY29kZS1hY3RpdmUtbGluZVxcYi9nLCAnJyk7XG5cdH1cblxuXHRfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgKz0gJyBjb2RlLWFjdGl2ZS1saW5lJztcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGY6ICgpID0+IHZvaWQpIHtcblx0aWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJyB8fCBkb2N1bWVudC5yZWFkeVN0YXRlIGFzIHN0cmluZyA9PT0gJ3VuaW5pdGlhbGl6ZWQnKSB7XG5cdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGYpO1xuXHR9IGVsc2Uge1xuXHRcdGYoKTtcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBBY3RpdmVMaW5lTWFya2VyIH0gZnJvbSAnLi9hY3RpdmVMaW5lTWFya2VyJztcbmltcG9ydCB7IG9uY2VEb2N1bWVudExvYWRlZCB9IGZyb20gJy4vZXZlbnRzJztcbmltcG9ydCB7IGNyZWF0ZVBvc3RlckZvclZzQ29kZSB9IGZyb20gJy4vbWVzc2FnaW5nJztcbmltcG9ydCB7IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0LCBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcbmltcG9ydCB7IGdldFNldHRpbmdzLCBnZXREYXRhIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5pbXBvcnQgdGhyb3R0bGUgPSByZXF1aXJlKCdsb2Rhc2gudGhyb3R0bGUnKTtcblxuZGVjbGFyZSB2YXIgYWNxdWlyZVZzQ29kZUFwaTogYW55O1xuXG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IEFjdGl2ZUxpbmVNYXJrZXIoKTtcbmNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuY29uc3QgdnNjb2RlID0gYWNxdWlyZVZzQ29kZUFwaSgpO1xuXG4vLyBTZXQgVlMgQ29kZSBzdGF0ZVxubGV0IHN0YXRlID0gZ2V0RGF0YTx7IGxpbmU6IG51bWJlciB9PignZGF0YS1zdGF0ZScpO1xudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblxuY29uc3QgbWVzc2FnaW5nID0gY3JlYXRlUG9zdGVyRm9yVnNDb2RlKHZzY29kZSk7XG5cbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG5cdHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5cbm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG5cdGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuXHRcdHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdGlmICghaXNOYU4oaW5pdGlhbExpbmUpKSB7XG5cdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdH1cblx0XHR9LCAwKTtcblx0fVxufSk7XG5cbmNvbnN0IG9uVXBkYXRlVmlldyA9ICgoKSA9PiB7XG5cdGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmU6IG51bWJlcikgPT4ge1xuXHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG5cdH0sIDUwKTtcblxuXHRyZXR1cm4gKGxpbmU6IG51bWJlciwgc2V0dGluZ3M6IGFueSkgPT4ge1xuXHRcdGlmICghaXNOYU4obGluZSkpIHtcblx0XHRcdHNldHRpbmdzLmxpbmUgPSBsaW5lO1xuXHRcdFx0ZG9TY3JvbGwobGluZSk7XG5cdFx0fVxuXHR9O1xufSkoKTtcblxubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG5cdGNvbnN0IGltYWdlSW5mbzogeyBpZDogc3RyaW5nLCBoZWlnaHQ6IG51bWJlciwgd2lkdGg6IG51bWJlciB9W10gPSBbXTtcblx0bGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcblx0aWYgKGltYWdlcykge1xuXHRcdGxldCBpO1xuXHRcdGZvciAoaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyBpKyspIHtcblx0XHRcdGNvbnN0IGltZyA9IGltYWdlc1tpXTtcblxuXHRcdFx0aWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuXHRcdFx0XHRpbWcuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHRpbWFnZUluZm8ucHVzaCh7XG5cdFx0XHRcdGlkOiBpbWcuaWQsXG5cdFx0XHRcdGhlaWdodDogaW1nLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IGltZy53aWR0aFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuXHR9XG59LCA1MCk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG5cdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZXZlbnQgPT4ge1xuXHRpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG5cdFx0Y2FzZSAnb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uJzpcblx0XHRcdG1hcmtlci5vbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24oZXZlbnQuZGF0YS5saW5lKTtcblx0XHRcdGJyZWFrO1xuXG5cdFx0Y2FzZSAndXBkYXRlVmlldyc6XG5cdFx0XHRvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG5cdFx0XHRicmVhaztcblx0fVxufSwgZmFsc2UpO1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcblx0aWYgKCFzZXR0aW5ncy5kb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHQvLyBJZ25vcmUgY2xpY2tzIG9uIGxpbmtzXG5cdGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lID09PSAnQScpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdH1cblxuXHRjb25zdCBvZmZzZXQgPSBldmVudC5wYWdlWTtcblx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcblx0fVxufSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIWV2ZW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0bGV0IG5vZGU6IGFueSA9IGV2ZW50LnRhcmdldDtcblx0d2hpbGUgKG5vZGUpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuXHRcdFx0aWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKG5vZGUuaHJlZi5zdGFydHNXaXRoKCdmaWxlOi8vJykgfHwgbm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ3ZzY29kZS1yZXNvdXJjZTonKSB8fCBub2RlLmhyZWYuc3RhcnRzV2l0aChzZXR0aW5ncy53ZWJ2aWV3UmVzb3VyY2VSb290KSkge1xuXHRcdFx0XHRjb25zdCBbcGF0aCwgZnJhZ21lbnRdID0gbm9kZS5ocmVmLnJlcGxhY2UoL14oZmlsZTpcXC9cXC98dnNjb2RlLXJlc291cmNlOikvaSwgJycpLnJlcGxhY2UobmV3IFJlZ0V4cChgXiR7ZXNjYXBlUmVnRXhwKHNldHRpbmdzLndlYnZpZXdSZXNvdXJjZVJvb3QpfWApKS5zcGxpdCgnIycpO1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NsaWNrTGluaycsIHsgcGF0aCwgZnJhZ21lbnQgfSk7XG5cdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblx0XHRcdGJyZWFrO1xuXHRcdH1cblx0XHRub2RlID0gbm9kZS5wYXJlbnROb2RlO1xuXHR9XG59LCB0cnVlKTtcblxuaWYgKHNldHRpbmdzLnNjcm9sbEVkaXRvcldpdGhQcmV2aWV3KSB7XG5cdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdzY3JvbGwnLCB0aHJvdHRsZSgoKSA9PiB7XG5cdFx0aWYgKHNjcm9sbERpc2FibGVkKSB7XG5cdFx0XHRzY3JvbGxEaXNhYmxlZCA9IGZhbHNlO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRjb25zdCBsaW5lID0gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQod2luZG93LnNjcm9sbFkpO1xuXHRcdFx0aWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcblx0XHRcdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdyZXZlYWxMaW5lJywgeyBsaW5lIH0pO1xuXHRcdFx0XHRzdGF0ZS5saW5lID0gbGluZTtcblx0XHRcdFx0dnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sIDUwKSk7XG59XG5cbmZ1bmN0aW9uIGVzY2FwZVJlZ0V4cCh0ZXh0OiBzdHJpbmcpIHtcblx0cmV0dXJuIHRleHQucmVwbGFjZSgvWy1bXFxde30oKSorPy4sXFxcXF4kfCNcXHNdL2csICdcXFxcJCYnKTtcbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgZ2V0U2V0dGluZ3MgfSBmcm9tICcuL3NldHRpbmdzJztcblxuZXhwb3J0IGludGVyZmFjZSBNZXNzYWdlUG9zdGVyIHtcblx0LyoqXG5cdCAqIFBvc3QgYSBtZXNzYWdlIHRvIHRoZSBtYXJrZG93biBleHRlbnNpb25cblx0ICovXG5cdHBvc3RNZXNzYWdlKHR5cGU6IHN0cmluZywgYm9keTogb2JqZWN0KTogdm9pZDtcbn1cblxuZXhwb3J0IGNvbnN0IGNyZWF0ZVBvc3RlckZvclZzQ29kZSA9ICh2c2NvZGU6IGFueSkgPT4ge1xuXHRyZXR1cm4gbmV3IGNsYXNzIGltcGxlbWVudHMgTWVzc2FnZVBvc3RlciB7XG5cdFx0cG9zdE1lc3NhZ2UodHlwZTogc3RyaW5nLCBib2R5OiBvYmplY3QpOiB2b2lkIHtcblx0XHRcdHZzY29kZS5wb3N0TWVzc2FnZSh7XG5cdFx0XHRcdHR5cGUsXG5cdFx0XHRcdHNvdXJjZTogZ2V0U2V0dGluZ3MoKS5zb3VyY2UsXG5cdFx0XHRcdGJvZHlcblx0XHRcdH0pO1xuXHRcdH1cblx0fTtcbn07XG5cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuXG5cbmZ1bmN0aW9uIGNsYW1wKG1pbjogbnVtYmVyLCBtYXg6IG51bWJlciwgdmFsdWU6IG51bWJlcikge1xuXHRyZXR1cm4gTWF0aC5taW4obWF4LCBNYXRoLm1heChtaW4sIHZhbHVlKSk7XG59XG5cbmZ1bmN0aW9uIGNsYW1wTGluZShsaW5lOiBudW1iZXIpIHtcblx0cmV0dXJuIGNsYW1wKDAsIGdldFNldHRpbmdzKCkubGluZUNvdW50IC0gMSwgbGluZSk7XG59XG5cblxuZXhwb3J0IGludGVyZmFjZSBDb2RlTGluZUVsZW1lbnQge1xuXHRlbGVtZW50OiBIVE1MRWxlbWVudDtcblx0bGluZTogbnVtYmVyO1xufVxuXG5jb25zdCBnZXRDb2RlTGluZUVsZW1lbnRzID0gKCgpID0+IHtcblx0bGV0IGVsZW1lbnRzOiBDb2RlTGluZUVsZW1lbnRbXTtcblx0cmV0dXJuICgpID0+IHtcblx0XHRpZiAoIWVsZW1lbnRzKSB7XG5cdFx0XHRlbGVtZW50cyA9IFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV07XG5cdFx0XHRmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS1saW5lJykpIHtcblx0XHRcdFx0Y29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJykhO1xuXHRcdFx0XHRpZiAoIWlzTmFOKGxpbmUpKSB7XG5cdFx0XHRcdFx0ZWxlbWVudHMucHVzaCh7IGVsZW1lbnQ6IGVsZW1lbnQgYXMgSFRNTEVsZW1lbnQsIGxpbmUgfSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIGVsZW1lbnRzO1xuXHR9O1xufSkoKTtcblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgbWFwIHRvIGEgc3BlY2lmaWMgdGFyZ2V0IGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqXG4gKiBJZiBhbiBleGFjdCBtYXRjaCwgcmV0dXJucyBhIHNpbmdsZSBlbGVtZW50LiBJZiB0aGUgbGluZSBpcyBiZXR3ZWVuIGVsZW1lbnRzLFxuICogcmV0dXJucyB0aGUgZWxlbWVudCBwcmlvciB0byBhbmQgdGhlIGVsZW1lbnQgYWZ0ZXIgdGhlIGdpdmVuIGxpbmUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUodGFyZ2V0TGluZTogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZU51bWJlciA9IE1hdGguZmxvb3IodGFyZ2V0TGluZSk7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRsZXQgcHJldmlvdXMgPSBsaW5lc1swXSB8fCBudWxsO1xuXHRmb3IgKGNvbnN0IGVudHJ5IG9mIGxpbmVzKSB7XG5cdFx0aWYgKGVudHJ5LmxpbmUgPT09IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzOiBlbnRyeSwgbmV4dDogdW5kZWZpbmVkIH07XG5cdFx0fSBlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuXHRcdFx0cmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG5cdFx0fVxuXHRcdHByZXZpb3VzID0gZW50cnk7XG5cdH1cblx0cmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG5cdGNvbnN0IHBvc2l0aW9uID0gb2Zmc2V0IC0gd2luZG93LnNjcm9sbFk7XG5cdGxldCBsbyA9IC0xO1xuXHRsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuXHR3aGlsZSAobG8gKyAxIDwgaGkpIHtcblx0XHRjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuXHRcdGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcblx0XHRcdGhpID0gbWlkO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGxvID0gbWlkO1xuXHRcdH1cblx0fVxuXHRjb25zdCBoaUVsZW1lbnQgPSBsaW5lc1toaV07XG5cdGNvbnN0IGhpQm91bmRzID0gaGlFbGVtZW50LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG5cdFx0Y29uc3QgbG9FbGVtZW50ID0gbGluZXNbbG9dO1xuXHRcdHJldHVybiB7IHByZXZpb3VzOiBsb0VsZW1lbnQsIG5leHQ6IGhpRWxlbWVudCB9O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmU6IG51bWJlcikge1xuXHRpZiAoIWdldFNldHRpbmdzKCkuc2Nyb2xsUHJldmlld1dpdGhFZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRpZiAobGluZSA8PSAwKSB7XG5cdFx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRpZiAoIXByZXZpb3VzKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdGxldCBzY3JvbGxUbyA9IDA7XG5cdGNvbnN0IHJlY3QgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuXHRpZiAobmV4dCAmJiBuZXh0LmxpbmUgIT09IHByZXZpb3VzLmxpbmUpIHtcblx0XHQvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuXHRcdGNvbnN0IGJldHdlZW5Qcm9ncmVzcyA9IChsaW5lIC0gcHJldmlvdXMubGluZSkgLyAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0Y29uc3QgZWxlbWVudE9mZnNldCA9IG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c1RvcDtcblx0XHRzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcblx0fSBlbHNlIHtcblx0XHRjb25zdCBwcm9ncmVzc0luRWxlbWVudCA9IGxpbmUgLSBNYXRoLmZsb29yKGxpbmUpO1xuXHRcdHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG5cdH1cblx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgTWF0aC5tYXgoMSwgd2luZG93LnNjcm9sbFkgKyBzY3JvbGxUbykpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0OiBudW1iZXIpIHtcblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmIChwcmV2aW91cykge1xuXHRcdGNvbnN0IHByZXZpb3VzQm91bmRzID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRjb25zdCBvZmZzZXRGcm9tUHJldmlvdXMgPSAob2Zmc2V0IC0gd2luZG93LnNjcm9sbFkgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuXHRcdGlmIChuZXh0KSB7XG5cdFx0XHRjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcblx0XHRcdGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgKiAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuXHRcdFx0Y29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0fVxuXHRyZXR1cm4gbnVsbDtcbn1cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgaW50ZXJmYWNlIFByZXZpZXdTZXR0aW5ncyB7XG5cdHJlYWRvbmx5IHNvdXJjZTogc3RyaW5nO1xuXHRyZWFkb25seSBsaW5lOiBudW1iZXI7XG5cdHJlYWRvbmx5IGxpbmVDb3VudDogbnVtYmVyO1xuXHRyZWFkb25seSBzY3JvbGxQcmV2aWV3V2l0aEVkaXRvcj86IGJvb2xlYW47XG5cdHJlYWRvbmx5IHNjcm9sbEVkaXRvcldpdGhQcmV2aWV3OiBib29sZWFuO1xuXHRyZWFkb25seSBkaXNhYmxlU2VjdXJpdHlXYXJuaW5nczogYm9vbGVhbjtcblx0cmVhZG9ubHkgZG91YmxlQ2xpY2tUb1N3aXRjaFRvRWRpdG9yOiBib29sZWFuO1xuXHRyZWFkb25seSB3ZWJ2aWV3UmVzb3VyY2VSb290OiBzdHJpbmc7XG59XG5cbmxldCBjYWNoZWRTZXR0aW5nczogUHJldmlld1NldHRpbmdzIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGF0YTxUID0ge30+KGtleTogc3RyaW5nKTogVCB7XG5cdGNvbnN0IGVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuXHRpZiAoZWxlbWVudCkge1xuXHRcdGNvbnN0IGRhdGEgPSBlbGVtZW50LmdldEF0dHJpYnV0ZShrZXkpO1xuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcblx0XHR9XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKTogUHJldmlld1NldHRpbmdzIHtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0Y2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNB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SSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN4QjtLQUNEO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsR0FBRyxFQUFFLENBQUMsQ0FBQztBQUNuRCxDQUFDO0FBVkQsMEJBVUM7QUFFRCxTQUFnQixXQUFXO0lBQzFCLElBQUksY0FBYyxFQUFFO1FBQ25CLE9BQU8sY0FBYyxDQUFDO0tBQ3RCO0lBRUQsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxJQUFJLGNBQWMsRUFBRTtRQUNuQixPQUFPLGNBQWMsQ0FBQztLQUN0QjtJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBWEQsa0NBV0MiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHtcbiBcdFx0XHRcdGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuIFx0XHRcdFx0Z2V0OiBnZXR0ZXJcbiBcdFx0XHR9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9wcmV2aWV3LXNyYy9pbmRleC50c1wiKTtcbiIsIi8qKlxuICogbG9kYXNoIChDdXN0b20gQnVpbGQpIDxodHRwczovL2xvZGFzaC5jb20vPlxuICogQnVpbGQ6IGBsb2Rhc2ggbW9kdWxhcml6ZSBleHBvcnRzPVwibnBtXCIgLW8gLi9gXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9ycyA8aHR0cHM6Ly9qcXVlcnkub3JnLz5cbiAqIFJlbGVhc2VkIHVuZGVyIE1JVCBsaWNlbnNlIDxodHRwczovL2xvZGFzaC5jb20vbGljZW5zZT5cbiAqIEJhc2VkIG9uIFVuZGVyc2NvcmUuanMgMS44LjMgPGh0dHA6Ly91bmRlcnNjb3JlanMub3JnL0xJQ0VOU0U+XG4gKiBDb3B5cmlnaHQgSmVyZW15IEFzaGtlbmFzLCBEb2N1bWVudENsb3VkIGFuZCBJbnZlc3RpZ2F0aXZlIFJlcG9ydGVycyAmIEVkaXRvcnNcbiAqL1xuXG4vKiogVXNlZCBhcyB0aGUgYFR5cGVFcnJvcmAgbWVzc2FnZSBmb3IgXCJGdW5jdGlvbnNcIiBtZXRob2RzLiAqL1xudmFyIEZVTkNfRVJST1JfVEVYVCA9ICdFeHBlY3RlZCBhIGZ1bmN0aW9uJztcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgdmFyaW91cyBgTnVtYmVyYCBjb25zdGFudHMuICovXG52YXIgTkFOID0gMCAvIDA7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBzeW1ib2xUYWcgPSAnW29iamVjdCBTeW1ib2xdJztcblxuLyoqIFVzZWQgdG8gbWF0Y2ggbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS4gKi9cbnZhciByZVRyaW0gPSAvXlxccyt8XFxzKyQvZztcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJhZCBzaWduZWQgaGV4YWRlY2ltYWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmFkSGV4ID0gL15bLStdMHhbMC05YS1mXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiaW5hcnkgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmluYXJ5ID0gL14wYlswMV0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3Qgb2N0YWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzT2N0YWwgPSAvXjBvWzAtN10rJC9pO1xuXG4vKiogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgd2l0aG91dCBhIGRlcGVuZGVuY3kgb24gYHJvb3RgLiAqL1xudmFyIGZyZWVQYXJzZUludCA9IHBhcnNlSW50O1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYGdsb2JhbGAgZnJvbSBOb2RlLmpzLiAqL1xudmFyIGZyZWVHbG9iYWwgPSB0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGdsb2JhbCAmJiBnbG9iYWwuT2JqZWN0ID09PSBPYmplY3QgJiYgZ2xvYmFsO1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYHNlbGZgLiAqL1xudmFyIGZyZWVTZWxmID0gdHlwZW9mIHNlbGYgPT0gJ29iamVjdCcgJiYgc2VsZiAmJiBzZWxmLk9iamVjdCA9PT0gT2JqZWN0ICYmIHNlbGY7XG5cbi8qKiBVc2VkIGFzIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb2JqZWN0LiAqL1xudmFyIHJvb3QgPSBmcmVlR2xvYmFsIHx8IGZyZWVTZWxmIHx8IEZ1bmN0aW9uKCdyZXR1cm4gdGhpcycpKCk7XG5cbi8qKiBVc2VkIGZvciBidWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZVxuICogW2B0b1N0cmluZ1RhZ2BdKGh0dHA6Ly9lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmplY3RUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXgsXG4gICAgbmF0aXZlTWluID0gTWF0aC5taW47XG5cbi8qKlxuICogR2V0cyB0aGUgdGltZXN0YW1wIG9mIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRoYXQgaGF2ZSBlbGFwc2VkIHNpbmNlXG4gKiB0aGUgVW5peCBlcG9jaCAoMSBKYW51YXJ5IDE5NzAgMDA6MDA6MDAgVVRDKS5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDIuNC4wXG4gKiBAY2F0ZWdvcnkgRGF0ZVxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgdGltZXN0YW1wLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmRlZmVyKGZ1bmN0aW9uKHN0YW1wKSB7XG4gKiAgIGNvbnNvbGUubG9nKF8ubm93KCkgLSBzdGFtcCk7XG4gKiB9LCBfLm5vdygpKTtcbiAqIC8vID0+IExvZ3MgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgaXQgdG9vayBmb3IgdGhlIGRlZmVycmVkIGludm9jYXRpb24uXG4gKi9cbnZhciBub3cgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHJvb3QuRGF0ZS5ub3coKTtcbn07XG5cbi8qKlxuICogQ3JlYXRlcyBhIGRlYm91bmNlZCBmdW5jdGlvbiB0aGF0IGRlbGF5cyBpbnZva2luZyBgZnVuY2AgdW50aWwgYWZ0ZXIgYHdhaXRgXG4gKiBtaWxsaXNlY29uZHMgaGF2ZSBlbGFwc2VkIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhlIGRlYm91bmNlZCBmdW5jdGlvbiB3YXNcbiAqIGludm9rZWQuIFRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgIG1ldGhvZCB0byBjYW5jZWxcbiAqIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLlxuICogUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2Agc2hvdWxkIGJlIGludm9rZWQgb24gdGhlXG4gKiBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGAgdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkXG4gKiB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50XG4gKiBjYWxscyB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHJldHVybiB0aGUgcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYFxuICogaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIGRlYm91bmNlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy5kZWJvdW5jZWAgYW5kIGBfLnRocm90dGxlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRlYm91bmNlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIGRlbGF5LlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9ZmFsc2VdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtudW1iZXJ9IFtvcHRpb25zLm1heFdhaXRdXG4gKiAgVGhlIG1heGltdW0gdGltZSBgZnVuY2AgaXMgYWxsb3dlZCB0byBiZSBkZWxheWVkIGJlZm9yZSBpdCdzIGludm9rZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGRlYm91bmNlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgY29zdGx5IGNhbGN1bGF0aW9ucyB3aGlsZSB0aGUgd2luZG93IHNpemUgaXMgaW4gZmx1eC5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdyZXNpemUnLCBfLmRlYm91bmNlKGNhbGN1bGF0ZUxheW91dCwgMTUwKSk7XG4gKlxuICogLy8gSW52b2tlIGBzZW5kTWFpbGAgd2hlbiBjbGlja2VkLCBkZWJvdW5jaW5nIHN1YnNlcXVlbnQgY2FsbHMuXG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgXy5kZWJvdW5jZShzZW5kTWFpbCwgMzAwLCB7XG4gKiAgICdsZWFkaW5nJzogdHJ1ZSxcbiAqICAgJ3RyYWlsaW5nJzogZmFsc2VcbiAqIH0pKTtcbiAqXG4gKiAvLyBFbnN1cmUgYGJhdGNoTG9nYCBpcyBpbnZva2VkIG9uY2UgYWZ0ZXIgMSBzZWNvbmQgb2YgZGVib3VuY2VkIGNhbGxzLlxuICogdmFyIGRlYm91bmNlZCA9IF8uZGVib3VuY2UoYmF0Y2hMb2csIDI1MCwgeyAnbWF4V2FpdCc6IDEwMDAgfSk7XG4gKiB2YXIgc291cmNlID0gbmV3IEV2ZW50U291cmNlKCcvc3RyZWFtJyk7XG4gKiBqUXVlcnkoc291cmNlKS5vbignbWVzc2FnZScsIGRlYm91bmNlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyBkZWJvdW5jZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIGRlYm91bmNlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsYXN0QXJncyxcbiAgICAgIGxhc3RUaGlzLFxuICAgICAgbWF4V2FpdCxcbiAgICAgIHJlc3VsdCxcbiAgICAgIHRpbWVySWQsXG4gICAgICBsYXN0Q2FsbFRpbWUsXG4gICAgICBsYXN0SW52b2tlVGltZSA9IDAsXG4gICAgICBsZWFkaW5nID0gZmFsc2UsXG4gICAgICBtYXhpbmcgPSBmYWxzZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICB3YWl0ID0gdG9OdW1iZXIod2FpdCkgfHwgMDtcbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICEhb3B0aW9ucy5sZWFkaW5nO1xuICAgIG1heGluZyA9ICdtYXhXYWl0JyBpbiBvcHRpb25zO1xuICAgIG1heFdhaXQgPSBtYXhpbmcgPyBuYXRpdmVNYXgodG9OdW1iZXIob3B0aW9ucy5tYXhXYWl0KSB8fCAwLCB3YWl0KSA6IG1heFdhaXQ7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIGludm9rZUZ1bmModGltZSkge1xuICAgIHZhciBhcmdzID0gbGFzdEFyZ3MsXG4gICAgICAgIHRoaXNBcmcgPSBsYXN0VGhpcztcblxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIHJlc3VsdCA9IGZ1bmMuYXBwbHkodGhpc0FyZywgYXJncyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxlYWRpbmdFZGdlKHRpbWUpIHtcbiAgICAvLyBSZXNldCBhbnkgYG1heFdhaXRgIHRpbWVyLlxuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICAvLyBTdGFydCB0aGUgdGltZXIgZm9yIHRoZSB0cmFpbGluZyBlZGdlLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgLy8gSW52b2tlIHRoZSBsZWFkaW5nIGVkZ2UuXG4gICAgcmV0dXJuIGxlYWRpbmcgPyBpbnZva2VGdW5jKHRpbWUpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtYWluaW5nV2FpdCh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZSxcbiAgICAgICAgcmVzdWx0ID0gd2FpdCAtIHRpbWVTaW5jZUxhc3RDYWxsO1xuXG4gICAgcmV0dXJuIG1heGluZyA/IG5hdGl2ZU1pbihyZXN1bHQsIG1heFdhaXQgLSB0aW1lU2luY2VMYXN0SW52b2tlKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHNob3VsZEludm9rZSh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZTtcblxuICAgIC8vIEVpdGhlciB0aGlzIGlzIHRoZSBmaXJzdCBjYWxsLCBhY3Rpdml0eSBoYXMgc3RvcHBlZCBhbmQgd2UncmUgYXQgdGhlXG4gICAgLy8gdHJhaWxpbmcgZWRnZSwgdGhlIHN5c3RlbSB0aW1lIGhhcyBnb25lIGJhY2t3YXJkcyBhbmQgd2UncmUgdHJlYXRpbmdcbiAgICAvLyBpdCBhcyB0aGUgdHJhaWxpbmcgZWRnZSwgb3Igd2UndmUgaGl0IHRoZSBgbWF4V2FpdGAgbGltaXQuXG4gICAgcmV0dXJuIChsYXN0Q2FsbFRpbWUgPT09IHVuZGVmaW5lZCB8fCAodGltZVNpbmNlTGFzdENhbGwgPj0gd2FpdCkgfHxcbiAgICAgICh0aW1lU2luY2VMYXN0Q2FsbCA8IDApIHx8IChtYXhpbmcgJiYgdGltZVNpbmNlTGFzdEludm9rZSA+PSBtYXhXYWl0KSk7XG4gIH1cblxuICBmdW5jdGlvbiB0aW1lckV4cGlyZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKTtcbiAgICBpZiAoc2hvdWxkSW52b2tlKHRpbWUpKSB7XG4gICAgICByZXR1cm4gdHJhaWxpbmdFZGdlKHRpbWUpO1xuICAgIH1cbiAgICAvLyBSZXN0YXJ0IHRoZSB0aW1lci5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHJlbWFpbmluZ1dhaXQodGltZSkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdHJhaWxpbmdFZGdlKHRpbWUpIHtcbiAgICB0aW1lcklkID0gdW5kZWZpbmVkO1xuXG4gICAgLy8gT25seSBpbnZva2UgaWYgd2UgaGF2ZSBgbGFzdEFyZ3NgIHdoaWNoIG1lYW5zIGBmdW5jYCBoYXMgYmVlblxuICAgIC8vIGRlYm91bmNlZCBhdCBsZWFzdCBvbmNlLlxuICAgIGlmICh0cmFpbGluZyAmJiBsYXN0QXJncykge1xuICAgICAgcmV0dXJuIGludm9rZUZ1bmModGltZSk7XG4gICAgfVxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhbmNlbCgpIHtcbiAgICBpZiAodGltZXJJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXJJZCk7XG4gICAgfVxuICAgIGxhc3RJbnZva2VUaW1lID0gMDtcbiAgICBsYXN0QXJncyA9IGxhc3RDYWxsVGltZSA9IGxhc3RUaGlzID0gdGltZXJJZCA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZsdXNoKCkge1xuICAgIHJldHVybiB0aW1lcklkID09PSB1bmRlZmluZWQgPyByZXN1bHQgOiB0cmFpbGluZ0VkZ2Uobm93KCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gZGVib3VuY2VkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCksXG4gICAgICAgIGlzSW52b2tpbmcgPSBzaG91bGRJbnZva2UodGltZSk7XG5cbiAgICBsYXN0QXJncyA9IGFyZ3VtZW50cztcbiAgICBsYXN0VGhpcyA9IHRoaXM7XG4gICAgbGFzdENhbGxUaW1lID0gdGltZTtcblxuICAgIGlmIChpc0ludm9raW5nKSB7XG4gICAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBsZWFkaW5nRWRnZShsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgICAgaWYgKG1heGluZykge1xuICAgICAgICAvLyBIYW5kbGUgaW52b2NhdGlvbnMgaW4gYSB0aWdodCBsb29wLlxuICAgICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgICAgICByZXR1cm4gaW52b2tlRnVuYyhsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGRlYm91bmNlZC5jYW5jZWwgPSBjYW5jZWw7XG4gIGRlYm91bmNlZC5mbHVzaCA9IGZsdXNoO1xuICByZXR1cm4gZGVib3VuY2VkO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSB0aHJvdHRsZWQgZnVuY3Rpb24gdGhhdCBvbmx5IGludm9rZXMgYGZ1bmNgIGF0IG1vc3Qgb25jZSBwZXJcbiAqIGV2ZXJ5IGB3YWl0YCBtaWxsaXNlY29uZHMuIFRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgXG4gKiBtZXRob2QgdG8gY2FuY2VsIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvXG4gKiBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS4gUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2BcbiAqIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZSBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGBcbiAqIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZCB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGVcbiAqIHRocm90dGxlZCBmdW5jdGlvbi4gU3Vic2VxdWVudCBjYWxscyB0byB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uIHJldHVybiB0aGVcbiAqIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2AgaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIHRocm90dGxlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy50aHJvdHRsZWAgYW5kIGBfLmRlYm91bmNlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIHRocm90dGxlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHRocm90dGxlIGludm9jYXRpb25zIHRvLlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IHRocm90dGxlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgZXhjZXNzaXZlbHkgdXBkYXRpbmcgdGhlIHBvc2l0aW9uIHdoaWxlIHNjcm9sbGluZy5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdzY3JvbGwnLCBfLnRocm90dGxlKHVwZGF0ZVBvc2l0aW9uLCAxMDApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHJlbmV3VG9rZW5gIHdoZW4gdGhlIGNsaWNrIGV2ZW50IGlzIGZpcmVkLCBidXQgbm90IG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IDUgbWludXRlcy5cbiAqIHZhciB0aHJvdHRsZWQgPSBfLnRocm90dGxlKHJlbmV3VG9rZW4sIDMwMDAwMCwgeyAndHJhaWxpbmcnOiBmYWxzZSB9KTtcbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCB0aHJvdHRsZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgdGhyb3R0bGVkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCB0aHJvdHRsZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gdGhyb3R0bGUoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGVhZGluZyA9IHRydWUsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICdsZWFkaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLmxlYWRpbmcgOiBsZWFkaW5nO1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cbiAgcmV0dXJuIGRlYm91bmNlKGZ1bmMsIHdhaXQsIHtcbiAgICAnbGVhZGluZyc6IGxlYWRpbmcsXG4gICAgJ21heFdhaXQnOiB3YWl0LFxuICAgICd0cmFpbGluZyc6IHRyYWlsaW5nXG4gIH0pO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIHRoZVxuICogW2xhbmd1YWdlIHR5cGVdKGh0dHA6Ly93d3cuZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1lY21hc2NyaXB0LWxhbmd1YWdlLXR5cGVzKVxuICogb2YgYE9iamVjdGAuIChlLmcuIGFycmF5cywgZnVuY3Rpb25zLCBvYmplY3RzLCByZWdleGVzLCBgbmV3IE51bWJlcigwKWAsIGFuZCBgbmV3IFN0cmluZygnJylgKVxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGFuIG9iamVjdCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0KHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChfLm5vb3ApO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdCh2YWx1ZSkge1xuICB2YXIgdHlwZSA9IHR5cGVvZiB2YWx1ZTtcbiAgcmV0dXJuICEhdmFsdWUgJiYgKHR5cGUgPT0gJ29iamVjdCcgfHwgdHlwZSA9PSAnZnVuY3Rpb24nKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZS4gQSB2YWx1ZSBpcyBvYmplY3QtbGlrZSBpZiBpdCdzIG5vdCBgbnVsbGBcbiAqIGFuZCBoYXMgYSBgdHlwZW9mYCByZXN1bHQgb2YgXCJvYmplY3RcIi5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZSwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZSh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShfLm5vb3ApO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0TGlrZSh2YWx1ZSkge1xuICByZXR1cm4gISF2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT0gJ29iamVjdCc7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhIGBTeW1ib2xgIHByaW1pdGl2ZSBvciBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYSBzeW1ib2wsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc1N5bWJvbChTeW1ib2wuaXRlcmF0b3IpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNTeW1ib2woJ2FiYycpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNTeW1ib2wodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnc3ltYm9sJyB8fFxuICAgIChpc09iamVjdExpa2UodmFsdWUpICYmIG9iamVjdFRvU3RyaW5nLmNhbGwodmFsdWUpID09IHN5bWJvbFRhZyk7XG59XG5cbi8qKlxuICogQ29udmVydHMgYHZhbHVlYCB0byBhIG51bWJlci5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcHJvY2Vzcy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIG51bWJlci5cbiAqIEBleGFtcGxlXG4gKlxuICogXy50b051bWJlcigzLjIpO1xuICogLy8gPT4gMy4yXG4gKlxuICogXy50b051bWJlcihOdW1iZXIuTUlOX1ZBTFVFKTtcbiAqIC8vID0+IDVlLTMyNFxuICpcbiAqIF8udG9OdW1iZXIoSW5maW5pdHkpO1xuICogLy8gPT4gSW5maW5pdHlcbiAqXG4gKiBfLnRvTnVtYmVyKCczLjInKTtcbiAqIC8vID0+IDMuMlxuICovXG5mdW5jdGlvbiB0b051bWJlcih2YWx1ZSkge1xuICBpZiAodHlwZW9mIHZhbHVlID09ICdudW1iZXInKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGlmIChpc1N5bWJvbCh2YWx1ZSkpIHtcbiAgICByZXR1cm4gTkFOO1xuICB9XG4gIGlmIChpc09iamVjdCh2YWx1ZSkpIHtcbiAgICB2YXIgb3RoZXIgPSB0eXBlb2YgdmFsdWUudmFsdWVPZiA9PSAnZnVuY3Rpb24nID8gdmFsdWUudmFsdWVPZigpIDogdmFsdWU7XG4gICAgdmFsdWUgPSBpc09iamVjdChvdGhlcikgPyAob3RoZXIgKyAnJykgOiBvdGhlcjtcbiAgfVxuICBpZiAodHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlID09PSAwID8gdmFsdWUgOiArdmFsdWU7XG4gIH1cbiAgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKHJlVHJpbSwgJycpO1xuICB2YXIgaXNCaW5hcnkgPSByZUlzQmluYXJ5LnRlc3QodmFsdWUpO1xuICByZXR1cm4gKGlzQmluYXJ5IHx8IHJlSXNPY3RhbC50ZXN0KHZhbHVlKSlcbiAgICA/IGZyZWVQYXJzZUludCh2YWx1ZS5zbGljZSgyKSwgaXNCaW5hcnkgPyAyIDogOClcbiAgICA6IChyZUlzQmFkSGV4LnRlc3QodmFsdWUpID8gTkFOIDogK3ZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aHJvdHRsZTtcbiIsInZhciBnO1xyXG5cclxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcclxuZyA9IChmdW5jdGlvbigpIHtcclxuXHRyZXR1cm4gdGhpcztcclxufSkoKTtcclxuXHJcbnRyeSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXHJcblx0ZyA9IGcgfHwgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpIHx8ICgxLCBldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2ggKGUpIHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxyXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xyXG59XHJcblxyXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXHJcbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXHJcbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gZztcclxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5pbXBvcnQgeyBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcblxuZXhwb3J0IGNsYXNzIEFjdGl2ZUxpbmVNYXJrZXIge1xuXHRwcml2YXRlIF9jdXJyZW50OiBhbnk7XG5cblx0b25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmU6IG51bWJlcikge1xuXHRcdGNvbnN0IHsgcHJldmlvdXMgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0XHR0aGlzLl91cGRhdGUocHJldmlvdXMgJiYgcHJldmlvdXMuZWxlbWVudCk7XG5cdH1cblxuXHRfdXBkYXRlKGJlZm9yZTogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHR0aGlzLl91bm1hcmtBY3RpdmVFbGVtZW50KHRoaXMuX2N1cnJlbnQpO1xuXHRcdHRoaXMuX21hcmtBY3RpdmVFbGVtZW50KGJlZm9yZSk7XG5cdFx0dGhpcy5fY3VycmVudCA9IGJlZm9yZTtcblx0fVxuXG5cdF91bm1hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQ6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkKSB7XG5cdFx0aWYgKCFlbGVtZW50KSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGVsZW1lbnQuY2xhc3NOYW1lID0gZWxlbWVudC5jbGFzc05hbWUucmVwbGFjZSgvXFxiY29kZS1hY3RpdmUtbGluZVxcYi9nLCAnJyk7XG5cdH1cblxuXHRfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgKz0gJyBjb2RlLWFjdGl2ZS1saW5lJztcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGY6ICgpID0+IHZvaWQpIHtcblx0aWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJyB8fCBkb2N1bWVudC5yZWFkeVN0YXRlIGFzIHN0cmluZyA9PT0gJ3VuaW5pdGlhbGl6ZWQnKSB7XG5cdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGYpO1xuXHR9IGVsc2Uge1xuXHRcdGYoKTtcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBBY3RpdmVMaW5lTWFya2VyIH0gZnJvbSAnLi9hY3RpdmVMaW5lTWFya2VyJztcbmltcG9ydCB7IG9uY2VEb2N1bWVudExvYWRlZCB9IGZyb20gJy4vZXZlbnRzJztcbmltcG9ydCB7IGNyZWF0ZVBvc3RlckZvclZzQ29kZSB9IGZyb20gJy4vbWVzc2FnaW5nJztcbmltcG9ydCB7IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0LCBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUsIGdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcbmltcG9ydCB7IGdldFNldHRpbmdzLCBnZXREYXRhIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5pbXBvcnQgdGhyb3R0bGUgPSByZXF1aXJlKCdsb2Rhc2gudGhyb3R0bGUnKTtcblxuZGVjbGFyZSB2YXIgYWNxdWlyZVZzQ29kZUFwaTogYW55O1xuXG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IEFjdGl2ZUxpbmVNYXJrZXIoKTtcbmNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuY29uc3QgdnNjb2RlID0gYWNxdWlyZVZzQ29kZUFwaSgpO1xuXG4vLyBTZXQgVlMgQ29kZSBzdGF0ZVxubGV0IHN0YXRlID0gZ2V0RGF0YTx7IGxpbmU6IG51bWJlciwgIGZyYWdtZW50OiBzdHJpbmcgfT4oJ2RhdGEtc3RhdGUnKTtcbnZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG5cbmNvbnN0IG1lc3NhZ2luZyA9IGNyZWF0ZVBvc3RlckZvclZzQ29kZSh2c2NvZGUpO1xuXG53aW5kb3cuY3NwQWxlcnRlci5zZXRQb3N0ZXIobWVzc2FnaW5nKTtcbndpbmRvdy5zdHlsZUxvYWRpbmdNb25pdG9yLnNldFBvc3RlcihtZXNzYWdpbmcpO1xuXG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xuXHR1cGRhdGVJbWFnZVNpemVzKCk7XG59O1xuXG5vbmNlRG9jdW1lbnRMb2FkZWQoKCkgPT4ge1xuXHRpZiAoc2V0dGluZ3Muc2Nyb2xsUHJldmlld1dpdGhFZGl0b3IpIHtcblx0XHRzZXRUaW1lb3V0KCgpID0+IHtcblx0XHRcdC8vIFRyeSB0byBzY3JvbGwgdG8gZnJhZ21lbnQgaWYgYXZhaWxhYmxlXG5cdFx0XHRpZiAoc3RhdGUuZnJhZ21lbnQpIHtcblx0XHRcdFx0Y29uc3QgZWxlbWVudCA9IGdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQoc3RhdGUuZnJhZ21lbnQpO1xuXHRcdFx0XHRpZiAoZWxlbWVudCkge1xuXHRcdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUoZWxlbWVudC5saW5lKTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdFx0aWYgKCFpc05hTihpbml0aWFsTGluZSkpIHtcblx0XHRcdFx0XHRzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG5cdFx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0sIDApO1xuXHR9XG59KTtcblxuY29uc3Qgb25VcGRhdGVWaWV3ID0gKCgpID0+IHtcblx0Y29uc3QgZG9TY3JvbGwgPSB0aHJvdHRsZSgobGluZTogbnVtYmVyKSA9PiB7XG5cdFx0c2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuXHRcdHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lKTtcblx0fSwgNTApO1xuXG5cdHJldHVybiAobGluZTogbnVtYmVyLCBzZXR0aW5nczogYW55KSA9PiB7XG5cdFx0aWYgKCFpc05hTihsaW5lKSkge1xuXHRcdFx0c2V0dGluZ3MubGluZSA9IGxpbmU7XG5cdFx0XHRkb1Njcm9sbChsaW5lKTtcblx0XHR9XG5cdH07XG59KSgpO1xuXG5sZXQgdXBkYXRlSW1hZ2VTaXplcyA9IHRocm90dGxlKCgpID0+IHtcblx0Y29uc3QgaW1hZ2VJbmZvOiB7IGlkOiBzdHJpbmcsIGhlaWdodDogbnVtYmVyLCB3aWR0aDogbnVtYmVyIH1bXSA9IFtdO1xuXHRsZXQgaW1hZ2VzID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2ltZycpO1xuXHRpZiAoaW1hZ2VzKSB7XG5cdFx0bGV0IGk7XG5cdFx0Zm9yIChpID0gMDsgaSA8IGltYWdlcy5sZW5ndGg7IGkrKykge1xuXHRcdFx0Y29uc3QgaW1nID0gaW1hZ2VzW2ldO1xuXG5cdFx0XHRpZiAoaW1nLmNsYXNzTGlzdC5jb250YWlucygnbG9hZGluZycpKSB7XG5cdFx0XHRcdGltZy5jbGFzc0xpc3QucmVtb3ZlKCdsb2FkaW5nJyk7XG5cdFx0XHR9XG5cblx0XHRcdGltYWdlSW5mby5wdXNoKHtcblx0XHRcdFx0aWQ6IGltZy5pZCxcblx0XHRcdFx0aGVpZ2h0OiBpbWcuaGVpZ2h0LFxuXHRcdFx0XHR3aWR0aDogaW1nLndpZHRoXG5cdFx0XHR9KTtcblx0XHR9XG5cblx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NhY2hlSW1hZ2VTaXplcycsIGltYWdlSW5mbyk7XG5cdH1cbn0sIDUwKTtcblxud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsICgpID0+IHtcblx0c2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuXHR1cGRhdGVJbWFnZVNpemVzKCk7XG59LCB0cnVlKTtcblxud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCBldmVudCA9PiB7XG5cdGlmIChldmVudC5kYXRhLnNvdXJjZSAhPT0gc2V0dGluZ3Muc291cmNlKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0c3dpdGNoIChldmVudC5kYXRhLnR5cGUpIHtcblx0XHRjYXNlICdvbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24nOlxuXHRcdFx0bWFya2VyLm9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbihldmVudC5kYXRhLmxpbmUpO1xuXHRcdFx0YnJlYWs7XG5cblx0XHRjYXNlICd1cGRhdGVWaWV3Jzpcblx0XHRcdG9uVXBkYXRlVmlldyhldmVudC5kYXRhLmxpbmUsIHNldHRpbmdzKTtcblx0XHRcdGJyZWFrO1xuXHR9XG59LCBmYWxzZSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2RibGNsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIXNldHRpbmdzLmRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcikge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdC8vIElnbm9yZSBjbGlja3Mgb24gbGlua3Ncblx0Zm9yIChsZXQgbm9kZSA9IGV2ZW50LnRhcmdldCBhcyBIVE1MRWxlbWVudDsgbm9kZTsgbm9kZSA9IG5vZGUucGFyZW50Tm9kZSBhcyBIVE1MRWxlbWVudCkge1xuXHRcdGlmIChub2RlLnRhZ05hbWUgPT09ICdBJykge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0fVxuXG5cdGNvbnN0IG9mZnNldCA9IGV2ZW50LnBhZ2VZO1xuXHRjb25zdCBsaW5lID0gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0KTtcblx0aWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcblx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2RpZENsaWNrJywgeyBsaW5lOiBNYXRoLmZsb29yKGxpbmUpIH0pO1xuXHR9XG59KTtcblxuZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBldmVudCA9PiB7XG5cdGlmICghZXZlbnQpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRsZXQgbm9kZTogYW55ID0gZXZlbnQudGFyZ2V0O1xuXHR3aGlsZSAobm9kZSkge1xuXHRcdGlmIChub2RlLnRhZ05hbWUgJiYgbm9kZS50YWdOYW1lID09PSAnQScgJiYgbm9kZS5ocmVmKSB7XG5cdFx0XHRpZiAobm9kZS5nZXRBdHRyaWJ1dGUoJ2hyZWYnKS5zdGFydHNXaXRoKCcjJykpIHtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0XHRpZiAobm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ2ZpbGU6Ly8nKSB8fCBub2RlLmhyZWYuc3RhcnRzV2l0aCgndnNjb2RlLXJlc291cmNlOicpIHx8IG5vZGUuaHJlZi5zdGFydHNXaXRoKHNldHRpbmdzLndlYnZpZXdSZXNvdXJjZVJvb3QpKSB7XG5cdFx0XHRcdGNvbnN0IFtwYXRoLCBmcmFnbWVudF0gPSBub2RlLmhyZWYucmVwbGFjZSgvXihmaWxlOlxcL1xcL3x2c2NvZGUtcmVzb3VyY2U6KS9pLCAnJykucmVwbGFjZShuZXcgUmVnRXhwKGBeJHtlc2NhcGVSZWdFeHAoc2V0dGluZ3Mud2Vidmlld1Jlc291cmNlUm9vdCl9YCkpLnNwbGl0KCcjJyk7XG5cdFx0XHRcdG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnY2xpY2tMaW5rJywgeyBwYXRoLCBmcmFnbWVudCB9KTtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0ZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXHRcdG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG5cdH1cbn0sIHRydWUpO1xuXG5pZiAoc2V0dGluZ3Muc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXcpIHtcblx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcblx0XHRpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcblx0XHRcdHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IGxpbmUgPSBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG5cdFx0XHRpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG5cdFx0XHRcdHN0YXRlLmxpbmUgPSBsaW5lO1xuXHRcdFx0XHR2c2NvZGUuc2V0U3RhdGUoc3RhdGUpO1xuXHRcdFx0fVxuXHRcdH1cblx0fSwgNTApKTtcbn1cblxuZnVuY3Rpb24gZXNjYXBlUmVnRXhwKHRleHQ6IHN0cmluZykge1xuXHRyZXR1cm4gdGV4dC5yZXBsYWNlKC9bLVtcXF17fSgpKis/LixcXFxcXiR8I1xcc10vZywgJ1xcXFwkJicpO1xufVxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IGdldFNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTWVzc2FnZVBvc3RlciB7XG5cdC8qKlxuXHQgKiBQb3N0IGEgbWVzc2FnZSB0byB0aGUgbWFya2Rvd24gZXh0ZW5zaW9uXG5cdCAqL1xuXHRwb3N0TWVzc2FnZSh0eXBlOiBzdHJpbmcsIGJvZHk6IG9iamVjdCk6IHZvaWQ7XG59XG5cbmV4cG9ydCBjb25zdCBjcmVhdGVQb3N0ZXJGb3JWc0NvZGUgPSAodnNjb2RlOiBhbnkpID0+IHtcblx0cmV0dXJuIG5ldyBjbGFzcyBpbXBsZW1lbnRzIE1lc3NhZ2VQb3N0ZXIge1xuXHRcdHBvc3RNZXNzYWdlKHR5cGU6IHN0cmluZywgYm9keTogb2JqZWN0KTogdm9pZCB7XG5cdFx0XHR2c2NvZGUucG9zdE1lc3NhZ2Uoe1xuXHRcdFx0XHR0eXBlLFxuXHRcdFx0XHRzb3VyY2U6IGdldFNldHRpbmdzKCkuc291cmNlLFxuXHRcdFx0XHRib2R5XG5cdFx0XHR9KTtcblx0XHR9XG5cdH07XG59O1xuXG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgZ2V0U2V0dGluZ3MgfSBmcm9tICcuL3NldHRpbmdzJztcblxuXG5mdW5jdGlvbiBjbGFtcChtaW46IG51bWJlciwgbWF4OiBudW1iZXIsIHZhbHVlOiBudW1iZXIpIHtcblx0cmV0dXJuIE1hdGgubWluKG1heCwgTWF0aC5tYXgobWluLCB2YWx1ZSkpO1xufVxuXG5mdW5jdGlvbiBjbGFtcExpbmUobGluZTogbnVtYmVyKSB7XG5cdHJldHVybiBjbGFtcCgwLCBnZXRTZXR0aW5ncygpLmxpbmVDb3VudCAtIDEsIGxpbmUpO1xufVxuXG5cbmV4cG9ydCBpbnRlcmZhY2UgQ29kZUxpbmVFbGVtZW50IHtcblx0ZWxlbWVudDogSFRNTEVsZW1lbnQ7XG5cdGxpbmU6IG51bWJlcjtcbn1cblxuY29uc3QgZ2V0Q29kZUxpbmVFbGVtZW50cyA9ICgoKSA9PiB7XG5cdGxldCBlbGVtZW50czogQ29kZUxpbmVFbGVtZW50W107XG5cdHJldHVybiAoKSA9PiB7XG5cdFx0aWYgKCFlbGVtZW50cykge1xuXHRcdFx0ZWxlbWVudHMgPSBbeyBlbGVtZW50OiBkb2N1bWVudC5ib2R5LCBsaW5lOiAwIH1dO1xuXHRcdFx0Zm9yIChjb25zdCBlbGVtZW50IG9mIGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtbGluZScpKSB7XG5cdFx0XHRcdGNvbnN0IGxpbmUgPSArZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbGluZScpITtcblx0XHRcdFx0aWYgKCFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRcdGVsZW1lbnRzLnB1c2goeyBlbGVtZW50OiBlbGVtZW50IGFzIEhUTUxFbGVtZW50LCBsaW5lIH0pO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiBlbGVtZW50cztcblx0fTtcbn0pKCk7XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKHRhcmdldExpbmU6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuXHRjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcblx0bGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcblx0Zm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuXHRcdGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG5cdFx0XHRyZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuXHRcdH0gZWxzZSBpZiAoZW50cnkubGluZSA+IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzLCBuZXh0OiBlbnRyeSB9O1xuXHRcdH1cblx0XHRwcmV2aW91cyA9IGVudHJ5O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzIH07XG59XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IGFyZSBhdCBhIHNwZWNpZmljIHBpeGVsIG9mZnNldCBvbiB0aGUgcGFnZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQ6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRjb25zdCBwb3NpdGlvbiA9IG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZO1xuXHRsZXQgbG8gPSAtMTtcblx0bGV0IGhpID0gbGluZXMubGVuZ3RoIC0gMTtcblx0d2hpbGUgKGxvICsgMSA8IGhpKSB7XG5cdFx0Y29uc3QgbWlkID0gTWF0aC5mbG9vcigobG8gKyBoaSkgLyAyKTtcblx0XHRjb25zdCBib3VuZHMgPSBsaW5lc1ttaWRdLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdFx0aWYgKGJvdW5kcy50b3AgKyBib3VuZHMuaGVpZ2h0ID49IHBvc2l0aW9uKSB7XG5cdFx0XHRoaSA9IG1pZDtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRsbyA9IG1pZDtcblx0XHR9XG5cdH1cblx0Y29uc3QgaGlFbGVtZW50ID0gbGluZXNbaGldO1xuXHRjb25zdCBoaUJvdW5kcyA9IGhpRWxlbWVudC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRpZiAoaGkgPj0gMSAmJiBoaUJvdW5kcy50b3AgPiBwb3NpdGlvbikge1xuXHRcdGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcblx0XHRyZXR1cm4geyBwcmV2aW91czogbG9FbGVtZW50LCBuZXh0OiBoaUVsZW1lbnQgfTtcblx0fVxuXHRyZXR1cm4geyBwcmV2aW91czogaGlFbGVtZW50IH07XG59XG5cbi8qKlxuICogQXR0ZW1wdCB0byByZXZlYWwgdGhlIGVsZW1lbnQgZm9yIGEgc291cmNlIGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lOiBudW1iZXIpIHtcblx0aWYgKCFnZXRTZXR0aW5ncygpLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0aWYgKGxpbmUgPD0gMCkge1xuXHRcdHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIDApO1xuXHRcdHJldHVybjtcblx0fVxuXG5cdGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0aWYgKCFwcmV2aW91cykge1xuXHRcdHJldHVybjtcblx0fVxuXHRsZXQgc2Nyb2xsVG8gPSAwO1xuXHRjb25zdCByZWN0ID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0Y29uc3QgcHJldmlvdXNUb3AgPSByZWN0LnRvcDtcblx0aWYgKG5leHQgJiYgbmV4dC5saW5lICE9PSBwcmV2aW91cy5saW5lKSB7XG5cdFx0Ly8gQmV0d2VlbiB0d28gZWxlbWVudHMuIEdvIHRvIHBlcmNlbnRhZ2Ugb2Zmc2V0IGJldHdlZW4gdGhlbS5cblx0XHRjb25zdCBiZXR3ZWVuUHJvZ3Jlc3MgPSAobGluZSAtIHByZXZpb3VzLmxpbmUpIC8gKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuXHRcdGNvbnN0IGVsZW1lbnRPZmZzZXQgPSBuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNUb3A7XG5cdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIGJldHdlZW5Qcm9ncmVzcyAqIGVsZW1lbnRPZmZzZXQ7XG5cdH0gZWxzZSB7XG5cdFx0Y29uc3QgcHJvZ3Jlc3NJbkVsZW1lbnQgPSBsaW5lIC0gTWF0aC5mbG9vcihsaW5lKTtcblx0XHRzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgKHJlY3QuaGVpZ2h0ICogcHJvZ3Jlc3NJbkVsZW1lbnQpO1xuXHR9XG5cdHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIE1hdGgubWF4KDEsIHdpbmRvdy5zY3JvbGxZICsgc2Nyb2xsVG8pKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKSB7XG5cdGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpO1xuXHRpZiAocHJldmlvdXMpIHtcblx0XHRjb25zdCBwcmV2aW91c0JvdW5kcyA9IHByZXZpb3VzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdFx0Y29uc3Qgb2Zmc2V0RnJvbVByZXZpb3VzID0gKG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcblx0XHRpZiAobmV4dCkge1xuXHRcdFx0Y29uc3QgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgPSBvZmZzZXRGcm9tUHJldmlvdXMgLyAobmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG5cdFx0XHRjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzICogKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuXHRcdFx0cmV0dXJuIGNsYW1wTGluZShsaW5lKTtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRjb25zdCBwcm9ncmVzc1dpdGhpbkVsZW1lbnQgPSBvZmZzZXRGcm9tUHJldmlvdXMgLyAocHJldmlvdXNCb3VuZHMuaGVpZ2h0KTtcblx0XHRcdGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50O1xuXHRcdFx0cmV0dXJuIGNsYW1wTGluZShsaW5lKTtcblx0XHR9XG5cdH1cblx0cmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogVHJ5IHRvIGZpbmQgdGhlIGh0bWwgZWxlbWVudCBieSB1c2luZyBhIGZyYWdtZW50IGlkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KGZyYWdtZW50OiBzdHJpbmcpOiBDb2RlTGluZUVsZW1lbnQgfCB1bmRlZmluZWQge1xuXHRyZXR1cm4gZ2V0Q29kZUxpbmVFbGVtZW50cygpLmZpbmQoKGVsZW1lbnQpID0+IHtcblx0XHRyZXR1cm4gZWxlbWVudC5lbGVtZW50LmlkID09PSBmcmFnbWVudDtcblx0fSk7XG59XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuZXhwb3J0IGludGVyZmFjZSBQcmV2aWV3U2V0dGluZ3Mge1xuXHRyZWFkb25seSBzb3VyY2U6IHN0cmluZztcblx0cmVhZG9ubHkgbGluZTogbnVtYmVyO1xuXHRyZWFkb25seSBsaW5lQ291bnQ6IG51bWJlcjtcblx0cmVhZG9ubHkgc2Nyb2xsUHJldmlld1dpdGhFZGl0b3I/OiBib29sZWFuO1xuXHRyZWFkb25seSBzY3JvbGxFZGl0b3JXaXRoUHJldmlldzogYm9vbGVhbjtcblx0cmVhZG9ubHkgZGlzYWJsZVNlY3VyaXR5V2FybmluZ3M6IGJvb2xlYW47XG5cdHJlYWRvbmx5IGRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcjogYm9vbGVhbjtcblx0cmVhZG9ubHkgd2Vidmlld1Jlc291cmNlUm9vdDogc3RyaW5nO1xufVxuXG5sZXQgY2FjaGVkU2V0dGluZ3M6IFByZXZpZXdTZXR0aW5ncyB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcblxuZXhwb3J0IGZ1bmN0aW9uIGdldERhdGE8VCA9IHt9PihrZXk6IHN0cmluZyk6IFQge1xuXHRjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcblx0aWYgKGVsZW1lbnQpIHtcblx0XHRjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcblx0XHRpZiAoZGF0YSkge1xuXHRcdFx0cmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG5cdFx0fVxuXHR9XG5cblx0dGhyb3cgbmV3IEVycm9yKGBDb3VsZCBub3QgbG9hZCBkYXRhIGZvciAke2tleX1gKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFNldHRpbmdzKCk6IFByZXZpZXdTZXR0aW5ncyB7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuXHRpZiAoY2FjaGVkU2V0dGluZ3MpIHtcblx0XHRyZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBsb2FkIHNldHRpbmdzJyk7XG59XG4iXSwic291cmNlUm9vdCI6IiJ9 \ No newline at end of file diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 5e83c38bd2..03a101e660 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -24,8 +24,9 @@ "onCommand:markdown.showLockedPreviewToSide", "onCommand:markdown.showSource", "onCommand:markdown.showPreviewSecuritySelector", - "onWebviewPanel:markdown.preview", - "onCommand:notebook.showPreview" + "onCommand:markdown.api.render", + "onCommand:notebook.showPreview", + "onWebviewPanel:markdown.preview" ], "contributes": { "commands": [ diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 6efaa4e310..a38a1c363d 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -6,7 +6,7 @@ import { ActiveLineMarker } from './activeLineMarker'; import { onceDocumentLoaded } from './events'; import { createPosterForVsCode } from './messaging'; -import { getEditorLineNumberForPageOffset, scrollToRevealSourceLine } from './scroll-sync'; +import { getEditorLineNumberForPageOffset, scrollToRevealSourceLine, getLineElementForFragment } from './scroll-sync'; import { getSettings, getData } from './settings'; import throttle = require('lodash.throttle'); @@ -19,7 +19,7 @@ const settings = getSettings(); const vscode = acquireVsCodeApi(); // Set VS Code state -let state = getData<{ line: number }>('data-state'); +let state = getData<{ line: number, fragment: string }>('data-state'); vscode.setState(state); const messaging = createPosterForVsCode(vscode); @@ -34,10 +34,19 @@ window.onload = () => { onceDocumentLoaded(() => { if (settings.scrollPreviewWithEditor) { setTimeout(() => { - const initialLine = +settings.line; - if (!isNaN(initialLine)) { - scrollDisabled = true; - scrollToRevealSourceLine(initialLine); + // Try to scroll to fragment if available + if (state.fragment) { + const element = getLineElementForFragment(state.fragment); + if (element) { + scrollDisabled = true; + scrollToRevealSourceLine(element.line); + } + } else { + const initialLine = +settings.line; + if (!isNaN(initialLine)) { + scrollDisabled = true; + scrollToRevealSourceLine(initialLine); + } } }, 0); } @@ -161,4 +170,4 @@ if (settings.scrollEditorWithPreview) { function escapeRegExp(text: string) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); -} \ No newline at end of file +} diff --git a/extensions/markdown-language-features/preview-src/scroll-sync.ts b/extensions/markdown-language-features/preview-src/scroll-sync.ts index 9ada06a5f9..91fe31c229 100644 --- a/extensions/markdown-language-features/preview-src/scroll-sync.ts +++ b/extensions/markdown-language-features/preview-src/scroll-sync.ts @@ -134,3 +134,12 @@ export function getEditorLineNumberForPageOffset(offset: number) { } return null; } + +/** + * Try to find the html element by using a fragment id + */ +export function getLineElementForFragment(fragment: string): CodeLineElement | undefined { + return getCodeLineElements().find((element) => { + return element.element.id === fragment; + }); +} diff --git a/extensions/markdown-language-features/src/commands/index.ts b/extensions/markdown-language-features/src/commands/index.ts index ad55d07467..63f46adc56 100644 --- a/extensions/markdown-language-features/src/commands/index.ts +++ b/extensions/markdown-language-features/src/commands/index.ts @@ -10,5 +10,5 @@ export { RefreshPreviewCommand } from './refreshPreview'; export { ShowPreviewSecuritySelectorCommand } from './showPreviewSecuritySelector'; export { MoveCursorToPositionCommand } from './moveCursorToPosition'; export { ToggleLockCommand } from './toggleLock'; -// {{SQL CARBON EDIT}} -export { ShowNotebookPreview } from './showNotebookPreview'; +export { ShowNotebookPreview } from './showNotebookPreview'; // {{SQL CARBON EDIT}} +export { RenderDocument } from './renderDocument'; diff --git a/extensions/markdown-language-features/src/commands/renderDocument.ts b/extensions/markdown-language-features/src/commands/renderDocument.ts new file mode 100644 index 0000000000..630e7cdf52 --- /dev/null +++ b/extensions/markdown-language-features/src/commands/renderDocument.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Command } from '../commandManager'; +import { MarkdownEngine } from '../markdownEngine'; +import { SkinnyTextDocument } from '../tableOfContentsProvider'; + +export class RenderDocument implements Command { + public readonly id = 'markdown.api.render'; + + public constructor( + private readonly engine: MarkdownEngine + ) { } + + public async execute(document: SkinnyTextDocument | string): Promise { + return this.engine.render(document); + } +} diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 03fe82929f..9566ccc3bf 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -83,7 +83,7 @@ function registerMarkdownCommands( commandManager.register(new commands.ShowPreviewSecuritySelectorCommand(previewSecuritySelector, previewManager)); commandManager.register(new commands.OpenDocumentLinkCommand(engine)); commandManager.register(new commands.ToggleLockCommand(previewManager)); - // {{SQL CARBON EDIT}} - commandManager.register(new commands.ShowNotebookPreview(engine)); + commandManager.register(new commands.ShowNotebookPreview(engine)); // {{SQL CARBON EDIT}} + commandManager.register(new commands.RenderDocument(engine)); return commandManager; } diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 2f93a4f50e..aa1f42d83c 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -89,6 +89,7 @@ export class MarkdownPreview extends Disposable { private isScrolling = false; private _disposed: boolean = false; private imageInfo: { id: string, width: number, height: number }[] = []; + private scrollToFragment: string | undefined; public static async revive( webview: vscode.WebviewPanel, @@ -171,19 +172,19 @@ export class MarkdownPreview extends Disposable { this._locked = locked; this.editor = webview; - this.editor.onDidDispose(() => { + this._register(this.editor.onDidDispose(() => { this.dispose(); - }, null, this._disposables); + })); - this.editor.onDidChangeViewState(e => { + this._register(this.editor.onDidChangeViewState(e => { this._onDidChangeViewStateEmitter.fire(e); - }, null, this._disposables); + })); - _contributionProvider.onContributionsChanged(() => { + this._register(_contributionProvider.onContributionsChanged(() => { setImmediate(() => this.refresh()); - }, null, this._disposables); + })); - this.editor.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { + this._register(this.editor.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { if (e.source !== this._resource.toString()) { return; } @@ -213,21 +214,21 @@ export class MarkdownPreview extends Disposable { vscode.window.showWarningMessage(localize('onPreviewStyleLoadError', "Could not load 'markdown.styles': {0}", e.body.unloadedStyles.join(', '))); break; } - }, null, this._disposables); + })); - vscode.workspace.onDidChangeTextDocument(event => { + this._register(vscode.workspace.onDidChangeTextDocument(event => { if (this.isPreviewOf(event.document.uri)) { this.refresh(); } - }, null, this._disposables); + })); - topmostLineMonitor.onDidChangeTopmostLine(event => { + this._register(topmostLineMonitor.onDidChangeTopmostLine(event => { if (this.isPreviewOf(event.resource)) { this.updateForView(event.resource, event.line); } - }, null, this._disposables); + })); - vscode.window.onDidChangeTextEditorSelection(event => { + this._register(vscode.window.onDidChangeTextEditorSelection(event => { if (this.isPreviewOf(event.textEditor.document.uri)) { this.postMessage({ type: 'onDidChangeTextEditorSelection', @@ -235,19 +236,19 @@ export class MarkdownPreview extends Disposable { source: this.resource.toString() }); } - }, null, this._disposables); + })); - vscode.window.onDidChangeActiveTextEditor(editor => { + this._register(vscode.window.onDidChangeActiveTextEditor(editor => { if (editor && isMarkdownFile(editor.document) && !this._locked) { this.update(editor.document.uri); } - }, null, this._disposables); + })); } - private readonly _onDisposeEmitter = new vscode.EventEmitter(); + private readonly _onDisposeEmitter = this._register(new vscode.EventEmitter()); public readonly onDispose = this._onDisposeEmitter.event; - private readonly _onDidChangeViewStateEmitter = new vscode.EventEmitter(); + private readonly _onDidChangeViewStateEmitter = this._register(new vscode.EventEmitter()); public readonly onDidChangeViewState = this._onDidChangeViewStateEmitter.event; public get resource(): vscode.Uri { @@ -264,7 +265,8 @@ export class MarkdownPreview extends Disposable { locked: this._locked, line: this.line, resourceColumn: this.resourceColumn, - imageInfo: this.imageInfo + imageInfo: this.imageInfo, + fragment: this.scrollToFragment }; } @@ -284,8 +286,12 @@ export class MarkdownPreview extends Disposable { public update(resource: vscode.Uri) { const editor = vscode.window.activeTextEditor; + // Reposition scroll preview, position scroll to the top if active text editor + // doesn't corresponds with preview if (editor && editor.document.uri.fsPath === resource.fsPath) { this.line = getVisibleLine(editor); + } else { + this.line = 0; } // If we have changed resources, cancel any pending updates @@ -433,8 +439,8 @@ export class MarkdownPreview extends Disposable { if (this._resource === markdownResource) { const self = this; const resourceProvider: WebviewResourceProvider = { - toWebviewResource: (resource) => { - return this.editor.webview.toWebviewResource(normalizeResource(markdownResource, resource)); + asWebviewUri: (resource) => { + return this.editor.webview.asWebviewUri(normalizeResource(markdownResource, resource)); }, get cspSource() { return self.editor.webview.cspSource; } }; @@ -520,11 +526,15 @@ export class MarkdownPreview extends Disposable { } private async onDidClickPreviewLink(path: string, fragment: string | undefined) { + this.scrollToFragment = undefined; const config = vscode.workspace.getConfiguration('markdown', this.resource); const openLinks = config.get('preview.openMarkdownLinks', 'inPreview'); if (openLinks === 'inPreview') { const markdownLink = await resolveLinkToMarkdownFile(path); if (markdownLink) { + if (fragment) { + this.scrollToFragment = fragment; + } this.update(markdownLink); return; } diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index 23dc342cf1..50b9b46f17 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -65,7 +65,7 @@ export class MarkdownContentProvider { scrollEditorWithPreview: config.scrollEditorWithPreview, doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor, disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings(), - webviewResourceRoot: resourceProvider.toWebviewResource(markdownDocument.uri).toString(), + webviewResourceRoot: resourceProvider.asWebviewUri(markdownDocument.uri).toString(), }; this.logger.log('provideTextDocumentContent', initialData); @@ -86,7 +86,7 @@ export class MarkdownContentProvider { data-state="${escapeAttribute(JSON.stringify(state || {}))}"> ${this.getStyles(resourceProvider, sourceUri, config, state)} - + ${body} @@ -110,7 +110,7 @@ export class MarkdownContentProvider { } private extensionResourcePath(resourceProvider: WebviewResourceProvider, mediaFile: string): string { - const webviewResource = resourceProvider.toWebviewResource( + const webviewResource = resourceProvider.asWebviewUri( vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile)))); return webviewResource.toString(); } @@ -126,17 +126,17 @@ export class MarkdownContentProvider { // Assume it must be a local file if (path.isAbsolute(href)) { - return resourceProvider.toWebviewResource(vscode.Uri.file(href)).toString(); + return resourceProvider.asWebviewUri(vscode.Uri.file(href)).toString(); } // Use a workspace relative path if there is a workspace const root = vscode.workspace.getWorkspaceFolder(resource); if (root) { - return resourceProvider.toWebviewResource(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString(); + return resourceProvider.asWebviewUri(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString(); } // Otherwise look relative to the markdown file - return resourceProvider.toWebviewResource(vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString(); + return resourceProvider.asWebviewUri(vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString(); } private computeCustomStyleSheetIncludes(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration): string { @@ -176,7 +176,7 @@ export class MarkdownContentProvider { private getStyles(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration, state?: any): string { const baseStyles: string[] = []; for (const resource of this.contributionProvider.contributions.previewStyles) { - baseStyles.push(``); + baseStyles.push(``); } return `${baseStyles.join('\n')} @@ -188,7 +188,7 @@ export class MarkdownContentProvider { const out: string[] = []; for (const resource of this.contributionProvider.contributions.previewScripts) { out.push(``); } diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 0a7a1249cd..6cc40396f6 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -118,7 +118,7 @@ export class MarkdownEngine { return md; } - private tokenize( + private tokenizeDocument( document: SkinnyTextDocument, config: MarkdownItConfig, engine: MarkdownIt @@ -131,25 +131,30 @@ export class MarkdownEngine { this.currentDocument = document.uri; this._slugCount = new Map(); - const text = document.getText(); - const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); + const tokens = this.tokenizeString(document.getText(), engine); this._tokenCache.update(document, config, tokens); return tokens; } - // {{SQL CARBON EDIT}} - Add renderText method - public async renderText(document: vscode.Uri, text: string): Promise { + public async renderText(document: vscode.Uri, text: string): Promise { // {{SQL CARBON EDIT}} - Add renderText method const config = this.getConfig(document); const engine = await this.getEngine(config); this.currentDocument = document; return engine.render(text, config); } - // {{SQL CARBON EDIT}} - End - public async render(document: SkinnyTextDocument): Promise { - const config = this.getConfig(document.uri); + private tokenizeString(text: string, engine: MarkdownIt) { + return engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); + } + + public async render(input: SkinnyTextDocument | string): Promise { + const config = this.getConfig(typeof input === 'string' ? undefined : input.uri); const engine = await this.getEngine(config); - return engine.renderer.render(this.tokenize(document, config, engine), { + const tokens = typeof input === 'string' + ? this.tokenizeString(input, engine) + : this.tokenizeDocument(input, config, engine); + + return engine.renderer.render(tokens, { ...(engine as any).options, ...config }, {}); @@ -158,14 +163,14 @@ export class MarkdownEngine { 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 this.tokenizeDocument(document, config, engine); } public cleanCache(): void { this._tokenCache.clean(); } - private getConfig(resource: vscode.Uri): MarkdownItConfig { + private getConfig(resource?: vscode.Uri): MarkdownItConfig { const config = vscode.workspace.getConfiguration('markdown', resource); return { breaks: config.get('preview.breaks', false), @@ -306,13 +311,13 @@ async function getMarkdownOptions(md: () => MarkdownIt) { 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) { + if (lang && ['tsx', 'typescriptreact'].includes(lang.toLocaleLowerCase())) { lang = 'jsx'; } if (lang && lang.toLocaleLowerCase() === 'json5') { lang = 'json'; } - if (lang && lang.toLocaleLowerCase() === 'c#') { + if (lang && ['c#', 'csharp'].includes(lang.toLocaleLowerCase())) { lang = 'cs'; } if (lang && hljs.getLanguage(lang)) { diff --git a/extensions/markdown-language-features/src/test/engine.test.ts b/extensions/markdown-language-features/src/test/engine.test.ts new file mode 100644 index 0000000000..670513267c --- /dev/null +++ b/extensions/markdown-language-features/src/test/engine.test.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * 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 vscode from 'vscode'; +import 'mocha'; + +import { InMemoryDocument } from './inMemoryDocument'; +import { createNewMarkdownEngine } from './engine'; + +const testFileName = vscode.Uri.file('test.md'); + +suite('markdown.engine', () => { + suite('rendering', () => { + const input = '# hello\n\nworld!'; + const output = '

hello

\n' + + '

world!

\n'; + + test('Renders a document', async () => { + const doc = new InMemoryDocument(testFileName, input); + const engine = createNewMarkdownEngine(); + assert.strictEqual(await engine.render(doc), output); + }); + + test('Renders a string', async () => { + const engine = createNewMarkdownEngine(); + assert.strictEqual(await engine.render(input), output); + }); + }); +}); diff --git a/extensions/markdown-language-features/src/util/resources.ts b/extensions/markdown-language-features/src/util/resources.ts index 77d2779261..f544dd6b5c 100644 --- a/extensions/markdown-language-features/src/util/resources.ts +++ b/extensions/markdown-language-features/src/util/resources.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; export interface WebviewResourceProvider { - toWebviewResource(resource: vscode.Uri): vscode.Uri; + asWebviewUri(resource: vscode.Uri): vscode.Uri; readonly cspSource: string; } @@ -30,4 +30,4 @@ export function normalizeResource( } } return resource; -} \ No newline at end of file +} diff --git a/extensions/mssql/src/main.ts b/extensions/mssql/src/main.ts index 72ba0ca84e..d9e330811f 100644 --- a/extensions/mssql/src/main.ts +++ b/extensions/mssql/src/main.ts @@ -16,7 +16,7 @@ import { ApiWrapper } from './apiWrapper'; import { UploadFilesCommand, MkDirCommand, SaveFileCommand, PreviewFileCommand, CopyPathCommand, DeleteFilesCommand } from './objectExplorerNodeProvider/hdfsCommands'; import { IPrompter } from './prompts/question'; import CodeAdapter from './prompts/adapter'; -import { mssql } from './mssql'; +import { IExtension } from './mssql'; import { OpenSparkJobSubmissionDialogCommand, OpenSparkJobSubmissionDialogFromFileCommand, OpenSparkJobSubmissionDialogTask } from './sparkFeature/dialog/dialogCommands'; import { OpenSparkYarnHistoryTask } from './sparkFeature/historyTask'; import { MssqlObjectExplorerNodeProvider, mssqlOutputChannel } from './objectExplorerNodeProvider/objectExplorerNodeProvider'; @@ -32,7 +32,7 @@ import { SqlToolsServer } from './sqlToolsServer'; const msgSampleCodeDataFrame = localize('msgSampleCodeDataFrame', 'This sample code loads the file into a data frame and shows the first 10 results.'); -export async function activate(context: vscode.ExtensionContext): Promise { +export async function activate(context: vscode.ExtensionContext): Promise { // lets make sure we support this platform first let supported = await Utils.verifyPlatform(); diff --git a/extensions/mssql/src/mssql.d.ts b/extensions/mssql/src/mssql.d.ts index 2542f1ee34..4eddb286e8 100644 --- a/extensions/mssql/src/mssql.d.ts +++ b/extensions/mssql/src/mssql.d.ts @@ -23,7 +23,7 @@ export const enum extension { /** * The APIs provided by Mssql extension */ -export interface mssql { +export interface IExtension { /** * Gets the object explorer API that supports querying over the connections supported by this extension * diff --git a/extensions/mssql/src/mssqlApiFactory.ts b/extensions/mssql/src/mssqlApiFactory.ts index 4c6bb000d4..81bed8d208 100644 --- a/extensions/mssql/src/mssqlApiFactory.ts +++ b/extensions/mssql/src/mssqlApiFactory.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { AppContext } from './appContext'; -import { mssql, ICmsService, IDacFxService, ISchemaCompareService, MssqlObjectExplorerBrowser } from './mssql'; +import { IExtension, ICmsService, IDacFxService, ISchemaCompareService, MssqlObjectExplorerBrowser } from './mssql'; import * as constants from './constants'; import { MssqlObjectExplorerNodeProvider } from './objectExplorerNodeProvider/objectExplorerNodeProvider'; import * as azdata from 'azdata'; -export function createMssqlApi(context: AppContext): mssql { +export function createMssqlApi(context: AppContext): IExtension { return { get cmsService() { return context.getService(constants.CmsService); diff --git a/extensions/schema-compare/src/dialogs/schemaCompareOptionsDialog.ts b/extensions/schema-compare/src/dialogs/schemaCompareOptionsDialog.ts index 50f668e3ac..db841df1fd 100644 --- a/extensions/schema-compare/src/dialogs/schemaCompareOptionsDialog.ts +++ b/extensions/schema-compare/src/dialogs/schemaCompareOptionsDialog.ts @@ -471,7 +471,7 @@ export class SchemaCompareOptionsDialog { } private async reset() { - let service = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.mssql).schemaCompare; + let service = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.IExtension).schemaCompare; let result = await service.schemaCompareGetDefaultOptions(); this.deploymentOptions = result.defaultDeploymentOptions; this.optionsChanged = true; diff --git a/extensions/schema-compare/src/schemaCompareMainWindow.ts b/extensions/schema-compare/src/schemaCompareMainWindow.ts index fcd159a504..654ff90194 100644 --- a/extensions/schema-compare/src/schemaCompareMainWindow.ts +++ b/extensions/schema-compare/src/schemaCompareMainWindow.ts @@ -1012,7 +1012,7 @@ export class SchemaCompareMainWindow { } private static async getService(providerName: string): Promise { - let service = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.mssql).schemaCompare; + let service = (vscode.extensions.getExtension(mssql.extension.name).exports as mssql.IExtension).schemaCompare; return service; } diff --git a/package.json b/package.json index fc2c456877..667f1e9153 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "fast-plist": "0.1.2", "glob": "^5.0.13", "gulp": "^4.0.0", - "gulp-atom-electron": "^1.21.1", + "gulp-atom-electron": "^1.22.0", "gulp-azure-storage": "^0.10.0", "gulp-buffer": "0.0.2", "gulp-concat": "^2.6.1", @@ -173,7 +173,7 @@ "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", "vsce": "1.48.0", - "vscode-debugprotocol": "1.35.0", + "vscode-debugprotocol": "1.36.0-pre.0", "vscode-nls-dev": "^3.3.1", "webpack": "^4.16.5", "webpack-cli": "^3.1.0", diff --git a/remote/package.json b/remote/package.json index 9bbc0f44c5..2e8ceed080 100644 --- a/remote/package.json +++ b/remote/package.json @@ -29,6 +29,6 @@ }, "optionalDependencies": { "vscode-windows-ca-certs": "0.1.0", - "vscode-windows-registry": "1.0.1" + "vscode-windows-registry": "1.0.2" } } diff --git a/remote/yarn.lock b/remote/yarn.lock index 54e0619915..dca0e2501f 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -743,7 +743,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -nan@^2.0.0, nan@^2.12.1, nan@^2.13.2, nan@^2.14.0: +nan@^2.0.0, nan@^2.13.2, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -1210,12 +1210,10 @@ vscode-windows-ca-certs@0.1.0: dependencies: node-addon-api "1.6.2" -vscode-windows-registry@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.1.tgz#bc9f765563eb6dc1c9ad9a41f9eaacc84dfadc7c" - integrity sha512-q0aKXi9Py1OBdmXIJJFeJBzpPJMMUxMJNBU9FysWIXEwJyMQGEVevKzM2J3Qz/cHSc5LVqibmoUWzZ7g+97qRg== - dependencies: - nan "^2.12.1" +vscode-windows-registry@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.2.tgz#b863e704a6a69c50b3098a55fbddbe595b0c124a" + integrity sha512-/CLLvuOSM2Vme2z6aNyB+4Omd7hDxpf4Thrt8ImxnXeQtxzel2bClJpFQvQqK/s4oaXlkBKS7LqVLeZM+uSVIA== xterm-addon-search@0.2.0-beta5: version "0.2.0-beta5" diff --git a/resources/completions/zsh/_code b/resources/completions/zsh/_code index 25dfe7ca98..c7abee1bc5 100644 --- a/resources/completions/zsh/_code +++ b/resources/completions/zsh/_code @@ -17,7 +17,7 @@ arguments=( '--telemetry[show all telemetry events which VS code collects]' '--extensions-dir[set the root path for extensions]:root path:_directories' '--list-extensions[list the installed extensions]' - '--category[filters instaled extension list by category, when using --list-extension]' + '--category[filters installed extension list by category, when using --list-extension]' '--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(-.)"' diff --git a/src/buildfile.js b/src/buildfile.js index 5e578b6da3..03da50adcd 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -23,13 +23,7 @@ exports.serviceWorker = [{ dest: 'vs/workbench/contrib/resources/browser/resourceServiceWorkerMain.js' }]; -exports.workerExtensionHost = [{ - name: 'vs/workbench/services/extensions/worker/extensionHostWorker', - // include: [], - prepend: ['vs/loader.js', 'vs/nls.js'], - append: ['vs/workbench/services/extensions/worker/extensionHostWorkerMain'], - dest: 'vs/workbench/services/extensions/worker/extensionHostWorkerMain.js' -}]; +exports.workerExtensionHost = [entrypoint('vs/workbench/services/extensions/worker/extensionHostWorker')]; exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.desktop.main']); exports.workbenchWeb = entrypoint('vs/workbench/workbench.web.api'); diff --git a/src/sql/workbench/browser/modelComponents/webview.component.ts b/src/sql/workbench/browser/modelComponents/webview.component.ts index 0fb85202ae..56443d9128 100644 --- a/src/sql/workbench/browser/modelComponents/webview.component.ts +++ b/src/sql/workbench/browser/modelComponents/webview.component.ts @@ -14,7 +14,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { WebviewContentOptions, IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/common/webview'; +import { WebviewContentOptions, IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; import { generateUuid } from 'vs/base/common/uuid'; import { ComponentBase } from 'sql/workbench/browser/modelComponents/componentBase'; diff --git a/src/sql/workbench/browser/parts/views/customView.ts b/src/sql/workbench/browser/parts/views/customView.ts index 16fab667b6..973a2a732d 100644 --- a/src/sql/workbench/browser/parts/views/customView.ts +++ b/src/sql/workbench/browser/parts/views/customView.ts @@ -76,7 +76,9 @@ export class CustomTreeViewPanel extends ViewletPanel { } renderBody(container: HTMLElement): void { - this.treeView.show(container); + if (this.treeView instanceof CustomTreeView) { + this.treeView.show(container); + } } layoutBody(height: number, width: number): void { diff --git a/src/sql/workbench/parts/dashboard/browser/contents/webviewContent.component.ts b/src/sql/workbench/parts/dashboard/browser/contents/webviewContent.component.ts index 90fc676d90..063adbe21b 100644 --- a/src/sql/workbench/parts/dashboard/browser/contents/webviewContent.component.ts +++ b/src/sql/workbench/parts/dashboard/browser/contents/webviewContent.component.ts @@ -17,7 +17,7 @@ import { IDashboardWebview, IDashboardViewService } from 'sql/platform/dashboard import { AngularDisposable } from 'sql/base/browser/lifecycle'; import * as azdata from 'azdata'; -import { WebviewElement, IWebviewService } from 'vs/workbench/contrib/webview/common/webview'; +import { WebviewElement, IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; @Component({ template: '', diff --git a/src/sql/workbench/parts/dashboard/browser/widgets/webview/webviewWidget.component.ts b/src/sql/workbench/parts/dashboard/browser/widgets/webview/webviewWidget.component.ts index e2998cfc77..f346bfb098 100644 --- a/src/sql/workbench/parts/dashboard/browser/widgets/webview/webviewWidget.component.ts +++ b/src/sql/workbench/parts/dashboard/browser/widgets/webview/webviewWidget.component.ts @@ -15,7 +15,7 @@ import { CommonServiceInterface } from 'sql/platform/bootstrap/browser/commonSer import { IDashboardWebview, IDashboardViewService } from 'sql/platform/dashboard/common/dashboardViewService'; import * as azdata from 'azdata'; -import { WebviewElement, IWebviewService } from 'vs/workbench/contrib/webview/common/webview'; +import { WebviewElement, IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; interface IWebviewWidgetConfig { id: string; diff --git a/src/sql/workbench/parts/webview/browser/webViewDialog.ts b/src/sql/workbench/parts/webview/browser/webViewDialog.ts index 2e320cc913..2e29e58595 100644 --- a/src/sql/workbench/parts/webview/browser/webViewDialog.ts +++ b/src/sql/workbench/parts/webview/browser/webViewDialog.ts @@ -18,7 +18,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import * as DOM from 'vs/base/browser/dom'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; import { generateUuid } from 'vs/base/common/uuid'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 2851c07421..be42ff6038 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -855,6 +855,7 @@ export const EventType = { KEY_UP: 'keyup', // HTML Document LOAD: 'load', + BEFORE_UNLOAD: 'beforeunload', UNLOAD: 'unload', ABORT: 'abort', ERROR: 'error', diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 07d282c874..bccc08bd36 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -9,6 +9,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import * as marked from 'vs/base/common/marked/marked'; +import * as insane from 'vs/base/common/insane/insane'; import { parse } from 'vs/base/common/marshalling'; import { cloneAndChange } from 'vs/base/common/objects'; import { escape } from 'vs/base/common/strings'; @@ -185,7 +186,20 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende renderer }; - element.innerHTML = marked.parse(markdown.value, markedOptions); + const allowedSchemes = ['http', 'https', 'mailto']; + if (markdown.isTrusted) { + allowedSchemes.push('command'); + } + + const renderedMarkdown = marked.parse(markdown.value, markedOptions); + element.innerHTML = insane(renderedMarkdown, { + allowedSchemes, + allowedAttributes: { + 'a': ['href', 'name', 'target', 'data-href'], + 'iframe': ['allowfullscreen', 'frameborder', 'src'], + 'img': ['src', 'title', 'alt', 'width', 'height'] + } + }); signalInnerHTML!(); return element; diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css index 6da9003b49..2e45f18052 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css @@ -1,7 +1,7 @@ @font-face { font-family: "octicons2"; - src: url("./octicons2.ttf?064476e75412ccad4ae99d4bf24539b4") format("truetype"), -url("./octicons2.svg?064476e75412ccad4ae99d4bf24539b4#octicons2") format("svg"); + src: url("./octicons2.ttf?dee9935d8deb764a470d194d9f0d07f7") format("truetype"), +url("./octicons2.svg?dee9935d8deb764a470d194d9f0d07f7#octicons2") format("svg"); } .octicon, .mega-octicon { diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg index e7d0d05d45..4028dd4566 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg @@ -9,7 +9,7 @@ + horiz-adv-x="1000" d=" M527.5 757.5H472.5L62.5 -8.75L90 -55H908.75L936.2499999999998 -8.75zM142.5 7.5L499.9999999999999 677.5L856.25 7.5zM468.7499999999999 445H531.25V195H468.75zM468.7499999999999 132.5H531.25V70H468.75z" /> @@ -198,7 +198,7 @@ horiz-adv-x="1000" d=" M125 132.5H187.5V383.125H125V132.5zM187.5 445.625V476.25L218.75 507.5H495.625L522.5 491.875L549.375 445H812.5H906.25L937.5 413.75V-86.25L906.25 -117.5H218.75L187.5 -86.25V70H93.75L62.5 101.25V663.75L93.75 695H370L397.5 679.375L424.375 632.5H781.25L812.5 601.25V445L750 446.875V570H406.25L379.375 585.625L352.5 632.5H125V445.625H187.5zM504.375 398.125L477.5 445H250V257.5H446.875L472.5 303.75L500 320H875V382.5H531.25L504.375 398.125zM465.625 195H250V-55H875V257.5H518.75L493.125 211.25L465.625 195z" /> + horiz-adv-x="1000" d=" M481.87625 632.5H906.25L938.125 601.25V257.5V-23.75L906.875 -55H94.375625L63.125625 -23.75V413.75V663.75L94.375625 695H406.875625L428.749375 685.62375L481.87625 632.5zM874.375 7.5V101.875L875 351.875625V445.625625H481.250625L427.501875 391.87625L405.624375 382.5H125V351.875625V101.875V7.5H874.375zM468.124375 507.5H874.375L875 569.374375H468.75L446.250625 578.750625L393.12375 631.874375H125V444.374375H392.501875L446.250625 498.12375L468.124375 507.5zM611.253125 253.128125L526.250625 338.753125L572.5 381.874375L711.25 243.1262500000001V198.75375L568.128125 60.625L524.3775 105.61875L611.875 190H526.250625C494.281875 188.5625 464.085 174.9187499999999 441.876875 151.88125C431.1175 141.175 422.6368750000001 128.4 416.94375 114.3249999999999C411.2512500000001 100.25625 408.4625000000001 85.1750000000001 408.75 70H346.25C345.860625 93.1687499999999 350.1443750000001 116.175 358.8425000000001 137.65C367.5406250000001 159.125 380.47375 178.6312499999999 396.875 195C429.649375 229.5450000000001 474.339375 250.32875 521.875 253.128125H611.253125z" /> @@ -225,7 +225,7 @@ horiz-adv-x="1000" d=" M568.75 545L537.5 695H462.5L431.25 545L387.5 526.25L262.5 607.5L206.25 557.5L287.5 432.5L275 388.75L125 357.5V282.5L275 251.25L293.75 201.25L212.5 76.25L262.5 26.25L387.5 107.5L437.5 88.75L462.5 -55H537.5L568.75 95L618.75 113.75L743.75 32.5L793.75 82.5L712.5 207.5L731.25 257.5L875 282.5V357.5L725 388.75L706.25 438.75L787.5 563.75L737.5 613.75L612.5 532.5L568.75 545zM587.5 757.5L618.75 607.5L750 688.75L875 563.75L787.5 432.5L937.5 407.5V232.5L787.5 201.25L875 70L750 -55L618.75 32.5L587.5 -117.5H412.5L381.25 32.5L250 -48.75L125 76.25L212.5 207.5L62.5 232.5V407.5L212.5 438.75L131.25 570L256.25 695L387.5 607.5L412.5 757.5H587.5zM625 320C625 251.25 568.75 195 500 195C431.25 195 375 251.25 375 320C375 388.75 431.25 445 500 445C568.75 445 625 388.75 625 320zM500 257.5C537.5 257.5 562.5 282.5 562.5 320C562.5 357.5 537.5 382.5 500 382.5C462.5 382.5 437.5 357.5 437.5 320C437.5 282.5 462.5 257.5 500 257.5z" /> + horiz-adv-x="1000" d=" M843.75 570H743.75C750 595 750 620 750 645C743.75 663.75 737.5 682.5 725 701.25C712.5 720 700 732.5 681.25 738.75C662.5 745 643.75 757.5 625 757.5C606.25 757.5 587.5 757.5 568.75 745C525 732.5 493.75 701.25 468.75 663.75C443.75 701.25 412.5 732.5 368.75 745C350 751.25 331.25 757.5 312.5 757.5C293.75 757.5 275 751.25 256.25 738.75C237.5 732.5 225 720 212.5 701.25C200 688.75 193.75 663.75 187.5 645C187.5 620 187.5 595 193.75 570H93.75L62.5 538.75V-23.75L93.75 -55H843.75L875 -23.75V538.75L843.75 570zM437.5 7.5H125V507.5H437.5V7.5zM437.5 570H250C250 576.25 250 576.25 250 582.5C243.75 601.25 243.75 613.75 243.75 632.5C250 645 250 657.5 262.5 663.75C268.75 676.25 281.25 682.5 293.75 688.75C300 695 312.5 695 325 695C337.5 695 350 695 362.5 688.75C381.25 682.5 400 670 412.5 651.25C425 632.5 437.5 613.75 437.5 588.75V570zM500 588.75C500 613.75 512.5 632.5 525 651.25C537.5 670 556.25 682.5 575 688.75C587.5 695 600 695 612.5 695C625 695 637.5 695 650 688.75C662.5 682.5 668.75 676.25 681.25 663.75C687.5 657.5 687.5 645 693.75 632.5C693.75 613.75 693.75 601.25 687.5 582.5C687.5 576.25 687.5 576.25 681.25 570H500V588.75zM812.5 7.5H500V507.5H812.5V7.5z" /> @@ -237,16 +237,16 @@ horiz-adv-x="1000" d=" M875.28125 477.43C875.425 506.336875 867.51875 534.713125 852.4562500000001 559.3812499999999C837.3812499999999 584.049375 815.74375 604.035625 789.9562500000001 617.10125C764.1687499999999 630.166875 735.2562499999999 635.795625 706.45625 633.358125C677.64375 630.9200000000001 650.09375 620.51125 626.86875 603.2975C603.645625 586.084375 585.671875 562.7462499999999 574.96375 535.89625C564.255625 509.045625 561.2287500000001 479.744375 566.2318750000001 451.273125C571.235 422.8025 584.060625 396.28375 603.2800000000001 374.693125C622.5 353.09875 647.3562499999999 337.28375 675.0500000000001 329.016875C664.825 308.2325 649 290.706875 629.3625000000001 278.420625C609.724375 266.13125 587.05 259.5625 563.8818749999999 259.450625H439.3225C393.21 259.2887500000001 348.80875 241.9706250000001 314.7625 210.8718749999999V511.74625C352.5675 519.463125 386.160625 540.9425 409.029375 572.020625C431.8981250000001 603.098125 442.41125 641.56 438.5331250000001 679.9493749999999C434.655625 718.33875 416.66125 753.920625 388.039375 779.797C359.4175 805.673125 322.2075 820 283.6225 820C245.0375 820 207.8275 805.673125 179.205 779.797C150.583125 753.920625 132.589375 718.33875 128.71125 679.9493749999999C124.8325 641.56 135.3475 603.098125 158.215625 572.020625C181.08375 540.9425 214.6775 519.463125 252.4825 511.74625V131.8375C214.748125 124.625 180.95875 103.84375 157.488125 73.43125C134.0175 43.0124999999999 122.4875 5.0625 125.073125 -33.2687500000001C127.65875 -71.5999999999999 144.18125 -107.6624999999999 171.524375 -134.6437500000001C198.8675 -161.6312499999999 235.140625 -177.68125 273.5025 -179.7687499999999C311.8643750000001 -181.8499999999999 349.6625 -169.8249999999999 379.7675 -145.9562499999999C409.873125 -122.09375 430.2043750000001 -88.03125 436.9262500000001 -50.20625C443.64875 -12.3812499999999 436.299375 26.59375 416.260625 59.375C396.223125 92.15625 364.883125 116.46875 328.1525000000001 127.7312500000001C338.3975000000001 148.49375 354.226875 165.9875 373.8643750000001 178.25625C393.501875 190.525 416.16875 197.070625 439.3225 197.170625H563.8818749999999C602.744375 197.349375 640.58125 209.643125 672.1312499999999 232.338125C703.6750000000001 255.0325000000001 727.35625 286.99875 739.8875 323.7856250000001C777.2125 328.693125 811.5 346.96625 836.4 375.21625C861.2937499999999 403.46625 875.1062499999999 439.7775 875.28125 477.43zM190.2025 664.27C190.2025 682.746875 195.68125 700.808125 205.94625 716.17125C216.211875 731.53375 230.801875 743.5074999999999 247.871875 750.57875C264.9425000000001 757.649 283.72625 759.499125 301.8475 755.894375C319.969375 752.29 336.615 743.3925 349.68 730.3275C362.745625 717.2625 371.6425 700.6168749999999 375.2475 682.495C378.851875 664.3731250000001 377.001875 645.589375 369.93125 628.5193750000001C362.86 611.449375 350.88625 596.85875 335.52375 586.59375C320.160625 576.32875 302.099375 570.85 283.6225 570.85C258.8456250000001 570.85 235.084375 580.6925 217.564375 598.211875C200.045 615.73125 190.2025 639.493125 190.2025 664.27zM377.0425 -20.80625C377.0425 -39.2875 371.563125 -57.34375 361.2981250000001 -72.7062500000001C351.033125 -88.06875 336.443125 -100.04375 319.3725 -107.11875C302.3025 -114.1875 283.51875 -116.0375 265.3975000000001 -112.4375C247.275625 -108.8312499999999 230.63 -99.93125 217.564375 -86.86875C204.499375 -73.8000000000001 195.6025 -57.15625 191.9975 -39.0375C188.393125 -20.9124999999999 190.243125 -2.13125 197.31375 14.9375C204.384375 32.0125 216.35875 46.6 231.72125 56.86875C247.08375 67.13125 265.145625 72.6125000000001 283.6225 72.6125000000001C308.39875 72.6125000000001 332.160625 62.76875 349.68 45.25C367.2000000000001 27.725 377.0425 3.96875 377.0425 -20.80625zM719.58125 384.01C701.10625 384.01 683.0437499999999 389.490625 667.68125 399.754375C652.3187499999999 410.0193750000001 640.34375 424.61 633.275 441.68C626.20625 458.75 624.351875 477.53375 627.95625 495.655C631.5625 513.776875 640.45625 530.423125 653.525 543.488125C666.5875 556.5531249999999 683.2375000000001 565.45 701.35625 569.0550000000001C719.475 572.659375 738.2625 570.809375 755.33125 563.73875C772.40625 556.668125 786.9937500000001 544.694375 797.25625 529.33125C807.51875 513.96875 813 495.906875 813 477.43C813 452.65375 803.15625 428.8918750000001 785.6375 411.3725C768.11875 393.854375 744.35625 384.01 719.58125 384.01z" /> + horiz-adv-x="1000" d=" M686.175 507.5C652.6875 541.7506249999999 608.665625 563.76125 561.1725 570V820H498.6725V570C447.8575 562.346875 401.35875 537.05125 367.330625 498.543125C333.3025 460.035 313.916875 410.776875 312.5743750000001 359.405625C311.2325 308.035 328.02 257.8306250000001 359.99125 217.598125C391.9625 177.36875 437.076875 149.6687499999999 487.423125 139.375H498.6725V-110.625H561.1725V139.375C579.265 141.65625 596.941875 146.49375 613.6724999999999 153.75C653.7937499999999 170.0625 688.0125 198.18 711.8 234.375C735.75 270.28375 748.575 312.4575 748.675 355.6218750000001C748.61875 412.495625 726.16875 467.06375 686.175 507.5V507.5zM642.4250000000001 242.500625C613.02375 213.2731250000001 573.2525 196.87125 531.7975 196.8768750000001C500.914375 196.8562500000001 470.7175 205.983125 445.02125 223.114375C419.3243750000001 240.245 399.281875 264.60625 387.423125 293.1218750000001C378.4468750000001 314.4525 374.340625 337.51625 375.404375 360.634375C376.468125 383.7518750000001 382.6756250000001 406.341875 393.5718750000001 426.758125C404.46875 447.174375 419.7806250000001 464.91 438.395 478.660625C457.00875 492.411875 478.45625 501.833125 501.173125 506.24875C511.360625 507.17 521.61 507.17 531.7975 506.24875C553.2325000000001 506.709375 574.525625 502.6675 594.2975 494.3775C622.813125 482.518125 647.175 462.471875 664.30625 436.775625C681.4375 411.0793750000001 690.5687499999999 380.881875 690.55 349.9987500000001C689.81875 329.375 685.025 309.0975 676.44375 290.3293750000001C667.8625000000001 271.5606250000001 655.66875 254.67 640.55 240.62375L642.4250000000001 242.500625z" /> + horiz-adv-x="1000" d=" M461.808125 8.0875L382.431875 87.46875L424.308125 131.2125L557.433125 -1.9125V-45.65625L424.308125 -178.78125L379.933125 -134.4125L459.3075 -55.0375H343.6825C323.14 -55.11875 302.78625 -51.13125 283.791875 -43.3062500000001C264.796875 -35.4875 247.538125 -23.98125 233.0125 -9.45625C218.486875 5.06875 206.981875 22.33125 199.15875 41.325C191.335625 60.31875 187.349375 80.675 187.4325 101.2125V448.0925C157.4875 454.468125 129.965 469.200625 108.058125 490.588125C86.3 512.5725 71.495625 540.4762499999999 65.49375 570.819375C59.4916875 601.161875 62.5575 632.6 74.3075 661.211875C86.166875 689.7275 106.21125 714.08875 131.9075 731.219375C157.60375 748.350625 187.799375 757.485 218.6825 757.464375C240.1175 757.925375 261.41 753.883125 281.1825 745.593125C300.123125 737.80125 317.3325000000001 726.330625 331.8150000000001 711.84875C346.296875 697.36625 357.765625 680.153125 365.5575 661.211875C373.848125 641.44 377.89375 620.146875 377.433125 598.711875C377.45375 567.82875 368.3225 537.631875 351.1912500000001 511.935625C334.060625 486.239375 309.6975 466.2 281.1825 454.34125C271.9106250000001 450.485 262.280625 447.553125 252.433125 445.59V101.8375C252.433125 76.9749999999999 262.309375 53.13125 279.89125 35.5500000000001C297.4725 17.96875 321.31875 8.0875 346.183125 8.0875H461.808125zM169.30875 523.7149999999999C184.685625 513.515 202.73 508.08 221.183125 508.09C238.14125 508.08375 254.78375 512.67625 269.3375000000001 521.380625C283.8918750000001 530.085 295.813125 542.576875 303.83 557.52125C311.8475000000001 572.465 315.660625 589.3025 314.864375 606.2425000000001C314.0675 623.1825 308.691875 639.586875 299.308125 653.7125C288.8243750000001 669.24375 274.061875 681.405625 256.808125 688.71625C239.778125 695.653125 221.074375 697.39 203.0575 693.713125C184.808125 690.184375 168.0325 681.275 154.889375 668.131875C141.745625 654.98875 132.836875 638.2168750000001 129.3075 619.9675C125.63125 601.95 127.37 583.24875 134.306875 566.21875C141.6175 548.9649999999999 153.776875 534.19875 169.30875 523.7149999999999zM814.93125 129.9625C844.975 123.8499999999999 872.575 109.08125 894.3125 87.4625000000001C923.5375 58.0625 939.9375 18.29375 939.93125 -23.1625C939.95625 -54.04375 930.825 -84.24375 913.69375 -109.9375C896.5625 -135.6375 872.1999999999999 -155.6812500000001 843.6875 -167.54375C815.1 -179.46875 783.6125000000001 -182.6312500000001 753.225 -176.625C722.8375 -170.625 694.91875 -155.73125 673.01875 -133.825C651.11875 -111.9249999999999 636.21875 -84.00625 630.21875 -53.61875C624.2162500000001 -23.2312499999999 627.38125 8.25 639.30625 36.8375C651.3625 65.1999999999999 671.3249999999999 89.5 696.80625 106.8375C713.60625 118.2125 732.525 126.0749999999999 752.43125 129.9625V476.8375C752.43125 501.70125 742.55625 525.5475 724.975 543.129375C707.3937500000001 560.7106249999999 683.55 570.5875 658.68125 570.5875H543.06L622.4325 491.21125L578.0600000000001 446.83875L444.933125 579.96375V623.71125L578.0600000000001 756.83625L622.4325 712.46375L543.06 633.0875H658.68125C679.225 633.1700000000001 699.5812500000001 629.184375 718.5749999999999 621.3612499999999C737.56875 613.538125 754.8249999999999 602.035 769.35 587.509375C783.875 572.98375 795.3875 555.7225000000001 803.20625 536.728125C811.03125 517.73375 815.0187500000001 497.379375 814.93125 476.8375V129.9625zM792.9937500000001 -116.4312500000001C814.5250000000001 -114.2687499999999 834.6375 -104.725 849.93125 -89.41875C865.24375 -74.125 874.7875 -54 876.95 -32.46875C879.11875 -10.9375 873.76875 10.6750000000001 861.80625 28.7125C851.325 44.24375 836.5625 56.4000000000001 819.30625 63.7125C802.28125 70.6500000000001 783.575 72.3874999999999 765.5625 68.7125C747.3125 65.18125 730.5374999999999 56.275 717.3937500000001 43.13125C704.25 29.9875 695.3375000000001 13.2125 691.8125 -5.0375C688.13125 -23.05 689.86875 -41.75625 696.80625 -58.78125C704.11875 -76.0375 716.275 -90.8062500000001 731.80625 -101.2875C749.84375 -113.2437500000001 771.4625000000001 -118.59375 792.9937500000001 -116.4312500000001z" /> + horiz-adv-x="1000" d=" M829.55625 336.840625C800.15625 366.068125 760.38125 382.4700000000001 718.9250000000001 382.464375C688.0437499999999 382.485 657.85 373.350625 632.15 356.219375C606.454375 339.08875 586.411875 314.7275 574.5525 286.2118750000001C570.90625 277.3493749999999 568.183125 268.1375 566.4275 258.7156250000001C502.53125 265.7275000000001 442.93875 294.318125 397.4856250000001 339.770625C352.033125 385.223125 323.43875 444.8193750000001 316.4275 508.715625C325.84875 510.47125 335.0643750000001 513.194375 343.9275 516.84125C372.443125 528.7 396.8037500000001 548.739375 413.935 574.435625C431.065625 600.131875 440.196875 630.32875 440.17625 661.211875C440.636875 682.646875 436.59125 703.94 428.3012500000001 723.711875C420.50875 742.653125 409.0425 759.866125 394.56 774.3484375C380.0775 788.83075 362.8687500000001 800.30125 343.9275 808.09325C324.1550000000001 816.38343125 302.8625 820.4254025 281.4275 819.9645975C250.544375 819.9851908125 220.346875 810.850375 194.650625 793.7195C168.954375 776.5885625 148.911875 752.2275 137.0525 723.711875C125.3025 695.1 122.23875 663.661875 128.240625 633.319375C134.2425 602.97625 149.043125 575.0725 170.80125 553.088125C192.708125 531.700625 220.2325 516.968125 250.1775 510.5925V191.8375000000001C230.268125 187.9500000000001 211.346875 180.0875 194.551875 168.7125C169.065625 151.3812499999999 149.10625 127.08125 137.0525 98.7125C125.123125 70.125 121.96 38.6437500000001 127.9625 8.25625C133.964375 -22.13125 148.86125 -50.05 170.763125 -71.95C192.665 -93.85 220.58375 -108.75 250.97125 -114.75C281.3581250000001 -120.75 312.844375 -117.59375 341.42875 -105.6625C369.944375 -93.8062500000001 394.305625 -73.75625 411.4362500000001 -48.0625C428.5668750000001 -22.36875 437.698125 7.83125 437.6775 38.7125C438.138125 60.15 434.0925 81.44375 425.8025 101.2125C418.010625 120.15625 406.54375 137.3625 392.06125 151.84375C377.579375 166.3250000000001 360.37 177.7937500000001 341.42875 185.5875C332.156875 189.4437499999999 322.525625 192.375 312.6775 194.3375V344.340625C344.968125 298.703125 387.865625 261.598125 437.6775 236.2168750000001C478.380625 215.3768749999999 522.745 202.6425000000001 568.3043749999999 198.718125C572.82375 176.05625 582.311875 154.675 596.086875 136.125C609.86125 117.5750000000001 627.58125 102.3125 647.9625 91.43125C668.34375 80.5562500000001 690.8874999999999 74.33125 713.96875 73.2125C737.05 72.09375 760.0875 76.1062499999999 781.4250000000001 84.9625C809.9437499999999 96.8249999999999 834.30625 116.86875 851.4375 142.5687499999999C868.56875 168.2625000000001 877.69375 198.459375 877.675 229.3425000000001C876.20625 270.06625 858.95 308.618125 829.55625 336.840625V336.840625zM317.053125 125.5875C334.3062500000001 118.28125 349.06875 106.11875 359.5525 90.5875C371.56625 72.5562500000001 376.96125 50.9250000000001 374.8225 29.3625C372.684375 7.8 363.14375 -12.3625 347.8225 -27.6812500000001C332.500625 -43.00625 312.346875 -52.5437499999999 290.7850000000001 -54.6875C269.223125 -56.8249999999999 247.58375 -51.425 229.55125 -39.4125C214.02 -28.9250000000001 201.8625 -14.1625 194.551875 3.09375C187.615 20.125 185.8775 38.825 189.554375 56.84375C193.083125 75.09375 201.9925 91.8625 215.135625 105.00625C228.27875 118.15 245.054375 127.05625 263.30375 130.5875C281.32125 134.2625000000001 300.023125 132.5250000000001 317.053125 125.5875V125.5875zM281.4275 569.964375C262.975 569.954375 244.92875 575.389375 229.55125 585.589375C214.02 596.073125 201.8625 610.839375 194.551875 628.093125C187.615 645.1231250000001 185.8775 663.8243749999999 189.554375 681.8418750000001C193.083125 700.09125 201.9925 716.863125 215.135625 730.00625C228.27875 743.149375 245.054375 752.05875 263.30375 755.5875C281.32125 759.2644375 300.023125 757.5274375 317.053125 750.590625C334.3062500000001 743.28 349.06875 731.118125 359.5525 715.586875C368.9362500000001 701.4612500000001 374.3125 685.056875 375.10875 668.1168749999999C375.905 651.176875 372.09375 634.339375 364.0768750000001 619.395625C356.059375 604.45125 344.13875 591.959375 329.584375 583.255C315.03 574.550625 298.38625 569.958125 281.4275 569.964375V569.964375zM785.175 159.9625C769.8812499999999 144.65625 749.76875 135.1125000000001 728.2375 132.9437499999999C706.70625 130.78125 685.0875 136.13125 667.0500000000001 148.0875C651.5187500000001 158.5749999999999 639.3625 173.3375 632.05 190.59375C625.1125 207.623125 623.3775 226.3243749999999 627.05625 244.341875C630.58125 262.59125 639.49375 279.363125 652.6374999999999 292.50625C665.78125 305.6493750000001 682.55625 314.5587500000001 700.80625 318.0875000000001C718.8187499999999 321.764375 737.525 320.0275000000001 754.5500000000001 313.090625C771.80625 305.78 786.56875 293.618125 797.0500000000001 278.086875C809.0125 260.051875 814.3625 238.436875 812.19375 216.906875C810.03125 195.3762499999999 800.4875000000001 175.25625 785.175 159.9625V159.9625z" /> + horiz-adv-x="1000" d=" M382.431875 149.96875L461.808125 70.5875H346.183125C321.31875 70.5875 297.4725 80.46875 279.89125 98.0500000000001C262.309375 115.63125 252.433125 139.4749999999999 252.433125 164.3375V508.09C262.280625 510.053125 271.9106250000001 512.985 281.1825 516.84125C309.6975 528.7 334.060625 548.739375 351.1912500000001 574.435625C368.3225 600.131875 377.45375 630.32875 377.433125 661.211875C377.89375 682.646875 373.848125 703.94 365.5575 723.711875C357.765625 742.653125 346.296875 759.866125 331.8150000000001 774.3484375C317.3325000000001 788.83075 300.123125 800.30125 281.1825 808.09325C261.41 816.38343125 240.1175 820.4254025 218.6825 819.9645975C187.799375 819.9851908125 157.60375 810.850375 131.9075 793.7195C106.21125 776.5885625 86.166875 752.2275 74.3075 723.711875C62.5575 695.1 59.4916875 663.661875 65.49375 633.319375C71.495625 602.97625 86.3 575.0725 108.058125 553.088125C129.965 531.700625 157.4875 516.968125 187.4325 510.5925V163.7125C187.349375 143.175 191.335625 122.81875 199.15875 103.8250000000001C206.981875 84.83125 218.486875 67.56875 233.0125 53.04375C247.538125 38.51875 264.796875 27.0125 283.791875 19.1937499999999C302.78625 11.36875 323.14 7.38125 343.6825 7.4625H459.3075L379.933125 -71.9125L424.308125 -116.28125L557.433125 16.84375V60.5875L424.308125 193.7125L382.431875 149.96875zM221.183125 570.59C202.73 570.5799999999999 184.685625 576.015 169.30875 586.215C153.776875 596.69875 141.6175 611.465 134.306875 628.71875C127.37 645.74875 125.63125 664.45 129.3075 682.4675C132.836875 700.716875 141.745625 717.48875 154.889375 730.631875C168.0325 743.775 184.808125 752.684375 203.0575 756.213125C221.074375 759.8900625 239.778125 758.1530625 256.808125 751.21625C274.061875 743.905625 288.8243750000001 731.74375 299.308125 716.2125C308.691875 702.086875 314.0675 685.6825 314.864375 668.7425000000001C315.660625 651.8025 311.8475000000001 634.965 303.83 620.02125C295.813125 605.076875 283.8918750000001 592.585 269.3375000000001 583.880625C254.78375 575.17625 238.14125 570.58375 221.183125 570.59V570.59z M894.3125 149.9625000000001C872.575 171.58125 844.975 186.3499999999999 814.93125 192.4625V539.3375000000001C815.0187500000001 559.879375 811.03125 580.23375 803.20625 599.228125C795.3875 618.2225 783.875 635.48375 769.35 650.009375C754.8249999999999 664.535 737.56875 676.038125 718.5749999999999 683.8612499999999C699.5812500000001 691.684375 679.225 695.67 658.68125 695.5875H543.06L622.4325 774.9636875L578.0600000000001 819.33624375L444.933125 686.21125V642.46375L578.0600000000001 509.33875L622.4325 553.7112500000001L543.06 633.0875H658.68125C683.55 633.0875 707.3937500000001 623.210625 724.975 605.629375C742.55625 588.0475 752.43125 564.20125 752.43125 539.3375000000001V192.4625C732.525 188.5749999999999 713.60625 180.7125 696.80625 169.3375C671.3249999999999 152 651.3625 127.6999999999999 639.30625 99.3375C627.38125 70.75 624.2162500000001 39.2687500000001 630.21875 8.88125C636.21875 -21.50625 651.11875 -49.425 673.01875 -71.325C694.91875 -93.23125 722.8375 -108.125 753.225 -114.125C783.6125000000001 -120.13125 815.1 -116.96875 843.6875 -105.04375C872.1999999999999 -93.1812500000001 896.5625 -73.1375 913.69375 -47.4375C930.825 -21.74375 939.95625 8.45625 939.93125 39.3375C939.9375 80.7937500000001 923.5375 120.5625 894.3125 149.9625000000001V149.9625000000001zM849.93125 -26.91875C834.6375 -42.225 814.5250000000001 -51.76875 792.9937500000001 -53.9312500000001C771.4625000000001 -56.09375 749.84375 -50.7437500000001 731.80625 -38.7875C716.275 -28.3062500000001 704.11875 -13.5375 696.80625 3.71875C689.86875 20.74375 688.13125 39.45 691.8125 57.4625C695.3375000000001 75.7125 704.25 92.4875 717.3937500000001 105.63125C730.5374999999999 118.775 747.3125 127.68125 765.5625 131.2125C783.575 134.8874999999999 802.28125 133.1500000000001 819.30625 126.2125C836.5625 118.9000000000001 851.325 106.74375 861.80625 91.2125C873.76875 73.1750000000001 879.11875 51.5625 876.95 30.03125C874.7875 8.5 865.24375 -11.625 849.93125 -26.91875V-26.91875z" /> @@ -261,7 +261,7 @@ horiz-adv-x="1000" d=" M93.75 -55H937.5V7.5H125V820H62.5V-23.75L93.75 -55zM187.5 101.25V601.25L218.75 632.5H343.75L375 601.25V101.25L343.75 70H218.75L187.5 101.25zM312.5 132.5V570H250V132.5H312.5zM687.5 726.25V101.25L718.75 70H843.75L875 101.25V726.25L843.75 757.5H718.75L687.5 726.25zM812.5 695V132.5H750V695H812.5zM437.5 101.25V476.25L468.75 507.5H593.75L625 476.25V101.25L593.75 70H468.75L437.5 101.25zM562.5 132.5V445H500V132.5H562.5z" /> + horiz-adv-x="1000" d=" M930 521.24625C924.95625 540.984375 917.175 559.9200000000001 906.875 577.498125C896.9875000000001 595.78375 884.3312500000001 612.439375 869.375 626.875625C847.6875 648.4918749999999 822 665.681875 793.75 677.4962499999999C736.8875 700.834375 673.1125 700.834375 616.249375 677.4962499999999C589.5475 666.19375 565.016875 650.3325 543.75125 630.6212499999999L540.6268749999999 626.875625L500.000625 586.24875L459.374375 626.875625L456.25 630.6212499999999C434.984375 650.3325 410.45375 666.19375 383.75125 677.4962499999999C326.88625 700.834375 263.115 700.834375 206.25 677.4962499999999C177.99875 665.681875 152.31625 648.4918749999999 130.6275 626.875625C115.656875 612.294375 102.82125 595.669375 92.50125 577.498125C82.66125 559.770625 75.100625 540.869375 70 521.24625C64.70125 500.845 62.1788125 479.821875 62.500625 458.74625C62.533125 438.9262500000001 65.0525 419.1925 70 400C75.24 380.6225 82.795 361.945 92.50125 344.374375C102.98375 326.31125 115.803125 309.7075 130.6275 294.996875L500.000625 -74.38125L869.375 294.996875C884.19375 309.556875 896.8249999999999 326.190625 906.875 344.374375C917.05625 361.78875 924.8375 380.500625 930 400C934.95 419.1925 937.46875 438.9262500000001 937.5 458.74625C937.825 479.821875 935.3 500.845 930 521.24625V521.24625zM867.5 419.3712500000001C863.8249999999999 405.395 858.3625000000001 391.949375 851.25 379.37V379.37C843.825 366.411875 834.8 354.445625 824.375 343.74875L498.749375 18.75L173.126875 343.74875C162.515625 354.435 153.2775 366.39875 145.626875 379.37C138.426875 392.2025 132.761875 405.8400000000001 128.750625 419.996875C125.548125 434.151875 123.8725 448.613125 123.75125 463.125625C123.83625 478.053125 125.51125 492.92625 128.750625 507.498125C132.644375 521.6956250000001 138.315 535.346875 145.626875 548.125C153.123125 561.2 162.37625 573.183125 173.126875 583.7462499999999C189.183125 599.5875 208.0425 612.301875 228.74875 621.245C270.475 637.936875 317.024375 637.936875 358.75 621.245C379.323125 612.676875 398.01125 600.15 413.750625 584.371875L498.749375 498.7475L583.7525 584.371875C599.49125 600.15 618.175625 612.676875 638.75 621.245C680.475 637.936875 727.025 637.936875 768.75 621.245C789.45625 612.301875 808.31875 599.5875 824.375 583.7462499999999C835.26875 573.46 844.35 561.42125 851.25 548.125V548.125C858.3625000000001 535.545 863.8249999999999 522.1 867.5 508.12375V508.12375C871.2562499999999 493.84375 873.14375 479.141875 873.125 464.376875C873.98125 449.275625 872.71875 434.1225 869.375 419.3712500000001H867.5z" /> @@ -279,13 +279,13 @@ horiz-adv-x="1000" d=" M93.75 -55H906.25L937.5 -23.75V257.5L764.375 736.25L735 757.5H266.875L237.5 736.875L62.5 273.75V-23.75L93.75 -55zM875 7.5H125V193.75H284.375625L330.625625 115.625L357.500625 100.625H643.125L670.625 116.875L713.75 193.75H875V7.5zM873.625 256.24875H695L668.125 239.99875L625 163.125H375.625625L328.750625 241.24875L301.875625 256.24875H125V257.5L288.75 695H712.5L873.625 256.24875z" /> + horiz-adv-x="1000" d=" M535.52375 755.545C634.96875 745.600625 727.7249999999999 700.92625 797.5 629.3775C873.51875 552.153125 918.46875 449.6550000000001 923.76875 341.42125C929.06875 233.188125 894.3625 126.7937499999999 826.25625 42.5125C763.6125000000001 -35.35625 675.70625 -88.85 577.7681249999999 -108.7125000000001C479.829375 -128.5749999999999 378.0275000000001 -113.55625 290.0025 -66.25C201.788125 -17.9124999999999 132.923125 59.3375 95.0025 152.5C56.912375 246.1462499999999 51.8418125 349.973125 80.626875 446.8825C109.35 543.4143750000001 170.498125 627.075 253.75125 683.7525C336.313125 740.0675 436.080625 765.489375 535.52375 755.545zM565.0037500000001 -47.49375C648.93125 -30.46875 724.2937499999999 15.275 778.125 81.875C836.4125 154.31875 866.06875 245.649375 861.4625 338.5143750000001C856.8562499999999 431.38 818.3000000000001 519.316875 753.125 585.630625C693.4312500000001 646.5675 614.216875 684.583125 529.3325 693.0375C444.448125 701.49125 359.293125 679.845625 288.7512500000001 631.88C235.653125 595.293125 192.7775 545.755 164.181875 487.959375C135.586875 430.16375 122.220625 366.025625 125.350625 301.61875C128.48 237.2125 148.001875 174.6687499999999 182.065625 119.9187500000001C216.129375 65.1687500000001 263.60625 20.0187500000001 320.0012500000001 -11.25C394.89625 -51.66875 481.6125 -64.49375 565.0037500000001 -47.49375zM531.87625 382.501875L469.37625 382.501875L469.37625 132.5L531.87625 132.5L531.87625 382.501875zM531.87625 507.501875L469.37625 507.501875L469.37625 445.001875L531.87625 445.001875L531.87625 507.501875z" /> + horiz-adv-x="1000" d=" M468.749375 757.5C388.40125 757.5 309.856875 733.673125 243.049375 689.03375C176.241875 644.3943750000001 124.17125 580.946875 93.42375 506.714375C62.675625 432.481875 54.631125 350.8006250000001 70.30625 271.995625C85.981875 193.19375 124.6725 120.8 181.4875 63.9875000000001C238.3025 7.175 310.690625 -31.51875 389.495625 -47.19375C468.3 -62.86875 549.9812499999999 -54.8249999999999 624.21375 -24.075C698.44375 6.66875 761.8937500000001 58.74375 806.53125 125.55C851.1750000000001 192.3562500000001 875 270.90125 875 351.25C875 458.994375 832.2 562.325 756.0124999999999 638.511875C679.825 714.69875 576.49375 757.5 468.749375 757.5zM468.749375 7.5C400.7625000000001 7.5 334.3 27.65625 277.770625 65.43125C221.24125 103.2000000000001 177.1825 156.89375 151.164375 219.70375C125.146875 282.51625 118.339375 351.63125 131.603125 418.3125C144.86625 484.993125 177.606875 546.2406249999999 225.680625 594.315C273.7550000000001 642.389375 335.00625 675.129375 401.6875 688.393125C468.368125 701.656875 537.48375 694.84875 600.295625 668.83125C663.10625 642.81375 716.79375 598.7581250000001 754.56875 542.22875C792.3375 485.699375 812.5 419.2375 812.5 351.25C812.5 260.081875 776.28125 172.65 711.8187499999999 108.1875C647.35 43.71875 559.9174999999999 7.5 468.749375 7.5zM500 570H437.5V257.5H500V570zM437.5 195H500V132.5H437.5V195z" /> @@ -306,7 +306,7 @@ horiz-adv-x="1000" d=" M709.4375 751.81C763.99375 740.70125 814.0625 713.746875 853.375 674.3199999999999C886.6125000000001 640.7975 911.03125 599.564375 924.44375 554.3043749999999C937.85625 509.04375 939.85 461.165 930.25 414.945C916.3375 351.58 881.325 294.8143750000001 830.9499999999999 253.94C780.56875 213.0650000000001 717.8125 190.5 652.9375 189.94375C625.56875 189.9 598.3375 193.88125 572.125 201.7574999999999L522.125 143.1937499999999L498.4375 132.25625H437.5V38.50625L406.25 7.25625H312.5V-86.49375L281.25 -117.74375H93.75L62.5 -86.49375V57.69375L71.625 79.75625L382.5 390.5700000000001C373.6775 418.9875 369.4575 448.6325 370 478.3825C370.76375 534.05375 387.9325 588.263125 419.3556250000001 634.224375C450.779375 680.185 495.061875 715.85625 546.660625 736.771875C598.2593750000001 757.6875 654.88125 762.918375 709.4375 751.81zM791.83125 301.8368750000001C831.08125 333.6287500000001 858.38125 377.805 869.25 427.1325L869.5 426.8200000000001C877.21875 462.88625 875.80625 500.309375 865.39375 535.6912500000001C854.975 571.073125 835.8875 603.293125 809.8625000000001 629.425625C783.8375 655.5587499999999 751.69375 674.77625 716.35625 685.334375C681.0125 695.8925 643.5999999999999 697.455625 607.5 689.8824999999999C558.79875 679.07375 515.114375 652.281875 483.40125 613.7731249999999C451.688125 575.264375 433.77 527.2525 432.5 477.3825C431.824375 448.950625 436.9056250000001 420.675625 447.4375 394.2575L440.5625 360.3825L125 44.75625V-55.24375H250V38.50625L281.25 69.75625H375V163.50625L406.25 194.75625H484.0625L538.8125 258.2575L573.875 267.0075000000001C599.149375 257.13125 626.05 252.0856250000001 653.1875 252.1325000000001C703.7 252.520625 752.58125 270.0450000000001 791.83125 301.8368750000001zM739.46875 472.5325C746.3375 482.810625 750 494.894375 750 507.255625C750 523.831875 743.4125 539.72875 731.69375 551.45C719.975 563.17125 704.075 569.755625 687.5 569.755625C675.1374999999999 569.755625 663.05625 566.0899999999999 652.775 559.2225000000001C642.5 552.355 634.4875 542.59375 629.75625 531.1737499999999C625.0250000000001 519.753125 623.789375 507.18625 626.1999999999999 495.0625C628.6125000000001 482.93875 634.5625 471.8025 643.30625 463.061875C652.04375 454.320625 663.1812500000001 448.368125 675.30625 445.9568750000001C687.4312500000001 443.545 700 444.783125 711.41875 449.513125C722.8375 454.24375 732.6 462.254375 739.46875 472.5325z" /> + horiz-adv-x="1000" d=" M875 632.5H187.5C170.92375 632.5 155.0275 625.918125 143.306875 614.196875C131.585625 602.47625 125 586.57625 125 570V132.5C125 115.925 131.585625 100.025 143.306875 88.3C155.0275 76.58125 170.92375 70 187.5 70H875C891.575 70 907.475 76.58125 919.19375 88.3C930.9125 100.025 937.5 115.925 937.5 132.5V570C937.5 586.57625 930.9125 602.47625 919.19375 614.196875C907.475 625.918125 891.575 632.5 875 632.5zM875 132.5H187.5V570H875V132.5zM687.5 507.5H625V445H687.5V507.5zM625 382.5H562.5V320H625V382.5zM750 507.5H812.5V445H750V507.5zM812.5 257.5H750V195H812.5V257.5zM375 257.5H687.5V195H375V257.5zM812.5 382.5H687.5V320H812.5V382.5zM500 507.5H562.5V445H500V507.5zM500 382.5H437.5V320H500V382.5zM250 257.5H312.5V195H250V257.5zM250 507.5H312.5V445H250V507.5zM437.5 507.5H375V445H437.5V507.5zM250 382.5H375V320H250V382.5z" /> @@ -330,7 +330,7 @@ horiz-adv-x="1000" d=" M677.04375 652.025625C630.3562499999999 698.714375 567.29375 725.344375 501.27125 726.25H498.77125C432.7487500000001 725.344375 369.685 698.714375 322.995625 652.025625C276.30625 605.3362500000001 249.676875 542.2725 248.77125 476.25C247.931875 429.3575 261.1212500000001 383.283125 286.64625 343.9375L483.361875 -55H516.680625L713.3937500000001 343.9375C738.9187499999999 383.283125 752.1125 429.3575 751.2687500000001 476.25C750.36875 542.2725 723.7375000000001 605.3362500000001 677.04375 652.025625zM495.27125 663.75L500.39625 663.125L505.02125 663.75C554.119375 661.611875 600.5325 640.745625 634.71875 605.441875C668.90625 570.138125 688.275 523.0787499999999 688.83125 473.9375C689.30625 439.0612500000001 679.125 404.8712500000001 659.64375 375.9375L658.39375 373.8125L657.3312500000001 371.625L500.02125 52.5625L342.70875 371.3125L341.64625 373.75L340.39625 375.875C320.9175 404.80875 310.734375 438.99875 311.20875 473.875C311.741875 523.073125 331.1287500000001 570.191875 365.374375 605.51875C399.62 640.8456249999999 446.1131250000001 661.688125 495.27125 663.75zM533.4875 528.2168750000001C523.209375 535.084375 511.125625 538.75 498.764375 538.75C482.188125 538.75 466.29125 532.165 454.57 520.444375C442.84875 508.723125 436.264375 492.82625 436.264375 476.25C436.264375 463.88875 439.929375 451.805 446.7975 441.526875C453.665 431.2487500000001 463.42625 423.2381250000001 474.84625 418.5075000000001C486.266875 413.776875 498.83375 412.539375 510.9575 414.950625C523.0812500000001 417.3625 534.2175 423.315 542.958125 432.055625C551.69875 440.796875 557.651875 451.933125 560.063125 464.056875C562.4749999999999 476.180625 561.2368749999999 488.7475 556.5068749999999 500.1675C551.77625 511.588125 543.7650000000001 521.349375 533.4875 528.2168750000001zM429.318125 580.18375C449.8737500000001 593.91875 474.04125 601.25 498.764375 601.25C531.91625 601.25 563.710625 588.080625 587.1524999999999 564.638125C610.594375 541.19625 623.7643750000001 509.401875 623.7643750000001 476.25C623.7643750000001 451.5275 616.433125 427.36 602.698125 406.8037500000001C588.9625000000001 386.2475 569.4399999999999 370.22625 546.599375 360.765C523.7587500000001 351.304375 498.625625 348.82875 474.3775 353.651875C450.13 358.475 427.8575 370.38 410.375625 387.861875C392.8943750000001 405.3431250000001 380.989375 427.6162500000001 376.16625 451.86375C371.3425000000001 476.11125 373.818125 501.244375 383.279375 524.0856249999999C392.74 546.92625 408.761875 566.44875 429.318125 580.18375z" /> + horiz-adv-x="1000" d=" M812.5 382.5H750V507.5C750 573.8043749999999 723.6625 637.38875 676.775 684.2731249999999C629.8937500000001 731.156875 566.3043749999999 757.5 500 757.5C433.695625 757.5 370.1075 731.156875 323.223125 684.2731249999999C276.33875 637.38875 250 573.8043749999999 250 507.5V382.5H187.5L125 320V-55L187.5 -117.5H812.5L875 -55V320L812.5 382.5zM312.5 507.5C312.5 557.2281250000001 332.253125 604.920625 367.41625 640.08375C402.5793750000001 675.246875 450.271875 695 500 695C549.728125 695 597.4206250000001 675.246875 632.58125 640.08375C667.74375 604.920625 687.5 557.2281250000001 687.5 507.5V382.5H312.5V507.5zM812.5 -55H187.5V320H812.5V-55z" /> @@ -378,16 +378,16 @@ horiz-adv-x="1000" d=" M538.125 632.5L896.875 536.875L937.5 507.5V86.25L914.375 56.25L531.25 -49.375L147.5 56.25L125 86.25V507.5L163.125 536.875L521.25 632.5H538.125zM532.5 570L282.5 507.5L316.875 495L531.25 438.75L718.75 489.375L778.125 507.5L532.5 570zM187.5 110L500 24.375V382.5L187.5 466.25V110zM562.5 382.5V24.375L875 110V468.125L748.75 433.5325V273.124375L686.25 256.874375V416.4075L562.5 382.5z" /> + horiz-adv-x="1000" d=" M908.7875 81.80625L837.5375 276.8143750000001V280.560625L530.6625 587.4375V664.936875C530.555 677.334375 528.2275 689.60875 523.78875 701.184375C518.92625 712.565 511.8287500000001 722.85375 502.918125 731.4425C494.0075 740.03125 483.464375 746.7475 471.9125 751.1875C460.398125 755.779375 448.04875 757.907 435.66125 757.435625C423.433125 757.6960625 411.289375 755.35375 400.035625 750.561875C388.8125 745.943125 378.615 739.146875 370.0331250000001 730.565C361.45125 721.983125 354.655 711.7825 350.036875 700.55875C345.245 689.305625 342.9025 677.165 343.1625 664.936875V461.18625L151.9125 273.68625C134.655625 255.95125 125 232.1781249999999 125 207.433125C125 182.6875 134.655625 158.9250000000001 151.9125 141.1875L378.788125 -85.6875C387.53875 -94.5124999999999 397.945625 -101.5187500000001 409.4125 -106.3125C432.275625 -115.4875000000001 457.8 -115.4875000000001 480.663125 -106.3125C492.13 -101.5187500000001 502.536875 -94.5124999999999 511.2875 -85.6875L819.4125 221.8143749999999L778.1625 84.3125C776.29375 74.81875 776.29375 65.05625 778.1625 55.5625C780.1687499999999 46.05625 784.21875 37.09375 790.0374999999999 29.30625C796.09375 21.75 803.5124999999999 15.3937500000001 811.9125 10.5625C820.68125 6.1875 830.25 3.6375 840.0375 3.0625C850.35 2.4312500000001 860.65625 4.3625 870.0375 8.6875C879.36875 12.90625 887.6875 19.09375 894.4125 26.8125C901.2125 34.69375 905.9375 44.14375 908.1625 54.3125C910.125 63.3562500000001 910.3375 72.6875 908.7875 81.80625V81.80625zM408.786875 664.936875C407.87 669.053125 407.87 673.3175 408.786875 677.43375C410.5456250000001 681.4449999999999 413.09625 685.061875 416.2862500000001 688.061875C419.674375 690.73875 423.473125 692.8475 427.53625 694.31C431.861875 695.239375 436.336875 695.239375 440.6625 694.31C448.248125 693.72375 455.36 690.389375 460.663125 684.93375C465.574375 679.19125 468.238125 671.8668749999999 468.1625 664.31125V585.560625L405.6625 523.0606250000001L408.786875 664.936875zM471.286875 -44.4375C468.64625 -47.6 465.18875 -49.975 461.2887500000001 -51.3125C457.548125 -52.9625 453.503125 -53.81875 449.4131250000001 -53.81875C445.32375 -53.81875 441.27875 -52.9625 437.538125 -51.3125C433.638125 -49.975 430.176875 -47.6 427.53625 -44.4375L200.660625 181.8125C197.81375 184.7312499999999 195.489375 188.1062499999999 193.786875 191.80625C192.15375 195.6637499999999 191.315 199.8125 191.315 203.999375C191.315 208.186875 192.15375 212.328125 193.786875 216.1837499999999C195.489375 219.885625 197.81375 223.27 200.660625 226.185625L472.538125 498.059375V319.935625C467.775625 315.57125 463.97875 310.2543750000001 461.395 304.33375C458.811875 298.4131250000001 457.499375 292.0175 457.53875 285.5575C456.77625 277.3318750000001 458.201875 269.053125 461.67 261.555625C465.138125 254.058125 470.525 247.60875 477.2875 242.86375C484.049375 238.11875 491.945 235.24375 500.175625 234.5325000000001C508.405625 233.8212500000001 516.6787499999999 235.2981249999999 524.154375 238.8125C531.630625 242.326875 538.0443750000001 247.753125 542.7475000000001 254.544375C547.450625 261.3356250000001 550.276875 269.2512500000001 550.9375 277.4856250000001C551.5981250000001 285.7206250000001 550.0693749999999 293.988125 546.50875 301.441875C542.948125 308.89625 537.4825 315.274375 530.6625 319.935625V496.808125L771.2875 254.933125L471.286875 -44.4375z" /> + horiz-adv-x="1000" d=" M826.875 757.5H735.625L220.00125 241.875L209.999375 228.1268750000001L62.5 -29.375L150.623125 -117.5L408.126875 30L421.875 40L937.5 555.62625V646.87375L826.875 757.5zM150.623125 -29.375L244.99875 158.125L335.625 67.5L150.623125 -29.375zM389.999375 99.375L279.373125 209.999375L779.375 709.999375L890 599.373125L389.999375 99.375z" /> + horiz-adv-x="1000" d=" M615.001875 757.5L558.124375 735.000625V601.25L344.375625 446.876875C312.088125 462.23125 276.160625 468.293125 240.62375 464.37875C205.404375 460.41 172.033125 446.54 144.375 424.3775V378.74625L321.87625 206.253125L60.001375 -55H154.99875L369.99875 159.99375L525.623125 8.125H573.1237500000001C595.604375 34.58125 609.7418749999999 67.1 613.746875 101.5875C617.75125 136.06875 611.44625 170.96875 595.626875 201.874375L755 409.378125H892.5L915.625 464.37875L615.001875 757.5zM740 474.373125L713.125 461.250625L533.74875 226.875625L529.99875 195C547.994375 160.61875 553.0962499999999 120.9375 544.37625 83.125L419.3762500000001 208.1225000000001L371.875625 254.371875L224.99875 397.499375C263.8625 405.660625 304.34125 400.6025 340 383.125625L373.1268750000001 386.8718750000001L614.3762499999999 561.249375L627.5 586.87625V658.7525L815 476.25L740 474.373125z" /> @@ -411,16 +411,13 @@ horiz-adv-x="1000" d=" M737.5 257.5L625 632.5H562.5L447.375625 217.5L374.375 526.875H313.75L240.625 257.5H62.5V195.625H264.375L295 218.75L341.25 387.5L411.875 70H476.25L593.125 510L684.375 217.5L714.375 195H937.5V257.5H737.5z" /> + horiz-adv-x="1000" d=" M468.750625 757.5C388.401875 757.5 309.8575 733.673125 243.05 689.03375C176.2425 644.3943750000001 124.174375 580.946875 93.42625 506.714375C62.678125 432.481875 54.6301875 350.8006250000001 70.305625 271.995625C85.980625 193.19375 124.67375 120.8 181.48875 63.9875000000001C238.30375 7.175 310.691875 -31.51875 389.49625 -47.19375C468.30125 -62.86875 549.9825000000001 -54.8249999999999 624.2149999999999 -24.075C698.45 6.66875 761.8937500000001 58.74375 806.53125 125.55C851.1750000000001 192.3562500000001 875 270.90125 875 351.25C875 458.994375 832.2 562.325 756.0124999999999 638.511875C679.825 714.69875 576.495 757.5 468.750625 757.5zM468.750625 7.5C400.763125 7.5 334.3006250000001 27.65625 277.77125 65.43125C221.241875 103.2000000000001 177.183125 156.89375 151.165625 219.70375C125.148125 282.51625 118.34 351.63125 131.60375 418.3125C144.8675 484.993125 177.6075 546.2406249999999 225.681875 594.315C273.75625 642.389375 335.0075 675.129375 401.688125 688.393125C468.369375 701.656875 537.484375 694.84875 600.29625 668.83125C663.10625 642.81375 716.79375 598.7581250000001 754.56875 542.22875C792.3375 485.699375 812.5 419.2375 812.5 351.25C812.5 260.081875 776.2875 172.65 711.8187499999999 108.1875C647.3562499999999 43.71875 559.91875 7.5 468.750625 7.5zM565.624375 533.7537500000001C554.82625 544.9975 541.85625 553.923125 527.5 559.99875C509.4375 567.2306249999999 490.073125 570.638125 470.626875 570.000625C451.7525 570.34375 433.015625 566.72125 415.62625 559.3731250000001C400.71625 553.1075000000001 387.455 543.485625 376.876875 531.25125C366.52625 519.9937500000001 358.46 506.83375 353.12625 492.50125C348.1675 477.9425 345.0175 462.83125 343.75 447.503125H420.6237500000001C421.078125 460.94125 426.6706250000001 473.68875 436.2487500000001 483.125C440.745625 487.805625 446.2025 491.455625 452.24375 493.82875C458.285 496.2025 464.768125 497.24125 471.24875 496.873125C476.83875 497.761875 482.535 497.761875 488.125 496.873125C493.26 494.93125 497.9425 491.951875 501.876875 488.1225C506.2187500000001 484.365 509.63875 479.6625 511.8749999999999 474.374375C514.435625 468.459375 515.713125 462.073125 515.625 455.62875C515.648125 444.834375 513.301875 434.166875 508.750625 424.3787500000001C504.211875 414.198125 498.550625 404.55 491.875 395.62375L470.00125 369.378125C462.50125 361.253125 454.99875 352.50125 448.12375 343.7512500000001C441.4575 335.041875 435.799375 325.61 431.25125 315.6293750000001C426.9156250000001 305.7924999999999 424.78 295.126875 424.999375 284.379375V243.1268750000001H500V273.7512500000001C500.211875 282.9181249999999 502.565625 291.910625 506.874375 300.004375C511.989375 309.015 517.844375 317.580625 524.375625 325.62375L546.875 353.1275C555.06625 362.590625 562.58375 372.6137500000001 569.374375 383.12625C576.556875 393.84625 582.4337499999999 405.3900000000001 586.875625 417.504375C591.43375 430.3475 593.7581250000001 443.87 593.75 457.498125C593.86125 471.696875 591.7543750000001 485.828125 587.50125 499.375625C582.781875 512.28125 575.31625 524.0125 565.624375 533.7537500000001zM425 205.6275000000001H498.125V132.5H425V205.6275000000001z" /> - + horiz-adv-x="1000" d=" M187.400625 471.2525C188.040625 562.236875 224.4025 649.326875 288.650625 713.753125L244.274375 757.5C206.480625 720.0231249999999 176.485 675.435 156.01375 626.3050000000001C135.541875 577.174375 125 524.4775 125 471.2525C125 418.0281250000001 135.541875 365.330625 156.01375 316.200625C176.485 267.07 206.480625 222.4825 244.274375 185.00625L288.650625 228.7525C224.4025 293.178125 188.040625 380.26875 187.400625 471.2525zM253.649375 471.2525C253.488125 434.3950000000001 260.705625 397.88 274.874375 363.8537500000001C289.0431250000001 329.8275 309.876875 298.9756250000001 336.15 273.125L380.52625 317.505C359.984375 337.270625 343.8037500000001 361.114375 333.025625 387.505C321.87125 414.0168750000001 316.1325 442.49 316.149375 471.2525C316.061875 499.820625 321.805 528.103125 333.025625 554.375C343.643125 581.03375 359.83875 605.114375 380.52625 625L336.15 669.380625C309.989375 643.445625 289.229375 612.5825 275.06875 578.575625C260.90875 544.5687499999999 253.6275 508.09 253.649375 471.2525zM731.775 270.63L687.4 315.0025C708.0125 334.9500000000001 724.1999999999999 359.01375 734.9 385.628125C746.05625 412.14 751.79375 440.613125 751.775 469.375625C751.8625 497.94375 746.125 526.23375 734.9 552.505625C724.28125 579.1643750000001 708.0875 603.245 687.4 623.13125L731.775 666.878125C757.8937500000001 640.915 778.61875 610.044375 792.75625 576.0425C806.9 542.040625 814.18125 505.575625 814.18125 468.75C814.18125 431.925 806.9 395.4675000000001 792.75625 361.465625C778.61875 327.46375 757.8937500000001 296.59375 731.775 270.63zM816.775 757.5L772.4 713.1275C804.4875000000001 681.54625 829.975 643.896875 847.36875 602.37125C864.7625 560.8456249999999 873.71875 516.274375 873.71875 471.2525C873.71875 426.23125 864.7625 381.659375 847.36875 340.13375C829.975 298.6081250000001 804.4875000000001 260.95875 772.4 229.3781250000001L816.775 185.00625C854.56875 222.4825 884.56875 267.07 905.04375 316.200625C925.5125 365.330625 936.05 418.0281250000001 936.05 471.2525C936.05 524.4775 925.5125 577.174375 905.04375 626.3050000000001C884.56875 675.435 854.56875 720.0231249999999 816.775 757.5zM624.54625 480.60625C626.6875 459.044375 621.2893750000001 437.413125 609.275625 419.380625C604.0875 414.09875 598.439375 409.281875 592.399375 404.999375L807.4 -78.125L749.9 -103.11875L701.7750000000001 5.00625H355.52625L307.4000000000001 -103.11875L249.900625 -78.125L464.90125 404.999375C451.840625 418.1925 442.9337500000001 434.924375 439.2775 453.125625C435.60125 471.1425 437.3381250000001 489.844375 444.275 506.874375C451.585625 524.128125 463.743125 538.894375 479.275 549.378125C497.306875 561.39125 518.9462500000001 566.79 540.508125 564.651875C562.0706250000001 562.51375 582.224375 552.973125 597.545625 537.65125C612.866875 522.3299999999999 622.4075 502.16875 624.54625 480.60625zM524.9025 501.8775C518.9775 500.48125 513.56375 497.44875 509.2775 493.12625C505.9193749999999 489.15875 503.55875 484.445 502.399375 479.378125C500.530625 473.48125 500.530625 467.148125 502.399375 461.250625C504.861875 455.578125 508.7343749999999 450.630625 513.64875 446.876875C518.9143750000001 443.639375 524.969375 441.9125 531.150625 441.8793750000001C539.38875 442.039375 547.24375 445.384375 553.0699999999999 451.2106250000001C558.89625 457.036875 562.240625 464.891875 562.400625 473.129375C562.368125 479.310625 560.6375 485.36625 557.4 490.63125C553.64625 495.54625 548.69875 499.415 543.02625 501.8775C537.1287500000001 503.74625 530.7993749999999 503.74625 524.9025 501.8775zM539.27625 373.749375H522.4L468.025 252.503125H593.025L539.27625 373.749375zM676.775 65L621.15125 190H441.150625L385.5250000000001 65H676.775z" /> @@ -429,7 +426,7 @@ horiz-adv-x="1000" d=" M394.153125 685.845L142.903125 434.594375V390.400625L394.153125 139.1500000000001L438.346875 183.34375L237.940625 383.75125H355.625C531.4475 383.75125 643.43125 345.945625 712.2062500000001 275.715625C781.0875000000001 205.370625 813.125 95.8937500000001 813.125 -63.125V-85H875.625V-63.125C875.625 102.2312500000001 842.6625 231.81875 756.85625 319.443125C670.9437499999999 407.1818750000001 538.5525 446.25125 355.625 446.25125H242.9475L438.346875 641.650625L394.153125 685.845z" /> + horiz-adv-x="1000" d=" M812.5 195H249.998125V695H499.998125V757.5H233.7475C219.413125 757.34125 205.226875 754.588125 191.87375 749.375C178.3075 743.470625 166.16 734.731875 156.248125 723.7475C146.12 713.150625 138.2525 700.606875 133.12375 686.875C127.9825 674.5899999999999 125.225 661.44 124.998125 648.125V54.375C124.848125 40.0125 127.6125 25.7624999999999 133.12375 12.5C144.080625 -14.2874999999999 165.20625 -35.6375 191.87375 -46.875C205.226875 -52.0875 219.413125 -54.84375 233.7475 -55H249.998125V7.5H233.7475C227.51125 7.48125 221.340625 8.7625 215.62375 11.25625C204.455625 16.1625 195.53375 25.08125 190.6225 36.25C189.674375 42.25 189.674375 48.36875 190.6225 54.375V85.625C189.674375 91.63125 189.674375 97.75 190.6225 103.75C195.53375 114.9187500000001 204.455625 123.8375 215.62375 128.74375C221.340625 131.2375 227.51125 132.51875 233.7475 132.5H812.5V7.5H562.498125V-55H843.75L875 -23.75V257.5H812.5V195zM375 632.5H312.5V570H375V632.5zM312.5 507.5H375V445H312.5V507.5zM312.5 382.5H375V320H312.5V382.5zM330 -117.5H312.5V70H500V-117.5H482.5L406.25 -23.125L330 -117.5zM625 757.5H906.25L937.5 726.25V351.25L906.25 320H750V257.5H687.5V320H625C608.42375 320 592.5275 326.5818750000001 580.806875 338.303125C569.085625 350.02375 562.5 365.92375 562.5 382.5V695C562.5 711.57625 569.085625 727.4762499999999 580.806875 739.196875C592.5275 750.918125 608.42375 757.5 625 757.5zM656.25 382.5H687.5V445H656.25C647.9625 445 640.0124999999999 441.705 634.15 435.845C628.2937499999999 429.984375 625 422.038125 625 413.75C625 405.461875 628.2937499999999 397.515625 634.15 391.6550000000001C640.0124999999999 385.795 647.9625 382.5 656.25 382.5zM750 382.5H875V445H750V382.5zM687.5 507.5H875V695H687.5V507.5z" /> @@ -468,7 +465,7 @@ horiz-adv-x="1000" d=" M218.75 570H62.5V632.5H187.5V757.5H250V601.25L218.75 570zM812.5 632.5V757.5H750V601.25L781.25 570H937.5V632.5H812.5zM750 38.75V-117.5H812.5V7.5H937.5V70H781.25L750 38.75zM62.5 70V7.5H187.5V-117.5H250V38.75L218.75 70H62.5zM750 163.75L718.75 132.5H281.25L250 163.75V476.25L281.25 507.5H718.75L750 476.25V163.75zM625 382.5H375V257.5H625V382.5z" /> + horiz-adv-x="1000" d=" M622.2881249999999 757.5C559.8087499999999 757.5645625 498.645625 739.565625 446.156875 705.674375C393.668125 671.783125 352.0925 623.4456250000001 326.44 566.475C300.7875 509.505 292.1500000000001 446.33 301.5668750000001 384.564375C310.9831250000001 322.7987500000001 338.05375 265.071875 379.516875 218.33375L62.5 -141.10625L106.69375 -180L422.535 178.2687500000001C463.19125 146.425 510.8675 124.7562499999999 561.59375 115.06875C612.319375 105.3875000000001 664.625 107.96875 714.15625 122.59375C763.68125 137.21875 809 163.46875 846.325 199.1575C883.6500000000001 234.844375 911.9125 278.933125 928.75 327.7537500000001C945.5875 376.5743750000001 950.50625 428.71125 943.1125 479.82125C935.71875 530.9312500000001 916.21875 579.534375 886.2375 621.580625C856.25 663.6268749999999 816.65625 697.899375 770.74375 721.541875C724.8312500000001 745.185 673.93125 757.5123125 622.2881249999999 757.5V757.5zM622.2881249999999 168.25C569.84375 168.25 518.5775 183.8000000000001 474.971875 212.9393749999999C431.36625 242.075625 397.3787500000001 283.4881250000001 377.3087500000001 331.94C357.2393750000001 380.3925000000001 351.99 433.7075000000001 362.221875 485.14375C372.453125 536.580625 397.708125 583.828125 434.791875 620.911875C471.875625 657.995625 519.1231250000001 683.250625 570.56 693.481875C621.99625 703.71375 675.3125 698.4606249999999 723.7624999999999 678.39125C772.2125 658.321875 813.625 624.3375 842.7624999999999 580.731875C871.9 537.12625 887.4499999999999 485.85625 887.4499999999999 433.411875C887.53125 398.56875 880.725 364.053125 867.425 331.846875C854.125 299.640625 834.6 270.3775 809.9625 245.739375C785.325 221.1012499999999 756.0625 201.5725 723.8562499999999 188.275C691.65 174.975 657.1312499999999 168.1687500000001 622.2881249999999 168.25V168.25z" /> @@ -486,7 +483,7 @@ horiz-adv-x="1000" d=" M688.75 584.375V486.875L751.25 548.75V663.75L720 695H157.50125L126.25125 663.75V633.375625L125 632.498125V-10.625L147.5 -39.375L460 -146.875L500 -117.5V-54.99375H720L751.25 -23.74375V7.50625V89.38125L688.75 151.88125V7.50625H500V525.6231250000001L479.375 554.3731250000001L252.2618750000001 632.5H438.75125H688.75V584.375zM437.5 -72.5L187.5 11.25V587.4981250000001L437.5 503.748125V-72.5zM845 289.996875H534.374375V352.496875H842.5L742.5 452.496875L786.875 496.246875L941.25 342.4968750000001V298.1218750000001L785.625 143.11875L741.875 186.86875L845 289.996875z" /> + horiz-adv-x="1000" d=" M256.938125 683.769375C328.885 731.8425 413.4700000000001 757.5 499.999375 757.5C616.031875 757.5 727.3125 711.40375 809.3625 629.356875C891.40625 547.309375 937.5 436.0325 937.5 320C937.5 233.470625 911.84375 148.88125 863.76875 76.9375C815.69375 4.9875 747.3625000000001 -51.0812500000001 667.4250000000001 -84.2C587.48 -117.3125 499.5125 -125.975 414.645625 -109.09375C329.77875 -92.2125 251.824375 -50.5437499999999 190.63875 10.64375C129.45375 71.83125 87.788125 149.78125 70.906875 234.65C54.026 319.516875 62.688125 407.484375 95.801875 487.426875C128.915 567.37 184.991875 635.69625 256.938125 683.769375zM291.66 8.2C353.328125 -33.0062499999999 425.83125 -55 499.999375 -55C599.455625 -55 694.8375 -15.4937500000001 765.1625 54.8312500000001C835.4875000000001 125.15625 875 220.5437499999999 875 320C875 394.168125 853.0062499999999 466.6675 811.8 528.335625C770.59375 590.004375 712.025 638.075625 643.5062499999999 666.458125C574.9825 694.84125 499.58375 702.2675 426.84125 687.798125C354.098125 673.328125 287.280625 637.611875 234.83625 585.1675C182.39125 532.7225 146.675 465.90125 132.205625 393.1581250000001C117.73625 320.415625 125.161875 245.013125 153.545 176.4937500000001C181.9275 107.96875 229.99125 49.40625 291.66 8.2zM406.25 382.5C406.25 347.9825 378.2675 320 343.75 320C309.2325 320 281.25 347.9825 281.25 382.5C281.25 417.0175 309.2325 445 343.75 445C378.2675 445 406.25 417.0175 406.25 382.5zM718.75 382.5C718.75 347.9825 690.7687500000001 320 656.25 320C621.7318750000001 320 593.75 347.9825 593.75 382.5C593.75 417.0175 621.7318750000001 445 656.25 445C690.7687500000001 445 718.75 417.0175 718.75 382.5zM500 132.5C466.038125 132.4187499999999 432.689375 141.55625 403.51875 158.9499999999999C374.348125 176.34375 350.44875 201.3306250000001 334.3737500000001 231.2475L279.9987500000001 201.24875C301.883125 160.75 334.5250000000001 127.08125 374.3250000000001 103.9500000000001C414.124375 80.81875 459.536875 69.13125 505.558125 70.1687500000001C551.57875 71.1999999999999 596.423125 84.9187499999999 635.14375 109.8125C673.8625000000001 134.70625 704.95625 169.8125 725 211.250625L668.75 238.1212500000001C653.375 206.4475000000001 629.39375 179.74375 599.551875 161.0625C569.70875 142.3875000000001 535.2075 132.4875000000001 500 132.5z" /> @@ -495,7 +492,7 @@ horiz-adv-x="1000" d=" M599.65875 429.243125L500 757.5L400.34125 429.243125H62.5L335.819375 216.7206250000001L234.35 -117.5L500 89.0625L765.6500000000001 -117.5L664.18125 216.7206250000001L937.5 429.243125H599.65875z" /> + horiz-adv-x="1000" d=" M256.938125 683.769375C328.885 731.8425 413.4700000000001 757.5 499.999375 757.5C616.031875 757.5 727.3125 711.40375 809.3625 629.356875C891.40625 547.309375 937.5 436.0325 937.5 320C937.5 233.470625 911.84375 148.88125 863.76875 76.9375C815.69375 4.9875 747.3625000000001 -51.0812500000001 667.4250000000001 -84.2C587.48 -117.3125 499.5125 -125.975 414.645625 -109.09375C329.77875 -92.2125 251.824375 -50.5437499999999 190.63875 10.64375C129.45375 71.83125 87.788125 149.78125 70.906875 234.65C54.026 319.516875 62.688125 407.484375 95.801875 487.426875C128.915 567.37 184.991875 635.69625 256.938125 683.769375zM291.66 8.2C353.328125 -33.0062499999999 425.83125 -55 499.999375 -55C599.455625 -55 694.8375 -15.4937500000001 765.1625 54.8312500000001C835.4875000000001 125.15625 875 220.5437499999999 875 320C875 394.168125 853.0062499999999 466.6675 811.8 528.335625C770.59375 590.004375 712.025 638.075625 643.5062499999999 666.458125C574.9825 694.84125 499.58375 702.2675 426.84125 687.798125C354.098125 673.328125 287.280625 637.611875 234.83625 585.1675C182.39125 532.7225 146.675 465.90125 132.205625 393.1581250000001C117.73625 320.415625 125.161875 245.013125 153.545 176.4937500000001C181.9275 107.96875 229.99125 49.40625 291.66 8.2zM508.1250000000001 353.125L660.625 507.5L704.375 463.125L551.875 308.75L704.375 153.75L660.625 110L508.1250000000001 264.375L356.25 110L312.5 153.75L465 308.75L312.5 463.125L356.25 507.5L508.1250000000001 353.125z" /> @@ -513,7 +510,7 @@ horiz-adv-x="1000" d=" M62.5 757.5H937.5V-117.5H62.5V757.5zM125 -55H875V695H125V-55zM250.005 463.306875L294.19875 507.500625L515.1700000000001 286.53L470.975625 242.335625L470.9737500000001 242.3375L294.1943750000001 65.55625L250 109.75L426.78 286.531875L250.005 463.306875z" /> + horiz-adv-x="1000" d=" M209.999375 382.5L62.5 7.5H146.251875L178.12375 99.375H319.374375L352.500625 7.5H437.5L290.62625 382.5H209.999375zM200.62625 161.875L249.374375 295.624375L298.12625 161.875H200.62625z M738.75 570H638.75L437.5 7.5H535L581.875 150.625H791.875L840 7.5H937.5L738.75 570zM604.3737500000001 226.25L678.125 450.623125C681.8125 462.21625 684.3249999999999 474.155625 685.625 486.251875C687.3125 474.268125 689.6062499999999 462.37375 692.5 450.623125L770.625 226.25H604.3737500000001z" /> @@ -528,7 +525,7 @@ horiz-adv-x="1000" d=" M333.584375 539.8824999999999L439.93 433.53625L386.4525 380.075L280.115 486.413125L226.023125 487.035625L115.971875 650.55625L169.440625 704.025625L332.961875 593.974375L333.584375 539.8824999999999zM513.393125 253.134375L513.3950000000001 253.1331250000001L566.871875 306.594375L566.870625 306.595625L636.8125 376.518125C668.00625 361.5487500000001 703.075 356.6125 737.19375 362.3893750000001C771.3125 368.166875 802.8 384.3725 827.31875 408.77625C851.73125 433.28625 867.95 464.75875 873.725 498.86C879.50625 532.961875 874.56875 568.015625 859.59375 599.19625L748.23125 487.66125L668.91875 566.9375L780.78125 678.24875C749.59375 693.385625 714.46875 698.435625 680.275 692.6975C646.0875000000001 686.95875 614.53125 670.71625 590.0006249999999 646.2275C565.47 621.73875 549.180625 590.22 543.396875 556.051875C537.6125000000001 521.88375 542.62125 486.7625 557.725625 455.570625L119.981875 17.95625V-21.56875L159.525625 -61.09375H199.07L513.393125 253.134375zM682.33125 -22.74375L507.78875 151.8L654.76875 298.733125C665.38125 296.2050000000001 676.15625 294.419375 687.0125 293.3887500000001L842.7375000000001 137.6624999999999C864.5 115.9000000000001 876.925 86.5750000000001 877.28125 56.1375C877.63125 25.70625 865.875 -3.3375000000001 844.60625 -24.6125C770.29375 -100.0125 682.33125 -22.74375 682.33125 -22.74375z" /> + horiz-adv-x="1000" d=" M750 570H625V632.5C625 649.07625 618.414375 664.97625 606.693125 676.696875C594.9725 688.418125 579.07625 695 562.5 695H375C358.42375 695 342.5275 688.418125 330.806875 676.696875C319.085625 664.97625 312.5 649.07625 312.5 632.5V570H125V507.5H187.5V-55L250 -117.5H687.5L750 -55V507.5H812.5V570H750zM375 632.5H562.5V570H375V632.5zM687.5 -55H250V507.5H687.5V-55zM437.5 445H500V7.5H437.5V445zM562.5 445H625V7.5H562.5V445zM312.5 445H375V7.5H312.5V445z" /> diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf index 654cfe91291a4b391122cf0b76277f89e8723e80..daa9a772ad2ea0963483e58ae0f3d30c2d558cc9 100644 GIT binary patch delta 4908 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 delta 4752 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 diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 8c3cf3cdc3..769b2a7101 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -117,12 +117,11 @@ abstract class ViewItem { if (typeof size === 'number') { this._size = size; this._cachedVisibleSize = undefined; + dom.addClass(container, 'visible'); } else { this._size = 0; this._cachedVisibleSize = size.cachedVisibleSize; } - - dom.addClass(container, 'visible'); } layout(_orthogonalSize: number | undefined): void { diff --git a/src/vs/base/common/insane/cgmanifest.json b/src/vs/base/common/insane/cgmanifest.json new file mode 100644 index 0000000000..bb94b81708 --- /dev/null +++ b/src/vs/base/common/insane/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "insane", + "repositoryUrl": "https://github.com/bevacqua/insane", + "commitHash": "7f5a809f44a37e7d11ee5343b2d8bca4be6ba4ae" + } + }, + "license": "MIT", + "version": "2.6.2" + } + ], + "version": 1 +} diff --git a/src/vs/base/common/insane/insane.d.ts b/src/vs/base/common/insane/insane.d.ts new file mode 100644 index 0000000000..ce9b0978ca --- /dev/null +++ b/src/vs/base/common/insane/insane.d.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export as namespace insane; + +export = insane; + +declare function insane( + html: string, + options?: { + readonly allowedSchemes?: readonly string[], + readonly allowedTags?: readonly string[], + readonly allowedAttributes?: { readonly [key: string]: string[] }, + }, + strict?: boolean, +): string; + +declare namespace insane { } diff --git a/src/vs/base/common/insane/insane.js b/src/vs/base/common/insane/insane.js new file mode 100644 index 0000000000..4ab91f7d8b --- /dev/null +++ b/src/vs/base/common/insane/insane.js @@ -0,0 +1,478 @@ +/* +The Source EULA (MIT) + +Copyright © 2015 Nicolas Bevacqua + +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. +*/ + +// ESM-comment-begin +let __insane_exports; +// ESM-comment-end + + + +(function () { function r(e, n, t) { function o(i, f) { if (!n[i]) { if (!e[i]) { var c = "function" == typeof require && require; if (!f && c) return c(i, !0); if (u) return u(i, !0); var a = new Error("Cannot find module '" + i + "'"); throw a.code = "MODULE_NOT_FOUND", a } var p = n[i] = { exports: {} }; e[i][0].call(p.exports, function (r) { var n = e[i][1][r]; return o(n || r) }, p, p.exports, r, e, n, t) } return n[i].exports } for (var u = "function" == typeof require && require, i = 0; i < t.length; i++)o(t[i]); return o } return r })()({ + 1: [function (require, module, exports) { + 'use strict'; + + var toMap = require('./toMap'); + var uris = ['background', 'base', 'cite', 'href', 'longdesc', 'src', 'usemap']; + + module.exports = { + uris: toMap(uris) // attributes that have an href and hence need to be sanitized + }; + + }, { "./toMap": 10 }], 2: [function (require, module, exports) { + 'use strict'; + + var defaults = { + allowedAttributes: { + '*': ['title', 'accesskey'], + a: ['href', 'name', 'target', 'aria-label'], + iframe: ['allowfullscreen', 'frameborder', 'src'], + img: ['src', 'alt', 'title', 'aria-label'] + }, + allowedClasses: {}, + allowedSchemes: ['http', 'https', 'mailto'], + allowedTags: [ + 'a', 'abbr', 'article', 'b', 'blockquote', 'br', 'caption', 'code', 'del', 'details', 'div', 'em', + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'ins', 'kbd', 'li', 'main', 'mark', + 'ol', 'p', 'pre', 'section', 'span', 'strike', 'strong', 'sub', 'summary', 'sup', 'table', + 'tbody', 'td', 'th', 'thead', 'tr', 'u', 'ul' + ], + filter: null + }; + + module.exports = defaults; + + }, {}], 3: [function (require, module, exports) { + 'use strict'; + + var toMap = require('./toMap'); + var voids = ['area', 'br', 'col', 'hr', 'img', 'wbr', 'input', 'base', 'basefont', 'link', 'meta']; + + module.exports = { + voids: toMap(voids) + }; + + }, { "./toMap": 10 }], 4: [function (require, module, exports) { + 'use strict'; + + var he = require('he'); + var assign = require('assignment'); + var parser = require('./parser'); + var sanitizer = require('./sanitizer'); + var defaults = require('./defaults'); + + function insane(html, options, strict) { + var buffer = []; + var configuration = strict === true ? options : assign({}, defaults, options); + var handler = sanitizer(buffer, configuration); + + parser(html, handler); + + return buffer.join(''); + } + + insane.defaults = defaults; + module.exports = insane; + __insane_exports = insane; + + }, { "./defaults": 2, "./parser": 7, "./sanitizer": 8, "assignment": 6, "he": 9 }], 5: [function (require, module, exports) { + 'use strict'; + + module.exports = function lowercase(string) { + return typeof string === 'string' ? string.toLowerCase() : string; + }; + + }, {}], 6: [function (require, module, exports) { + 'use strict'; + + function assignment(result) { + var stack = Array.prototype.slice.call(arguments, 1); + var item; + var key; + while (stack.length) { + item = stack.shift(); + for (key in item) { + if (item.hasOwnProperty(key)) { + if (Object.prototype.toString.call(result[key]) === '[object Object]') { + result[key] = assignment(result[key], item[key]); + } else { + result[key] = item[key]; + } + } + } + } + return result; + } + + module.exports = assignment; + + }, {}], 7: [function (require, module, exports) { + 'use strict'; + + var he = require('he'); + var lowercase = require('./lowercase'); + var attributes = require('./attributes'); + var elements = require('./elements'); + var rstart = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/; + var rend = /^<\s*\/\s*([\w:-]+)[^>]*>/; + var rattrs = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g; + var rtag = /^'); + if (index >= 0) { + if (handler.comment) { + handler.comment(html.substring(4, index)); + } + html = html.substring(index + 3); + chars = false; + } + } + + function parseTagDecode() { + if (!chars) { + return; + } + var text; + var index = html.indexOf('<'); + if (index >= 0) { + text = html.substring(0, index); + html = html.substring(index); + } else { + text = html; + html = ''; + } + if (handler.chars) { + handler.chars(text); + } + } + + function parseStartTag(tag, tagName, rest, unary) { + var attrs = {}; + var low = lowercase(tagName); + var u = elements.voids[low] || !!unary; + + rest.replace(rattrs, attrReplacer); + + if (!u) { + stack.push(low); + } + if (handler.start) { + handler.start(low, attrs, u); + } + + function attrReplacer(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) { + if (doubleQuotedValue === void 0 && singleQuotedValue === void 0 && unquotedValue === void 0) { + attrs[name] = void 0; // attribute is like + } else { + attrs[name] = he.decode(doubleQuotedValue || singleQuotedValue || unquotedValue || ''); + } + } + } + + function parseEndTag(tag, tagName) { + var i; + var pos = 0; + var low = lowercase(tagName); + if (low) { + for (pos = stack.length - 1; pos >= 0; pos--) { + if (stack[pos] === low) { + break; // find the closest opened tag of the same type + } + } + } + if (pos >= 0) { + for (i = stack.length - 1; i >= pos; i--) { + if (handler.end) { // close all the open elements, up the stack + handler.end(stack[i]); + } + } + stack.length = pos; + } + } + } + + module.exports = parser; + + }, { "./attributes": 1, "./elements": 3, "./lowercase": 5, "he": 9 }], 8: [function (require, module, exports) { + 'use strict'; + + var he = require('he'); + var lowercase = require('./lowercase'); + var attributes = require('./attributes'); + var elements = require('./elements'); + + function sanitizer(buffer, options) { + var last; + var context; + var o = options || {}; + + reset(); + + return { + start: start, + end: end, + chars: chars + }; + + function out(value) { + buffer.push(value); + } + + function start(tag, attrs, unary) { + var low = lowercase(tag); + + if (context.ignoring) { + ignore(low); return; + } + if ((o.allowedTags || []).indexOf(low) === -1) { + ignore(low); return; + } + if (o.filter && !o.filter({ tag: low, attrs: attrs })) { + ignore(low); return; + } + + out('<'); + out(low); + Object.keys(attrs).forEach(parse); + out(unary ? '/>' : '>'); + + function parse(key) { + var value = attrs[key]; + var classesOk = (o.allowedClasses || {})[low] || []; + var attrsOk = (o.allowedAttributes || {})[low] || []; + attrsOk = attrsOk.concat((o.allowedAttributes || {})['*'] || []); + var valid; + var lkey = lowercase(key); + if (lkey === 'class' && attrsOk.indexOf(lkey) === -1) { + value = value.split(' ').filter(isValidClass).join(' ').trim(); + valid = value.length; + } else { + valid = attrsOk.indexOf(lkey) !== -1 && (attributes.uris[lkey] !== true || testUrl(value)); + } + if (valid) { + out(' '); + out(key); + if (typeof value === 'string') { + out('="'); + out(he.encode(value)); + out('"'); + } + } + function isValidClass(className) { + return classesOk && classesOk.indexOf(className) !== -1; + } + } + } + + function end(tag) { + var low = lowercase(tag); + var allowed = (o.allowedTags || []).indexOf(low) !== -1; + if (allowed) { + if (context.ignoring === false) { + out(''); + } else { + unignore(low); + } + } else { + unignore(low); + } + } + + function testUrl(text) { + var start = text[0]; + if (start === '#' || start === '/') { + return true; + } + var colon = text.indexOf(':'); + if (colon === -1) { + return true; + } + var questionmark = text.indexOf('?'); + if (questionmark !== -1 && colon > questionmark) { + return true; + } + var hash = text.indexOf('#'); + if (hash !== -1 && colon > hash) { + return true; + } + return o.allowedSchemes.some(matches); + + function matches(scheme) { + return text.indexOf(scheme + ':') === 0; + } + } + + function chars(text) { + if (context.ignoring === false) { + out(o.transformText ? o.transformText(text) : text); + } + } + + function ignore(tag) { + if (elements.voids[tag]) { + return; + } + if (context.ignoring === false) { + context = { ignoring: tag, depth: 1 }; + } else if (context.ignoring === tag) { + context.depth++; + } + } + + function unignore(tag) { + if (context.ignoring === tag) { + if (--context.depth <= 0) { + reset(); + } + } + } + + function reset() { + context = { ignoring: false, depth: 0 }; + } + } + + module.exports = sanitizer; + + }, { "./attributes": 1, "./elements": 3, "./lowercase": 5, "he": 9 }], 9: [function (require, module, exports) { + 'use strict'; + + var escapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + var unescapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'" + }; + var rescaped = /(&|<|>|"|')/g; + var runescaped = /[&<>"']/g; + + function escapeHtmlChar(match) { + return escapes[match]; + } + function unescapeHtmlChar(match) { + return unescapes[match]; + } + + function escapeHtml(text) { + return text == null ? '' : String(text).replace(runescaped, escapeHtmlChar); + } + + function unescapeHtml(html) { + return html == null ? '' : String(html).replace(rescaped, unescapeHtmlChar); + } + + escapeHtml.options = unescapeHtml.options = {}; + + module.exports = { + encode: escapeHtml, + escape: escapeHtml, + decode: unescapeHtml, + unescape: unescapeHtml, + version: '1.0.0-browser' + }; + + }, {}], 10: [function (require, module, exports) { + 'use strict'; + + function toMap(list) { + return list.reduce(asKey, {}); + } + + function asKey(accumulator, item) { + accumulator[item] = true; + return accumulator; + } + + module.exports = toMap; + + }, {}] +}, {}, [4]); + +// BEGIN MONACOCHANGE +// __marked_exports = marked; +// }).call(this); + +// ESM-comment-begin +define(function() { return __insane_exports; }); +// ESM-comment-end diff --git a/src/vs/base/common/insane/insane.license.txt b/src/vs/base/common/insane/insane.license.txt new file mode 100644 index 0000000000..b980cef0bc --- /dev/null +++ b/src/vs/base/common/insane/insane.license.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright © 2015 Nicolas Bevacqua + +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. diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index cb865f2771..258dd79a64 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -49,6 +49,8 @@ export namespace Schemas { export const vscodeRemote: string = 'vscode-remote'; + export const vscodeRemoteResource: string = 'vscode-remote-resource'; + export const userData: string = 'vscode-userdata'; } @@ -64,7 +66,7 @@ class RemoteAuthoritiesImpl { } public set(authority: string, host: string, port: number): void { - this._hosts[authority] = (host === 'localhost' ? '127.0.0.1' : host); + this._hosts[authority] = host; this._ports[authority] = port; } @@ -76,9 +78,8 @@ class RemoteAuthoritiesImpl { const host = this._hosts[authority]; const port = this._ports[authority]; const connectionToken = this._connectionTokens[authority]; - const scheme = (host === '127.0.0.1' ? Schemas.http : Schemas.vscodeRemote); return URI.from({ - scheme: scheme, + scheme: Schemas.vscodeRemoteResource, authority: `${host}:${port}`, path: `/vscode-remote2`, query: `path=${encodeURIComponent(path)}&tkn=${encodeURIComponent(connectionToken)}` diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 1cb2676933..97962916e6 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -93,14 +93,12 @@ export function PlatformToString(platform: Platform) { } let _platform: Platform = Platform.Web; -if (_isNative) { - if (_isMacintosh) { - _platform = Platform.Mac; - } else if (_isWindows) { - _platform = Platform.Windows; - } else if (_isLinux) { - _platform = Platform.Linux; - } +if (_isMacintosh) { + _platform = Platform.Mac; +} else if (_isWindows) { + _platform = Platform.Windows; +} else if (_isLinux) { + _platform = Platform.Linux; } export const isWindows = _isWindows; diff --git a/src/vs/base/common/search.ts b/src/vs/base/common/search.ts new file mode 100644 index 0000000000..8d5a91edf7 --- /dev/null +++ b/src/vs/base/common/search.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as strings from './strings'; + +export function buildReplaceStringWithCasePreserved(matches: string[] | null, pattern: string): string { + if (matches && (matches[0] !== '')) { + if (matches[0].toUpperCase() === matches[0]) { + return pattern.toUpperCase(); + } else if (matches[0].toLowerCase() === matches[0]) { + return pattern.toLowerCase(); + } else if (strings.containsUppercaseCharacter(matches[0][0])) { + return pattern[0].toUpperCase() + pattern.substr(1); + } else { + // we don't understand its pattern yet. + return pattern; + } + } else { + return pattern; + } +} diff --git a/src/vs/base/parts/quickopen/common/quickOpen.ts b/src/vs/base/parts/quickopen/common/quickOpen.ts index 86afadaefd..de59fcb2de 100644 --- a/src/vs/base/parts/quickopen/common/quickOpen.ts +++ b/src/vs/base/parts/quickopen/common/quickOpen.ts @@ -68,6 +68,8 @@ export interface IDataSource { export interface IRenderer { getHeight(entry: T): number; getTemplateId(entry: T): string; + // rationale: will be replaced by quickinput later + // tslint:disable-next-line: no-dom-globals renderTemplate(templateId: string, container: HTMLElement, styles: any): any; renderElement(entry: T, templateId: string, templateData: any, styles: any): void; disposeTemplate(templateId: string, templateData: any): void; @@ -92,4 +94,4 @@ export interface IModel { runner: IRunner; filter?: IFilter; accessibilityProvider?: IAccessiblityProvider; -} \ No newline at end of file +} diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 9b8e42d3a3..8f8f45fb15 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -10,7 +10,7 @@ + content="default-src 'none'; img-src 'self' https: data: blob:; media-src 'none'; frame-src 'self' {{WEBVIEW_ENDPOINT}} https://*.vscode-webview-test.com; script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https:; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss: https:; font-src 'self' blob:;"> diff --git a/src/vs/code/electron-browser/workbench/workbench.html b/src/vs/code/electron-browser/workbench/workbench.html index 004dd3db57..e95fcdb9c5 100644 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ b/src/vs/code/electron-browser/workbench/workbench.html @@ -4,7 +4,7 @@ - + diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index b64ef63fc6..0bda375a38 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -53,7 +53,7 @@ import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; -import { IMenubarService } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService'; import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc'; import { hasArgs } from 'vs/platform/environment/node/argv'; @@ -560,7 +560,7 @@ export class CodeApplication extends Disposable { electronIpcServer.registerChannel('url', urlChannel); const storageMainService = accessor.get(IStorageMainService); - const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService as StorageMainService)); + const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService)); electronIpcServer.registerChannel('storage', storageChannel); // Log level management @@ -688,9 +688,9 @@ export class CodeApplication extends Disposable { } private handleRemoteAuthorities(): void { - protocol.registerHttpProtocol(Schemas.vscodeRemote, (request, callback) => { + protocol.registerHttpProtocol(Schemas.vscodeRemoteResource, (request, callback) => { callback({ - url: request.url.replace(/^vscode-remote:/, 'http:'), + url: request.url.replace(/^vscode-remote-resource:/, 'http:'), method: request.method }); }); diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index d537b9ea9e..72a78cd049 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -14,16 +14,25 @@ import { IOpenerService, IOpener } from 'vs/platform/opener/common/opener'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { IDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { localize } from 'vs/nls'; +import { IProductService } from 'vs/platform/product/common/product'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import Severity from 'vs/base/common/severity'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export class OpenerService implements IOpenerService { - _serviceBrand: any; + _serviceBrand!: ServiceIdentifier; private readonly _opener = new LinkedList(); constructor( @ICodeEditorService private readonly _editorService: ICodeEditorService, @ICommandService private readonly _commandService: ICommandService, + @IStorageService private readonly _storageService: IStorageService, + @IDialogService private readonly _dialogService: IDialogService, + @IProductService private readonly _productService: IProductService ) { // } @@ -33,7 +42,7 @@ export class OpenerService implements IOpenerService { return { dispose: remove }; } - async open(resource: URI, options?: { openToSide?: boolean }): Promise { + async open(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise { // no scheme ?!? if (!resource.scheme) { return Promise.resolve(false); @@ -49,14 +58,58 @@ export class OpenerService implements IOpenerService { return this._doOpen(resource, options); } - private _doOpen(resource: URI, options?: { openToSide?: boolean }): Promise { + private _doOpen(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise { - const { scheme, path, query, fragment } = resource; + const { scheme, authority, path, query, fragment } = resource; - if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https) || equalsIgnoreCase(scheme, Schemas.mailto)) { - // open http or default mail application - return this.openExternal(resource); + if (equalsIgnoreCase(scheme, Schemas.mailto) || (options && options.openExternal)) { + // open default mail application + return this._doOpenExternal(resource); + } + if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) { + let trustedDomains: string[] = ['https://code.visualstudio.com']; + try { + const trustedDomainsSrc = this._storageService.get('http.trustedDomains', StorageScope.GLOBAL); + if (trustedDomainsSrc) { + trustedDomains = JSON.parse(trustedDomainsSrc); + } + } catch (err) { } + + const domainToOpen = `${scheme}://${authority}`; + + if (isDomainTrusted(domainToOpen, trustedDomains)) { + return this._doOpenExternal(resource); + } else { + return this._dialogService.show( + Severity.Info, + localize( + 'openExternalLinkAt', + 'Do you want {0} to open the external website?\n{1}', + this._productService.nameShort, + resource.toString(true) + ), + [ + localize('openLink', 'Open Link'), + localize('cancel', 'Cancel'), + localize('configureTrustedDomains', 'Configure Trusted Domains') + ], + { + cancelId: 1 + }).then((choice) => { + if (choice === 0) { + return this._doOpenExternal(resource); + } else if (choice === 2) { + return this._commandService.executeCommand('workbench.action.configureTrustedDomains', domainToOpen).then((pickedDomains: string[]) => { + if (pickedDomains.indexOf(domainToOpen) !== -1) { + return this._doOpenExternal(resource); + } + return Promise.resolve(false); + }); + } + return Promise.resolve(false); + }); + } } else if (equalsIgnoreCase(scheme, Schemas.command)) { // run command or bail out if command isn't known if (!CommandsRegistry.getCommand(path)) { @@ -100,9 +153,27 @@ export class OpenerService implements IOpenerService { } } - openExternal(resource: URI): Promise { + private _doOpenExternal(resource: URI): Promise { dom.windowOpenNoOpener(encodeURI(resource.toString(true))); return Promise.resolve(true); } } + +/** + * Check whether a domain like https://www.microsoft.com matches + * the list of trusted domains. + */ +function isDomainTrusted(domain: string, trustedDomains: string[]) { + for (let i = 0; i < trustedDomains.length; i++) { + if (trustedDomains[i] === '*') { + return true; + } + + if (trustedDomains[i] === domain) { + return true; + } + } + + return false; +} diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index c3fb85785c..6a9eb69ef9 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -86,6 +86,14 @@ export class CursorModelState { class AutoClosedAction { + public static getAllAutoClosedCharacters(autoClosedActions: AutoClosedAction[]): Range[] { + let autoClosedCharacters: Range[] = []; + for (const autoClosedAction of autoClosedActions) { + autoClosedCharacters = autoClosedCharacters.concat(autoClosedAction.getAutoClosedCharactersRanges()); + } + return autoClosedCharacters; + } + private readonly _model: ITextModel; private _autoClosedCharactersDecorations: string[]; @@ -593,11 +601,12 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { } const closeChar = m[1]; - const openChar = this.context.config.autoClosingPairsClose[closeChar]; - if (!openChar) { + const autoClosingPairsCandidates = this.context.config.autoClosingPairsClose2.get(closeChar); + if (!autoClosingPairsCandidates || autoClosingPairsCandidates.length !== 1) { return null; } + const openChar = autoClosingPairsCandidates[0].open; const closeCharIndex = edit.text.length - m[2].length - 1; const openCharIndex = edit.text.lastIndexOf(openChar, closeCharIndex - 1); if (openCharIndex === -1) { @@ -738,7 +747,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { private _interpretCompositionEnd(source: string) { if (!this._isDoingComposition && source === 'keyboard') { // composition finishes, let's check if we need to auto complete if necessary. - this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections())); + const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); + this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters)); } } @@ -756,14 +766,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { chr = text.charAt(i); } - let autoClosedCharacters: Range[] = []; - if (this._autoClosedActions.length > 0) { - for (let i = 0, len = this._autoClosedActions.length; i < len; i++) { - autoClosedCharacters = autoClosedCharacters.concat(this._autoClosedActions[i].getAutoClosedCharactersRanges()); - } - } - - // Here we must interpret each typed character individually, that's why we create a new context + // Here we must interpret each typed character individually + const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); this._executeEditOperation(TypeOperations.typeWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters, chr)); } diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index d54fcd6d40..8c733dd015 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -15,7 +15,7 @@ import { ICommand, IConfiguration, ScrollType } from 'vs/editor/common/editorCom import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; -import { IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration'; +import { IAutoClosingPair, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; @@ -67,11 +67,22 @@ export interface ICursors { export interface CharacterMap { [char: string]: string; } +export interface MultipleCharacterMap { + [char: string]: string[]; +} const autoCloseAlways = () => true; const autoCloseNever = () => false; const autoCloseBeforeWhitespace = (chr: string) => (chr === ' ' || chr === '\t'); +function appendEntry(target: Map, key: K, value: V): void { + if (target.has(key)) { + target.get(key)!.push(value); + } else { + target.set(key, [value]); + } +} + export class CursorConfiguration { _cursorMoveConfigurationBrand: void; @@ -90,8 +101,8 @@ export class CursorConfiguration { public readonly autoClosingQuotes: EditorAutoClosingStrategy; public readonly autoSurround: EditorAutoSurroundStrategy; public readonly autoIndent: boolean; - public readonly autoClosingPairsOpen: CharacterMap; - public readonly autoClosingPairsClose: CharacterMap; + public readonly autoClosingPairsOpen2: Map; + public readonly autoClosingPairsClose2: Map; public readonly surroundingPairs: CharacterMap; public readonly shouldAutoCloseBefore: { quote: (ch: string) => boolean, bracket: (ch: string) => boolean }; @@ -138,8 +149,8 @@ export class CursorConfiguration { this.autoSurround = c.autoSurround; this.autoIndent = c.autoIndent; - this.autoClosingPairsOpen = {}; - this.autoClosingPairsClose = {}; + this.autoClosingPairsOpen2 = new Map(); + this.autoClosingPairsClose2 = new Map(); this.surroundingPairs = {}; this._electricChars = null; @@ -151,8 +162,10 @@ export class CursorConfiguration { let autoClosingPairs = CursorConfiguration._getAutoClosingPairs(languageIdentifier); if (autoClosingPairs) { for (const pair of autoClosingPairs) { - this.autoClosingPairsOpen[pair.open] = pair.close; - this.autoClosingPairsClose[pair.close] = pair.open; + appendEntry(this.autoClosingPairsOpen2, pair.open.charAt(pair.open.length - 1), pair); + if (pair.close.length === 1) { + appendEntry(this.autoClosingPairsClose2, pair.close, pair); + } } } @@ -190,7 +203,7 @@ export class CursorConfiguration { } } - private static _getAutoClosingPairs(languageIdentifier: LanguageIdentifier): IAutoClosingPair[] | null { + private static _getAutoClosingPairs(languageIdentifier: LanguageIdentifier): StandardAutoClosingPairConditional[] | null { try { return LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id); } catch (e) { diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index 7c40f5454f..ac23ceace3 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -63,7 +63,8 @@ export class DeleteOperations { const lineText = model.getLineContent(position.lineNumber); const character = lineText[position.column - 2]; - if (!config.autoClosingPairsOpen.hasOwnProperty(character)) { + const autoClosingPairCandidates = config.autoClosingPairsOpen2.get(character); + if (!autoClosingPairCandidates) { return false; } @@ -78,9 +79,14 @@ export class DeleteOperations { } const afterCharacter = lineText[position.column - 1]; - const closeCharacter = config.autoClosingPairsOpen[character]; - if (afterCharacter !== closeCharacter) { + let foundAutoClosingPair = false; + for (const autoClosingPairCandidate of autoClosingPairCandidates) { + if (autoClosingPairCandidate.open === character && autoClosingPairCandidate.close === afterCharacter) { + foundAutoClosingPair = true; + } + } + if (!foundAutoClosingPair) { return false; } } diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index abcfd19c64..45de4b2cf6 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -13,9 +13,10 @@ import { CursorColumns, CursorConfiguration, EditOperationResult, EditOperationT import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; import { ICommand, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { EnterAction, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { EnterAction, IndentAction, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; @@ -433,7 +434,11 @@ export class TypeOperations { private static _isAutoClosingCloseCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[], ch: string): boolean { const autoCloseConfig = isQuote(ch) ? config.autoClosingQuotes : config.autoClosingBrackets; - if (autoCloseConfig === 'never' || !config.autoClosingPairsClose.hasOwnProperty(ch)) { + if (autoCloseConfig === 'never') { + return false; + } + + if (!config.autoClosingPairsClose2.has(ch)) { return false; } @@ -469,15 +474,6 @@ export class TypeOperations { return true; } - private static _countNeedlesInHaystack(haystack: string, needle: string): number { - let cnt = 0; - let lastIndex = -1; - while ((lastIndex = haystack.indexOf(needle, lastIndex + 1)) !== -1) { - cnt++; - } - return cnt; - } - private static _runAutoClosingCloseCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { @@ -492,65 +488,98 @@ export class TypeOperations { }); } - private static _isBeforeClosingBrace(config: CursorConfiguration, ch: string, characterAfter: string) { - const thisBraceIsSymmetric = (config.autoClosingPairsOpen[ch] === ch); - let isBeforeCloseBrace = false; - for (let otherCloseBrace in config.autoClosingPairsClose) { - const otherBraceIsSymmetric = (config.autoClosingPairsOpen[otherCloseBrace] === otherCloseBrace); - if (!thisBraceIsSymmetric && otherBraceIsSymmetric) { - continue; - } - if (characterAfter === otherCloseBrace) { - isBeforeCloseBrace = true; - break; - } - } - - return isBeforeCloseBrace; - } - - private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean { - const chIsQuote = isQuote(ch); - const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; - - if (autoCloseConfig === 'never' || !config.autoClosingPairsOpen.hasOwnProperty(ch)) { + private static _isBeforeClosingBrace(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional, characterAfter: string) { + const otherAutoClosingPairs = config.autoClosingPairsClose2.get(characterAfter); + if (!otherAutoClosingPairs) { return false; } - let shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; + const thisBraceIsSymmetric = (autoClosingPair.open === autoClosingPair.close); + for (const otherAutoClosingPair of otherAutoClosingPairs) { + const otherBraceIsSymmetric = (otherAutoClosingPair.open === otherAutoClosingPair.close); + if (!thisBraceIsSymmetric && otherBraceIsSymmetric) { + continue; + } + return true; + } + + return false; + } + + private static _findAutoClosingPairOpen(config: CursorConfiguration, model: ITextModel, positions: Position[], ch: string): StandardAutoClosingPairConditional | null { + const autoClosingPairCandidates = config.autoClosingPairsOpen2.get(ch); + if (!autoClosingPairCandidates) { + return null; + } + + // Determine which auto-closing pair it is + let autoClosingPair: StandardAutoClosingPairConditional | null = null; + for (const autoClosingPairCandidate of autoClosingPairCandidates) { + if (autoClosingPair === null || autoClosingPairCandidate.open.length > autoClosingPair.open.length) { + let candidateIsMatch = true; + for (const position of positions) { + const relevantText = model.getValueInRange(new Range(position.lineNumber, position.column - autoClosingPairCandidate.open.length + 1, position.lineNumber, position.column)); + if (relevantText + ch !== autoClosingPairCandidate.open) { + candidateIsMatch = false; + break; + } + } + + if (candidateIsMatch) { + autoClosingPair = autoClosingPairCandidate; + } + } + } + return autoClosingPair; + } + + private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): StandardAutoClosingPairConditional | null { + const chIsQuote = isQuote(ch); + const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; + if (autoCloseConfig === 'never') { + return null; + } + + const autoClosingPair = this._findAutoClosingPairOpen(config, model, selections.map(s => s.getPosition()), ch); + if (!autoClosingPair) { + return null; + } + + const shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; if (!selection.isEmpty()) { - return false; + return null; } const position = selection.getPosition(); const lineText = model.getLineContent(position.lineNumber); - // Do not auto-close ' or " after a word character - if ((chIsQuote && position.column > 1) && autoCloseConfig !== 'always') { - const wordSeparators = getMapForWordSeparators(config.wordSeparators); - const characterBeforeCode = lineText.charCodeAt(position.column - 2); - const characterBeforeType = wordSeparators.get(characterBeforeCode); - if (characterBeforeType === WordCharacterClass.Regular) { - return false; - } - } - // Only consider auto closing the pair if a space follows or if another autoclosed pair follows - const characterAfter = lineText.charAt(position.column - 1); - if (characterAfter) { - let isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, ch, characterAfter); + if (lineText.length > position.column - 1) { + const characterAfter = lineText.charAt(position.column - 1); + const isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, autoClosingPair, characterAfter); if (!isBeforeCloseBrace && !shouldAutoCloseBefore(characterAfter)) { - return false; + return null; } } if (!model.isCheapToTokenize(position.lineNumber)) { // Do not force tokenization - return false; + return null; + } + + // Do not auto-close ' or " after a word character + if (autoClosingPair.open.length === 1 && chIsQuote && autoCloseConfig !== 'always') { + const wordSeparators = getMapForWordSeparators(config.wordSeparators); + if (insertOpenCharacter && position.column > 1 && wordSeparators.get(lineText.charCodeAt(position.column - 2)) === WordCharacterClass.Regular) { + return null; + } + if (!insertOpenCharacter && position.column > 2 && wordSeparators.get(lineText.charCodeAt(position.column - 3)) === WordCharacterClass.Regular) { + return null; + } } model.forceTokenization(position.lineNumber); @@ -558,25 +587,24 @@ export class TypeOperations { let shouldAutoClosePair = false; try { - shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column); + shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(autoClosingPair, lineTokens, insertOpenCharacter ? position.column : position.column - 1); } catch (e) { onUnexpectedError(e); } if (!shouldAutoClosePair) { - return false; + return null; } } - return true; + return autoClosingPair; } - private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): EditOperationResult { + private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean, autoClosingPair: StandardAutoClosingPairConditional): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; - const closeCharacter = config.autoClosingPairsOpen[ch]; - commands[i] = new TypeWithAutoClosingCommand(selection, ch, closeCharacter); + commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPair.close); } return new EditOperationResult(EditOperationType.Typing, commands, { shouldPushStackElementBefore: true, @@ -679,14 +707,6 @@ export class TypeOperations { return null; } - if (electricAction.appendText) { - const command = new ReplaceCommandWithOffsetCursorState(selection, ch + electricAction.appendText, 0, -electricAction.appendText.length); - return new EditOperationResult(EditOperationType.Typing, [command], { - shouldPushStackElementBefore: false, - shouldPushStackElementAfter: true - }); - } - if (electricAction.matchOpenBracket) { let endColumn = (lineTokens.getLineContent() + ch).lastIndexOf(electricAction.matchOpenBracket) + 1; let match = model.findMatchingBracketUp(electricAction.matchOpenBracket, { @@ -722,87 +742,44 @@ export class TypeOperations { return null; } - public static compositionEndWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[]): EditOperationResult | null { - if (config.autoClosingQuotes === 'never') { + /** + * This is very similar with typing, but the character is already in the text buffer! + */ + public static compositionEndWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[]): EditOperationResult | null { + let ch: string | null = null; + // extract last typed character + for (const selection of selections) { + if (!selection.isEmpty()) { + return null; + } + const position = selection.getPosition(); + const currentChar = model.getValueInRange(new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column)); + if (ch === null) { + ch = currentChar; + } else if (ch !== currentChar) { + return null; + } + } + + if (!ch) { return null; } - let commands: ICommand[] = []; - - for (let i = 0; i < selections.length; i++) { - if (!selections[i].isEmpty()) { - continue; - } - const position = selections[i].getPosition(); - const lineText = model.getLineContent(position.lineNumber); - const ch = lineText.charAt(position.column - 2); - - if (config.autoClosingPairsClose.hasOwnProperty(ch)) { // first of all, it's a closing tag - if (ch === config.autoClosingPairsClose[ch] /** isEqualPair */) { - const lineTextBeforeCursor = lineText.substr(0, position.column - 2); - const chCntBefore = this._countNeedlesInHaystack(lineTextBeforeCursor, ch); - - if (chCntBefore % 2 === 1) { - continue; // it pairs with the opening tag. - } - } - } - - // As we are not typing in a new character, so we don't need to run `_runAutoClosingCloseCharType` - // Next step, let's try to check if it's an open char. - if (config.autoClosingPairsOpen.hasOwnProperty(ch)) { - if (isQuote(ch) && position.column > 2) { - const wordSeparators = getMapForWordSeparators(config.wordSeparators); - const characterBeforeCode = lineText.charCodeAt(position.column - 3); - const characterBeforeType = wordSeparators.get(characterBeforeCode); - if (characterBeforeType === WordCharacterClass.Regular) { - continue; - } - } - - const characterAfter = lineText.charAt(position.column - 1); - - if (characterAfter) { - let isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, ch, characterAfter); - let shouldAutoCloseBefore = isQuote(ch) ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; - if (isBeforeCloseBrace) { - // In normal auto closing logic, we will auto close if the cursor is even before a closing brace intentionally. - // However for composition mode, we do nothing here as users might clear all the characters for composition and we don't want to do a unnecessary auto close. - // Related: microsoft/vscode#57250. - continue; - } - if (!shouldAutoCloseBefore(characterAfter)) { - continue; - } - } - - if (!model.isCheapToTokenize(position.lineNumber)) { - // Do not force tokenization - continue; - } - - model.forceTokenization(position.lineNumber); - const lineTokens = model.getLineTokens(position.lineNumber); - - let shouldAutoClosePair = false; - - try { - shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column - 1); - } catch (e) { - onUnexpectedError(e); - } - - if (shouldAutoClosePair) { - const closeCharacter = config.autoClosingPairsOpen[ch]; - commands[i] = new ReplaceCommandWithOffsetCursorState(selections[i], closeCharacter, 0, -closeCharacter.length); - } - } + if (this._isAutoClosingCloseCharType(config, model, selections, autoClosedCharacters, ch)) { + // Unfortunately, the close character is at this point "doubled", so we need to delete it... + const commands = selections.map(s => new ReplaceCommand(new Range(s.positionLineNumber, s.positionColumn, s.positionLineNumber, s.positionColumn + 1), '', false)); + return new EditOperationResult(EditOperationType.Typing, commands, { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: false + }); } - return new EditOperationResult(EditOperationType.Typing, commands, { - shouldPushStackElementBefore: true, - shouldPushStackElementAfter: false - }); + const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, false); + if (autoClosingPairOpenCharType) { + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairOpenCharType); + } + + return null; } public static typeWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[], ch: string): EditOperationResult { @@ -840,8 +817,9 @@ export class TypeOperations { return this._runAutoClosingCloseCharType(prevEditOperationType, config, model, selections, ch); } - if (this._isAutoClosingOpenCharType(config, model, selections, ch)) { - return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch); + const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, true); + if (autoClosingPairOpenCharType) { + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairOpenCharType); } if (this._isSurroundSelectionType(config, model, selections, ch)) { @@ -929,12 +907,14 @@ export class TypeOperations { export class TypeWithAutoClosingCommand extends ReplaceCommandWithOffsetCursorState { - private _closeCharacter: string; + private readonly _openCharacter: string; + private readonly _closeCharacter: string; public closeCharacterRange: Range | null; public enclosingRange: Range | null; - constructor(selection: Selection, openCharacter: string, closeCharacter: string) { - super(selection, openCharacter + closeCharacter, 0, -closeCharacter.length); + constructor(selection: Selection, openCharacter: string, insertOpenCharacter: boolean, closeCharacter: string) { + super(selection, (insertOpenCharacter ? openCharacter : '') + closeCharacter, 0, -closeCharacter.length); + this._openCharacter = openCharacter; this._closeCharacter = closeCharacter; this.closeCharacterRange = null; this.enclosingRange = null; @@ -944,7 +924,7 @@ export class TypeWithAutoClosingCommand extends ReplaceCommandWithOffsetCursorSt let inverseEditOperations = helper.getInverseEditOperations(); let range = inverseEditOperations[0].range; this.closeCharacterRange = new Range(range.startLineNumber, range.endColumn - this._closeCharacter.length, range.endLineNumber, range.endColumn); - this.enclosingRange = range; + this.enclosingRange = new Range(range.startLineNumber, range.endColumn - this._openCharacter.length - this._closeCharacter.length, range.endLineNumber, range.endColumn); return super.computeCursorState(model, helper); } } diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index 9f65e8d3de..d64d5ad623 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -78,7 +78,9 @@ export interface LanguageConfiguration { * * @deprecated Will be replaced by a better API soon. */ - __electricCharacterSupport?: IBracketElectricCharacterContribution; + __electricCharacterSupport?: { + docComment?: IDocComment; + }; } /** @@ -155,10 +157,6 @@ export interface OnEnterRule { action: EnterAction; } -export interface IBracketElectricCharacterContribution { - docComment?: IDocComment; -} - /** * Definition of documentation comments (e.g. Javadoc/JSdoc) */ diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index 98c3832417..513189706e 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -12,7 +12,7 @@ import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; -import { EnterAction, FoldingRules, IAutoClosingPair, IAutoClosingPairConditional, IndentAction, IndentationRule, LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; +import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; @@ -97,16 +97,7 @@ export class RichEditSupport { public get electricCharacter(): BracketElectricCharacterSupport | null { if (!this._electricCharacter) { - let autoClosingPairs: IAutoClosingPairConditional[] = []; - if (this._conf.autoClosingPairs) { - autoClosingPairs = this._conf.autoClosingPairs; - } else if (this._conf.brackets) { - autoClosingPairs = this._conf.brackets.map(b => { - return { open: b[0], close: b[1] }; - }); - } - - this._electricCharacter = new BracketElectricCharacterSupport(this.brackets, autoClosingPairs, this._conf.__electricCharacterSupport); + this._electricCharacter = new BracketElectricCharacterSupport(this.brackets); } return this._electricCharacter; } @@ -261,7 +252,7 @@ export class LanguageConfigurationRegistryImpl { return value.characterPair || null; } - public getAutoClosingPairs(languageId: LanguageId): IAutoClosingPair[] { + public getAutoClosingPairs(languageId: LanguageId): StandardAutoClosingPairConditional[] { let characterPairSupport = this._getCharacterPairSupport(languageId); if (!characterPairSupport) { return []; @@ -285,13 +276,9 @@ export class LanguageConfigurationRegistryImpl { return characterPairSupport.getSurroundingPairs(); } - public shouldAutoClosePair(character: string, context: LineTokens, column: number): boolean { - let scopedLineTokens = createScopedLineTokens(context, column - 1); - let characterPairSupport = this._getCharacterPairSupport(scopedLineTokens.languageId); - if (!characterPairSupport) { - return false; - } - return characterPairSupport.shouldAutoClosePair(character, scopedLineTokens, column - scopedLineTokens.firstCharOffset); + public shouldAutoClosePair(autoClosingPair: StandardAutoClosingPairConditional, context: LineTokens, column: number): boolean { + const scopedLineTokens = createScopedLineTokens(context, column - 1); + return CharacterPairSupport.shouldAutoClosePair(autoClosingPair, scopedLineTokens, column - scopedLineTokens.firstCharOffset); } // end characterPair diff --git a/src/vs/editor/common/modes/supports/characterPair.ts b/src/vs/editor/common/modes/supports/characterPair.ts index 0490ebf13c..37e3386e19 100644 --- a/src/vs/editor/common/modes/supports/characterPair.ts +++ b/src/vs/editor/common/modes/supports/characterPair.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharacterPair, IAutoClosingPair, IAutoClosingPairConditional, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; +import { IAutoClosingPair, StandardAutoClosingPairConditional, LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; import { ScopedLineTokens } from 'vs/editor/common/modes/supports'; export class CharacterPairSupport { @@ -15,7 +15,7 @@ export class CharacterPairSupport { private readonly _surroundingPairs: IAutoClosingPair[]; private readonly _autoCloseBefore: string; - constructor(config: { brackets?: CharacterPair[]; autoClosingPairs?: IAutoClosingPairConditional[], surroundingPairs?: IAutoClosingPair[], autoCloseBefore?: string }) { + constructor(config: LanguageConfiguration) { if (config.autoClosingPairs) { this._autoClosingPairs = config.autoClosingPairs.map(el => new StandardAutoClosingPairConditional(el)); } else if (config.brackets) { @@ -24,12 +24,18 @@ export class CharacterPairSupport { this._autoClosingPairs = []; } + if (config.__electricCharacterSupport && config.__electricCharacterSupport.docComment) { + const docComment = config.__electricCharacterSupport.docComment; + // IDocComment is legacy, only partially supported + this._autoClosingPairs.push(new StandardAutoClosingPairConditional({ open: docComment.open, close: docComment.close || '' })); + } + this._autoCloseBefore = typeof config.autoCloseBefore === 'string' ? config.autoCloseBefore : CharacterPairSupport.DEFAULT_AUTOCLOSE_BEFORE_LANGUAGE_DEFINED; this._surroundingPairs = config.surroundingPairs || this._autoClosingPairs; } - public getAutoClosingPairs(): IAutoClosingPair[] { + public getAutoClosingPairs(): StandardAutoClosingPairConditional[] { return this._autoClosingPairs; } @@ -37,22 +43,15 @@ export class CharacterPairSupport { return this._autoCloseBefore; } - public shouldAutoClosePair(character: string, context: ScopedLineTokens, column: number): boolean { + public static shouldAutoClosePair(autoClosingPair: StandardAutoClosingPairConditional, context: ScopedLineTokens, column: number): boolean { // Always complete on empty line if (context.getTokenCount() === 0) { return true; } - let tokenIndex = context.findTokenIndexAtOffset(column - 2); - let standardTokenType = context.getStandardTokenType(tokenIndex); - - for (const autoClosingPair of this._autoClosingPairs) { - if (autoClosingPair.open === character) { - return autoClosingPair.isOK(standardTokenType); - } - } - - return false; + const tokenIndex = context.findTokenIndexAtOffset(column - 2); + const standardTokenType = context.getStandardTokenType(tokenIndex); + return autoClosingPair.isOK(standardTokenType); } public getSurroundingPairs(): IAutoClosingPair[] { diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts index 5dfb1ff4e1..ddd2527038 100644 --- a/src/vs/editor/common/modes/supports/electricCharacter.ts +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAutoClosingPairConditional, IBracketElectricCharacterContribution, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { ScopedLineTokens, ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; import { BracketsUtils, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; @@ -12,29 +11,17 @@ import { BracketsUtils, RichEditBrackets } from 'vs/editor/common/modes/supports * @internal */ export interface IElectricAction { - // Only one of the following properties should be defined: - // The line will be indented at the same level of the line // which contains the matching given bracket type. - matchOpenBracket?: string; - - // The text will be appended after the electric character. - appendText?: string; + matchOpenBracket: string; } export class BracketElectricCharacterSupport { private readonly _richEditBrackets: RichEditBrackets | null; - private readonly _complexAutoClosePairs: StandardAutoClosingPairConditional[]; - constructor(richEditBrackets: RichEditBrackets | null, autoClosePairs: IAutoClosingPairConditional[], contribution: IBracketElectricCharacterContribution | null | undefined) { - contribution = contribution || {}; + constructor(richEditBrackets: RichEditBrackets | null) { this._richEditBrackets = richEditBrackets; - this._complexAutoClosePairs = autoClosePairs.filter(pair => pair.open.length > 1 && !!pair.close).map(el => new StandardAutoClosingPairConditional(el)); - if (contribution.docComment) { - // IDocComment is legacy, only partially supported - this._complexAutoClosePairs.push(new StandardAutoClosingPairConditional({ open: contribution.docComment.open, close: contribution.docComment.close || '' })); - } } public getElectricCharacters(): string[] { @@ -48,11 +35,6 @@ export class BracketElectricCharacterSupport { } } - // auto close - for (let pair of this._complexAutoClosePairs) { - result.push(pair.open.charAt(pair.open.length - 1)); - } - // Filter duplicate entries result = result.filter((item, pos, array) => { return array.indexOf(item) === pos; @@ -62,12 +44,6 @@ export class BracketElectricCharacterSupport { } public onElectricCharacter(character: string, context: ScopedLineTokens, column: number): IElectricAction | null { - return (this._onElectricAutoClose(character, context, column) || - this._onElectricAutoIndent(character, context, column)); - } - - private _onElectricAutoIndent(character: string, context: ScopedLineTokens, column: number): IElectricAction | null { - if (!this._richEditBrackets || this._richEditBrackets.brackets.length === 0) { return null; } @@ -103,44 +79,4 @@ export class BracketElectricCharacterSupport { matchOpenBracket: bracketText }; } - - private _onElectricAutoClose(character: string, context: ScopedLineTokens, column: number): IElectricAction | null { - if (!this._complexAutoClosePairs.length) { - return null; - } - - let line = context.getLineContent(); - - for (let i = 0, len = this._complexAutoClosePairs.length; i < len; i++) { - let pair = this._complexAutoClosePairs[i]; - - // See if the right electric character was pressed - if (character !== pair.open.charAt(pair.open.length - 1)) { - continue; - } - - // check if the full open bracket matches - let start = column - pair.open.length + 1; - let actual = line.substring(start - 1, column - 1) + character; - if (actual !== pair.open) { - continue; - } - - let lastTokenIndex = context.findTokenIndexAtOffset(column - 1); - let lastTokenStandardType = context.getStandardTokenType(lastTokenIndex); - // If we're in a scope listed in 'notIn', do nothing - if (!pair.isOK(lastTokenStandardType)) { - continue; - } - - // If this line already contains the closing tag, do nothing. - if (line.indexOf(pair.close, column - 1) >= 0) { - continue; - } - - return { appendText: pair.close }; - } - - return null; - } } diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 54cfaa9be8..9f489d9398 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -202,16 +202,33 @@ export class OutlineGroup extends TreeElement { } } +class MovingAverage { + + private _n = 1; + private _val = 0; + + update(value: number): this { + this._val = this._val + (value - this._val) / this._n; + this._n += 1; + return this; + } + + get value(): number { + return this._val; + } +} + export class OutlineModel extends TreeElement { + private static readonly _requestDurations = new LRUCache(50, 0.7); private static readonly _requests = new LRUCache, model: OutlineModel | undefined }>(9, 0.75); private static readonly _keys = new class { private _counter = 1; private _data = new WeakMap(); - for(textModel: ITextModel): string { - return `${textModel.id}/${textModel.getVersionId()}/${this._hash(DocumentSymbolProviderRegistry.all(textModel))}`; + for(textModel: ITextModel, version: boolean): string { + return `${textModel.id}/${version ? textModel.getVersionId() : ''}/${this._hash(DocumentSymbolProviderRegistry.all(textModel))}`; } private _hash(providers: DocumentSymbolProvider[]): string { @@ -231,7 +248,7 @@ export class OutlineModel extends TreeElement { static create(textModel: ITextModel, token: CancellationToken): Promise { - let key = this._keys.for(textModel); + let key = this._keys.for(textModel, true); let data = OutlineModel._requests.get(key); if (!data) { @@ -243,6 +260,18 @@ export class OutlineModel extends TreeElement { model: undefined, }; OutlineModel._requests.set(key, data); + + // keep moving average of request durations + const now = Date.now(); + data.promise.then(() => { + let key = this._keys.for(textModel, false); + let avg = this._requestDurations.get(key); + if (!avg) { + avg = new MovingAverage(); + this._requestDurations.set(key, avg); + } + avg.update(Date.now() - now); + }); } if (data!.model) { @@ -272,7 +301,18 @@ export class OutlineModel extends TreeElement { }); } - static _create(textModel: ITextModel, token: CancellationToken): Promise { + static getRequestDelay(textModel: ITextModel | null): number { + if (!textModel) { + return 350; + } + const avg = this._requestDurations.get(this._keys.for(textModel, false)); + if (!avg) { + return 350; + } + return Math.max(350, Math.floor(1.3 * avg.value)); + } + + private static _create(textModel: ITextModel, token: CancellationToken): Promise { const cts = new CancellationTokenSource(token); const result = new OutlineModel(textModel); diff --git a/src/vs/editor/contrib/find/replacePattern.ts b/src/vs/editor/contrib/find/replacePattern.ts index 581062f875..7c5cb455fd 100644 --- a/src/vs/editor/contrib/find/replacePattern.ts +++ b/src/vs/editor/contrib/find/replacePattern.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from 'vs/base/common/charCode'; -import { containsUppercaseCharacter } from 'vs/base/common/strings'; +import { buildReplaceStringWithCasePreserved } from 'vs/base/common/search'; const enum ReplacePatternKind { StaticValue = 0, @@ -51,17 +51,8 @@ export class ReplacePattern { public buildReplaceString(matches: string[] | null, preserveCase?: boolean): string { if (this._state.kind === ReplacePatternKind.StaticValue) { - if (preserveCase && matches && (matches[0] !== '')) { - if (matches[0].toUpperCase() === matches[0]) { - return this._state.staticValue.toUpperCase(); - } else if (matches[0].toLowerCase() === matches[0]) { - return this._state.staticValue.toLowerCase(); - } else if (containsUppercaseCharacter(matches[0][0])) { - return this._state.staticValue[0].toUpperCase() + this._state.staticValue.substr(1); - } else { - // we don't understand its pattern yet. - return this._state.staticValue; - } + if (preserveCase) { + return buildReplaceStringWithCasePreserved(matches, this._state.staticValue); } else { return this._state.staticValue; } diff --git a/src/vs/editor/contrib/find/test/replacePattern.test.ts b/src/vs/editor/contrib/find/test/replacePattern.test.ts index bc22aaff5c..04da0bda2d 100644 --- a/src/vs/editor/contrib/find/test/replacePattern.test.ts +++ b/src/vs/editor/contrib/find/test/replacePattern.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { ReplacePattern, ReplacePiece, parseReplaceString } from 'vs/editor/contrib/find/replacePattern'; +import { buildReplaceStringWithCasePreserved } from 'vs/base/common/search'; suite('Replace Pattern test', () => { @@ -154,6 +155,29 @@ suite('Replace Pattern test', () => { assert.equal(actual, 'a{}'); }); + test('buildReplaceStringWithCasePreserved test', () => { + let replacePattern = 'Def'; + let actual: string | string[] = 'abc'; + + assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'def'); + actual = 'Abc'; + assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'Def'); + actual = 'ABC'; + assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'DEF'); + + actual = ['abc', 'Abc']; + assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'def'); + actual = ['Abc', 'abc']; + assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def'); + actual = ['ABC', 'abc']; + assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'DEF'); + + actual = ['AbC']; + assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def'); + actual = ['aBC']; + assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def'); + }); + test('preserve case', () => { let replacePattern = parseReplaceString('Def'); let actual = replacePattern.buildReplaceString(['abc'], true); diff --git a/src/vs/editor/contrib/folding/indentRangeProvider.ts b/src/vs/editor/contrib/folding/indentRangeProvider.ts index 7f8e600f6e..6f2d080847 100644 --- a/src/vs/editor/contrib/folding/indentRangeProvider.ts +++ b/src/vs/editor/contrib/folding/indentRangeProvider.ts @@ -105,7 +105,11 @@ export class RangesCollector { } -interface PreviousRegion { indent: number; line: number; marker: boolean; } +interface PreviousRegion { + indent: number; // indent or -2 if a marker + endAbove: number; // end line number for the region above + line: number; // start line of the region. Only used for marker regions. +} export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, foldingRangesLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): FoldingRegions { const tabSize = model.getOptions().tabSize; @@ -117,16 +121,19 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol } let previousRegions: PreviousRegion[] = []; - previousRegions.push({ indent: -1, line: model.getLineCount() + 1, marker: false }); // sentinel, to make sure there's at least one entry + let line = model.getLineCount() + 1; + previousRegions.push({ indent: -1, endAbove: line, line }); // sentinel, to make sure there's at least one entry for (let line = model.getLineCount(); line > 0; line--) { let lineContent = model.getLineContent(line); let indent = TextModel.computeIndentLevel(lineContent, tabSize); let previous = previousRegions[previousRegions.length - 1]; if (indent === -1) { - if (offSide && !previous.marker) { - // for offSide languages, empty lines are associated to the next block - previous.line = line; + if (offSide) { + // for offSide languages, empty lines are associated to the previous block + // note: the next block is already written to the results, so this only + // impacts the end position of the block before + previous.endAbove = line; } continue; // only whitespace } @@ -136,7 +143,7 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol if (m[1]) { // start pattern match // discard all regions until the folding pattern let i = previousRegions.length - 1; - while (i > 0 && !previousRegions[i].marker) { + while (i > 0 && previousRegions[i].indent !== -2) { i--; } if (i > 0) { @@ -145,15 +152,15 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol // new folding range from pattern, includes the end line result.insertFirst(line, previous.line, indent); - previous.marker = false; - previous.indent = indent; previous.line = line; + previous.indent = indent; + previous.endAbove = line; continue; } else { // no end marker found, treat line as a regular line } } else { // end pattern match - previousRegions.push({ indent: -2, line, marker: true }); + previousRegions.push({ indent: -2, endAbove: line, line }); continue; } } @@ -165,16 +172,16 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol } while (previous.indent > indent); // new folding range - let endLineNumber = previous.line - 1; + let endLineNumber = previous.endAbove - 1; if (endLineNumber - line >= 1) { // needs at east size 1 result.insertFirst(line, endLineNumber, indent); } } if (previous.indent === indent) { - previous.line = line; + previous.endAbove = line; } else { // previous.indent < indent // new region with a bigger indent - previousRegions.push({ indent, line, marker: false }); + previousRegions.push({ indent, endAbove: line, line }); } } return result.toIndentRanges(model); diff --git a/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts b/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts index 56799b80ad..0a1779aea9 100644 --- a/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts +++ b/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts @@ -316,5 +316,17 @@ suite('Folding with regions', () => { /* 8*/ '#endregionff', ], [], true, markers); }); - + test('Issue 79359', () => { + assertRanges([ + /* 1*/ '#region', + /* 2*/ '', + /* 3*/ 'class A', + /* 4*/ ' foo', + /* 5*/ '', + /* 6*/ 'class A', + /* 7*/ ' foo', + /* 8*/ '', + /* 9*/ '#endregion', + ], [r(1, 9, -1, true), r(3, 4, 0), r(6, 7, 0)], true, markers); + }); }); diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 56cf187a1c..bfa5b7738f 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -38,6 +38,9 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IProductService } from 'vs/platform/product/common/product'; +import { IStorageService } from 'vs/platform/storage/common/storage'; type Omit = Pick>; @@ -51,7 +54,13 @@ function withAllStandaloneServices(domElement: H } if (!services.has(IOpenerService)) { - services.set(IOpenerService, new OpenerService(services.get(ICodeEditorService), services.get(ICommandService))); + services.set(IOpenerService, new OpenerService( + services.get(ICodeEditorService), + services.get(ICommandService), + services.get(IStorageService), + services.get(IDialogService), + services.get(IProductService) + )); } let result = callback(services); diff --git a/src/vs/editor/standalone/common/monarch/monarchCompile.ts b/src/vs/editor/standalone/common/monarch/monarchCompile.ts index 9ff8d2ef55..eb33b55d6a 100644 --- a/src/vs/editor/standalone/common/monarch/monarchCompile.ts +++ b/src/vs/editor/standalone/common/monarch/monarchCompile.ts @@ -27,11 +27,9 @@ function isArrayOf(elemType: (x: any) => boolean, obj: any): boolean { if (!(Array.isArray(obj))) { return false; } - for (let idx in obj) { - if (obj.hasOwnProperty(idx)) { - if (!(elemType(obj[idx]))) { - return false; - } + for (const el of obj) { + if (!(elemType(el))) { + return false; } } return true; @@ -298,10 +296,8 @@ function compileAction(lexer: monarchCommon.ILexerMin, ruleName: string, action: } else if (Array.isArray(action)) { let results: monarchCommon.FuzzyAction[] = []; - for (let idx in action) { - if (action.hasOwnProperty(idx)) { - results[idx] = compileAction(lexer, ruleName, action[idx]); - } + for (let i = 0, len = action.length; i < len; i++) { + results[i] = compileAction(lexer, ruleName, action[i]); } return { group: results }; } @@ -331,13 +327,10 @@ function compileAction(lexer: monarchCommon.ILexerMin, ruleName: string, action: const def = lexer.defaultToken; return { test: function (id, matches, state, eos) { - for (let idx in cases) { - if (cases.hasOwnProperty(idx)) { - const _case = cases[idx]; - const didmatch = (!_case.test || _case.test(id, matches, state, eos)); - if (didmatch) { - return _case.value; - } + for (const _case of cases) { + const didmatch = (!_case.test || _case.test(id, matches, state, eos)); + if (didmatch) { + return _case.value; } } return def; @@ -425,64 +418,61 @@ export function compile(languageId: string, json: IMonarchLanguage): monarchComm // Compile an array of rules into newrules where RegExp objects are created. function addRules(state: string, newrules: monarchCommon.IRule[], rules: any[]) { - for (let idx in rules) { - if (rules.hasOwnProperty(idx)) { - const rule = rules[idx]; - let include = rule.include; - if (include) { - if (typeof (include) !== 'string') { - throw monarchCommon.createError(lexer, 'an \'include\' attribute must be a string at: ' + state); - } - if (include[0] === '@') { - include = include.substr(1); // peel off starting @ - } - if (!json.tokenizer[include]) { - throw monarchCommon.createError(lexer, 'include target \'' + include + '\' is not defined at: ' + state); - } - addRules(state + '.' + include, newrules, json.tokenizer[include]); + for (const rule of rules) { + + let include = rule.include; + if (include) { + if (typeof (include) !== 'string') { + throw monarchCommon.createError(lexer, 'an \'include\' attribute must be a string at: ' + state); } - else { - const newrule = new Rule(state); + if (include[0] === '@') { + include = include.substr(1); // peel off starting @ + } + if (!json.tokenizer[include]) { + throw monarchCommon.createError(lexer, 'include target \'' + include + '\' is not defined at: ' + state); + } + addRules(state + '.' + include, newrules, json.tokenizer[include]); + } + else { + const newrule = new Rule(state); - - // Set up new rule attributes - if (Array.isArray(rule) && rule.length >= 1 && rule.length <= 3) { - newrule.setRegex(lexerMin, rule[0]); - if (rule.length >= 3) { - if (typeof (rule[1]) === 'string') { - newrule.setAction(lexerMin, { token: rule[1], next: rule[2] }); - } - else if (typeof (rule[1]) === 'object') { - const rule1 = rule[1]; - rule1.next = rule[2]; - newrule.setAction(lexerMin, rule1); - } - else { - throw monarchCommon.createError(lexer, 'a next state as the last element of a rule can only be given if the action is either an object or a string, at: ' + state); - } + // Set up new rule attributes + if (Array.isArray(rule) && rule.length >= 1 && rule.length <= 3) { + newrule.setRegex(lexerMin, rule[0]); + if (rule.length >= 3) { + if (typeof (rule[1]) === 'string') { + newrule.setAction(lexerMin, { token: rule[1], next: rule[2] }); + } + else if (typeof (rule[1]) === 'object') { + const rule1 = rule[1]; + rule1.next = rule[2]; + newrule.setAction(lexerMin, rule1); } else { - newrule.setAction(lexerMin, rule[1]); + throw monarchCommon.createError(lexer, 'a next state as the last element of a rule can only be given if the action is either an object or a string, at: ' + state); } } else { - if (!rule.regex) { - throw monarchCommon.createError(lexer, 'a rule must either be an array, or an object with a \'regex\' or \'include\' field at: ' + state); - } - if (rule.name) { - if (typeof rule.name === 'string') { - newrule.name = rule.name; - } - } - if (rule.matchOnlyAtStart) { - newrule.matchOnlyAtLineStart = bool(rule.matchOnlyAtLineStart, false); - } - newrule.setRegex(lexerMin, rule.regex); - newrule.setAction(lexerMin, rule.action); + newrule.setAction(lexerMin, rule[1]); } - - newrules.push(newrule); } + else { + if (!rule.regex) { + throw monarchCommon.createError(lexer, 'a rule must either be an array, or an object with a \'regex\' or \'include\' field at: ' + state); + } + if (rule.name) { + if (typeof rule.name === 'string') { + newrule.name = rule.name; + } + } + if (rule.matchOnlyAtStart) { + newrule.matchOnlyAtLineStart = bool(rule.matchOnlyAtLineStart, false); + } + newrule.setRegex(lexerMin, rule.regex); + newrule.setAction(lexerMin, rule.action); + } + + newrules.push(newrule); } } } @@ -520,26 +510,24 @@ export function compile(languageId: string, json: IMonarchLanguage): monarchComm { open: '<', close: '>', token: 'delimiter.angle' }]; } let brackets: IMonarchLanguageBracket[] = []; - for (let bracketIdx in json.brackets) { - if (json.brackets.hasOwnProperty(bracketIdx)) { - let desc = json.brackets[bracketIdx]; - if (desc && Array.isArray(desc) && desc.length === 3) { - desc = { token: desc[2], open: desc[0], close: desc[1] }; - } - if (desc.open === desc.close) { - throw monarchCommon.createError(lexer, 'open and close brackets in a \'brackets\' attribute must be different: ' + desc.open + - '\n hint: use the \'bracket\' attribute if matching on equal brackets is required.'); - } - if (typeof desc.open === 'string' && typeof desc.token === 'string' && typeof desc.close === 'string') { - brackets.push({ - token: desc.token + lexer.tokenPostfix, - open: monarchCommon.fixCase(lexer, desc.open), - close: monarchCommon.fixCase(lexer, desc.close) - }); - } - else { - throw monarchCommon.createError(lexer, 'every element in the \'brackets\' array must be a \'{open,close,token}\' object or array'); - } + for (let el of json.brackets) { + let desc: any = el; + if (desc && Array.isArray(desc) && desc.length === 3) { + desc = { token: desc[2], open: desc[0], close: desc[1] }; + } + if (desc.open === desc.close) { + throw monarchCommon.createError(lexer, 'open and close brackets in a \'brackets\' attribute must be different: ' + desc.open + + '\n hint: use the \'bracket\' attribute if matching on equal brackets is required.'); + } + if (typeof desc.open === 'string' && typeof desc.token === 'string' && typeof desc.close === 'string') { + brackets.push({ + token: desc.token + lexer.tokenPostfix, + open: monarchCommon.fixCase(lexer, desc.open), + close: monarchCommon.fixCase(lexer, desc.close) + }); + } + else { + throw monarchCommon.createError(lexer, 'every element in the \'brackets\' array must be a \'{open,close,token}\' object or array'); } } lexer.brackets = brackets; diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index b2032fa5c0..2aaad1bf06 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -228,8 +228,6 @@ class MonarchLineState implements modes.IState { } } -const hasOwnProperty = Object.hasOwnProperty; - interface IMonarchTokensCollector { enterMode(startOffset: number, modeId: string): void; emit(startOffset: number, type: string): void; @@ -423,22 +421,24 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { public getLoadStatus(): ILoadStatus { let promises: Thenable[] = []; for (let nestedModeId in this._embeddedModes) { - const tokenizationSupport = modes.TokenizationRegistry.get(nestedModeId); - if (tokenizationSupport) { - // The nested mode is already loaded - if (tokenizationSupport instanceof MonarchTokenizer) { - const nestedModeStatus = tokenizationSupport.getLoadStatus(); - if (nestedModeStatus.loaded === false) { - promises.push(nestedModeStatus.promise); + if (this._embeddedModes.hasOwnProperty(nestedModeId)) { + const tokenizationSupport = modes.TokenizationRegistry.get(nestedModeId); + if (tokenizationSupport) { + // The nested mode is already loaded + if (tokenizationSupport instanceof MonarchTokenizer) { + const nestedModeStatus = tokenizationSupport.getLoadStatus(); + if (nestedModeStatus.loaded === false) { + promises.push(nestedModeStatus.promise); + } } + continue; } - continue; - } - const tokenizationSupportPromise = modes.TokenizationRegistry.getPromise(nestedModeId); - if (tokenizationSupportPromise) { - // The nested mode is in the process of being loaded - promises.push(tokenizationSupportPromise); + const tokenizationSupportPromise = modes.TokenizationRegistry.getPromise(nestedModeId); + if (tokenizationSupportPromise) { + // The nested mode is in the process of being loaded + promises.push(tokenizationSupportPromise); + } } } @@ -490,11 +490,7 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { let popOffset = -1; let hasEmbeddedPopRule = false; - for (let idx in rules) { - if (!hasOwnProperty.call(rules, idx)) { - continue; - } - let rule: monarchCommon.IRule = rules[idx]; + for (const rule of rules) { if (!monarchCommon.isIAction(rule.action) || rule.action.nextEmbedded !== '@pop') { continue; } @@ -619,16 +615,13 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { // try each rule until we match let restOfLine = line.substr(pos); - for (let idx in rules) { - if (hasOwnProperty.call(rules, idx)) { - let rule: monarchCommon.IRule = rules[idx]; - if (pos === 0 || !rule.matchOnlyAtLineStart) { - matches = restOfLine.match(rule.regex); - if (matches) { - matched = matches[0]; - action = rule.action; - break; - } + for (const rule of rules) { + if (pos === 0 || !rule.matchOnlyAtLineStart) { + matches = restOfLine.match(rule.regex); + if (matches) { + matched = matches[0]; + action = rule.action; + break; } } } diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index c4eedca3fc..b67317a20d 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -3998,8 +3998,12 @@ suite('autoClosingPairs', () => { { open: '\'', close: '\'', notIn: ['string', 'comment'] }, { open: '\"', close: '\"', notIn: ['string'] }, { open: '`', close: '`', notIn: ['string', 'comment'] }, - { open: '/**', close: ' */', notIn: ['string'] } + { open: '/**', close: ' */', notIn: ['string'] }, + { open: 'begin', close: 'end', notIn: ['string'] } ], + __electricCharacterSupport: { + docComment: { open: '/**', close: ' */' } + } })); } @@ -4439,6 +4443,28 @@ suite('autoClosingPairs', () => { mode.dispose(); }); + test('multi-character autoclose', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + '', + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + + model.setValue('begi'); + cursor.setSelections('test', [new Selection(1, 5, 1, 5)]); + cursorCommand(cursor, H.Type, { text: 'n' }, 'keyboard'); + assert.strictEqual(model.getLineContent(1), 'beginend'); + + model.setValue('/*'); + cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); + cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + assert.strictEqual(model.getLineContent(1), '/** */'); + }); + mode.dispose(); + }); + test('issue #55314: Do not auto-close when ending with open', () => { const languageId = new LanguageIdentifier('myElectricMode', 5); class ElectricMode extends MockMode { @@ -4477,7 +4503,7 @@ suite('autoClosingPairs', () => { model.forceTokenization(model.getLineCount()); assertType(model, cursor, 3, 4, '"', '"', `does not double quote when ending with open`); model.forceTokenization(model.getLineCount()); - assertType(model, cursor, 4, 2, '"', '""', `double quote when ending with open`); + assertType(model, cursor, 4, 2, '"', '"', `does not double quote when ending with open`); model.forceTokenization(model.getLineCount()); assertType(model, cursor, 4, 3, '"', '"', `does not double quote when ending with open`); }); @@ -4772,31 +4798,18 @@ suite('autoClosingPairs', () => { // on the mac US intl kb layout - // Typing ` + space + // Typing ' + space cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); - assert.equal(model.getValue(), '\'\''); - // Typing " + space within string - cursor.setSelections('test', [new Selection(1, 2, 1, 2)]); - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '"' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '"' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); - - assert.equal(model.getValue(), '\'"\''); - - // Typing ' + space after ' - model.setValue('\''); - cursor.setSelections('test', [new Selection(1, 2, 1, 2)]); + // Typing one more ' + space cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); - assert.equal(model.getValue(), '\'\''); // Typing ' as a closing tag diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index 2450a860d0..20d09d3ffc 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -7,6 +7,10 @@ import { URI } from 'vs/base/common/uri'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices'; import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands'; +import { deepClone } from 'vs/base/common/objects'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IProductService } from 'vs/platform/product/common/product'; +import { IStorageService } from 'vs/platform/storage/common/storage'; suite('OpenerService', function () { @@ -24,19 +28,77 @@ suite('OpenerService', function () { } }; + function getStorageService(trustedDomainsSetting: string[]) { + let _settings = deepClone(trustedDomainsSetting); + + return new class implements IStorageService { + get = () => JSON.stringify(_settings); + store = (key: string, val: string) => _settings = JSON.parse(val); + + // Don't care + _serviceBrand: any; + + onDidChangeStorage = () => ({ dispose: () => { } }); + onWillSaveState = () => ({ dispose: () => { } }); + + getBoolean = () => true; + getNumber = () => 0; + remove = () => { }; + logStorage = () => { }; + }; + } + + function getDialogService() { + return new class implements IDialogService { + _showInvoked = 0; + show = () => { + this._showInvoked++; + return Promise.resolve({} as any); + } + get confirmInvoked() { return this._showInvoked; } + + // Don't care + _serviceBrand: any; + confirm = () => { + return Promise.resolve({} as any); + } + }; + } + + function getProductService(): IProductService { + return new class { + nameShort: 'VS Code'; + + _serviceBrand: any; + } as IProductService; + } + + setup(function () { lastCommand = undefined; }); test('delegate to editorService, scheme:///fff', function () { - const openerService = new OpenerService(editorService, NullCommandService); + const openerService = new OpenerService( + editorService, + NullCommandService, + getStorageService([]), + getDialogService(), + getProductService() + ); openerService.open(URI.parse('another:///somepath')); assert.equal(editorService.lastInput!.options!.selection, undefined); }); test('delegate to editorService, scheme:///fff#L123', function () { - const openerService = new OpenerService(editorService, NullCommandService); + const openerService = new OpenerService( + editorService, + NullCommandService, + getStorageService([]), + getDialogService(), + getProductService() + ); openerService.open(URI.parse('file:///somepath#L23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); @@ -59,7 +121,13 @@ suite('OpenerService', function () { test('delegate to editorService, scheme:///fff#123,123', function () { - const openerService = new OpenerService(editorService, NullCommandService); + const openerService = new OpenerService( + editorService, + NullCommandService, + getStorageService([]), + getDialogService(), + getProductService() + ); openerService.open(URI.parse('file:///somepath#23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); @@ -78,7 +146,13 @@ suite('OpenerService', function () { test('delegate to commandsService, command:someid', function () { - const openerService = new OpenerService(editorService, commandService); + const openerService = new OpenerService( + editorService, + commandService, + getStorageService([]), + getDialogService(), + getProductService() + ); const id = `aCommand${Math.random()}`; CommandsRegistry.registerCommand(id, function () { }); @@ -98,4 +172,70 @@ suite('OpenerService', function () { assert.equal(lastCommand!.args[0], 12); assert.equal(lastCommand!.args[1], true); }); + + test('links are protected by dialog.show', function () { + const dialogService = getDialogService(); + const openerService = new OpenerService( + editorService, + commandService, + getStorageService([]), + dialogService, + getProductService() + ); + + openerService.open(URI.parse('https://www.microsoft.com')); + assert.equal(dialogService.confirmInvoked, 1); + }); + + test('links on the whitelisted domains can be opened without dialog.show', function () { + const dialogService = getDialogService(); + const openerService = new OpenerService( + editorService, + commandService, + getStorageService(['https://microsoft.com']), + dialogService, + getProductService() + ); + + openerService.open(URI.parse('https://microsoft.com')); + openerService.open(URI.parse('https://microsoft.com/')); + openerService.open(URI.parse('https://microsoft.com/en-us/')); + openerService.open(URI.parse('https://microsoft.com/en-us/?foo=bar')); + openerService.open(URI.parse('https://microsoft.com/en-us/?foo=bar#baz')); + + assert.equal(dialogService.confirmInvoked, 0); + }); + + test('variations of links are protected by dialog confirmation', function () { + const dialogService = getDialogService(); + const openerService = new OpenerService( + editorService, + commandService, + getStorageService(['https://microsoft.com']), + dialogService, + getProductService() + ); + + openerService.open(URI.parse('http://microsoft.com')); + openerService.open(URI.parse('https://www.microsoft.com')); + + assert.equal(dialogService.confirmInvoked, 2); + }); + + test('* removes all link protection', function () { + const dialogService = getDialogService(); + const openerService = new OpenerService( + editorService, + commandService, + getStorageService(['*']), + dialogService, + getProductService() + ); + + openerService.open(URI.parse('https://code.visualstudio.com/')); + openerService.open(URI.parse('https://www.microsoft.com')); + openerService.open(URI.parse('https://www.github.com')); + + assert.equal(dialogService.confirmInvoked, 0); + }); }); diff --git a/src/vs/editor/test/common/modes/supports/characterPair.test.ts b/src/vs/editor/test/common/modes/supports/characterPair.test.ts index a36942f4b7..62af25ed30 100644 --- a/src/vs/editor/test/common/modes/supports/characterPair.test.ts +++ b/src/vs/editor/test/common/modes/supports/characterPair.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { StandardTokenType } from 'vs/editor/common/modes'; import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils'; +import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; suite('CharacterPairSupport', () => { @@ -52,8 +53,21 @@ suite('CharacterPairSupport', () => { assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); }); + function findAutoClosingPair(characterPairSupport: CharacterPairSupport, character: string): StandardAutoClosingPairConditional | null { + for (const autoClosingPair of characterPairSupport.getAutoClosingPairs()) { + if (autoClosingPair.open === character) { + return autoClosingPair; + } + } + return null; + } + function testShouldAutoClose(characterPairSupport: CharacterPairSupport, line: TokenText[], character: string, column: number): boolean { - return characterPairSupport.shouldAutoClosePair(character, createFakeScopedLineTokens(line), column); + const autoClosingPair = findAutoClosingPair(characterPairSupport, character); + if (!autoClosingPair) { + return false; + } + return CharacterPairSupport.shouldAutoClosePair(autoClosingPair, createFakeScopedLineTokens(line), column); } test('shouldAutoClosePair in empty line', () => { diff --git a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts index 40c5c93ec4..30d0608626 100644 --- a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts +++ b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts @@ -21,86 +21,20 @@ suite('Editor Modes - Auto Indentation', () => { assert.deepEqual(actual, null); } - function testAppends(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number, appendText: string): void { - let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset); - assert.deepEqual(actual, { appendText: appendText }); - } - function testMatchBracket(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number, matchOpenBracket: string): void { let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset); assert.deepEqual(actual, { matchOpenBracket: matchOpenBracket }); } - test('Doc comments', () => { - let brackets = new BracketElectricCharacterSupport(null, [{ open: '/**', close: ' */' }], null); - - testAppends(brackets, [ - { text: '/*', type: StandardTokenType.Other }, - ], '*', 3, ' */'); - - testDoesNothing(brackets, [ - { text: '/*', type: StandardTokenType.Other }, - { text: ' ', type: StandardTokenType.Other }, - { text: '*/', type: StandardTokenType.Other }, - ], '*', 3); - }); - test('getElectricCharacters uses all sources and dedups', () => { let sup = new BracketElectricCharacterSupport( new RichEditBrackets(fakeLanguageIdentifier, [ ['{', '}'], ['(', ')'] - ]), [ - { open: '{', close: '}', notIn: ['string', 'comment'] }, - { open: '"', close: '"', notIn: ['string', 'comment'] }, - { open: 'begin', close: 'end', notIn: ['string'] } - ], - { docComment: { open: '/**', close: ' */' } } + ]) ); - assert.deepEqual(sup.getElectricCharacters(), ['}', ')', 'n', '*']); - }); - - test('auto-close', () => { - let sup = new BracketElectricCharacterSupport( - new RichEditBrackets(fakeLanguageIdentifier, [ - ['{', '}'], - ['(', ')'] - ]), [ - { open: '{', close: '}', notIn: ['string', 'comment'] }, - { open: '"', close: '"', notIn: ['string', 'comment'] }, - { open: 'begin', close: 'end', notIn: ['string'] } - ], - { docComment: { open: '/**', close: ' */' } } - ); - - testDoesNothing(sup, [], 'a', 0); - - testDoesNothing(sup, [{ text: 'egi', type: StandardTokenType.Other }], 'b', 1); - testDoesNothing(sup, [{ text: 'bgi', type: StandardTokenType.Other }], 'e', 2); - testDoesNothing(sup, [{ text: 'bei', type: StandardTokenType.Other }], 'g', 3); - testDoesNothing(sup, [{ text: 'beg', type: StandardTokenType.Other }], 'i', 4); - - testDoesNothing(sup, [{ text: 'egin', type: StandardTokenType.Other }], 'b', 1); - testDoesNothing(sup, [{ text: 'bgin', type: StandardTokenType.Other }], 'e', 2); - testDoesNothing(sup, [{ text: 'bein', type: StandardTokenType.Other }], 'g', 3); - testDoesNothing(sup, [{ text: 'begn', type: StandardTokenType.Other }], 'i', 4); - testAppends(sup, [{ text: 'begi', type: StandardTokenType.Other }], 'n', 5, 'end'); - - testDoesNothing(sup, [{ text: '3gin', type: StandardTokenType.Other }], 'b', 1); - testDoesNothing(sup, [{ text: 'bgin', type: StandardTokenType.Other }], '3', 2); - testDoesNothing(sup, [{ text: 'b3in', type: StandardTokenType.Other }], 'g', 3); - testDoesNothing(sup, [{ text: 'b3gn', type: StandardTokenType.Other }], 'i', 4); - testDoesNothing(sup, [{ text: 'b3gi', type: StandardTokenType.Other }], 'n', 5); - - testDoesNothing(sup, [{ text: 'begi', type: StandardTokenType.String }], 'n', 5); - - testAppends(sup, [{ text: '"', type: StandardTokenType.String }, { text: 'begi', type: StandardTokenType.Other }], 'n', 6, 'end'); - testDoesNothing(sup, [{ text: '"', type: StandardTokenType.String }, { text: 'begi', type: StandardTokenType.String }], 'n', 6); - - testAppends(sup, [{ text: '/*', type: StandardTokenType.String }], '*', 3, ' */'); - - testDoesNothing(sup, [{ text: 'begi', type: StandardTokenType.Other }, { text: 'end', type: StandardTokenType.Other }], 'n', 5); + assert.deepEqual(sup.getElectricCharacters(), ['}', ')']); }); test('matchOpenBracket', () => { @@ -108,12 +42,7 @@ suite('Editor Modes - Auto Indentation', () => { new RichEditBrackets(fakeLanguageIdentifier, [ ['{', '}'], ['(', ')'] - ]), [ - { open: '{', close: '}', notIn: ['string', 'comment'] }, - { open: '"', close: '"', notIn: ['string', 'comment'] }, - { open: 'begin', close: 'end', notIn: ['string'] } - ], - { docComment: { open: '/**', close: ' */' } } + ]) ); testDoesNothing(sup, [{ text: '\t{', type: StandardTokenType.Other }], '\t', 1); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index e1a0dbd6d6..e09632e0e9 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4565,7 +4565,9 @@ declare namespace monaco.languages { * * @deprecated Will be replaced by a better API soon. */ - __electricCharacterSupport?: IBracketElectricCharacterContribution; + __electricCharacterSupport?: { + docComment?: IDocComment; + }; } /** @@ -4640,10 +4642,6 @@ declare namespace monaco.languages { action: EnterAction; } - export interface IBracketElectricCharacterContribution { - docComment?: IDocComment; - } - /** * Definition of documentation comments (e.g. Javadoc/JSdoc) */ diff --git a/src/vs/nls.d.ts b/src/vs/nls.d.ts index 3f7da9613c..14b2baed54 100644 --- a/src/vs/nls.d.ts +++ b/src/vs/nls.d.ts @@ -8,5 +8,12 @@ export interface ILocalizeInfo { comment: string[]; } +/** + * Localize a message. `message` can contain `{n}` notation where it is replaced by the nth value in `...args`. + */ export declare function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string; + +/** + * Localize a message. `message` can contain `{n}` notation where it is replaced by the nth value in `...args`. + */ export declare function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string; diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index b4a74af261..deff85d629 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -99,6 +99,10 @@ export const enum ConfigurationScope { * Resource specific configuration, which can be configured in the user, workspace or folder settings. */ RESOURCE, + /** + * Machine specific configuration that can also be configured in workspace or folder settings. + */ + MACHINE_OVERRIDABLE, } export interface IConfigurationPropertySchema extends IJSONSchema { diff --git a/src/vs/platform/download/common/downloadService.ts b/src/vs/platform/download/common/downloadService.ts index 528c5f2a96..9d6bb2b882 100644 --- a/src/vs/platform/download/common/downloadService.ts +++ b/src/vs/platform/download/common/downloadService.ts @@ -20,7 +20,7 @@ export class DownloadService implements IDownloadService { ) { } async download(resource: URI, target: URI, cancellationToken: CancellationToken = CancellationToken.None): Promise { - if (resource.scheme === Schemas.file) { + if (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote) { await this.fileService.copy(resource, target); return; } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 3d32f3d96d..ac5239f4c0 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -199,6 +199,7 @@ export interface IExtensionManagementService { zip(extension: ILocalExtension): Promise; unzip(zipLocation: URI, type: ExtensionType): Promise; + getManifest(vsix: URI): Promise; install(vsix: URI): Promise; installFromGallery(extension: IGalleryExtension): Promise; uninstall(extension: ILocalExtension, force?: boolean): Promise; diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index f7b9114bc7..79f2cb6282 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IURITransformer, DefaultURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; import { cloneAndChange } from 'vs/base/common/objects'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI { return URI.revive(transformer ? transformer.transformIncoming(uri) : uri); @@ -62,6 +62,7 @@ export class ExtensionManagementChannel implements IServerChannel { case 'zip': return this.service.zip(transformIncomingExtension(args[0], uriTransformer)).then(uri => transformOutgoingURI(uri, uriTransformer)); case 'unzip': return this.service.unzip(transformIncomingURI(args[0], uriTransformer), args[1]); case 'install': return this.service.install(transformIncomingURI(args[0], uriTransformer)); + case 'getManifest': return this.service.getManifest(transformIncomingURI(args[0], uriTransformer)); case 'installFromGallery': return this.service.installFromGallery(args[0]); case 'uninstall': return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), args[1]); case 'reinstallFromGallery': return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer)); @@ -99,6 +100,10 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer return Promise.resolve(this.channel.call('install', [vsix])).then(local => transformIncomingExtension(local, null)); } + getManifest(vsix: URI): Promise { + return Promise.resolve(this.channel.call('getManifest', [vsix])); + } + installFromGallery(extension: IGalleryExtension): Promise { return Promise.resolve(this.channel.call('installFromGallery', [extension])).then(local => transformIncomingExtension(local, null)); } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 0c934fcfa3..876729ff15 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -164,6 +164,12 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.install(zipLocation, type).then(local => local.identifier); } + async getManifest(vsix: URI): Promise { + const downloadLocation = await this.downloadVsix(vsix); + const zipPath = path.resolve(downloadLocation.fsPath); + return getManifest(zipPath); + } + private collectFiles(extension: ILocalExtension): Promise { const collectFilesFromDirectory = async (dir: string): Promise => { diff --git a/src/vs/platform/history/common/historyStorage.ts b/src/vs/platform/history/common/historyStorage.ts index 7e248d2576..ee0980fdd3 100644 --- a/src/vs/platform/history/common/historyStorage.ts +++ b/src/vs/platform/history/common/historyStorage.ts @@ -71,7 +71,7 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine result.workspaces.push({ folderUri: URI.file(workspace) }); } else if (isLegacySerializedWorkspace(workspace)) { result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }); - } else if (isUriComponents(window)) { + } else if (isUriComponents(workspace)) { // added by 1.26-insiders result.workspaces.push({ folderUri: URI.revive(workspace) }); } diff --git a/src/vs/platform/lifecycle/browser/lifecycleService.ts b/src/vs/platform/lifecycle/browser/lifecycleService.ts index 1e3a593679..0ace697ee8 100644 --- a/src/vs/platform/lifecycle/browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/browser/lifecycleService.ts @@ -8,6 +8,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; import { localize } from 'vs/nls'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { addDisposableListener, EventType } from 'vs/base/browser/dom'; export class BrowserLifecycleService extends AbstractLifecycleService { @@ -22,10 +23,10 @@ export class BrowserLifecycleService extends AbstractLifecycleService { } private registerListeners(): void { - window.onbeforeunload = () => this.beforeUnload(); + addDisposableListener(window, EventType.BEFORE_UNLOAD, () => this.onBeforeUnload()); } - private beforeUnload(): string | null { + private onBeforeUnload(): string | null { let veto = false; // Before Shutdown @@ -34,7 +35,7 @@ export class BrowserLifecycleService extends AbstractLifecycleService { if (value === true) { veto = true; } else if (value instanceof Promise && !veto) { - console.warn(new Error('Long running onBeforeShutdown currently not supported')); + console.warn(new Error('Long running onBeforeShutdown currently not supported in the web')); veto = true; } }, @@ -49,11 +50,14 @@ export class BrowserLifecycleService extends AbstractLifecycleService { // No Veto: continue with Will Shutdown this._onWillShutdown.fire({ join() { - console.warn(new Error('Long running onWillShutdown currently not supported')); + console.warn(new Error('Long running onWillShutdown currently not supported in the web')); }, reason: ShutdownReason.QUIT }); + // Finally end with Shutdown event + this._onShutdown.fire(); + return null; } } diff --git a/src/vs/platform/menubar/electron-browser/menubarService.ts b/src/vs/platform/menubar/electron-browser/menubarService.ts index 7d6da91f4c..941c8df81e 100644 --- a/src/vs/platform/menubar/electron-browser/menubarService.ts +++ b/src/vs/platform/menubar/electron-browser/menubarService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService, IMenubarData } from 'vs/platform/menubar/node/menubar'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 9a909d4a85..4b8bc6737b 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -17,7 +17,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { mnemonicMenuLabel as baseMnemonicLabel } from 'vs/base/common/labels'; import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService } from 'vs/platform/history/common/history'; -import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction, IMenubarMenu, isMenubarMenuItemUriAction } from 'vs/platform/menubar/common/menubar'; +import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction, IMenubarMenu, isMenubarMenuItemUriAction } from 'vs/platform/menubar/node/menubar'; import { URI } from 'vs/base/common/uri'; import { IStateService } from 'vs/platform/state/common/state'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; diff --git a/src/vs/platform/menubar/electron-main/menubarService.ts b/src/vs/platform/menubar/electron-main/menubarService.ts index e7f208b127..0250c1bf86 100644 --- a/src/vs/platform/menubar/electron-main/menubarService.ts +++ b/src/vs/platform/menubar/electron-main/menubarService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService, IMenubarData } from 'vs/platform/menubar/node/menubar'; import { Menubar } from 'vs/platform/menubar/electron-main/menubar'; import { ILogService } from 'vs/platform/log/common/log'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/platform/menubar/common/menubar.ts b/src/vs/platform/menubar/node/menubar.ts similarity index 100% rename from src/vs/platform/menubar/common/menubar.ts rename to src/vs/platform/menubar/node/menubar.ts diff --git a/src/vs/platform/menubar/node/menubarIpc.ts b/src/vs/platform/menubar/node/menubarIpc.ts index b6f9875bef..947a7aaaf1 100644 --- a/src/vs/platform/menubar/node/menubarIpc.ts +++ b/src/vs/platform/menubar/node/menubarIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IMenubarService } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { Event } from 'vs/base/common/event'; export class MenubarChannel implements IServerChannel { diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index e5e67e3543..8fe4413305 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -11,6 +11,7 @@ export const IOpenerService = createDecorator('openerService'); export interface IOpener { open(resource: URI, options?: { openToSide?: boolean }): Promise; + open(resource: URI, options?: { openExternal?: boolean }): Promise; } export interface IOpenerService { @@ -29,18 +30,11 @@ export interface IOpenerService { * @return A promise that resolves when the opening is done. */ open(resource: URI, options?: { openToSide?: boolean }): Promise; - - /** - * Opens a URL externally. - * - * @param url A resource to open externally. - */ - openExternal(resource: URI): Promise; + open(resource: URI, options?: { openExternal?: boolean }): Promise; } export const NullOpenerService: IOpenerService = Object.freeze({ _serviceBrand: undefined, registerOpener() { return { dispose() { } }; }, open() { return Promise.resolve(false); }, - openExternal() { return Promise.resolve(false); } }); diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 1d0241e76c..b35863aeac 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -37,7 +37,7 @@ export class BrowserStorageService extends Disposable implements IStorageService private workspaceStorageFile: URI; private initializePromise: Promise; - private periodicSaveScheduler = this._register(new RunOnceScheduler(() => this.saveState(), 5000)); + private periodicSaveScheduler = this._register(new RunOnceScheduler(() => this.collectState(), 5000)); get hasPendingUpdate(): boolean { return this.globalStorageDatabase.hasPendingUpdate || this.workspaceStorageDatabase.hasPendingUpdate; @@ -57,14 +57,19 @@ export class BrowserStorageService extends Disposable implements IStorageService this.periodicSaveScheduler.schedule(); } - private saveState(): void { + private collectState(): void { runWhenIdle(() => { // this event will potentially cause new state to be stored // since new state will only be created while the document // has focus, one optimization is to not run this when the // document has no focus, assuming that state has not changed - if (document.hasFocus()) { + // + // another optimization is to not collect more state if we + // have a pending update already running which indicates + // that the connection is either slow or disconnected and + // thus unhealthy. + if (document.hasFocus() && !this.hasPendingUpdate) { this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); } @@ -197,7 +202,7 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase this._register(this.fileService.watch(this.file)); this._register(this.fileService.onFileChanges(e => { if (document.hasFocus()) { - return; // ignore changes from ourselves by checking for focus + return; // optimization: ignore changes from ourselves by checking for focus } if (!e.contains(this.file, FileChangeType.UPDATED)) { @@ -251,15 +256,17 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase await this.pendingUpdate; - this._hasPendingUpdate = true; + this.pendingUpdate = (async () => { + try { + this._hasPendingUpdate = true; + + await this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items)))); - this.pendingUpdate = this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items)))) - .then(() => { this.ensureWatching(); // now that the file must exist, ensure we watch it for changes - }) - .finally(() => { + } finally { this._hasPendingUpdate = false; - }); + } + })(); return this.pendingUpdate; } diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts index 5fd4173c66..b23c2d507c 100644 --- a/src/vs/platform/storage/node/storageIpc.ts +++ b/src/vs/platform/storage/node/storageIpc.ts @@ -5,7 +5,7 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; -import { StorageMainService, IStorageChangeEvent } from 'vs/platform/storage/node/storageMainService'; +import { IStorageChangeEvent, IStorageMainService } from 'vs/platform/storage/node/storageMainService'; import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/parts/storage/common/storage'; import { mapToSerializable, serializableToMap, values } from 'vs/base/common/map'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -38,7 +38,7 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC constructor( private logService: ILogService, - private storageMainService: StorageMainService + private storageMainService: IStorageMainService ) { super(); @@ -203,4 +203,4 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS dispose(this.onDidChangeItemsOnMainListener); } -} \ No newline at end of file +} diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index cee5afcf1b..867843c92e 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -34,6 +34,16 @@ export interface IStorageMainService { */ readonly onWillSaveState: Event; + /** + * Access to all cached items of this storage service. + */ + readonly items: Map; + + /** + * Required call to ensure the service can be used. + */ + initialize(): Promise; + /** * Retrieve an element stored with the given key from storage. Use * the provided defaultValue if the element is null or undefined. diff --git a/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts b/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts index 2683d577a4..72b734b71b 100644 --- a/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts @@ -16,18 +16,19 @@ import { cleanRemoteAuthority } from 'vs/platform/telemetry/common/telemetryUtil export async function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string | undefined, version: string | undefined, machineId: string, remoteAuthority?: string): Promise<{ [name: string]: string | undefined }> { const result: { [name: string]: string | undefined; } = Object.create(null); - const instanceId = storageService.get(instanceStorageKey, StorageScope.GLOBAL)!; const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL)!; const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!; + /** + * Note: In the web, session date information is fetched from browser storage, so these dates are tied to a specific + * browser and not the machine overall. + */ // __GDPR__COMMON__ "common.firstSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.firstSessionDate'] = firstSessionDate; // __GDPR__COMMON__ "common.lastSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.lastSessionDate'] = lastSessionDate || ''; // __GDPR__COMMON__ "common.isNewSession" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.isNewSession'] = !lastSessionDate ? '1' : '0'; - // __GDPR__COMMON__ "common.instanceId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - result['common.instanceId'] = instanceId; // __GDPR__COMMON__ "common.remoteAuthority" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.remoteAuthority'] = cleanRemoteAuthority(remoteAuthority); diff --git a/src/vs/platform/url/electron-browser/urlService.ts b/src/vs/platform/url/electron-browser/urlService.ts index 0f87fd18ad..9648f39901 100644 --- a/src/vs/platform/url/electron-browser/urlService.ts +++ b/src/vs/platform/url/electron-browser/urlService.ts @@ -27,12 +27,16 @@ export class RelayURLService extends URLService implements IURLHandler { openerService.registerOpener(this); } - async open(uri: URI): Promise { - if (uri.scheme !== product.urlProtocol) { + async open(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise { + if (options && options.openExternal) { return false; } - return await this.urlService.open(uri); + if (resource.scheme !== product.urlProtocol) { + return false; + } + + return await this.urlService.open(resource); } handleURL(uri: URI): Promise { diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 2177348b39..cf96c54ae8 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -240,6 +240,8 @@ export interface IWindowService { closeWorkspace(): Promise; updateTouchBar(items: ISerializableCommandAction[][]): Promise; enterWorkspace(path: URI): Promise; + // rationale: will eventually move to electron-browser + // tslint:disable-next-line: no-dom-globals toggleFullScreen(target?: HTMLElement): Promise; setRepresentedFilename(fileName: string): Promise; getRecentlyOpened(): Promise; diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 0502fedae5..7c52f5f9e5 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -343,11 +343,12 @@ suite('WorkspacesMainService', () => { const untitledTwo = await createWorkspace([os.tmpdir(), process.cwd()]); assert.ok(fs.existsSync(untitledTwo.configPath.fsPath)); assert.ok(fs.existsSync(untitledOne.configPath.fsPath), `Unexpected workspaces count of 1 (expected 2): ${untitledOne.configPath.fsPath} does not exist anymore?`); + const untitledHome = dirname(dirname(untitledTwo.configPath)); + const beforeGettingUntitledWorkspaces = fs.readdirSync(untitledHome.fsPath).map(name => fs.readFileSync(joinPath(untitledHome, name, 'workspace.json').fsPath, 'utf8')); untitled = service.getUntitledWorkspacesSync(); assert.ok(fs.existsSync(untitledOne.configPath.fsPath), `Unexpected workspaces count of 1 (expected 2): ${untitledOne.configPath.fsPath} does not exist anymore?`); if (untitled.length === 1) { - const untitledHome = dirname(dirname(untitledTwo.configPath)); - assert.fail(`Unexpected workspaces count of 1 (expected 2), all workspaces:\n ${fs.readdirSync(untitledHome.fsPath).map(name => fs.readFileSync(joinPath(untitledHome, name, 'workspace.json').fsPath, 'utf8'))}`); + assert.fail(`Unexpected workspaces count of 1 (expected 2), all workspaces:\n ${fs.readdirSync(untitledHome.fsPath).map(name => fs.readFileSync(joinPath(untitledHome, name, 'workspace.json').fsPath, 'utf8'))}, before getUntitledWorkspacesSync: ${beforeGettingUntitledWorkspaces}`); } assert.equal(2, untitled.length); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index ca8ecc9bf2..e6e647346b 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5923,6 +5923,30 @@ declare module 'vscode' { * @param message Body of the message. */ postMessage(message: any): Thenable; + + /** + * Convert a uri for the local file system to one that can be used inside webviews. + * + * Webviews cannot directly load resoruces from the workspace or local file system using `file:` uris. The + * `asWebviewUri` function takes a local `file:` uri and converts it into a uri that can be used inside of + * a webview to load the same resource: + * + * ```ts + * webview.html = `` + * ``` + */ + asWebviewUri(localResource: Uri): Uri; + + /** + * Content security policy source for webview resources. + * + * This is the origin that should be used in a content security policy rule: + * + * ``` + * img-src https: ${webview.cspSource} ...; + * ``` + */ + readonly cspSource: string; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 21cf23818c..8ba2173645 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1141,35 +1141,4 @@ declare module 'vscode' { } //#endregion - - //#region Webview Resource Roots - - export interface Webview { - /** - * Convert a uri for the local file system to one that can be used inside webviews. - * - * Webviews cannot directly load resoruces from the workspace or local file system using `file:` uris. The - * `toWebviewResource` function takes a local `file:` uri and converts it into a uri that can be used inside of - * a webview to load the same resource: - * - * ```ts - * webview.html = `` - * ``` - */ - toWebviewResource(localResource: Uri): Uri; - - /** - * Content security policy source for webview resources. - * - * This is the origin that should be used in a content security policy rule: - * - * ``` - * img-src https: ${webview.cspSource} ...; - * ``` - */ - readonly cspSource: string; - } - - //#endregion - } diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index f0d144d58a..4306632596 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -8,7 +8,7 @@ import * as modes from 'vs/editor/common/modes'; import { MainContext, MainThreadEditorInsetsShape, IExtHostContext, ExtHostEditorInsetsShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from '../common/extHostCustomers'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts index 42e4f24866..a2dc74aaab 100644 --- a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts +++ b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts @@ -5,24 +5,43 @@ import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { MainThreadDiagnosticsShape, MainContext, IExtHostContext } from '../common/extHost.protocol'; +import { MainThreadDiagnosticsShape, MainContext, IExtHostContext, ExtHostDiagnosticsShape, ExtHostContext } from '../common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { IDisposable } from 'vs/base/common/lifecycle'; @extHostNamedCustomer(MainContext.MainThreadDiagnostics) export class MainThreadDiagnostics implements MainThreadDiagnosticsShape { private readonly _activeOwners = new Set(); + + private readonly _proxy: ExtHostDiagnosticsShape; private readonly _markerService: IMarkerService; + private readonly _markerListener: IDisposable; constructor( extHostContext: IExtHostContext, @IMarkerService markerService: IMarkerService ) { + this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDiagnostics); this._markerService = markerService; + this._markerListener = this._markerService.onMarkerChanged(this._forwardMarkers, this); } dispose(): void { + this._markerListener.dispose(); this._activeOwners.forEach(owner => this._markerService.changeAll(owner, [])); + this._activeOwners.clear(); + } + + private _forwardMarkers(resources: URI[]): void { + const data: [UriComponents, IMarkerData[]][] = []; + for (const resource of resources) { + data.push([ + resource, + this._markerService.read({ resource }).filter(marker => !this._activeOwners.has(marker.owner)) + ]); + } + this._proxy.$acceptMarkersChange(data); } $changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void { diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index e9ac823fa7..01469d3733 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -11,6 +11,7 @@ import { distinct } from 'vs/base/common/arrays'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { isUndefinedOrNull, isNumber } from 'vs/base/common/types'; import { Registry } from 'vs/platform/registry/common/platform'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadTreeViews) export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape { @@ -21,25 +22,28 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie constructor( extHostContext: IExtHostContext, @IViewsService private readonly viewsService: IViewsService, - @INotificationService private readonly notificationService: INotificationService + @INotificationService private readonly notificationService: INotificationService, + @IExtensionService private readonly extensionService: IExtensionService ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void { - const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService); - this._dataProviders.set(treeViewId, dataProvider); - const viewer = this.getTreeView(treeViewId); - if (viewer) { - viewer.dataProvider = dataProvider; - viewer.showCollapseAllAction = !!options.showCollapseAll; - viewer.canSelectMany = !!options.canSelectMany; - this.registerListeners(treeViewId, viewer); - this._proxy.$setVisible(treeViewId, viewer.visible); - } else { - this.notificationService.error('No view is registered with id: ' + treeViewId); - } + this.extensionService.whenInstalledExtensionsRegistered().then(() => { + const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService); + this._dataProviders.set(treeViewId, dataProvider); + const viewer = this.getTreeView(treeViewId); + if (viewer) { + viewer.dataProvider = dataProvider; + viewer.showCollapseAllAction = !!options.showCollapseAll; + viewer.canSelectMany = !!options.canSelectMany; + this.registerListeners(treeViewId, viewer); + this._proxy.$setVisible(treeViewId, viewer.visible); + } else { + this.notificationService.error('No view is registered with id: ' + treeViewId); + } + }); } $reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise { diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index d181d029dd..f8c9c75d35 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -5,30 +5,62 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import * as map from 'vs/base/common/map'; +import { startsWith } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; import { localize } from 'vs/nls'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IProductService } from 'vs/platform/product/common/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewPanelViewStateData } from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; +import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { extHostNamedCustomer } from '../common/extHostCustomers'; -import { IProductService } from 'vs/platform/product/common/product'; -import { startsWith } from 'vs/base/common/strings'; -import { Webview } from 'vs/workbench/contrib/webview/common/webview'; interface OldMainThreadWebviewState { readonly viewType: string; state: any; } +/** + * Bi-directional map between webview handles and inputs. + */ +class WebviewHandleStore { + private readonly _handlesToInputs = new Map(); + private readonly _inputsToHandles = new Map(); + + public add(handle: string, input: WebviewEditorInput): void { + this._handlesToInputs.set(handle, input); + this._inputsToHandles.set(input, handle); + } + + public getHandleForInput(input: WebviewEditorInput): string | undefined { + return this._inputsToHandles.get(input); + } + + public getInputForHandle(handle: string): WebviewEditorInput | undefined { + return this._handlesToInputs.get(handle); + } + + public delete(handle: string): void { + const input = this.getInputForHandle(handle); + this._handlesToInputs.delete(handle); + if (input) { + this._inputsToHandles.delete(input); + } + } + + public get size(): number { + return this._handlesToInputs.size; + } +} + @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape { @@ -43,12 +75,9 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews private static revivalPool = 0; private readonly _proxy: ExtHostWebviewsShape; - private readonly _webviewEditorInputs = new Map(); - private readonly _webviews = new Map(); + private readonly _webviewEditorInputs = new WebviewHandleStore(); private readonly _revivers = new Map(); - private _activeWebview: WebviewPanelHandle | undefined = undefined; - constructor( context: IExtHostContext, @IExtensionService extensionService: IExtensionService, @@ -62,8 +91,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews super(); this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews); - this._register(_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this)); - this._register(_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this)); + this._register(_editorService.onDidActiveEditorChange(this.updateWebviewViewStates, this)); + this._register(_editorService.onDidVisibleEditorsChange(this.updateWebviewViewStates, this)); // This reviver's only job is to activate webview extensions // This should trigger the real reviver to be registered from the extension host side. @@ -104,8 +133,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews }); this.hookupWebviewEventDelegate(handle, webview); - this._webviewEditorInputs.set(handle, webview); - this._webviews.set(handle, webview.webview); + this._webviewEditorInputs.add(handle, webview); /* __GDPR__ "webviews:createWebviewPanel" : { @@ -175,8 +203,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews } const handle = `revival-${MainThreadWebviews.revivalPool++}`; - this._webviewEditorInputs.set(handle, webviewEditorInput); - this._webviews.set(handle, webviewEditorInput.webview); + this._webviewEditorInputs.add(handle, webviewEditorInput); this.hookupWebviewEventDelegate(handle, webviewEditorInput); let state = undefined; @@ -234,7 +261,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews input.onDispose(() => { this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { this._webviewEditorInputs.delete(handle); - this._webviews.delete(handle); }); }); input.webview.onDidUpdateState((newState: any) => { @@ -246,70 +272,35 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews }); } - private onActiveEditorChanged() { - const activeEditor = this._editorService.activeControl; - let newActiveWebview: { input: WebviewEditorInput, handle: WebviewPanelHandle } | undefined = undefined; - if (activeEditor && activeEditor.input instanceof WebviewEditorInput) { - for (const handle of map.keys(this._webviewEditorInputs)) { - const input = this._webviewEditorInputs.get(handle)!; - if (input.matches(activeEditor.input)) { - newActiveWebview = { input, handle }; - break; - } - } - } - - if (newActiveWebview && newActiveWebview.handle === this._activeWebview) { - // Webview itself unchanged but position may have changed - this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, { - active: true, - visible: true, - position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || 0) - }); + private updateWebviewViewStates() { + if (!this._webviewEditorInputs.size) { return; } - // Broadcast view state update for currently active - if (typeof this._activeWebview !== 'undefined') { - const oldActiveWebview = this._webviewEditorInputs.get(this._activeWebview); - if (oldActiveWebview) { - this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, { - active: false, - visible: this._editorService.visibleControls.some(editor => !!editor.input && editor.input.matches(oldActiveWebview)), - position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || 0), - }); - } - } + const activeInput = this._editorService.activeControl && this._editorService.activeControl.input; + const viewStates: WebviewPanelViewStateData = {}; + for (const group of this._editorGroupService.groups) { + for (const input of group.editors) { + if (!(input instanceof WebviewEditorInput)) { + continue; + } - // Then for newly active - if (newActiveWebview) { - this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, { - active: true, - visible: true, - position: editorGroupToViewColumn(this._editorGroupService, activeEditor ? activeEditor.group : ACTIVE_GROUP), - }); - this._activeWebview = newActiveWebview.handle; - } else { - this._activeWebview = undefined; - } - } + input.updateGroup(group.id); - private onVisibleEditorsChanged(): void { - this._webviewEditorInputs.forEach((input, handle) => { - for (const workbenchEditor of this._editorService.visibleControls) { - if (workbenchEditor.input && workbenchEditor.input.matches(input)) { - const editorPosition = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group!); - - input.updateGroup(workbenchEditor.group!.id); - this._proxy.$onDidChangeWebviewPanelViewState(handle, { - active: handle === this._activeWebview, - visible: true, - position: editorPosition - }); - break; + const handle = this._webviewEditorInputs.getHandleForInput(input); + if (handle) { + viewStates[handle] = { + visible: input === group.activeEditor, + active: input === activeInput, + position: editorGroupToViewColumn(this._editorGroupService, group.id || 0), + }; } } - }); + } + + if (Object.keys(viewStates).length) { + this._proxy.$onDidChangeWebviewPanelViewStates(viewStates); + } } private onDidClickLink(handle: WebviewPanelHandle, link: URI): void { @@ -342,28 +333,19 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews } private tryGetWebviewEditorInput(handle: WebviewPanelHandle): WebviewEditorInput | undefined { - return this._webviewEditorInputs.get(handle); + return this._webviewEditorInputs.getInputForHandle(handle); } private getWebview(handle: WebviewPanelHandle): Webview { - const webview = this.tryGetWebview(handle); - if (!webview) { - throw new Error('Unknown webview handle:' + handle); - } - return webview; - } - - private tryGetWebview(handle: WebviewPanelHandle): Webview | undefined { - return this._webviews.get(handle); + return this.getWebviewEditorInput(handle).webview; } private static getDeserializationFailedContents(viewType: string) { return ` - - + ${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)} `; diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts index e473d27452..81ff9c95cf 100644 --- a/src/vs/workbench/api/browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/browser/mainThreadWindow.ts @@ -59,7 +59,7 @@ export class MainThreadWindow implements MainThreadWindowShape { } } - return this.openerService.openExternal(uri); + return this.openerService.open(uri, { openExternal: true }); } private getOrCreateTunnel(remotePort: number): Promise | undefined { diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index 8ec65cecf2..c453b79961 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -175,6 +175,11 @@ CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async functio return windowService.addRecentlyOpened([recent]); }); +CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function (accessor: ServicesAccessor) { + const windowService = accessor.get(IWindowService); + return windowService.getRecentlyOpened(); +}); + export class SetEditorLayoutAPICommand { public static ID = 'vscode.setEditorLayout'; public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Promise { @@ -205,4 +210,4 @@ CommandsRegistry.registerCommand({ } }] } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index b4ef5c9d07..6d6ecca707 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -39,13 +39,14 @@ const configurationEntrySchema: IJSONSchema = { }, scope: { type: 'string', - enum: ['application', 'machine', 'window', 'resource'], + enum: ['application', 'machine', 'window', 'resource', 'machine-overridable'], default: 'window', enumDescriptions: [ - nls.localize('scope.application.description', "Application specific configuration, which can be configured only in the user settings."), - nls.localize('scope.machine.description', "Machine specific configuration, which can be configured only in the user settings when the extension is running locally, or only in the remote settings when the extension is running remotely."), - nls.localize('scope.window.description', "Window specific configuration, which can be configured in the user, remote or workspace settings."), - nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the user, remote, workspace or folder settings.") + nls.localize('scope.application.description', "Configuration that can be configured only in the user settings."), + nls.localize('scope.machine.description', "Configuration that can be configured only in the user settings when the extension is running locally, or only in the remote settings when the extension is running remotely."), + nls.localize('scope.window.description', "Configuration that can be configured in the user, remote or workspace settings."), + nls.localize('scope.resource.description', "Configuration that can be configured in the user, remote, workspace or folder settings."), + nls.localize('scope.machine-overridable.description', "Machine configuration that can be configured also in workspace or folder settings.") ], description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `application`, `machine`, `window` and `resource`.") }, @@ -215,6 +216,8 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten propertyConfiguration.scope = ConfigurationScope.MACHINE; } else if (propertyConfiguration.scope.toString() === 'resource') { propertyConfiguration.scope = ConfigurationScope.RESOURCE; + } else if (propertyConfiguration.scope.toString() === 'machine-overridable') { + propertyConfiguration.scope = ConfigurationScope.MACHINE_OVERRIDABLE; } else { propertyConfiguration.scope = ConfigurationScope.WINDOW; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 016b4b097c..38a0ac4362 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -553,15 +553,17 @@ export interface MainThreadWebviewsShape extends IDisposable { $unregisterSerializer(viewType: string): void; } -export interface WebviewPanelViewState { - readonly active: boolean; - readonly visible: boolean; - readonly position: EditorViewColumn; +export interface WebviewPanelViewStateData { + [handle: string]: { + readonly active: boolean; + readonly visible: boolean; + readonly position: EditorViewColumn; + }; } export interface ExtHostWebviewsShape { $onMessage(handle: WebviewPanelHandle, message: any): void; - $onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, newState: WebviewPanelViewState): void; + $onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void; $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; } @@ -745,7 +747,7 @@ export interface ExtHostConfigurationShape { } export interface ExtHostDiagnosticsShape { - + $acceptMarkersChange(data: [UriComponents, IMarkerData[]][]): void; } export interface ExtHostDocumentContentProvidersShape { diff --git a/src/vs/workbench/api/common/extHostCodeInsets.ts b/src/vs/workbench/api/common/extHostCodeInsets.ts index 459a7e52cd..2e289fee40 100644 --- a/src/vs/workbench/api/common/extHostCodeInsets.ts +++ b/src/vs/workbench/api/common/extHostCodeInsets.ts @@ -10,7 +10,7 @@ import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor'; import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; import * as vscode from 'vscode'; import { ExtHostEditorInsetsShape, MainThreadEditorInsetsShape } from './extHost.protocol'; -import { toWebviewResource, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; +import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import { generateUuid } from 'vs/base/common/uuid'; export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { @@ -65,8 +65,8 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { private _html: string = ''; private _options: vscode.WebviewOptions = Object.create(null); - toWebviewResource(resource: vscode.Uri): vscode.Uri { - return toWebviewResource(that._initData, this._uuid, resource); + asWebviewUri(resource: vscode.Uri): vscode.Uri { + return asWebviewUri(that._initData, this._uuid, resource); } get cspSource(): string { diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index 104e463744..31d4c33104 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import * as vscode from 'vscode'; import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol'; import { DiagnosticSeverity } from './extHostTypes'; @@ -20,12 +20,12 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { private readonly _owner: string; private readonly _maxDiagnosticsPerFile: number; private readonly _onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>; - private readonly _proxy: MainThreadDiagnosticsShape; + private readonly _proxy: MainThreadDiagnosticsShape | undefined; private _isDisposed = false; private _data = new Map(); - constructor(name: string, owner: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) { + constructor(name: string, owner: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape | undefined, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) { this._name = name; this._owner = owner; this._maxDiagnosticsPerFile = maxDiagnosticsPerFile; @@ -36,7 +36,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { dispose(): void { if (!this._isDisposed) { this._onDidChangeDiagnostics.fire(keys(this._data)); - this._proxy.$clear(this._owner); + if (this._proxy) { + this._proxy.$clear(this._owner); + } this._data = undefined!; this._isDisposed = true; } @@ -112,6 +114,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { this._onDidChangeDiagnostics.fire(toSync); // compute change and send to main side + if (!this._proxy) { + return; + } const entries: [URI, IMarkerData[]][] = []; for (let uri of toSync) { let marker: IMarkerData[] = []; @@ -149,7 +154,6 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { entries.push([uri, marker]); } - this._proxy.$changeMany(this._owner, entries); } @@ -157,14 +161,18 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { this._checkDisposed(); this._onDidChangeDiagnostics.fire([uri]); this._data.delete(uri.toString()); - this._proxy.$changeMany(this._owner, [[uri, undefined]]); + if (this._proxy) { + this._proxy.$changeMany(this._owner, [[uri, undefined]]); + } } clear(): void { this._checkDisposed(); this._onDidChangeDiagnostics.fire(keys(this._data)); this._data.clear(); - this._proxy.$clear(this._owner); + if (this._proxy) { + this._proxy.$clear(this._owner); + } } forEach(callback: (uri: URI, diagnostics: ReadonlyArray, collection: DiagnosticCollection) => any, thisArg?: any): void { @@ -311,4 +319,20 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape { }); return res; } + + private _mirrorCollection: vscode.DiagnosticCollection | undefined; + + $acceptMarkersChange(data: [UriComponents, IMarkerData[]][]): void { + + if (!this._mirrorCollection) { + const name = '_generated_mirror'; + const collection = new DiagnosticCollection(name, name, ExtHostDiagnostics._maxDiagnosticsPerFile, undefined, this._onDidChangeDiagnostics); + this._collections.set(name, collection); + this._mirrorCollection = collection; + } + + for (const [uri, markers] of data) { + this._mirrorCollection.set(URI.revive(uri), markers.map(converter.Diagnostic.to)); + } + } } diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index 1d2f89ffc7..1a7bd52c3e 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -24,12 +24,12 @@ import { IExtensionApiFactory as sqlIApiFactory } from 'sql/workbench/api/common interface LoadFunction { - (request: string, parent: { filename: string; }, isMain: any): any; + (request: string): any; } export interface INodeModuleFactory { //{{SQL CARBON EDIT}} export interface readonly nodeModuleName: string | string[]; - load(request: string, parent: URI, isMain: any, original: LoadFunction): any; + load(request: string, parent: URI, original: LoadFunction): any; alternativeModuleName?(name: string): string | undefined; } @@ -250,7 +250,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory { }; } - public load(request: string, parent: URI, isMain: any, original: LoadFunction): any { + public load(request: string, parent: URI, original: LoadFunction): any { // get extension id from filename and api for extension const extension = this._extensionPaths.findSubstr(parent.fsPath); if (extension) { @@ -258,7 +258,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory { this.sendShimmingTelemetry(); } - this._original = original(request, { filename: parent.fsPath }, isMain); + this._original = original(request); return this._impl; } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 72b54684de..08b22c4e62 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -113,6 +113,15 @@ export namespace DiagnosticTag { } return undefined; } + export function to(value: MarkerTag): vscode.DiagnosticTag | undefined { + switch (value) { + case MarkerTag.Unnecessary: + return types.DiagnosticTag.Unnecessary; + case MarkerTag.Deprecated: + return types.DiagnosticTag.Deprecated; + } + return undefined; + } } export namespace Diagnostic { @@ -127,6 +136,15 @@ export namespace Diagnostic { tags: Array.isArray(value.tags) ? coalesce(value.tags.map(DiagnosticTag.from)) : undefined, }; } + + export function to(value: IMarkerData): vscode.Diagnostic { + const res = new types.Diagnostic(Range.to(value), value.message, DiagnosticSeverity.to(value.severity)); + res.source = value.source; + res.code = value.code; + res.relatedInformation = value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.to); + res.tags = value.tags && coalesce(value.tags.map(DiagnosticTag.to)); + return res; + } } export namespace DiagnosticRelatedInformation { diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 33df83ce7a..169fc7f604 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -5,15 +5,15 @@ import { Emitter, Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import * as modes from 'vs/editor/common/modes'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import * as vscode from 'vscode'; -import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol'; +import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol'; import { Disposable } from './extHostTypes'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import * as modes from 'vs/editor/common/modes'; -import { WebviewInitData, toWebviewResource } from 'vs/workbench/api/common/shared/webview'; -import { generateUuid } from 'vs/base/common/uuid'; type IconPath = URI | { light: URI, dark: URI }; @@ -35,8 +35,8 @@ export class ExtHostWebview implements vscode.Webview { this._onMessageEmitter.dispose(); } - public toWebviewResource(resource: vscode.Uri): vscode.Uri { - return toWebviewResource(this._initData, this._handle, resource); + public asWebviewUri(resource: vscode.Uri): vscode.Uri { + return asWebviewUri(this._initData, this._handle, resource); } public get cspSource(): string { @@ -89,11 +89,12 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { private readonly _options: vscode.WebviewPanelOptions; private readonly _webview: ExtHostWebview; - private _isDisposed: boolean = false; private _viewColumn: vscode.ViewColumn | undefined; private _visible: boolean = true; private _active: boolean = true; + _isDisposed: boolean = false; + readonly _onDisposeEmitter = new Emitter(); public readonly onDidDispose: Event = this._onDisposeEmitter.event; @@ -297,21 +298,21 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { } } - public $onDidChangeWebviewPanelViewState( - handle: WebviewPanelHandle, - newState: WebviewPanelViewState - ): void { - const panel = this.getWebviewPanel(handle); - if (!panel) { - return; - } + public $onDidChangeWebviewPanelViewStates(newStates: WebviewPanelViewStateData): void { + for (const handle of Object.keys(newStates)) { + const panel = this.getWebviewPanel(handle); + if (!panel || panel._isDisposed) { + continue; + } - const viewColumn = typeConverters.ViewColumn.to(newState.position); - if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) { - panel._setActive(newState.active); - panel._setVisible(newState.visible); - panel._setViewColumn(viewColumn); - panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel }); + const newState = newStates[handle]; + const viewColumn = typeConverters.ViewColumn.to(newState.position); + if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) { + panel._setActive(newState.active); + panel._setVisible(newState.visible); + panel._setViewColumn(viewColumn); + panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel }); + } } } diff --git a/src/vs/workbench/api/common/shared/webview.ts b/src/vs/workbench/api/common/shared/webview.ts index 26d1161993..cd73dfe51a 100644 --- a/src/vs/workbench/api/common/shared/webview.ts +++ b/src/vs/workbench/api/common/shared/webview.ts @@ -11,7 +11,7 @@ export interface WebviewInitData { readonly webviewCspSource: string; } -export function toWebviewResource( +export function asWebviewUri( initData: WebviewInitData, uuid: string, resource: vscode.Uri diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index f3e9aae4d4..0c5ba0dc85 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -31,7 +31,11 @@ class NodeModuleRequireInterceptor extends RequireInterceptor { if (!that._factories.has(request)) { return original.apply(this, arguments); } - return that._factories.get(request)!.load(request, URI.file(parent.filename), isMain, original); + return that._factories.get(request)!.load( + request, + URI.file(parent.filename), + request => original.apply(this, [request, parent, isMain]) + ); }; } } diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 915129d569..7fa1f5c156 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -497,6 +497,7 @@ export class ExtHostTerminalService implements IExtHostTerminalService, ExtHostT const terminalConfig = configProvider.getConfiguration('terminal.integrated'); const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), lastActiveWorkspace ? lastActiveWorkspace : undefined, this._variableResolver, activeWorkspaceRootUri, terminalConfig.cwd, this._logService); + shellLaunchConfig.cwd = initialCwd; const envFromConfig = this._apiInspectConfigToPlain(configProvider.getConfiguration('terminal.integrated').inspect(`env.${platformKey}`)); const baseEnv = terminalConfig.get('inheritEnv', true) ? process.env as platform.IProcessEnvironment : await this._getNonInheritedEnv(); diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index c138c1591c..f6d890d679 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -89,7 +89,7 @@ class WorkerRequireInterceptor extends RequireInterceptor { } if (this._factories.has(request)) { - return this._factories.get(request)!.load(request, parent, false, () => { throw new Error(); }); + return this._factories.get(request)!.load(request, parent, () => { throw new Error('CANNOT LOAD MODULE from here.'); }); } return undefined; } diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 5f17d11647..e5c2df5af2 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -113,7 +113,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array r.resource.fsPath === file.path) /* prevent duplicates */) { + if (file && file.path /* Electron only */ && !resources.some(r => r.resource.fsPath === file.path) /* prevent duplicates */) { try { resources.push({ resource: URI.file(file.path), isExternal: true }); } catch (error) { diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index b01fdd82f1..5b7b0f159e 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -317,40 +317,34 @@ class ResourceLabelWidget extends IconLabel { } notifyFormattersChange(): void { - if (this.label && this.label.resource) { - this.setFile(this.label.resource, this.options); - } this.render(false); } setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void { - const hasResourceChanged = this.hasResourceChanged(label, options); + const hasPathLabelChanged = this.hasPathLabelChanged(label, options); + const clearIconCache = this.clearIconCache(label, options); this.label = label; this.options = options; - if (hasResourceChanged) { + if (hasPathLabelChanged) { this.computedPathLabel = undefined; // reset path label due to resource change } - this.render(hasResourceChanged); + this.render(clearIconCache); } - private hasResourceChanged(label: IResourceLabelProps, options?: IResourceLabelOptions): boolean { - const newResource = label ? label.resource : undefined; + private clearIconCache(newLabel: IResourceLabelProps, newOptions?: IResourceLabelOptions): boolean { + const newResource = newLabel ? newLabel.resource : undefined; const oldResource = this.label ? this.label.resource : undefined; - const newFileKind = options ? options.fileKind : undefined; + const newFileKind = newOptions ? newOptions.fileKind : undefined; const oldFileKind = this.options ? this.options.fileKind : undefined; if (newFileKind !== oldFileKind) { return true; // same resource but different kind (file, folder) } - if (newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource)) { - return true; - } - if (newResource && oldResource) { return newResource.toString() !== oldResource.toString(); } @@ -362,6 +356,12 @@ class ResourceLabelWidget extends IconLabel { return true; } + private hasPathLabelChanged(newLabel: IResourceLabelProps, newOptions?: IResourceLabelOptions): boolean { + const newResource = newLabel ? newLabel.resource : undefined; + + return !!newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource); + } + setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void { this.setResource({ resource: toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }), @@ -460,6 +460,7 @@ class ResourceLabelWidget extends IconLabel { } iconLabelOptions.extraClasses = this.computedIconClasses.slice(0); } + if (this.options && this.options.extraClasses) { iconLabelOptions.extraClasses!.push(...this.options.extraClasses); } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index f7e05094e6..37910829ad 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -9,7 +9,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { size } from 'vs/base/common/collections'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { isEqual, dirname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -36,14 +36,14 @@ type FileInfo = { path: FileElement[], folder?: IWorkspaceFolder }; export class EditorBreadcrumbsModel { - private readonly _disposables: IDisposable[] = []; + private readonly _disposables = new DisposableStore(); private readonly _fileInfo: FileInfo; private readonly _cfgFilePath: BreadcrumbsConfig<'on' | 'off' | 'last'>; private readonly _cfgSymbolPath: BreadcrumbsConfig<'on' | 'off' | 'last'>; private _outlineElements: Array = []; - private _outlineDisposables: IDisposable[] = []; + private _outlineDisposables = new DisposableStore(); private _onDidUpdate = new Emitter(); readonly onDidUpdate: Event = this._onDidUpdate.event; @@ -58,8 +58,8 @@ export class EditorBreadcrumbsModel { this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(configurationService); this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(configurationService); - this._disposables.push(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this))); - this._disposables.push(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this))); + this._disposables.add(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this))); + this._disposables.add(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this))); this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(this._uri, workspaceService); this._bindToEditor(); @@ -69,7 +69,7 @@ export class EditorBreadcrumbsModel { dispose(): void { this._cfgFilePath.dispose(); this._cfgSymbolPath.dispose(); - dispose(this._disposables); + this._disposables.dispose(); } isRelative(): boolean { @@ -133,20 +133,27 @@ export class EditorBreadcrumbsModel { if (!this._editor) { return; } - // update as model changes - this._disposables.push(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline())); - this._disposables.push(this._editor.onDidChangeModel(_ => this._updateOutline())); - this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._updateOutline())); - this._disposables.push(Event.debounce(this._editor.onDidChangeModelContent, _ => _, 350)(_ => this._updateOutline(true))); + // update as language, model, providers changes + this._disposables.add(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline())); + this._disposables.add(this._editor.onDidChangeModel(_ => this._updateOutline())); + this._disposables.add(this._editor.onDidChangeModelLanguage(_ => this._updateOutline())); + + // update soon'ish as model content change + const updateSoon = new TimeoutTimer(); + this._disposables.add(updateSoon); + this._disposables.add(this._editor.onDidChangeModelContent(_ => { + const timeout = OutlineModel.getRequestDelay(this._editor!.getModel()); + updateSoon.cancelAndSet(() => this._updateOutline(true), timeout); + })); this._updateOutline(); // stop when editor dies - this._disposables.push(this._editor.onDidDispose(() => this._outlineDisposables = dispose(this._outlineDisposables))); + this._disposables.add(this._editor.onDidDispose(() => this._outlineDisposables.clear())); } private _updateOutline(didChangeContent?: boolean): void { - this._outlineDisposables = dispose(this._outlineDisposables); + this._outlineDisposables.clear(); if (!didChangeContent) { this._updateOutlineElements([]); } @@ -162,7 +169,7 @@ export class EditorBreadcrumbsModel { const versionIdThen = buffer.getVersionId(); const timeout = new TimeoutTimer(); - this._outlineDisposables.push({ + this._outlineDisposables.add({ dispose: () => { source.cancel(); source.dispose(); @@ -180,7 +187,7 @@ export class EditorBreadcrumbsModel { model = model.adopt(); this._updateOutlineElements(this._getOutlineElements(model, editor.getPosition())); - this._outlineDisposables.push(editor.onDidChangeCursorPosition(_ => { + this._outlineDisposables.add(editor.onDidChangeCursorPosition(_ => { timeout.cancelAndSet(() => { if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId() && editor.getModel()) { this._updateOutlineElements(this._getOutlineElements(model, editor.getPosition())); diff --git a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css index d236342506..520925d9cf 100644 --- a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css @@ -17,6 +17,10 @@ text-decoration-line: underline; } +.monaco-workbench .monaco-breadcrumb-item.shows-symbol-icon .symbol-icon.block { + padding-right: 6px; +} + /* todo@joh move somewhere else */ .monaco-workbench .monaco-breadcrumbs-picker .arrow { diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsActions.css b/src/vs/workbench/browser/parts/notifications/media/notificationsActions.css index 8f428f1205..48c3111061 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsActions.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsActions.css @@ -66,4 +66,4 @@ .vs .monaco-workbench > .notifications-center > .notifications-center-header .hide-all-notifications-action { background-image: url('tree-expanded-light.svg'); -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index 54247e8ba0..cbcf1787b0 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -91,6 +91,7 @@ .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-toolbar-container { display: none; + height: 22px; } .monaco-workbench .notifications-list-container .notification-list-item:hover .notification-list-item-toolbar-container, @@ -142,4 +143,4 @@ .monaco-workbench .notifications-list-container .progress-bit { height: 2px; bottom: 0; -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 93cb19474d..a72b0e3891 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IMenubarMenu, IMenubarMenuItemAction, IMenubarMenuItemSubmenu, IMenubarKeybinding, IMenubarService, IMenubarData, MenubarMenuItem } from 'vs/platform/menubar/common/menubar'; import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService'; import { IWindowService, MenuBarVisibility, IWindowsService, getTitleBarStyle, IURIToOpen } from 'vs/platform/windows/common/windows'; @@ -33,7 +32,6 @@ import { attachMenuStyler } from 'vs/platform/theme/common/styler'; import { assign } from 'vs/base/common/objects'; import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { isFullscreen } from 'vs/base/browser/browser'; @@ -134,7 +132,7 @@ export abstract class MenubarControl extends Disposable { this.menuUpdater.schedule(); } - protected calculateActionLabel(action: IAction | IMenubarMenuItemAction): string { + protected calculateActionLabel(action: { id: string; label: string; }): string { let label = action.label; switch (action.id) { default: @@ -252,195 +250,6 @@ export abstract class MenubarControl extends Disposable { } } -export class NativeMenubarControl extends MenubarControl { - constructor( - @IMenuService menuService: IMenuService, - @IWindowService windowService: IWindowService, - @IWindowsService windowsService: IWindowsService, - @IContextKeyService contextKeyService: IContextKeyService, - @IKeybindingService keybindingService: IKeybindingService, - @IConfigurationService configurationService: IConfigurationService, - @ILabelService labelService: ILabelService, - @IUpdateService updateService: IUpdateService, - @IStorageService storageService: IStorageService, - @INotificationService notificationService: INotificationService, - @IPreferencesService preferencesService: IPreferencesService, - @IEnvironmentService environmentService: IEnvironmentService, - @IAccessibilityService accessibilityService: IAccessibilityService, - @IMenubarService private readonly menubarService: IMenubarService - ) { - super( - menuService, - windowService, - windowsService, - contextKeyService, - keybindingService, - configurationService, - labelService, - updateService, - storageService, - notificationService, - preferencesService, - environmentService, - accessibilityService); - - if (isMacintosh && !isWeb) { - this.menus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService)); - this.topLevelTitles['Preferences'] = nls.localize('mPreferences', "Preferences"); - } - - for (const topLevelMenuName of Object.keys(this.topLevelTitles)) { - const menu = this.menus[topLevelMenuName]; - if (menu) { - this._register(menu.onDidChange(() => this.updateMenubar())); - } - } - - this.windowService.getRecentlyOpened().then((recentlyOpened) => { - this.recentlyOpened = recentlyOpened; - - this.doUpdateMenubar(true); - }); - - this.registerListeners(); - } - - protected doUpdateMenubar(firstTime: boolean): void { - // Send menus to main process to be rendered by Electron - const menubarData = { menus: {}, keybindings: {} }; - if (this.getMenubarMenus(menubarData)) { - this.menubarService.updateMenubar(this.windowService.windowId, menubarData); - } - } - - private getMenubarMenus(menubarData: IMenubarData): boolean { - if (!menubarData) { - return false; - } - - menubarData.keybindings = this.getAdditionalKeybindings(); - for (const topLevelMenuName of Object.keys(this.topLevelTitles)) { - const menu = this.menus[topLevelMenuName]; - if (menu) { - const menubarMenu: IMenubarMenu = { items: [] }; - this.populateMenuItems(menu, menubarMenu, menubarData.keybindings); - if (menubarMenu.items.length === 0) { - // Menus are incomplete - return false; - } - menubarData.menus[topLevelMenuName] = menubarMenu; - } - } - - return true; - } - - private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding | undefined }) { - let groups = menu.getActions(); - for (let group of groups) { - const [, actions] = group; - - actions.forEach(menuItem => { - - if (menuItem instanceof SubmenuItemAction) { - const submenu = { items: [] }; - - if (!this.menus[menuItem.item.submenu]) { - this.menus[menuItem.item.submenu] = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); - this._register(this.menus[menuItem.item.submenu]!.onDidChange(() => this.updateMenubar())); - } - - const menuToDispose = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); - this.populateMenuItems(menuToDispose, submenu, keybindings); - - let menubarSubmenuItem: IMenubarMenuItemSubmenu = { - id: menuItem.id, - label: menuItem.label, - submenu: submenu - }; - - menuToPopulate.items.push(menubarSubmenuItem); - menuToDispose.dispose(); - } else { - if (menuItem.id === 'workbench.action.openRecent') { - const actions = this.getOpenRecentActions().map(this.transformOpenRecentAction); - menuToPopulate.items.push(...actions); - } - - let menubarMenuItem: IMenubarMenuItemAction = { - id: menuItem.id, - label: menuItem.label - }; - - if (menuItem.checked) { - menubarMenuItem.checked = true; - } - - if (!menuItem.enabled) { - menubarMenuItem.enabled = false; - } - - menubarMenuItem.label = this.calculateActionLabel(menubarMenuItem); - keybindings[menuItem.id] = this.getMenubarKeybinding(menuItem.id); - menuToPopulate.items.push(menubarMenuItem); - } - }); - - menuToPopulate.items.push({ id: 'vscode.menubar.separator' }); - } - - if (menuToPopulate.items.length > 0) { - menuToPopulate.items.pop(); - } - } - - private transformOpenRecentAction(action: Separator | (IAction & { uri: URI })): MenubarMenuItem { - if (action instanceof Separator) { - return { id: 'vscode.menubar.separator' }; - } - - return { - id: action.id, - uri: action.uri, - enabled: action.enabled, - label: action.label - }; - } - - private getAdditionalKeybindings(): { [id: string]: IMenubarKeybinding } { - const keybindings: { [id: string]: IMenubarKeybinding } = {}; - if (isMacintosh) { - const keybinding = this.getMenubarKeybinding('workbench.action.quit'); - if (keybinding) { - keybindings['workbench.action.quit'] = keybinding; - } - } - - return keybindings; - } - - private getMenubarKeybinding(id: string): IMenubarKeybinding | undefined { - const binding = this.keybindingService.lookupKeybinding(id); - if (!binding) { - return undefined; - } - - // first try to resolve a native accelerator - const electronAccelerator = binding.getElectronAccelerator(); - if (electronAccelerator) { - return { label: electronAccelerator, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) }; - } - - // we need this fallback to support keybindings that cannot show in electron menus (e.g. chords) - const acceleratorLabel = binding.getLabel(); - if (acceleratorLabel) { - return { label: acceleratorLabel, isNative: false, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) }; - } - - return undefined; - } -} - export class CustomMenubarControl extends MenubarControl { private menubar: MenuBar; private container: HTMLElement; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index a055c8c63d..43de53c166 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -27,7 +27,7 @@ import { URI } from 'vs/base/common/uri'; import { Color } from 'vs/base/common/color'; import { trim } from 'vs/base/common/strings'; import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; -import { MenubarControl, NativeMenubarControl, CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; +import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { template, getBaseLabel } from 'vs/base/common/labels'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -65,7 +65,7 @@ export class TitlebarPart extends Part implements ITitleService { private windowControls: HTMLElement; private maxRestoreControl: HTMLElement; private appIcon: HTMLElement; - private menubarPart: MenubarControl; + private customMenubar: CustomMenubarControl | undefined; private menubar: HTMLElement; private resizer: HTMLElement; private lastLayoutDimensions: Dimension; @@ -332,19 +332,16 @@ export class TitlebarPart extends Part implements ITitleService { }))); } - // Menubar: the menubar part which is responsible for populating both the custom and native menubars - if ((isMacintosh && !isWeb) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { - this.menubarPart = this.instantiationService.createInstance(NativeMenubarControl); - } else { - const customMenubarControl = this.instantiationService.createInstance(CustomMenubarControl); - this.menubarPart = customMenubarControl; + // Menubar: install a custom menu bar depending on configuration + if (getTitleBarStyle(this.configurationService, this.environmentService) !== 'native' && (!isMacintosh || isWeb)) { + this.customMenubar = this._register(this.instantiationService.createInstance(CustomMenubarControl)); this.menubar = append(this.element, $('div.menubar')); this.menubar.setAttribute('role', 'menubar'); - customMenubarControl.create(this.menubar); + this.customMenubar.create(this.menubar); - this._register(customMenubarControl.onVisibilityChange(e => this.onMenubarVisibilityChanged(e))); - this._register(customMenubarControl.onFocusStateChange(e => this.onMenubarFocusChanged(e))); + this._register(this.customMenubar.onVisibilityChange(e => this.onMenubarVisibilityChanged(e))); + this._register(this.customMenubar.onFocusStateChange(e => this.onMenubarFocusChanged(e))); } // Title @@ -547,7 +544,7 @@ export class TitlebarPart extends Part implements ITitleService { } private adjustTitleMarginToCenter(): void { - if (this.menubarPart instanceof CustomMenubarControl) { + if (this.customMenubar) { const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10; const rightMarker = this.element.clientWidth - (this.windowControls ? this.windowControls.clientWidth : 0) - 10; @@ -588,9 +585,9 @@ export class TitlebarPart extends Part implements ITitleService { runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter()); - if (this.menubarPart instanceof CustomMenubarControl) { + if (this.customMenubar) { const menubarDimension = new Dimension(0, dimension.height); - this.menubarPart.layout(menubarDimension); + this.customMenubar.layout(menubarDimension); } } } diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index ece095c111..28aa41a1d5 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -69,7 +69,9 @@ export class CustomTreeViewPanel extends ViewletPanel { } renderBody(container: HTMLElement): void { - this.treeView.show(container); + if (this.treeView instanceof CustomTreeView) { + this.treeView.show(container); + } } layoutBody(height: number, width: number): void { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 782c544fac..7eea94e08e 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -204,7 +204,7 @@ class CodeRendererMain extends Disposable { version: '1.38.0-unknown', nameLong: 'Unknown', extensionAllowedProposedApi: [], - }, ...{ urlProtocol: '', enableTelemetry: false } + }, ...{ urlProtocol: '' } }; return { _serviceBrand: undefined, ...productConfiguration }; } diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 9f0d903ec0..5152445ec7 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -50,12 +50,12 @@ export class Workbench extends Layout { private readonly _onBeforeShutdown = this._register(new Emitter()); readonly onBeforeShutdown: Event = this._onBeforeShutdown.event; - private readonly _onShutdown = this._register(new Emitter()); - readonly onShutdown: Event = this._onShutdown.event; - private readonly _onWillShutdown = this._register(new Emitter()); readonly onWillShutdown: Event = this._onWillShutdown.event; + private readonly _onShutdown = this._register(new Emitter()); + readonly onShutdown: Event = this._onShutdown.event; + constructor( parent: HTMLElement, private readonly serviceCollection: ServiceCollection, @@ -429,7 +429,7 @@ export class Workbench extends Layout { // Restore Editor Center Mode if (this.state.editor.restoreCentered) { - this.centerEditorLayout(true); + this.centerEditorLayout(true, true); } // Emit a warning after 10s if restore does not complete diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 8b190469c0..c575d45278 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -334,8 +334,6 @@ export interface ITreeView extends IDisposable { layout(height: number, width: number): void; - show(container: HTMLElement): void; - getOptimalWidth(): number; reveal(item: ITreeItem): Promise; diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 1a48fdf264..d936aeec52 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -169,7 +169,7 @@ class SelectionToReplAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); const panelService = accessor.get(IPanelService); const viewModel = debugService.getViewModel(); @@ -179,9 +179,8 @@ class SelectionToReplAction extends EditorAction { } const text = editor.getModel().getValueInRange(editor.getSelection()); - return session.addReplExpression(viewModel.focusedStackFrame!, text) - .then(() => panelService.openPanel(REPL_ID, true)) - .then(_ => undefined); + await session.addReplExpression(viewModel.focusedStackFrame!, text); + await panelService.openPanel(REPL_ID, true); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 0199dd9ee8..d7b0ff88b9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -404,7 +404,7 @@ export class DebugService implements IDebugService { .then(() => false); } - return launch && launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false); + return !!launch && launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false); }); } @@ -542,6 +542,10 @@ export class DebugService implements IDebugService { if (this.layoutService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getValue('debug').openExplorerOnEnd) { this.viewletService.openViewlet(EXPLORER_VIEWLET_ID); } + + // Data breakpoints that can not be persisted should be cleared when a session ends + const dataBreakpoints = this.model.getDataBreakpoints().filter(dbp => !dbp.canPersist); + dataBreakpoints.forEach(dbp => this.model.removeDataBreakpoints(dbp.getId())); } })); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index b33cc2cdb4..791801615e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -31,6 +31,8 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel'; import { onUnexpectedError } from 'vs/base/common/errors'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView'; export class DebugSession implements IDebugSession { @@ -66,7 +68,8 @@ export class DebugSession implements IDebugSession { @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @INotificationService private readonly notificationService: INotificationService, @IProductService private readonly productService: IProductService, - @IWindowsService private readonly windowsService: IWindowsService + @IWindowsService private readonly windowsService: IWindowsService, + @IOpenerService private readonly openerService: IOpenerService ) { this.id = generateUuid(); this.repl = new ReplModel(this); @@ -167,7 +170,7 @@ export class DebugSession implements IDebugSession { return dbgr.createDebugAdapter(this).then(debugAdapter => { - this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService); + this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService, this.openerService); return this.raw.start().then(() => { @@ -552,7 +555,8 @@ export class DebugSession implements IDebugSession { insertText: item.text || item.label, kind: completionKindFromString(item.type || 'property'), filterText: (item.start && item.length) ? text.substr(item.start, item.length).concat(item.label) : undefined, - range: Range.fromPositions(position.delta(0, -(item.length || overwriteBefore)), position) + range: Range.fromPositions(position.delta(0, -(item.length || overwriteBefore)), position), + sortText: item.sortText }); } }); @@ -903,12 +907,13 @@ export class DebugSession implements IDebugSession { this._onDidChangeREPLElements.fire(); } - addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise { + async addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise { const viewModel = this.debugService.getViewModel(); - return this.repl.addReplExpression(stackFrame, name) - .then(() => this._onDidChangeREPLElements.fire()) - // Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some. - .then(() => this.debugService.focusStackFrame(viewModel.focusedStackFrame, viewModel.focusedThread, viewModel.focusedSession)); + await this.repl.addReplExpression(stackFrame, name); + this._onDidChangeREPLElements.fire(); + // Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some. + this.debugService.focusStackFrame(viewModel.focusedStackFrame, viewModel.focusedThread, viewModel.focusedSession); + variableSetEmitter.fire(); } appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void { diff --git a/src/vs/workbench/contrib/debug/browser/linkDetector.ts b/src/vs/workbench/contrib/debug/browser/linkDetector.ts index 318895d585..ed2eaaa179 100644 --- a/src/vs/workbench/contrib/debug/browser/linkDetector.ts +++ b/src/vs/workbench/contrib/debug/browser/linkDetector.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import strings = require('vs/base/common/strings'); +import * as strings from 'vs/base/common/strings'; import { isAbsolute } from 'vs/base/common/path'; import { URI as uri } from 'vs/base/common/uri'; import { isMacintosh } from 'vs/base/common/platform'; diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index e115330026..440ecee010 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -17,6 +17,7 @@ import { IWindowsService } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { env as processEnv } from 'vs/base/common/process'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; /** * This interface represents a single command line argument split into a "prefix" and a "path" half. @@ -74,7 +75,8 @@ export class RawDebugSession { dbgr: IDebugger, private readonly telemetryService: ITelemetryService, public readonly customTelemetryService: ITelemetryService | undefined, - private readonly windowsService: IWindowsService + private readonly windowsService: IWindowsService, + private readonly openerService: IOpenerService ) { this.debugAdapter = debugAdapter; @@ -652,7 +654,7 @@ export class RawDebugSession { const label = error.urlLabel ? error.urlLabel : nls.localize('moreInfo', "More Info"); return createErrorWithActions(userMessage, { actions: [new Action('debug.moreInfo', label, undefined, true, () => { - window.open(error.url); + this.openerService.open(URI.parse(error.url)); return Promise.resolve(null); })] }); diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 668ed589a8..60613a2a50 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -62,7 +62,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { VariablesRenderer, variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView'; +import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView'; const $ = dom.$; @@ -104,6 +104,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati private scopedInstantiationService!: IInstantiationService; private replElementsChangeListener: IDisposable | undefined; private styleElement: HTMLStyleElement | undefined; + private completionItemProvider: IDisposable | undefined; constructor( @IDebugService private readonly debugService: IDebugService, @@ -131,7 +132,33 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this._register(this.debugService.getViewModel().onDidFocusSession(session => { if (session) { sessionsToIgnore.delete(session); + if (this.completionItemProvider) { + this.completionItemProvider.dispose(); + } + if (session.capabilities.supportsCompletionsRequest) { + this.completionItemProvider = CompletionProviderRegistry.register({ scheme: DEBUG_SCHEME, pattern: '**/replinput', hasAccessToAllModels: true }, { + triggerCharacters: session.capabilities.completionTriggerCharacters || ['.'], + provideCompletionItems: async (_: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken): Promise => { + // Disable history navigation because up and down are used to navigate through the suggest widget + this.historyNavigationEnablement.set(false); + + const model = this.replInput.getModel(); + if (model) { + const word = model.getWordAtPosition(position); + const overwriteBefore = word ? word.word.length : 0; + const text = model.getLineContent(position.lineNumber); + const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; + const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; + const suggestions = await session.completions(frameId, text, position, overwriteBefore); + return { suggestions }; + } + + return Promise.resolve({ suggestions: [] }); + } + }); + } } + this.selectSession(); })); this._register(this.debugService.onWillNewSession(newSession => { @@ -274,7 +301,6 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati revealLastElement(this.tree); this.history.add(this.replInput.getValue()); this.replInput.setValue(''); - variableSetEmitter.fire(); const shouldRelayout = this.replInputHeight > Repl.REPL_INPUT_INITIAL_HEIGHT; this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT; if (shouldRelayout) { @@ -418,6 +444,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati private createReplInput(container: HTMLElement): void { this.replInputContainer = dom.append(container, $('.repl-input-wrapper')); + this.replInputContainer.title = nls.localize('debugConsole', "Debug Console"); const { scopedContextKeyService, historyNavigationEnablement } = createAndBindHistoryNavigationWidgetScopedContextKeyService(this.contextKeyService, { target: this.replInputContainer, historyNavigator: this }); this.historyNavigationEnablement = historyNavigationEnablement; @@ -430,34 +457,6 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati options.readOnly = true; this.replInput = this.scopedInstantiationService.createInstance(CodeEditorWidget, this.replInputContainer, options, getSimpleCodeEditorWidgetOptions()); - CompletionProviderRegistry.register({ scheme: DEBUG_SCHEME, pattern: '**/replinput', hasAccessToAllModels: true }, { - triggerCharacters: ['.'], - provideCompletionItems: (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken): Promise => { - // Disable history navigation because up and down are used to navigate through the suggest widget - this.historyNavigationEnablement.set(false); - - const focusedSession = this.debugService.getViewModel().focusedSession; - if (focusedSession && focusedSession.capabilities.supportsCompletionsRequest) { - - const model = this.replInput.getModel(); - if (model) { - const word = model.getWordAtPosition(position); - const overwriteBefore = word ? word.word.length : 0; - const text = model.getLineContent(position.lineNumber); - const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; - const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; - - return focusedSession.completions(frameId, text, position, overwriteBefore).then(suggestions => { - return { suggestions }; - }, err => { - return { suggestions: [] }; - }); - } - } - return Promise.resolve({ suggestions: [] }); - } - }); - this._register(this.replInput.onDidScrollChange(e => { if (!e.scrollHeightChanged) { return; diff --git a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts index 513d0383d1..a2376fe4d9 100644 --- a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts +++ b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts @@ -168,7 +168,7 @@ declare module DebugProtocol { category?: string; /** The output to report. */ output: string; - /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. */ + /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31 - 1). */ variablesReference?: number; /** An optional source location where the output was produced. */ source?: Source; @@ -284,9 +284,9 @@ declare module DebugProtocol { /** Response to 'runInTerminal' request. */ export interface RunInTerminalResponse extends Response { body: { - /** The process ID. */ + /** The process ID. The value should be less than or equal to 2147483647 (2^31 - 1). */ processId?: number; - /** The process ID of the terminal shell. */ + /** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31 - 1). */ shellProcessId?: number; }; } @@ -757,7 +757,7 @@ declare module DebugProtocol { } /** Pause request; value of command field is 'pause'. - The request suspenses the debuggee. + The request suspends the debuggee. The debug adapter first sends the response and then a 'stopped' event (with reason 'pause') after the thread has been paused successfully. */ export interface PauseRequest extends Request { @@ -842,7 +842,7 @@ declare module DebugProtocol { export interface VariablesArguments { /** The Variable reference. */ variablesReference: number; - /** Optional filter to limit the child variables to either named or indexed. If ommited, both types are fetched. */ + /** Optional filter to limit the child variables to either named or indexed. If omitted, both types are fetched. */ filter?: 'indexed' | 'named'; /** The index of the first variable to return; if omitted children start at 0. */ start?: number; @@ -887,14 +887,14 @@ declare module DebugProtocol { value: string; /** The type of the new value. Typically shown in the UI when hovering over the value. */ type?: string; - /** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ + /** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ variablesReference?: number; /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ namedVariables?: number; /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ indexedVariables?: number; }; @@ -1041,14 +1041,14 @@ declare module DebugProtocol { type?: string; /** Properties of a evaluate result that can be used to determine how to render the result in the UI. */ presentationHint?: VariablePresentationHint; - /** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ + /** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ variablesReference: number; /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ namedVariables?: number; /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ indexedVariables?: number; /** Memory reference to a location appropriate for this result. For pointer type eval results, this is generally a reference to the memory address contained in the pointer. */ @@ -1086,14 +1086,14 @@ declare module DebugProtocol { type?: string; /** Properties of a value that can be used to determine how to render the result in the UI. */ presentationHint?: VariablePresentationHint; - /** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ + /** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ variablesReference?: number; /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ namedVariables?: number; /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ indexedVariables?: number; }; @@ -1294,6 +1294,8 @@ declare module DebugProtocol { supportsStepInTargetsRequest?: boolean; /** The debug adapter supports the 'completions' request. */ supportsCompletionsRequest?: boolean; + /** The set of characters that should trigger completion in a REPL. If not specified, the UI should assume the '.' character. */ + completionTriggerCharacters?: string[]; /** The debug adapter supports the 'modules' request. */ supportsModulesRequest?: boolean; /** The set of additional module information exposed by the debug adapter. */ @@ -1433,7 +1435,7 @@ declare module DebugProtocol { name?: string; /** The path of the source to be shown in the UI. It is only used to locate and load the content of the source if no sourceReference is specified (or its value is 0). */ path?: string; - /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. */ + /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. The value should be less than or equal to 2147483647 (2^31 - 1). */ sourceReference?: number; /** An optional hint for how to present the source in the UI. A value of 'deemphasize' can be used to indicate that the source is not available or that it is skipped on stepping. */ presentationHint?: 'normal' | 'emphasize' | 'deemphasize'; @@ -1668,6 +1670,8 @@ declare module DebugProtocol { label: string; /** If text is not falsy then it is inserted instead of the label. */ text?: string; + /** A string that should be used when comparing this item with other items. When `falsy` the label is used. */ + sortText?: string; /** The item's type. Typically the client uses this information to render the item in the UI with an icon. */ type?: CompletionItemType; /** This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added. @@ -1729,7 +1733,7 @@ declare module DebugProtocol { /** This enumeration defines all possible conditions when a thrown exception should result in a break. never: never breaks, always: always breaks, - unhandled: breaks when excpetion unhandled, + unhandled: breaks when exception unhandled, userUnhandled: breaks if the exception is not handled by user code. */ export type ExceptionBreakMode = 'never' | 'always' | 'unhandled' | 'userUnhandled'; @@ -1766,7 +1770,7 @@ declare module DebugProtocol { instructionBytes?: string; /** Text representing the instruction and its operands, in an implementation-defined format. */ instruction: string; - /** Name of the symbol that correponds with the location of this instruction, if any. */ + /** Name of the symbol that corresponds with the location of this instruction, if any. */ symbol?: string; /** Source location that corresponds to this instruction, if any. Should always be set (if available) on the first instruction returned, but can be omitted afterwards if this instruction maps to the same source file as the previous instruction. */ location?: Source; diff --git a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts index e2385a1503..a7d1ec2418 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts @@ -13,9 +13,10 @@ import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel'; import { IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug'; +import { NullOpenerService } from 'vs/platform/opener/common/opener'; function createMockSession(model: DebugModel, name = 'mockSession', parentSession?: DebugSession | undefined): DebugSession { - return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, parentSession, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!); + return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, parentSession, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService); } suite('Debug - Model', () => { @@ -427,7 +428,7 @@ suite('Debug - Model', () => { // Repl output test('repl output', () => { - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService); const repl = new ReplModel(session); repl.appendToRepl('first line\n', severity.Error); repl.appendToRepl('second line ', severity.Error); diff --git a/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts index c2c058e111..d1bb982aa6 100644 --- a/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts +++ b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts @@ -11,6 +11,8 @@ import { IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/exten import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Disposable } from 'vs/base/common/lifecycle'; import { language } from 'vs/base/common/platform'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; export class ExperimentalPrompts extends Disposable implements IWorkbenchContribution { @@ -18,7 +20,8 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib @IExperimentService private readonly experimentService: IExperimentService, @IViewletService private readonly viewletService: IViewletService, @INotificationService private readonly notificationService: INotificationService, - @ITelemetryService private readonly telemetryService: ITelemetryService + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IOpenerService private readonly openerService: IOpenerService ) { super(); @@ -65,7 +68,7 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib run: () => { logTelemetry(commandText); if (command.externalLink) { - window.open(command.externalLink); + this.openerService.open(URI.parse(command.externalLink)); } else if (command.curatedExtensionsKey && Array.isArray(command.curatedExtensionsList)) { this.viewletService.openViewlet('workbench.view.extensions', true) .then(viewlet => viewlet as IExtensionsViewlet) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index efe2cbbf0f..fd5dbed24f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -50,11 +50,12 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry'; import { isUndefined } from 'vs/base/common/types'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { renderDashboardContributions } from 'sql/workbench/parts/extensions/browser/contributionRenders'; // {{SQL CARBON EDIT}} import { generateUuid } from 'vs/base/common/uuid'; import { platform } from 'vs/base/common/process'; +import { URI } from 'vs/base/common/uri'; function removeEmbeddedSVGs(documentContent: string): string { const newDocument = new DOMParser().parseFromString(documentContent, 'text/html'); @@ -186,7 +187,7 @@ export class ExtensionEditor extends BaseEditor { @IStorageService storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, - @IWebviewService private readonly webviewService: IWebviewService, + @IWebviewService private readonly webviewService: IWebviewService ) { super(ExtensionEditor.ID, telemetryService, themeService, storageService); this.extensionReadme = null; @@ -355,8 +356,8 @@ export class ExtensionEditor extends BaseEditor { toggleClass(template.publisher, 'clickable', !!extension.url); toggleClass(template.rating, 'clickable', !!extension.url); if (extension.url) { - this.transientDisposables.add(this.onClick(template.name, () => window.open(extension.url))); - this.transientDisposables.add(this.onClick(template.rating, () => window.open(`${extension.url}#review-details`))); + this.transientDisposables.add(this.onClick(template.name, () => this.openerService.open(URI.parse(extension.url!)))); + this.transientDisposables.add(this.onClick(template.rating, () => this.openerService.open(URI.parse(`${extension.url}#review-details`)))); this.transientDisposables.add(this.onClick(template.publisher, () => { this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) @@ -364,7 +365,7 @@ export class ExtensionEditor extends BaseEditor { })); if (extension.licenseUrl) { - this.transientDisposables.add(this.onClick(template.license, () => window.open(extension.licenseUrl))); + this.transientDisposables.add(this.onClick(template.license, () => this.openerService.open(URI.parse(extension.licenseUrl!)))); template.license.style.display = 'initial'; } else { template.license.style.display = 'none'; @@ -384,7 +385,7 @@ export class ExtensionEditor extends BaseEditor { // {{SQL CARBON EDIT}} - End if (extension.repository) { - this.transientDisposables.add(this.onClick(template.repository, () => window.open(extension.repository))); + this.transientDisposables.add(this.onClick(template.repository, () => this.openerService.open(URI.parse(extension.repository!)))); template.repository.style.display = 'initial'; } else { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index 1b75151813..7e9d010438 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -534,8 +534,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return false; } - const id = recommendationsToSuggest[0]; - const tip = this._importantExeBasedRecommendations[id]; + const extensionId = recommendationsToSuggest[0]; + const tip = this._importantExeBasedRecommendations[extensionId]; const message = localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.friendlyName!, tip.exeFriendlyName || basename(tip.windowsPath!)); this.notificationService.prompt(Severity.Info, message, @@ -548,8 +548,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'install', extensionId: name }); - this.instantiationService.createInstance(InstallRecommendedExtensionAction, id).run(); + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'install', extensionId }); + this.instantiationService.createInstance(InstallRecommendedExtensionAction, extensionId).run(); } }, { label: localize('showRecommendations', "Show Recommendations"), @@ -560,7 +560,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'show', extensionId: name }); + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'show', extensionId }); const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); recommendationsAction.run(); @@ -570,14 +570,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe label: choiceNever, isSecondary: true, run: () => { - this.addToImportantRecommendationsIgnore(id); + this.addToImportantRecommendationsIgnore(extensionId); /* __GDPR__ "exeExtensionRecommendations:popup" : { "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name }); + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId }); this.notificationService.prompt( Severity.Info, localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), @@ -600,7 +600,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'cancelled', extensionId: name }); + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'cancelled', extensionId }); } } ); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts index ab6f972288..a6cf97a013 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts @@ -17,6 +17,7 @@ import { join } from 'vs/base/common/path'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; abstract class RepoInfo { abstract get base(): string; @@ -117,6 +118,7 @@ class ReportExtensionSlowAction extends Action { readonly repoInfo: RepoInfo, readonly profile: IExtensionHostProfile, @IDialogService private readonly _dialogService: IDialogService, + @IOpenerService private readonly _openerService: IOpenerService ) { super('report.slow', localize('cmd.report', "Report Issue")); } @@ -140,7 +142,7 @@ class ReportExtensionSlowAction extends Action { - VSCode version: \`${pkg.version}\`\n\n${message}`); const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues/new/?body=${body}&title=${title}`; - window.open(url); + this._openerService.open(URI.parse(url)); this._dialogService.show( Severity.Info, @@ -158,6 +160,7 @@ class ShowExtensionSlowAction extends Action { readonly repoInfo: RepoInfo, readonly profile: IExtensionHostProfile, @IDialogService private readonly _dialogService: IDialogService, + @IOpenerService private readonly _openerService: IOpenerService ) { super('show.slow', localize('cmd.show', "Show Issues")); } @@ -172,7 +175,7 @@ class ShowExtensionSlowAction extends Action { // show issues const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues?utf8=✓&q=is%3Aissue+state%3Aopen+%22Extension+causes+high+cpu+load%22`; - window.open(url); + this._openerService.open(URI.parse(url)); this._dialogService.show( Severity.Info, diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 0d0ff4715c..f3e4e5d74b 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -44,6 +44,8 @@ import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/pl import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; export const IExtensionHostProfileService = createDecorator('extensionHostProfileService'); export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey('profileSessionState', 'none'); @@ -120,7 +122,8 @@ export class RuntimeExtensionsEditor extends BaseEditor { @IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService, @IStorageService storageService: IStorageService, @ILabelService private readonly _labelService: ILabelService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + @IOpenerService private readonly _openerService: IOpenerService ) { super(RuntimeExtensionsEditor.ID, telemetryService, themeService, storageService); @@ -312,7 +315,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { data.actionbar.push(this._instantiationService.createInstance(SlowExtensionAction, element.description, element.unresponsiveProfile), { icon: true, label: true }); } if (isNonEmptyArray(element.status.runtimeErrors)) { - data.actionbar.push(new ReportExtensionIssueAction(element), { icon: true, label: true }); + data.actionbar.push(new ReportExtensionIssueAction(element, this._openerService), { icon: true, label: true }); } let title: string; @@ -416,7 +419,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { const actions: IAction[] = []; - actions.push(new ReportExtensionIssueAction(e.element)); + actions.push(new ReportExtensionIssueAction(e.element, this._openerService)); actions.push(new Separator()); if (e.element.marketplaceInfo) { @@ -480,7 +483,7 @@ export class ReportExtensionIssueAction extends Action { marketplaceInfo: IExtension; status?: IExtensionsStatus; unresponsiveProfile?: IExtensionHostProfile - }) { + }, @IOpenerService private readonly openerService: IOpenerService) { super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue'); this.enabled = extension.marketplaceInfo && extension.marketplaceInfo.type === ExtensionType.User @@ -490,7 +493,7 @@ export class ReportExtensionIssueAction extends Action { } async run(): Promise { - window.open(this._url); + this.openerService.open(URI.parse(this._url)); } private static _generateNewIssueUrl(extension: { diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index 76ad9b4801..cf570b3efd 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -20,6 +20,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IProductService } from 'vs/platform/product/common/product'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export interface IFeedback { feedback: string; @@ -27,7 +28,7 @@ export interface IFeedback { } export interface IFeedbackDelegate { - submitFeedback(feedback: IFeedback): void; + submitFeedback(feedback: IFeedback, openerService: IOpenerService): void; getCharacterLimit(sentiment: number): number; } @@ -66,7 +67,8 @@ export class FeedbackDropdown extends Dropdown { @IIntegrityService private readonly integrityService: IIntegrityService, @IThemeService private readonly themeService: IThemeService, @IStatusbarService private readonly statusbarService: IStatusbarService, - @IProductService productService: IProductService + @IProductService productService: IProductService, + @IOpenerService private readonly openerService: IOpenerService ) { super(container, options); @@ -415,7 +417,7 @@ export class FeedbackDropdown extends Dropdown { this.feedbackDelegate.submitFeedback({ feedback: this.feedbackDescriptionInput.value, sentiment: this.sentiment - }); + }, this.openerService); this.hide(); } diff --git a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts index e725cab005..9896eeccc5 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts @@ -12,6 +12,8 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStatusbarService, StatusbarAlignment, IStatusbarEntry, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar'; import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; class TwitterFeedbackService implements IFeedbackDelegate { @@ -23,11 +25,11 @@ class TwitterFeedbackService implements IFeedbackDelegate { return TwitterFeedbackService.HASHTAGS.join(','); } - submitFeedback(feedback: IFeedback): void { + submitFeedback(feedback: IFeedback, openerService: IOpenerService): void { const queryString = `?${feedback.sentiment === 1 ? `hashtags=${this.combineHashTagsAsString()}&` : null}ref_src=twsrc%5Etfw&related=twitterapi%2Ctwitter&text=${encodeURIComponent(feedback.feedback)}&tw_p=tweetbutton&via=${TwitterFeedbackService.VIA_NAME}`; const url = TwitterFeedbackService.TWITTER_URL + queryString; - window.open(url); + openerService.open(URI.parse(url)); } getCharacterLimit(sentiment: number): number { diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index 10a258a03a..0e7eaa23ce 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -38,7 +38,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { BinaryFileEditor.ID, { openInternal: (input, options) => this.openInternal(input, options), - openExternal: resource => this.openerService.openExternal(resource) + openExternal: resource => this.openerService.open(resource, { openExternal: true }) }, telemetryService, themeService, diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index 104c35d8af..a06eb7222d 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -59,7 +59,6 @@ export class EmptyView extends ViewletPanel { container.appendChild(titleContainer); this.titleElement = document.createElement('span'); - this.titleElement.textContent = name; titleContainer.appendChild(this.titleElement); } diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index f2a5356b6d..0fc86ca123 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -203,17 +203,20 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { switch (verbosity) { case Verbosity.SHORT: title = this.shortTitle; + // already decorated by getName() break; default: case Verbosity.MEDIUM: title = this.mediumTitle; + title = this.decorateLabel(title); break; case Verbosity.LONG: title = this.longTitle; + title = this.decorateLabel(title); break; } - return this.decorateLabel(title); + return title; } private decorateLabel(label: string): string { diff --git a/src/vs/workbench/contrib/outline/browser/outlinePanel.ts b/src/vs/workbench/contrib/outline/browser/outlinePanel.ts index 0260b7d2b4..a6a3532a1d 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePanel.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePanel.ts @@ -122,7 +122,8 @@ class RequestOracle { let handle: any; let contentListener = codeEditor.onDidChangeModelContent(event => { clearTimeout(handle); - handle = setTimeout(() => this._callback(codeEditor!, event), 350); + const timeout = OutlineModel.getRequestDelay(codeEditor!.getModel()); + handle = setTimeout(() => this._callback(codeEditor!, event), timeout); }); let modeListener = codeEditor.onDidChangeModelLanguage(_ => { this._callback(codeEditor!, undefined); diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts index 5852c7b4fd..f532511aef 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts @@ -17,6 +17,7 @@ import { PerfviewInput } from 'vs/workbench/contrib/performance/electron-browser import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { URI } from 'vs/base/common/uri'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export class StartupProfiler implements IWorkbenchContribution { @@ -28,6 +29,7 @@ export class StartupProfiler implements IWorkbenchContribution { @IClipboardService private readonly _clipboardService: IClipboardService, @ILifecycleService lifecycleService: ILifecycleService, @IExtensionService extensionService: IExtensionService, + @IOpenerService private readonly _openerService: IOpenerService ) { // wait for everything to be ready Promise.all([ @@ -116,6 +118,6 @@ export class StartupProfiler implements IWorkbenchContribution { const baseUrl = product.reportIssueUrl; const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&'; - window.open(`${baseUrl}${queryStringPrefix}body=${encodeURIComponent(body)}`); + this._openerService.open(URI.parse(`${baseUrl}${queryStringPrefix}body=${encodeURIComponent(body)}`)); } } diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index 0e8f644168..4004d0ea1b 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -789,7 +789,7 @@ class KeybindingItemRenderer implements IListRenderer; defineWhenExpression(keybindingEntry: IKeybindingItemEntry): void; diff --git a/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts index 898ed401c3..bb42c2073e 100644 --- a/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts @@ -99,18 +99,17 @@ class GotoLineEntry extends EditorQuickOpenEntry { if (this.editorService.activeTextEditorWidget && this.invalidRange(maxLineNumber)) { const position = this.editorService.activeTextEditorWidget.getPosition(); if (position) { - const currentLine = position.lineNumber; if (maxLineNumber > 0) { - return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}. Type a line number between 1 and {1} to navigate to.", currentLine, maxLineNumber); + return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}, Column: {1}. Type a line number between 1 and {2} to navigate to.", position.lineNumber, position.column, maxLineNumber); } - return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); + return nls.localize('gotoLineLabelEmpty', "Current Line: {0}, Column: {1}. Type a line number to navigate to.", position.lineNumber, position.column); } } // Input valid, indicate action - return this.column ? nls.localize('gotoLineColumnLabel', "Go to line {0} and character {1}.", this.line, this.column) : nls.localize('gotoLineLabel', "Go to line {0}.", this.line); + return this.column ? nls.localize('gotoLineColumnLabel', "Go to line {0} and column {1}.", this.line, this.column) : nls.localize('gotoLineLabel', "Go to line {0}.", this.line); } private invalidRange(maxLineNumber: number = this.getMaxLineNumber()): boolean { @@ -229,8 +228,7 @@ export class GotoLineHandler extends QuickOpenHandler { if (this.editorService.activeTextEditorWidget) { const position = this.editorService.activeTextEditorWidget.getPosition(); if (position) { - const currentLine = position.lineNumber; - return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); + return nls.localize('gotoLineLabelEmpty', "Current Line: {0}, Column: {1}. Type a line number to navigate to.", position.lineNumber, position.column); } } diff --git a/src/vs/workbench/contrib/search/browser/media/searchview.css b/src/vs/workbench/contrib/search/browser/media/searchview.css index 7c6a95e413..a7e2f0d36c 100644 --- a/src/vs/workbench/contrib/search/browser/media/searchview.css +++ b/src/vs/workbench/contrib/search/browser/media/searchview.css @@ -60,6 +60,20 @@ display: inline-flex; } +.search-view .search-widget .replace-input { + position: relative; + display: flex; + display: -webkit-flex; + vertical-align: middle; + width: auto !important; +} + +.search-view .search-widget .replace-input > .controls { + position: absolute; + top: 3px; + right: 2px; +} + .search-view .search-widget .replace-container.disabled { display: none; } diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 61ad9222fb..9a0b1a25fa 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -7,14 +7,11 @@ import * as DOM from 'vs/base/browser/dom'; import { Action } from 'vs/base/common/actions'; import { INavigator } from 'vs/base/common/iterator'; import { createKeybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { normalizeDriveLetter } from 'vs/base/common/labels'; -import { Schemas } from 'vs/base/common/network'; -import { normalize } from 'vs/base/common/path'; import { isWindows, OS } from 'vs/base/common/platform'; import { repeat } from 'vs/base/common/strings'; -import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ILabelService } from 'vs/platform/label/common/label'; import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -667,14 +664,11 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction { } } -function uriToClipboardString(resource: URI): string { - return resource.scheme === Schemas.file ? normalize(normalizeDriveLetter(resource.fsPath)) : resource.toString(); -} - export const copyPathCommand: ICommandHandler = async (accessor, fileMatch: FileMatch | FolderMatch) => { const clipboardService = accessor.get(IClipboardService); + const labelService = accessor.get(ILabelService); - const text = uriToClipboardString(fileMatch.resource); + const text = labelService.getUriLabel(fileMatch.resource, { noPrefix: true }); await clipboardService.writeText(text); }; @@ -706,25 +700,26 @@ function matchToString(match: Match, indent = 0): string { } const lineDelimiter = isWindows ? '\r\n' : '\n'; -function fileMatchToString(fileMatch: FileMatch, maxMatches: number): { text: string, count: number } { +function fileMatchToString(fileMatch: FileMatch, maxMatches: number, labelService: ILabelService): { text: string, count: number } { const matchTextRows = fileMatch.matches() .sort(searchMatchComparer) .slice(0, maxMatches) .map(match => matchToString(match, 2)); + const uriString = labelService.getUriLabel(fileMatch.resource, { noPrefix: true }); return { - text: `${uriToClipboardString(fileMatch.resource)}${lineDelimiter}${matchTextRows.join(lineDelimiter)}`, + text: `${uriString}${lineDelimiter}${matchTextRows.join(lineDelimiter)}`, count: matchTextRows.length }; } -function folderMatchToString(folderMatch: FolderMatch | BaseFolderMatch, maxMatches: number): { text: string, count: number } { +function folderMatchToString(folderMatch: FolderMatch | BaseFolderMatch, maxMatches: number, labelService: ILabelService): { text: string, count: number } { const fileResults: string[] = []; let numMatches = 0; const matches = folderMatch.matches().sort(searchMatchComparer); for (let i = 0; i < folderMatch.fileCount() && numMatches < maxMatches; i++) { - const fileResult = fileMatchToString(matches[i], maxMatches - numMatches); + const fileResult = fileMatchToString(matches[i], maxMatches - numMatches, labelService); numMatches += fileResult.count; fileResults.push(fileResult.text); } @@ -738,14 +733,15 @@ function folderMatchToString(folderMatch: FolderMatch | BaseFolderMatch, maxMatc const maxClipboardMatches = 1e4; export const copyMatchCommand: ICommandHandler = async (accessor, match: RenderableMatch) => { const clipboardService = accessor.get(IClipboardService); + const labelService = accessor.get(ILabelService); let text: string | undefined; if (match instanceof Match) { text = matchToString(match); } else if (match instanceof FileMatch) { - text = fileMatchToString(match, maxClipboardMatches).text; + text = fileMatchToString(match, maxClipboardMatches, labelService).text; } else if (match instanceof BaseFolderMatch) { - text = folderMatchToString(match, maxClipboardMatches).text; + text = folderMatchToString(match, maxClipboardMatches, labelService).text; } if (text) { @@ -753,12 +749,12 @@ export const copyMatchCommand: ICommandHandler = async (accessor, match: Rendera } }; -function allFolderMatchesToString(folderMatches: Array, maxMatches: number): string { +function allFolderMatchesToString(folderMatches: Array, maxMatches: number, labelService: ILabelService): string { const folderResults: string[] = []; let numMatches = 0; folderMatches = folderMatches.sort(searchMatchComparer); for (let i = 0; i < folderMatches.length && numMatches < maxMatches; i++) { - const folderResult = folderMatchToString(folderMatches[i], maxMatches - numMatches); + const folderResult = folderMatchToString(folderMatches[i], maxMatches - numMatches, labelService); if (folderResult.count) { numMatches += folderResult.count; folderResults.push(folderResult.text); @@ -772,12 +768,13 @@ export const copyAllCommand: ICommandHandler = async (accessor) => { const viewletService = accessor.get(IViewletService); const panelService = accessor.get(IPanelService); const clipboardService = accessor.get(IClipboardService); + const labelService = accessor.get(ILabelService); const searchView = getSearchView(viewletService, panelService); if (searchView) { const root = searchView.searchResult; - const text = allFolderMatchesToString(root.folderMatches(), maxClipboardMatches); + const text = allFolderMatchesToString(root.folderMatches(), maxClipboardMatches, labelService); await clipboardService.writeText(text); } }; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 38c5d1e192..4fbf221d2c 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -61,6 +61,7 @@ import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/v import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; const $ = dom.$; @@ -153,6 +154,7 @@ export class SearchView extends ViewletPanel { @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IKeybindingService keybindingService: IKeybindingService, @IStorageService storageService: IStorageService, + @IOpenerService private readonly openerService: IOpenerService ) { super({ ...(options as IViewletPanelOptions), id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService); @@ -364,6 +366,7 @@ export class SearchView extends ViewletPanel { const searchHistory = history.search || this.viewletState['query.searchHistory'] || []; const replaceHistory = history.replace || this.viewletState['query.replaceHistory'] || []; const showReplace = typeof this.viewletState['view.showReplace'] === 'boolean' ? this.viewletState['view.showReplace'] : true; + const preserveCase = this.viewletState['query.preserveCase'] === true; this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, container, { value: contentPattern, @@ -372,7 +375,8 @@ export class SearchView extends ViewletPanel { isCaseSensitive: isCaseSensitive, isWholeWords: isWholeWords, searchHistory: searchHistory, - replaceHistory: replaceHistory + replaceHistory: replaceHistory, + preserveCase: preserveCase })); if (showReplace) { @@ -390,6 +394,12 @@ export class SearchView extends ViewletPanel { this.viewModel.replaceActive = state; this.refreshTree(); })); + + this._register(this.searchWidget.onPreserveCaseChange((state) => { + this.viewModel.preserveCase = state; + this.refreshTree(); + })); + this._register(this.searchWidget.onReplaceValueChanged((value) => { this.viewModel.replaceString = this.searchWidget.getReplaceValue(); this.delayedRefresh.trigger(() => this.refreshTree()); @@ -1465,7 +1475,7 @@ export class SearchView extends ViewletPanel { private onLearnMore = (e: MouseEvent): void => { dom.EventHelper.stop(e, false); - window.open('https://go.microsoft.com/fwlink/?linkid=853977'); + this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=853977')); } private updateSearchResultCount(disregardExcludesAndIgnores?: boolean): void { @@ -1641,6 +1651,7 @@ export class SearchView extends ViewletPanel { const patternExcludes = this.inputPatternExcludes.getValue().trim(); const patternIncludes = this.inputPatternIncludes.getValue().trim(); const useExcludesAndIgnoreFiles = this.inputPatternExcludes.useExcludesAndIgnoreFiles(); + const preserveCase = this.viewModel.preserveCase; this.viewletState['query.contentPattern'] = contentPattern; this.viewletState['query.regex'] = isRegex; @@ -1649,6 +1660,7 @@ export class SearchView extends ViewletPanel { this.viewletState['query.folderExclusions'] = patternExcludes; this.viewletState['query.folderIncludes'] = patternIncludes; this.viewletState['query.useExcludesAndIgnoreFiles'] = useExcludesAndIgnoreFiles; + this.viewletState['query.preserveCase'] = preserveCase; const isReplaceShown = this.searchAndReplaceWidget.isReplaceShown(); this.viewletState['view.showReplace'] = isReplaceShown; diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 42173f92b3..55a35a9896 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -33,6 +33,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; export interface ISearchWidgetOptions { value?: string; @@ -42,6 +43,7 @@ export interface ISearchWidgetOptions { isWholeWords?: boolean; searchHistory?: string[]; replaceHistory?: string[]; + preserveCase?: boolean; } class ReplaceAllAction extends Action { @@ -97,6 +99,7 @@ export class SearchWidget extends Widget { replaceInputFocusTracker: dom.IFocusTracker; private replaceInputBoxFocused: IContextKey; private _replaceHistoryDelayer: Delayer; + private _preserveCase: Checkbox; private ignoreGlobalFindBufferOnNextFocus = false; private previousGlobalFindBufferValue: string; @@ -113,6 +116,9 @@ export class SearchWidget extends Widget { private _onReplaceStateChange = this._register(new Emitter()); readonly onReplaceStateChange: Event = this._onReplaceStateChange.event; + private _onPreserveCaseChange = this._register(new Emitter()); + readonly onPreserveCaseChange: Event = this._onPreserveCaseChange.event; + private _onReplaceValueChanged = this._register(new Emitter()); readonly onReplaceValueChanged: Event = this._onReplaceValueChanged.event; @@ -333,13 +339,34 @@ export class SearchWidget extends Widget { private renderReplaceInput(parent: HTMLElement, options: ISearchWidgetOptions): void { this.replaceContainer = dom.append(parent, dom.$('.replace-container.disabled')); - const replaceBox = dom.append(this.replaceContainer, dom.$('.input-box')); + const replaceBox = dom.append(this.replaceContainer, dom.$('.replace-input')); + this.replaceInput = this._register(new ContextScopedHistoryInputBox(replaceBox, this.contextViewService, { ariaLabel: nls.localize('label.Replace', 'Replace: Type replace term and press Enter to preview or Escape to cancel'), placeholder: nls.localize('search.replace.placeHolder', "Replace"), history: options.replaceHistory || [], flexibleHeight: true }, this.contextKeyService)); + + this._preserveCase = this._register(new Checkbox({ + actionClassName: 'monaco-preserve-case', + title: nls.localize('label.preserveCaseCheckbox', "Preserve Case"), + isChecked: !!options.preserveCase, + })); + + this._register(this._preserveCase.onChange(viaKeyboard => { + if (!viaKeyboard) { + this.replaceInput.focus(); + this._onPreserveCaseChange.fire(this._preserveCase.checked); + } + })); + + let controls = document.createElement('div'); + controls.className = 'controls'; + controls.style.display = 'block'; + controls.appendChild(this._preserveCase.domNode); + replaceBox.appendChild(controls); + this._register(attachInputBoxStyler(this.replaceInput, this.themeService)); this.onkeydown(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent)); this.replaceInput.value = options.replaceValue || ''; diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index a5b02af85f..b86038edbe 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -103,17 +103,17 @@ export class Match { } const fullMatchText = this.fullMatchText(); - let replaceString = searchModel.replacePattern.getReplaceString(fullMatchText); + let replaceString = searchModel.replacePattern.getReplaceString(fullMatchText, searchModel.preserveCase); // If match string is not matching then regex pattern has a lookahead expression if (replaceString === null) { const fullMatchTextWithTrailingContent = this.fullMatchText(true); - replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithTrailingContent); + replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithTrailingContent, searchModel.preserveCase); // Search/find normalize line endings - check whether \r prevents regex from matching if (replaceString === null) { const fullMatchTextWithoutCR = fullMatchTextWithTrailingContent.replace(/\r\n/g, '\n'); - replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithoutCR); + replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithoutCR, searchModel.preserveCase); } } @@ -895,6 +895,7 @@ export class SearchModel extends Disposable { private _replaceActive: boolean = false; private _replaceString: string | null = null; private _replacePattern: ReplacePattern | null = null; + private _preserveCase: boolean = false; private readonly _onReplaceTermChanged: Emitter = this._register(new Emitter()); readonly onReplaceTermChanged: Event = this._onReplaceTermChanged.event; @@ -926,6 +927,14 @@ export class SearchModel extends Disposable { return this._replaceString || ''; } + set preserveCase(value: boolean) { + this._preserveCase = value; + } + + get preserveCase(): boolean { + return this._preserveCase; + } + set replaceString(replaceString: string) { this._replaceString = replaceString; if (this._searchQuery) { diff --git a/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts index ca1d8d32d7..4deb6a0173 100644 --- a/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts @@ -16,6 +16,8 @@ import { ISurveyData } from 'vs/platform/product/common/product'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; class LanguageSurvey { @@ -25,7 +27,8 @@ class LanguageSurvey { notificationService: INotificationService, telemetryService: ITelemetryService, modelService: IModelService, - textFileService: ITextFileService + textFileService: ITextFileService, + openerService: IOpenerService ) { const SESSION_COUNT_KEY = `${data.surveyId}.sessionCount`; const LAST_SESSION_DATE_KEY = `${data.surveyId}.lastSessionDate`; @@ -94,7 +97,7 @@ class LanguageSurvey { run: () => { telemetryService.publicLog(`${data.surveyId}.survey/takeShortSurvey`); telemetryService.getTelemetryInfo().then(info => { - window.open(`${data.surveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`); + openerService.open(URI.parse(`${data.surveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`)); storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); }); @@ -126,11 +129,12 @@ class LanguageSurveysContribution implements IWorkbenchContribution { @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IModelService modelService: IModelService, - @ITextFileService textFileService: ITextFileService + @ITextFileService textFileService: ITextFileService, + @IOpenerService openerService: IOpenerService ) { product.surveys .filter(surveyData => surveyData.surveyId && surveyData.editCount && surveyData.languageId && surveyData.surveyUrl && surveyData.userProbability) - .map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService)); + .map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService, openerService)); } } diff --git a/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts index e97d7b087f..da0aeb9732 100644 --- a/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts @@ -13,6 +13,8 @@ import pkg from 'vs/platform/product/node/package'; import product from 'vs/platform/product/node/product'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; const PROBABILITY = 0.15; const SESSION_COUNT_KEY = 'nps/sessionCount'; @@ -25,7 +27,8 @@ class NPSContribution implements IWorkbenchContribution { constructor( @IStorageService storageService: IStorageService, @INotificationService notificationService: INotificationService, - @ITelemetryService telemetryService: ITelemetryService + @ITelemetryService telemetryService: ITelemetryService, + @IOpenerService openerService: IOpenerService ) { const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, ''); if (skipVersion) { @@ -64,7 +67,7 @@ class NPSContribution implements IWorkbenchContribution { label: nls.localize('takeSurvey', "Take Survey"), run: () => { telemetryService.getTelemetryInfo().then(info => { - window.open(`${product.npsSurveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`); + openerService.open(URI.parse(`${product.npsSurveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`)); storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); }); @@ -87,4 +90,4 @@ class NPSContribution implements IWorkbenchContribution { if (language === 'en' && product.npsSurveyUrl) { const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(NPSContribution, LifecyclePhase.Restored); -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index b3f3fce8b0..99e22327d8 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -1620,7 +1620,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer nls.localize('TaskService.noWorkspace', "Tasks are only available on a workspace folder."), [{ label: nls.localize('TaskService.learnMore', "Learn More"), - run: () => window.open('https://code.visualstudio.com/docs/editor/tasks') + run: () => this.openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks')) }] ); return false; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index d14a2dd864..bc7b49f930 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -298,6 +298,11 @@ configurationRegistry.registerConfiguration({ description: nls.localize('terminal.integrated.experimentalRefreshOnResume', "An experimental setting that will refresh the terminal renderer when the system is resumed."), type: 'boolean', default: false + }, + 'terminal.integrated.experimentalUseTitleEvent': { + description: nls.localize('terminal.integrated.experimentalUseTitleEvent', "An experimental setting that will use the terminal title event for the dropdown title. This setting will only apply to new terminals."), + type: 'boolean', + default: false } } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 6f2c3348ac..0b725ab686 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -1034,7 +1034,7 @@ export class RenameTerminalAction extends Action { prompt: nls.localize('workbench.action.terminal.rename.prompt', "Enter terminal name"), }).then(name => { if (name) { - terminalInstance.setTitle(name, false); + terminalInstance.setTitle(name, TitleEventSource.Api); } }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 8d766d77f7..43be816bce 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -25,7 +25,7 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager'; -import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; @@ -973,11 +973,22 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._processManager.onProcessResolvedShellLaunchConfig(e => this._setResolvedShellLaunchConfig(e)); if (this._shellLaunchConfig.name) { - this.setTitle(this._shellLaunchConfig.name, false); + this.setTitle(this._shellLaunchConfig.name, TitleEventSource.Api); } else { // Only listen for process title changes when a name is not provided - this.setTitle(this._shellLaunchConfig.executable, true); - this._messageTitleDisposable = this._processManager.onProcessTitle(title => this.setTitle(title ? title : '', true)); + if (this._configHelper.config.experimentalUseTitleEvent) { + this._processManager.ptyProcessReady.then(() => { + this._terminalInstanceService.getDefaultShellAndArgs(false).then(e => { + this.setTitle(e.shell, TitleEventSource.Sequence); + }); + this._xtermReadyPromise.then(xterm => { + this._messageTitleDisposable = xterm.onTitleChange(e => this._onTitleChange(e)); + }); + }); + } else { + this.setTitle(this._shellLaunchConfig.executable, TitleEventSource.Process); + this._messageTitleDisposable = this._processManager.onProcessTitle(title => this.setTitle(title ? title : '', TitleEventSource.Process)); + } } if (platform.isWindows) { @@ -1149,7 +1160,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._createProcess(); if (oldTitle !== this._title) { - this.setTitle(this._title, true); + this.setTitle(this._title, TitleEventSource.Process); } this._processManager.onProcessData(data => this._onProcessData(data)); @@ -1168,6 +1179,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._sendLineData(buffer, buffer.baseY + buffer.cursorY); } + private _onTitleChange(title: string): void { + if (this.isTitleSetByProcess) { + this.setTitle(title, TitleEventSource.Sequence); + } + } + private _sendLineData(buffer: IBuffer, lineIndex: number): void { let line = buffer.getLine(lineIndex); if (!line) { @@ -1348,23 +1365,26 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._processManager.ptyProcessReady.then(() => this._processManager.setDimensions(cols, rows)); } - public setTitle(title: string | undefined, eventFromProcess: boolean): void { + public setTitle(title: string | undefined, eventSource: TitleEventSource): void { if (!title) { return; } - if (eventFromProcess) { - title = path.basename(title); - if (platform.isWindows) { - // Remove the .exe extension - title = title.split('.exe')[0]; - } - } else { - // If the title has not been set by the API or the rename command, unregister the handler that - // automatically updates the terminal name - dispose(this._messageTitleDisposable); - this._messageTitleDisposable = undefined; - dispose(this._windowsShellHelper); - this._windowsShellHelper = undefined; + switch (eventSource) { + case TitleEventSource.Process: + title = path.basename(title); + if (platform.isWindows) { + // Remove the .exe extension + title = title.split('.exe')[0]; + } + break; + case TitleEventSource.Api: + // If the title has not been set by the API or the rename command, unregister the handler that + // automatically updates the terminal name + dispose(this._messageTitleDisposable); + this._messageTitleDisposable = undefined; + dispose(this._windowsShellHelper); + this._windowsShellHelper = undefined; + break; } const didTitleChange = title !== this._title; this._title = title; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 5a693e6ac8..215bf89bed 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -276,7 +276,7 @@ export class TerminalPanel extends Panel { const resources = e.dataTransfer.getData(DataTransfers.RESOURCES); if (resources) { path = URI.parse(JSON.parse(resources)[0]).fsPath; - } else if (e.dataTransfer.files.length > 0) { + } else if (e.dataTransfer.files.length > 0 && e.dataTransfer.files[0].path /* Electron only */) { // Check if the file was dragged from the filesystem path = URI.file(e.dataTransfer.files[0].path).fsPath; } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 6418393afd..a1a738b1b9 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -117,6 +117,7 @@ export interface ITerminalConfiguration { splitCwd: 'workspaceRoot' | 'initial' | 'inherited'; windowsEnableConpty: boolean; experimentalRefreshOnResume: boolean; + experimentalUseTitleEvent: boolean; } export interface ITerminalConfigHelper { @@ -636,7 +637,7 @@ export interface ITerminalInstance { /** * Sets the title of the terminal instance. */ - setTitle(title: string, eventFromProcess: boolean): void; + setTitle(title: string, eventSource: TitleEventSource): void; waitForTitle(): Promise; @@ -769,6 +770,15 @@ export enum LinuxDistro { Unknown } +export enum TitleEventSource { + /** From the API or the rename command that overrides any other type */ + Api, + /** From the process name property*/ + Process, + /** From the VT sequence */ + Sequence +} + export interface IWindowsShellHelper extends IDisposable { getShellName(): Promise; } diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 3ed0c6bc9a..e926c53ae5 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -62,7 +62,7 @@ export function addTerminalEnvironmentKeys(env: platform.IProcessEnvironment, ve env['COLORTERM'] = 'truecolor'; } -function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnvironment | NodeJS.ProcessEnv | undefined) { +function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnvironment | undefined) { if (!other) { return; } diff --git a/src/vs/workbench/contrib/terminal/node/terminalRemote.ts b/src/vs/workbench/contrib/terminal/node/terminalRemote.ts index f2a9bcc01c..07f2ff1f56 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalRemote.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalRemote.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { TERMINAL_ACTION_CATEGORY, ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_ACTION_CATEGORY, ITerminalService, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { Action } from 'vs/base/common/actions'; import { URI } from 'vs/base/common/uri'; @@ -39,7 +39,7 @@ export class CreateNewLocalTerminalAction extends Action { const disposable = instance.onTitleChanged(() => { if (instance.title && instance.title.trim().length > 0) { disposable.dispose(); - instance.setTitle(`${instance.title} (Local)`, false); + instance.setTitle(`${instance.title} (Local)`, TitleEventSource.Api); } }); diff --git a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts index badb8a1756..7f207bed2c 100644 --- a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts +++ b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts @@ -5,9 +5,9 @@ import * as platform from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; -import { ITerminalInstance, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalInstance, IWindowsShellHelper, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { Terminal as XTermTerminal } from 'xterm'; -import WindowsProcessTreeType = require('windows-process-tree'); +import * as WindowsProcessTreeType from 'windows-process-tree'; import { Disposable } from 'vs/base/common/lifecycle'; const SHELL_EXECUTABLES = [ @@ -80,7 +80,7 @@ export class WindowsShellHelper extends Disposable implements IWindowsShellHelpe if (platform.isWindows && this._terminalInstance.isTitleSetByProcess) { this.getShellName().then(title => { if (!this._isDisposed) { - this._terminalInstance.setTitle(title, true); + this._terminalInstance.setTitle(title, TitleEventSource.Process); } }); } diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 1404e17734..62e6746c55 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -4,13 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { IURLService } from 'vs/platform/url/common/url'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export class OpenUrlAction extends Action { @@ -34,5 +37,97 @@ export class OpenUrlAction extends Action { } } -Registry.as(ActionExtensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), 'Open URL', localize('developer', "Developer")); \ No newline at end of file +Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( + new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), + 'Open URL', + localize('developer', 'Developer') +); + +const VSCODE_DOMAIN = 'https://code.visualstudio.com'; + +const configureTrustedDomainsHandler = ( + quickInputService: IQuickInputService, + storageService: IStorageService, + domainToConfigure?: string +) => { + let trustedDomains: string[] = [VSCODE_DOMAIN]; + + try { + const trustedDomainsSrc = storageService.get('http.trustedDomains', StorageScope.GLOBAL); + if (trustedDomainsSrc) { + trustedDomains = JSON.parse(trustedDomainsSrc); + } + } catch (err) { } + + const domainQuickPickItems: IQuickPickItem[] = trustedDomains + .filter(d => d !== '*') + .map(d => { + return { + type: 'item', + label: d, + id: d, + picked: true, + }; + }); + + const specialQuickPickItems: IQuickPickItem[] = [ + { + type: 'item', + label: localize('openAllLinksWithoutPrompt', 'Open all links without prompt'), + id: '*', + picked: trustedDomains.indexOf('*') !== -1 + } + ]; + + let domainToConfigureItem: IQuickPickItem | undefined = undefined; + if (domainToConfigure && trustedDomains.indexOf(domainToConfigure) === -1) { + domainToConfigureItem = { + type: 'item', + label: domainToConfigure, + id: domainToConfigure, + picked: true, + description: localize('trustDomainAndOpenLink', 'Trust domain and open link') + }; + specialQuickPickItems.push(domainToConfigureItem); + } + + const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] = domainQuickPickItems.length === 0 + ? specialQuickPickItems + : [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems]; + + return quickInputService.pick(quickPickItems, { + canPickMany: true, + activeItem: domainToConfigureItem + }).then(result => { + if (result) { + const pickedDomains = result.map(r => r.id); + storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL); + + return pickedDomains; + } + + return []; + }); +}; + +const configureTrustedDomainCommand = { + id: 'workbench.action.configureTrustedDomains', + description: { + description: localize('configureTrustedDomains', 'Configure Trusted Domains'), + args: [{ name: 'domainToConfigure', schema: { type: 'string' } }] + }, + handler: (accessor: ServicesAccessor, domainToConfigure?: string) => { + const quickInputService = accessor.get(IQuickInputService); + const storageService = accessor.get(IStorageService); + + return configureTrustedDomainsHandler(quickInputService, storageService, domainToConfigure); + } +}; + +CommandsRegistry.registerCommand(configureTrustedDomainCommand); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: configureTrustedDomainCommand.id, + title: configureTrustedDomainCommand.description.description + } +}); diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 5b9498719a..73a50a2a9d 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -6,7 +6,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { IWebviewService, Webview, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, Webview, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { memoize } from 'vs/base/common/decorators'; diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 0b62195f03..2dc6eae30f 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -289,6 +289,11 @@ const csp = newDocument.querySelector('meta[http-equiv="Content-Security-Policy"]'); if (!csp) { host.postMessage('no-csp-found'); + } else { + // Rewrite vscode-resource in csp + if (data.endpoint) { + csp.setAttribute('content', csp.getAttribute('content').replace(/vscode-resource:/g, data.endpoint)); + } } // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index bb193a440d..05448c4076 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -15,7 +15,7 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; -import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/common/webview'; +import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview'; import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand } from '../browser/webviewCommands'; import { WebviewEditor } from '../browser/webviewEditor'; import { WebviewEditorInput } from '../browser/webviewEditorInput'; diff --git a/src/vs/workbench/contrib/webview/common/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts similarity index 100% rename from src/vs/workbench/contrib/webview/common/webview.ts rename to src/vs/workbench/contrib/webview/browser/webview.ts diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index cb97cb4537..2a3c0e2327 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -15,7 +15,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions } from 'vs/workbench/common/editor'; import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; -import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/common/webview'; +import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index 306aab5e10..3d12b9ed27 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { EditorInput, EditorModel, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor'; -import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/common/webview'; +import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { UnownedDisposable as Unowned } from 'vs/base/common/lifecycle'; class WebviewIconsManager { diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts index aeef55b1f6..0d2032646b 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { GroupIdentifier } from 'vs/workbench/common/editor'; -import { IWebviewService, WebviewOptions, WebviewContentOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, WebviewOptions, WebviewContentOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { RevivedWebviewEditorInput, WebviewEditorInput } from './webviewEditorInput'; diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 0378d48b03..c6e0cdc7ab 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -5,7 +5,7 @@ import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; @@ -202,7 +202,8 @@ export class IFrameWebview extends Disposable implements Webview { this._send('content', { contents: this.content.html, options: this.content.options, - state: this.content.state + state: this.content.state, + endpoint: this.endpoint, }); } diff --git a/src/vs/workbench/contrib/webview/browser/webviewService.ts b/src/vs/workbench/contrib/webview/browser/webviewService.ts index eec65329d0..911c3d5424 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewService.ts @@ -5,7 +5,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; -import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { DynamicWebviewEditorOverlay } from './dynamicWebviewEditorOverlay'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts index 19a80db001..f726efbd00 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts @@ -13,7 +13,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; -import { IWebviewService, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview'; import * as webviewCommands from 'vs/workbench/contrib/webview/electron-browser/webviewCommands'; import { ElectronWebviewService } from 'vs/workbench/contrib/webview/electron-browser/webviewService'; @@ -89,4 +89,4 @@ function registerWebViewCommands(editorId: string): void { } } -registerWebViewCommands(WebviewEditor.ID); \ No newline at end of file +registerWebViewCommands(WebviewEditor.ID); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts index e8c55b2e3a..7cc1ef28bf 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts @@ -9,7 +9,7 @@ import { Command, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; -import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/common/webview'; +import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; export class OpenWebviewDeveloperToolsAction extends Action { static readonly ID = 'workbench.action.webview.openDeveloperTools'; @@ -101,4 +101,4 @@ function withActiveWebviewBasedWebview(accessor: ServicesAccessor, f: (webview: } }); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 58e9189e23..9b99555bc4 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -20,7 +20,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; -import { Webview, WebviewContentOptions, WebviewOptions, WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/webview'; +import { Webview, WebviewContentOptions, WebviewOptions, WebviewResourceScheme } from 'vs/workbench/contrib/webview/browser/webview'; import { registerFileProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService'; import { WebviewFindWidget } from '../browser/webviewFindWidget'; @@ -327,12 +327,19 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { { const rawEvent = event.args[0]; const bounds = this._webview.getBoundingClientRect(); - window.dispatchEvent(new MouseEvent(rawEvent.type, { - ...rawEvent, - clientX: rawEvent.clientX + bounds.left, - clientY: rawEvent.clientY + bounds.top, - })); - return; + try { + window.dispatchEvent(new MouseEvent(rawEvent.type, { + ...rawEvent, + clientX: rawEvent.clientX + bounds.left, + clientY: rawEvent.clientY + bounds.top, + })); + return; + } + catch (TypeError) { + // CustomEvent was treated as MouseEvent so don't do anything - https://github.com/microsoft/vscode/issues/78915 + return; + } + } case 'did-set-content': @@ -489,7 +496,11 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { if (!this._webview) { return; } - this._webview.focus(); + try { + this._webview.focus(); + } catch { + // noop + } this._send('focus'); // Handle focus change programmatically (do not rely on event from ) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts index f22a28a23a..e2e414341f 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts @@ -7,7 +7,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DynamicWebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay'; import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; -import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; export class ElectronWebviewService implements IWebviewService { @@ -38,4 +38,4 @@ export class ElectronWebviewService implements IWebviewService { ): WebviewEditorOverlay { return this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts index bb0e37b2bc..f4f248962f 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts @@ -9,6 +9,8 @@ import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/ import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import * as platform from 'vs/base/common/platform'; import product from 'vs/platform/product/node/product'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; export class GettingStarted implements IWorkbenchContribution { @@ -20,7 +22,8 @@ export class GettingStarted implements IWorkbenchContribution { constructor( @IStorageService private readonly storageService: IStorageService, @IEnvironmentService environmentService: IEnvironmentService, - @ITelemetryService private readonly telemetryService: ITelemetryService + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IOpenerService private readonly openerService: IOpenerService ) { this.appName = product.nameLong; @@ -50,7 +53,7 @@ export class GettingStarted implements IWorkbenchContribution { if (platform.isLinux && platform.isRootUser()) { return; } - window.open(url); + this.openerService.open(URI.parse(url)); } private handleWelcome(): void { diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 090dcbf5ba..f10967ff25 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import * as strings from 'vs/base/common/strings'; import { ICommandService } from 'vs/platform/commands/common/commands'; import * as arrays from 'vs/base/common/arrays'; -import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts index eb0af87319..6aaaf9dfe9 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts @@ -8,7 +8,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { Action } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; -import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; +import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { Schemas } from 'vs/base/common/network'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts index 263bc199c6..2330b83f78 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { WalkThroughPart } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart'; import { WalkThroughArrowUp, WalkThroughArrowDown, WalkThroughPageUp, WalkThroughPageDown } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions'; import { WalkThroughContentProvider, WalkThroughSnippetContentProvider } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider'; @@ -48,13 +48,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughPageUp); KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughPageDown); -// {{SQL CARBON EDIT}} - Disable unused menu item -// MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { -// group: '1_welcome', -// command: { -// id: 'workbench.action.showInteractivePlayground', -// title: localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "I&&nteractive Playground") -// }, -// order: 2 -// }); -// {{SQL CARBON EDIT}} - End \ No newline at end of file +/*MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { {{SQL CARBON EDIT}} remove menu item + group: '1_welcome', + command: { + id: 'workbench.action.showInteractivePlayground', + title: localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "I&&nteractive Playground") + }, + order: 2 +});*/ diff --git a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts similarity index 100% rename from src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts rename to src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index b94381e080..b1acd7d532 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -12,7 +12,7 @@ import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/com import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import * as marked from 'vs/base/common/marked/marked'; import { IModelService } from 'vs/editor/common/services/modelService'; diff --git a/src/vs/workbench/electron-browser/actions/helpActions.ts b/src/vs/workbench/electron-browser/actions/helpActions.ts index 9185f28c8c..a9394d45bc 100644 --- a/src/vs/workbench/electron-browser/actions/helpActions.ts +++ b/src/vs/workbench/electron-browser/actions/helpActions.ts @@ -8,6 +8,8 @@ import * as nls from 'vs/nls'; import product from 'vs/platform/product/node/product'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; export class KeybindingsReferenceAction extends Action { @@ -19,13 +21,14 @@ export class KeybindingsReferenceAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { - window.open(KeybindingsReferenceAction.URL); + this.openerService.open(URI.parse(KeybindingsReferenceAction.URL)); return Promise.resolve(); } @@ -41,13 +44,14 @@ export class OpenDocumentationUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { - window.open(OpenDocumentationUrlAction.URL); + this.openerService.open(URI.parse(OpenDocumentationUrlAction.URL)); return Promise.resolve(); } @@ -63,13 +67,14 @@ export class OpenIntroductoryVideosUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { - window.open(OpenIntroductoryVideosUrlAction.URL); + this.openerService.open(URI.parse(OpenIntroductoryVideosUrlAction.URL)); return Promise.resolve(); } @@ -85,13 +90,14 @@ export class OpenTipsAndTricksUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { - window.open(OpenTipsAndTricksUrlAction.URL); + this.openerService.open(URI.parse(OpenTipsAndTricksUrlAction.URL)); return Promise.resolve(); } } @@ -107,6 +113,7 @@ export class OpenNewsletterSignupUrlAction extends Action { constructor( id: string, label: string, + @IOpenerService private readonly openerService: IOpenerService, @ITelemetryService telemetryService: ITelemetryService ) { super(id, label); @@ -116,7 +123,7 @@ export class OpenNewsletterSignupUrlAction extends Action { async run(): Promise { const info = await this.telemetryService.getTelemetryInfo(); - window.open(`${OpenNewsletterSignupUrlAction.URL}?machineId=${encodeURIComponent(info.machineId)}`); + this.openerService.open(URI.parse(`${OpenNewsletterSignupUrlAction.URL}?machineId=${encodeURIComponent(info.machineId)}`)); } } @@ -127,14 +134,15 @@ export class OpenTwitterUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { if (product.twitterUrl) { - window.open(product.twitterUrl); + this.openerService.open(URI.parse(product.twitterUrl)); } return Promise.resolve(); @@ -148,14 +156,15 @@ export class OpenRequestFeatureUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { if (product.requestFeatureUrl) { - window.open(product.requestFeatureUrl); + this.openerService.open(URI.parse(product.requestFeatureUrl)); } return Promise.resolve(); @@ -169,7 +178,8 @@ export class OpenLicenseUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } @@ -178,9 +188,9 @@ export class OpenLicenseUrlAction extends Action { if (product.licenseUrl) { if (language) { const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; - window.open(`${product.licenseUrl}${queryArgChar}lang=${language}`); + this.openerService.open(URI.parse(`${product.licenseUrl}${queryArgChar}lang=${language}`)); } else { - window.open(product.licenseUrl); + this.openerService.open(URI.parse(product.licenseUrl)); } } @@ -195,7 +205,8 @@ export class OpenPrivacyStatementUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } @@ -204,9 +215,9 @@ export class OpenPrivacyStatementUrlAction extends Action { if (product.privacyStatementUrl) { if (language) { const queryArgChar = product.privacyStatementUrl.indexOf('?') > 0 ? '&' : '?'; - window.open(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`); + this.openerService.open(URI.parse(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`)); } else { - window.open(product.privacyStatementUrl); + this.openerService.open(URI.parse(product.privacyStatementUrl)); } } diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 8cee13b607..9cfbb6d9a4 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -14,7 +14,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledResourceInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IRunKeybindingInWindowRequest } from 'vs/platform/windows/common/windows'; +import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IRunKeybindingInWindowRequest, getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; @@ -24,7 +24,7 @@ import { IResourceInput } from 'vs/platform/editor/common/editor'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; import { ipcRenderer as ipc, webFrame, crashReporter, Event } from 'electron'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; -import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -32,7 +32,7 @@ import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecyc import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; -import { isRootUser, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; +import { isRootUser, isWindows, isMacintosh, isLinux, isWeb } from 'vs/base/common/platform'; import product from 'vs/platform/product/node/product'; import pkg from 'vs/platform/product/node/package'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -45,6 +45,17 @@ import { coalesce } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { isEqual } from 'vs/base/common/resources'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { MenubarControl } from '../browser/parts/titlebar/menubarControl'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IUpdateService } from 'vs/platform/update/common/update'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IPreferencesService } from '../services/preferences/common/preferences'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IMenubarService, IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/node/menubar'; +import { withNullAsUndefined } from 'vs/base/common/types'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { Schemas } from 'vs/base/common/network'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))), @@ -91,7 +102,9 @@ export class ElectronWindow extends Disposable { @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @ITextFileService private readonly textFileService: ITextFileService + @ITextFileService private readonly textFileService: ITextFileService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IOpenerService private readonly openerService: IOpenerService ) { super(); @@ -297,13 +310,13 @@ export class ElectronWindow extends Disposable { private create(): void { - // Handle window.open() calls - const $this = this; - window.open = function (url: string, target: string, features: string, replace: boolean): Window | null { - $this.windowsService.openExternal(url); + // Native menu controller + if (isMacintosh || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + this._register(this.instantiationService.createInstance(NativeMenubarControl)); + } - return null; - }; + // Handle open calls + this.setupOpenHandlers(); // Emit event when vscode is ready this.lifecycleService.when(LifecyclePhase.Ready).then(() => ipc.send('vscode:workbenchReady', this.windowService.windowId)); @@ -341,6 +354,39 @@ export class ElectronWindow extends Disposable { } } + private setupOpenHandlers(): void { + + // Block window.open() calls + const $this = this; + window.open = function (): Window | null { + throw new Error('Prevented call to window.open(). Use IOpenerService instead!'); + }; + + // Handle internal open() calls + this.openerService.registerOpener({ + async open(resource: URI, options?: { openToSide?: boolean; openExternal?: boolean; } | undefined): Promise { + + // If either the caller wants to open externally or the + // scheme is one where we prefer to open externally + // we handle this resource by delegating the opening to + // the main process to prevent window focus issues. + const scheme = resource.scheme.toLowerCase(); + const preferOpenExternal = (scheme === Schemas.mailto || scheme === Schemas.http || scheme === Schemas.https); + if ((options && options.openExternal) || preferOpenExternal) { + const success = await $this.windowsService.openExternal(encodeURI(resource.toString(true))); + if (!success && resource.scheme === Schemas.file) { + // if opening failed, and this is a file, we can still try to reveal it + await $this.windowsService.showItemInFolder(resource); + } + + return true; + } + + return false; // not handled by us + } + }); + } + private updateTouchbarMenu(): void { if (!isMacintosh) { return; // macOS only @@ -538,3 +584,192 @@ export class ElectronWindow extends Disposable { return this.editorService.openEditors(resources); } } + +class NativeMenubarControl extends MenubarControl { + constructor( + @IMenuService menuService: IMenuService, + @IWindowService windowService: IWindowService, + @IWindowsService windowsService: IWindowsService, + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService keybindingService: IKeybindingService, + @IConfigurationService configurationService: IConfigurationService, + @ILabelService labelService: ILabelService, + @IUpdateService updateService: IUpdateService, + @IStorageService storageService: IStorageService, + @INotificationService notificationService: INotificationService, + @IPreferencesService preferencesService: IPreferencesService, + @IEnvironmentService environmentService: IEnvironmentService, + @IAccessibilityService accessibilityService: IAccessibilityService, + @IMenubarService private readonly menubarService: IMenubarService + ) { + super( + menuService, + windowService, + windowsService, + contextKeyService, + keybindingService, + configurationService, + labelService, + updateService, + storageService, + notificationService, + preferencesService, + environmentService, + accessibilityService); + + if (isMacintosh && !isWeb) { + this.menus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService)); + this.topLevelTitles['Preferences'] = nls.localize('mPreferences', "Preferences"); + } + + for (const topLevelMenuName of Object.keys(this.topLevelTitles)) { + const menu = this.menus[topLevelMenuName]; + if (menu) { + this._register(menu.onDidChange(() => this.updateMenubar())); + } + } + + this.windowService.getRecentlyOpened().then((recentlyOpened) => { + this.recentlyOpened = recentlyOpened; + + this.doUpdateMenubar(true); + }); + + this.registerListeners(); + } + + protected doUpdateMenubar(firstTime: boolean): void { + + // Send menus to main process to be rendered by Electron + const menubarData = { menus: {}, keybindings: {} }; + if (this.getMenubarMenus(menubarData)) { + this.menubarService.updateMenubar(this.windowService.windowId, menubarData); + } + } + + private getMenubarMenus(menubarData: IMenubarData): boolean { + if (!menubarData) { + return false; + } + + menubarData.keybindings = this.getAdditionalKeybindings(); + for (const topLevelMenuName of Object.keys(this.topLevelTitles)) { + const menu = this.menus[topLevelMenuName]; + if (menu) { + const menubarMenu: IMenubarMenu = { items: [] }; + this.populateMenuItems(menu, menubarMenu, menubarData.keybindings); + if (menubarMenu.items.length === 0) { + return false; // Menus are incomplete + } + menubarData.menus[topLevelMenuName] = menubarMenu; + } + } + + return true; + } + + private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding | undefined }) { + let groups = menu.getActions(); + for (let group of groups) { + const [, actions] = group; + + actions.forEach(menuItem => { + + if (menuItem instanceof SubmenuItemAction) { + const submenu = { items: [] }; + + if (!this.menus[menuItem.item.submenu]) { + this.menus[menuItem.item.submenu] = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); + this._register(this.menus[menuItem.item.submenu]!.onDidChange(() => this.updateMenubar())); + } + + const menuToDispose = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); + this.populateMenuItems(menuToDispose, submenu, keybindings); + + let menubarSubmenuItem: IMenubarMenuItemSubmenu = { + id: menuItem.id, + label: menuItem.label, + submenu: submenu + }; + + menuToPopulate.items.push(menubarSubmenuItem); + menuToDispose.dispose(); + } else { + if (menuItem.id === 'workbench.action.openRecent') { + const actions = this.getOpenRecentActions().map(this.transformOpenRecentAction); + menuToPopulate.items.push(...actions); + } + + let menubarMenuItem: IMenubarMenuItemAction = { + id: menuItem.id, + label: menuItem.label + }; + + if (menuItem.checked) { + menubarMenuItem.checked = true; + } + + if (!menuItem.enabled) { + menubarMenuItem.enabled = false; + } + + menubarMenuItem.label = this.calculateActionLabel(menubarMenuItem); + keybindings[menuItem.id] = this.getMenubarKeybinding(menuItem.id); + menuToPopulate.items.push(menubarMenuItem); + } + }); + + menuToPopulate.items.push({ id: 'vscode.menubar.separator' }); + } + + if (menuToPopulate.items.length > 0) { + menuToPopulate.items.pop(); + } + } + + private transformOpenRecentAction(action: Separator | (IAction & { uri: URI })): MenubarMenuItem { + if (action instanceof Separator) { + return { id: 'vscode.menubar.separator' }; + } + + return { + id: action.id, + uri: action.uri, + enabled: action.enabled, + label: action.label + }; + } + + private getAdditionalKeybindings(): { [id: string]: IMenubarKeybinding } { + const keybindings: { [id: string]: IMenubarKeybinding } = {}; + if (isMacintosh) { + const keybinding = this.getMenubarKeybinding('workbench.action.quit'); + if (keybinding) { + keybindings['workbench.action.quit'] = keybinding; + } + } + + return keybindings; + } + + private getMenubarKeybinding(id: string): IMenubarKeybinding | undefined { + const binding = this.keybindingService.lookupKeybinding(id); + if (!binding) { + return undefined; + } + + // first try to resolve a native accelerator + const electronAccelerator = binding.getElectronAccelerator(); + if (electronAccelerator) { + return { label: electronAccelerator, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) }; + } + + // we need this fallback to support keybindings that cannot show in electron menus (e.g. chords) + const acceleratorLabel = binding.getLabel(); + if (acceleratorLabel) { + return { label: acceleratorLabel, isNative: false, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) }; + } + + return undefined; + } +} diff --git a/src/vs/workbench/services/configuration/common/configuration.ts b/src/vs/workbench/services/configuration/common/configuration.ts index 87c3e15e4d..805b1ec871 100644 --- a/src/vs/workbench/services/configuration/common/configuration.ts +++ b/src/vs/workbench/services/configuration/common/configuration.ts @@ -17,9 +17,9 @@ export const folderSettingsSchemaId = 'vscode://schemas/settings/folder'; export const launchSchemaId = 'vscode://schemas/launch'; export const LOCAL_MACHINE_SCOPES = [ConfigurationScope.APPLICATION, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]; -export const REMOTE_MACHINE_SCOPES = [ConfigurationScope.MACHINE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]; -export const WORKSPACE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]; -export const FOLDER_SCOPES = [ConfigurationScope.RESOURCE]; +export const REMOTE_MACHINE_SCOPES = [ConfigurationScope.MACHINE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.MACHINE_OVERRIDABLE]; +export const WORKSPACE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.MACHINE_OVERRIDABLE]; +export const FOLDER_SCOPES = [ConfigurationScope.RESOURCE, ConfigurationScope.MACHINE_OVERRIDABLE]; export const TASKS_CONFIGURATION_KEY = 'tasks'; export const LAUNCH_CONFIGURATION_KEY = 'launch'; @@ -36,4 +36,4 @@ export interface IConfigurationCache { write(key: ConfigurationKey, content: string): Promise; remove(key: ConfigurationKey): Promise; -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 88feb8c443..8fd60dfc75 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -15,6 +15,7 @@ import { joinPath } from 'vs/base/common/resources'; import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; +import { generateUuid } from 'vs/base/common/uuid'; export class BrowserWindowConfiguration implements IWindowConfiguration { @@ -80,6 +81,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment this.appNameLong = 'Visual Studio Code - Web'; this.configuration.remoteAuthority = options.remoteAuthority; + this.configuration.machineId = generateUuid(); this.userRoamingDataHome = URI.file('/User').with({ scheme: Schemas.userData }); this.settingsResource = joinPath(this.userRoamingDataHome, 'settings.json'); this.keybindingsResource = joinPath(this.userRoamingDataHome, 'keybindings.json'); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 1ec22693e1..80f4a79bc5 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -8,7 +8,7 @@ import { IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionType, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, isLanguagePackExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -17,6 +17,8 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens import { localize } from 'vs/nls'; import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IProductService } from 'vs/platform/product/common/product'; +import { Schemas } from 'vs/base/common/network'; +import { IDownloadService } from 'vs/platform/download/common/download'; export class ExtensionManagementService extends Disposable implements IExtensionManagementService { @@ -34,6 +36,7 @@ export class ExtensionManagementService extends Disposable implements IExtension @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IConfigurationService protected readonly configurationService: IConfigurationService, @IProductService protected readonly productService: IProductService, + @IDownloadService protected readonly downloadService: IDownloadService, ) { super(); if (this.extensionManagementServerService.localExtensionManagementServer) { @@ -142,15 +145,43 @@ export class ExtensionManagementService extends Disposable implements IExtension } async install(vsix: URI): Promise { + if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { + const manifest = await this.getManifest(vsix); + if (isLanguagePackExtension(manifest)) { + // Install on both servers + const [local] = await Promise.all(this.servers.map(server => this.installVSIX(vsix, server))); + return local; + } + if (isUIExtension(manifest, this.productService, this.configurationService)) { + // Install only on local server + return this.installVSIX(vsix, this.extensionManagementServerService.localExtensionManagementServer); + } + // Install only on remote server + return this.installVSIX(vsix, this.extensionManagementServerService.remoteExtensionManagementServer); + } if (this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); + return this.installVSIX(vsix, this.extensionManagementServerService.localExtensionManagementServer); } if (this.extensionManagementServerService.remoteExtensionManagementServer) { - return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix); + return this.installVSIX(vsix, this.extensionManagementServerService.remoteExtensionManagementServer); } return Promise.reject('No Servers to Install'); } + protected installVSIX(vsix: URI, server: IExtensionManagementServer): Promise { + return server.extensionManagementService.install(vsix); + } + + getManifest(vsix: URI): Promise { + if (vsix.scheme === Schemas.file && this.extensionManagementServerService.localExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getManifest(vsix); + } + if (vsix.scheme === Schemas.vscodeRemote && this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.getManifest(vsix); + } + return Promise.reject('No Servers'); + } + async installFromGallery(gallery: IGalleryExtension): Promise { if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None); diff --git a/src/vs/workbench/services/extensionManagement/node/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/node/extensionManagementService.ts index e7e13cd1cb..bbaeed3f95 100644 --- a/src/vs/workbench/services/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/node/extensionManagementService.ts @@ -3,35 +3,25 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { tmpdir } from 'os'; +import { generateUuid } from 'vs/base/common/uuid'; import { ILocalExtension, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; -import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; -import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { ExtensionManagementService as BaseExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { Schemas } from 'vs/base/common/network'; +import * as path from 'vs/base/common/path'; export class ExtensionManagementService extends BaseExtensionManagementService { - async install(vsix: URI): Promise { - if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { - const manifest = await getManifest(vsix.fsPath); - if (isLanguagePackExtension(manifest)) { - // Install on both servers - const [local] = await Promise.all(this.servers.map(server => server.extensionManagementService.install(vsix))); - return local; - } - if (isUIExtension(manifest, this.productService, this.configurationService)) { - // Install only on local server - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); - } - // Install only on remote server - return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix); + protected async installVSIX(vsix: URI, server: IExtensionManagementServer): Promise { + if (vsix.scheme === Schemas.vscodeRemote && server === this.extensionManagementServerService.localExtensionManagementServer) { + const downloadedLocation = URI.file(path.join(tmpdir(), generateUuid())); + await this.downloadService.download(vsix, downloadedLocation); + vsix = downloadedLocation; } - if (this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); - } - return Promise.reject('No Servers to Install'); + return server.extensionManagementService.install(vsix); } } diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 6471343fd1..31841423f4 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -27,6 +27,7 @@ import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browse import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; +import { DeltaExtensionsResult } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -81,21 +82,21 @@ export class ExtensionService extends AbstractExtensionService implements IExten }; } - protected _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { + protected _createExtensionHosts(_isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { const result: ExtensionHostProcessManager[] = []; - const remoteAgentConnection = this._remoteAgentService.getConnection()!; - const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService))); - const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService))); - const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.parse('empty:value')); //todo@joh - const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); + const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents); result.push(webHostProcessManager); - const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); - const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); - result.push(remoteExtHostProcessManager); + const remoteAgentConnection = this._remoteAgentService.getConnection(); + if (remoteAgentConnection) { + const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService))); + const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); + const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); + result.push(remoteExtHostProcessManager); + } return result; } @@ -103,27 +104,35 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected async _scanAndHandleExtensions(): Promise { // fetch the remote environment let [remoteEnv, localExtensions] = await Promise.all([ - >this._remoteAgentService.getEnvironment(), + this._remoteAgentService.getEnvironment(), this._staticExtensions.getExtensions() ]); + let result: DeltaExtensionsResult; + // local: only enabled and web'ish extension localExtensions = localExtensions.filter(ext => this._isEnabled(ext) && isWebExtension(ext, this._configService)); this._checkEnableProposedApi(localExtensions); - // remote: only enabled and none-web'ish extension - remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService)); - this._checkEnableProposedApi(remoteEnv.extensions); + if (!remoteEnv) { + result = this._registry.deltaExtensions(localExtensions, []); - // in case of overlap, the remote wins - const isRemoteExtension = new Set(); - remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier))); - localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier))); + } else { + // remote: only enabled and none-web'ish extension + remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService)); + this._checkEnableProposedApi(remoteEnv.extensions); - // save for remote extension's init data - this._remoteExtensionsEnvironmentData = remoteEnv; + // in case of overlap, the remote wins + const isRemoteExtension = new Set(); + remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier))); + localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier))); + + // save for remote extension's init data + this._remoteExtensionsEnvironmentData = remoteEnv; + + result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []); + } - const result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []); if (result.removedDueToLooping.length > 0) { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); } diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index a580361305..995ceca43d 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -62,6 +62,7 @@ function patchProcess(allowExit: boolean) { } } as (code?: number) => never; + // override Electron's process.crash() method process.crash = function () { const err = new Error('An extension called process.crash() and this was prevented.'); console.warn(err.stack); diff --git a/src/vs/workbench/services/files/common/workspaceWatcher.ts b/src/vs/workbench/services/files/common/workspaceWatcher.ts index f8595bd3b5..5d8d5c03fe 100644 --- a/src/vs/workbench/services/files/common/workspaceWatcher.ts +++ b/src/vs/workbench/services/files/common/workspaceWatcher.ts @@ -16,6 +16,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { INotificationService, Severity, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; import { FileService } from 'vs/platform/files/common/fileService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export class WorkspaceWatcher extends Disposable { @@ -25,7 +26,8 @@ export class WorkspaceWatcher extends Disposable { @IFileService private readonly fileService: FileService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @INotificationService private readonly notificationService: INotificationService + @INotificationService private readonly notificationService: INotificationService, + @IOpenerService private readonly openerService: IOpenerService ) { super(); @@ -77,7 +79,7 @@ export class WorkspaceWatcher extends Disposable { localize('netVersionError', "The Microsoft .NET Framework 4.5 is required. Please follow the link to install it."), [{ label: localize('installNet', "Download .NET Framework 4.5"), - run: () => window.open('https://go.microsoft.com/fwlink/?LinkId=786533') + run: () => this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?LinkId=786533')) }], { sticky: true, @@ -93,7 +95,7 @@ export class WorkspaceWatcher extends Disposable { localize('enospcError', "Unable to watch for file changes in this large workspace. Please follow the instructions link to resolve this issue."), [{ label: localize('learnMore', "Instructions"), - run: () => window.open('https://go.microsoft.com/fwlink/?linkid=867693') + run: () => this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=867693')) }], { sticky: true, diff --git a/src/vs/workbench/services/integrity/node/integrityService.ts b/src/vs/workbench/services/integrity/node/integrityService.ts index befe1eebca..b25b20a8f4 100644 --- a/src/vs/workbench/services/integrity/node/integrityService.ts +++ b/src/vs/workbench/services/integrity/node/integrityService.ts @@ -15,6 +15,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; interface IStorageData { dontShowPrompt: boolean; @@ -64,7 +65,8 @@ export class IntegrityServiceImpl implements IIntegrityService { constructor( @INotificationService private readonly notificationService: INotificationService, @IStorageService storageService: IStorageService, - @ILifecycleService private readonly lifecycleService: ILifecycleService + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IOpenerService private readonly openerService: IOpenerService ) { this._storage = new IntegrityStorage(storageService); @@ -91,7 +93,7 @@ export class IntegrityServiceImpl implements IIntegrityService { [ { label: nls.localize('integrity.moreInformation', "More Information"), - run: () => window.open(URI.parse(product.checksumFailMoreInfoUrl).toString(true)) + run: () => this.openerService.open(URI.parse(product.checksumFailMoreInfoUrl)) }, { label: nls.localize('integrity.dontShowAgain', "Don't Show Again"), diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index fc85ce0a2a..0709fb7f32 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -45,7 +45,7 @@ import * as objects from 'vs/base/common/objects'; import { IKeymapService } from 'vs/workbench/services/keybinding/common/keymapInfo'; import { getDispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; import { isArray } from 'vs/base/common/types'; -import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard'; +import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/browser/navigatorKeyboard'; import { ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/base/common/scanCode'; interface ContributedKeyBinding { diff --git a/src/vs/workbench/services/keybinding/browser/keymapService.ts b/src/vs/workbench/services/keybinding/browser/keymapService.ts index 59254c0ee1..982f41413c 100644 --- a/src/vs/workbench/services/keybinding/browser/keymapService.ts +++ b/src/vs/workbench/services/keybinding/browser/keymapService.ts @@ -25,7 +25,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ConfigExtensions, IConfigurationRegistry, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard'; +import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/browser/navigatorKeyboard'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IStorageService } from 'vs/platform/storage/common/storage'; diff --git a/src/vs/workbench/services/keybinding/common/navigatorKeyboard.ts b/src/vs/workbench/services/keybinding/browser/navigatorKeyboard.ts similarity index 100% rename from src/vs/workbench/services/keybinding/common/navigatorKeyboard.ts rename to src/vs/workbench/services/keybinding/browser/navigatorKeyboard.ts diff --git a/src/vs/workbench/services/opener/electron-browser/openerService.ts b/src/vs/workbench/services/opener/electron-browser/openerService.ts deleted file mode 100644 index efba9695d2..0000000000 --- a/src/vs/workbench/services/opener/electron-browser/openerService.ts +++ /dev/null @@ -1,40 +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 { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { OpenerService as BaseOpenerService } from 'vs/editor/browser/services/openerService'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { Schemas } from 'vs/base/common/network'; -import { URI } from 'vs/base/common/uri'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; - -export class OpenerService extends BaseOpenerService { - - _serviceBrand!: ServiceIdentifier; - - constructor( - @ICodeEditorService codeEditorService: ICodeEditorService, - @ICommandService commandService: ICommandService, - @IWindowsService private readonly windowsService: IWindowsService - ) { - super(codeEditorService, commandService); - } - - async openExternal(resource: URI): Promise { - const success = this.windowsService.openExternal(encodeURI(resource.toString(true))); - if (!success && resource.scheme === Schemas.file) { - await this.windowsService.showItemInFolder(resource); - - return true; - } - - return success; - } -} - -registerSingleton(IOpenerService, OpenerService, true); diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index e4cd5ccb77..81bc747ebd 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -23,6 +23,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorModel } from 'vs/workbench/common/editor'; import { IFilterMetadata, IFilterResult, IGroupFilter, IKeybindingsEditorModel, ISearchResultGroup, ISetting, ISettingMatch, ISettingMatcher, ISettingsEditorModel, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; import { withNullAsUndefined, isArray } from 'vs/base/common/types'; +import { FOLDER_SCOPES, WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; export const nullRange: IRange = { startLineNumber: -1, startColumn: -1, endLineNumber: -1, endColumn: -1 }; export function isNullRange(range: IRange): boolean { return range.startLineNumber === -1 && range.startColumn === -1 && range.endLineNumber === -1 && range.endColumn === -1; } @@ -659,11 +660,14 @@ export class DefaultSettings extends Disposable { } private matchesScope(property: IConfigurationNode): boolean { + if (!property.scope) { + return true; + } if (this.target === ConfigurationTarget.WORKSPACE_FOLDER) { - return property.scope === ConfigurationScope.RESOURCE; + return FOLDER_SCOPES.indexOf(property.scope) !== -1; } if (this.target === ConfigurationTarget.WORKSPACE) { - return property.scope === ConfigurationScope.WINDOW || property.scope === ConfigurationScope.RESOURCE; + return WORKSPACE_SCOPES.indexOf(property.scope) !== -1; } return true; } diff --git a/src/vs/workbench/services/search/common/replace.ts b/src/vs/workbench/services/search/common/replace.ts index 1f7b15da7b..a7554be51a 100644 --- a/src/vs/workbench/services/search/common/replace.ts +++ b/src/vs/workbench/services/search/common/replace.ts @@ -6,6 +6,7 @@ import * as strings from 'vs/base/common/strings'; import { IPatternInfo } from 'vs/workbench/services/search/common/search'; import { CharCode } from 'vs/base/common/charCode'; +import { buildReplaceStringWithCasePreserved } from 'vs/base/common/search'; export class ReplacePattern { @@ -54,7 +55,7 @@ export class ReplacePattern { * Returns the replace string for the first match in the given text. * If text has no matches then returns null. */ - getReplaceString(text: string): string | null { + getReplaceString(text: string, preserveCase?: boolean): string | null { this._regExp.lastIndex = 0; let match = this._regExp.exec(text); if (match) { @@ -65,12 +66,20 @@ export class ReplacePattern { let replaceString = text.replace(this._regExp, this.pattern); return replaceString.substr(match.index, match[0].length - (text.length - replaceString.length)); } - return this.pattern; + return this.buildReplaceString(match, preserveCase); } return null; } + public buildReplaceString(matches: string[] | null, preserveCase?: boolean): string { + if (preserveCase) { + return buildReplaceStringWithCasePreserved(matches, this._replacePattern); + } else { + return this._replacePattern; + } + } + /** * \n => LF * \t => TAB diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 9c9ba96540..dd68f51c13 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -17,15 +17,15 @@ export class BrowserTextFileService extends TextFileService { } }; - protected beforeShutdown(reason: ShutdownReason): boolean { + protected onBeforeShutdown(reason: ShutdownReason): boolean { // Web: we cannot perform long running in the shutdown phase // As such we need to check sync if there are any dirty files // that have not been backed up yet and then prevent the shutdown // if that is the case. - return this.doBeforeShutdownSync(reason); + return this.doBeforeShutdownSync(); } - private doBeforeShutdownSync(reason: ShutdownReason): boolean { + private doBeforeShutdownSync(): boolean { const dirtyResources = this.getDirty(); if (!dirtyResources.length) { return false; // no dirty: no veto diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index bd85cbd100..cdc806a076 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -107,7 +107,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer private registerListeners(): void { // Lifecycle - this.lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown(event.reason))); + this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(event.reason))); this.lifecycleService.onShutdown(this.dispose, this); // Files configuration changes @@ -118,7 +118,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer })); } - protected beforeShutdown(reason: ShutdownReason): boolean | Promise { + protected onBeforeShutdown(reason: ShutdownReason): boolean | Promise { // Dirty files need treatment on shutdown const dirty = this.getDirty(); diff --git a/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts b/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts index 7704241748..359ede6728 100644 --- a/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts +++ b/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts @@ -209,8 +209,7 @@ export class InMemoryUserDataProvider extends Disposable implements IFileSystemP readonly onDidChangeFile: Event = this._onDidChangeFile.event; private _bufferedChanges: IFileChange[] = []; - private _fireSoonHandle?: NodeJS.Timer; - + private _fireSoonHandle?: any; watch(resource: URI, opts: IWatchOptions): IDisposable { // ignore, fires for all changes... @@ -229,4 +228,4 @@ export class InMemoryUserDataProvider extends Disposable implements IFileSystemP this._bufferedChanges.length = 0; }, 5); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts index 61a5cba0e3..e763ff57bf 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts @@ -424,4 +424,40 @@ suite('ExtHostDiagnostics', () => { collection.set(URI.parse('test:me'), array); assert.equal(callCount, 3); // same but un-equal array }); + + test('Diagnostics created by tasks aren\'t accessible to extensions #47292', async function () { + const diags = new ExtHostDiagnostics(new class implements IMainContext { + getProxy(id: any): any { + return {}; + } + set(): any { + return null; + } + assertRegistered(): void { + + } + }); + + + // + const uri = URI.parse('foo:bar'); + const data: IMarkerData[] = [{ + message: 'message', + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 1, + severity: 3 + }]; + + const p1 = Event.toPromise(diags.onDidChangeDiagnostics); + diags.$acceptMarkersChange([[uri, data]]); + await p1; + assert.equal(diags.getDiagnostics(uri).length, 1); + + const p2 = Event.toPromise(diags.onDidChangeDiagnostics); + diags.$acceptMarkersChange([[uri, []]]); + await p2; + assert.equal(diags.getDiagnostics(uri).length, 0); + }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts index de23d0e3fa..2b22905536 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts @@ -48,7 +48,7 @@ suite('ExtHostWebview', () => { assert.strictEqual(lastInvokedDeserializer, serializerB); }); - test('toWebviewResource for desktop vscode-resource scheme', () => { + test('asWebviewUri for desktop vscode-resource scheme', () => { const shape = createNoopMainThreadWebviews(); const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { webviewCspSource: '', @@ -57,37 +57,37 @@ suite('ExtHostWebview', () => { const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); assert.strictEqual( - webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html')).toString(), + webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString(), 'vscode-resource:/Users/codey/file.html', 'Unix basic' ); assert.strictEqual( - webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html#frag')).toString(), + webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html#frag')).toString(), 'vscode-resource:/Users/codey/file.html#frag', 'Unix should preserve fragment' ); assert.strictEqual( - webview.webview.toWebviewResource(URI.parse('file:///Users/codey/f%20ile.html')).toString(), + webview.webview.asWebviewUri(URI.parse('file:///Users/codey/f%20ile.html')).toString(), 'vscode-resource:/Users/codey/f%20ile.html', 'Unix with encoding' ); assert.strictEqual( - webview.webview.toWebviewResource(URI.parse('file://localhost/Users/codey/file.html')).toString(), + webview.webview.asWebviewUri(URI.parse('file://localhost/Users/codey/file.html')).toString(), 'vscode-resource://localhost/Users/codey/file.html', 'Unix should preserve authority' ); assert.strictEqual( - webview.webview.toWebviewResource(URI.parse('file:///c:/codey/file.txt')).toString(), + webview.webview.asWebviewUri(URI.parse('file:///c:/codey/file.txt')).toString(), 'vscode-resource:/c%3A/codey/file.txt', 'Windows C drive' ); }); - test('toWebviewResource for web endpoint', () => { + test('asWebviewUri for web endpoint', () => { const shape = createNoopMainThreadWebviews(); const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { @@ -101,31 +101,31 @@ suite('ExtHostWebview', () => { } assert.strictEqual( - stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html')).toString()), + stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString()), 'webview.contoso.com/commit///Users/codey/file.html', 'Unix basic' ); assert.strictEqual( - stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html#frag')).toString()), + stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html#frag')).toString()), 'webview.contoso.com/commit///Users/codey/file.html#frag', 'Unix should preserve fragment' ); assert.strictEqual( - stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/f%20ile.html')).toString()), + stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/f%20ile.html')).toString()), 'webview.contoso.com/commit///Users/codey/f%20ile.html', 'Unix with encoding' ); assert.strictEqual( - stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file://localhost/Users/codey/file.html')).toString()), + stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file://localhost/Users/codey/file.html')).toString()), 'webview.contoso.com/commit//localhost/Users/codey/file.html', 'Unix should preserve authority' ); assert.strictEqual( - stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///c:/codey/file.txt')).toString()), + stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///c:/codey/file.txt')).toString()), 'webview.contoso.com/commit///c%3A/codey/file.txt', 'Windows C drive' ); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts index b79a3a725c..1ea8809088 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { MarkerService } from 'vs/platform/markers/common/markerService'; import { MainThreadDiagnostics } from 'vs/workbench/api/browser/mainThreadDiagnostics'; import { URI } from 'vs/base/common/uri'; +import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; suite('MainThreadDiagnostics', function () { @@ -19,7 +20,16 @@ suite('MainThreadDiagnostics', function () { test('clear markers on dispose', function () { - let diag = new MainThreadDiagnostics(null!, markerService); + let diag = new MainThreadDiagnostics(new class implements IExtHostContext { + remoteAuthority = ''; + assertRegistered() { } + set(v: any): any { return null; } + getProxy(): any { + return { + $acceptMarkersChange() { } + }; + } + }, markerService); diag.$changeMany('foo', [[URI.file('a'), [{ code: '666', diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 83fb8c8cf8..cf370a8a7a 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -97,6 +97,8 @@ import { IMenuService } from 'vs/platform/actions/common/actions'; import { MenuService } from 'vs/platform/actions/common/menuService'; import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/common/downloadService'; +import { OpenerService } from 'vs/editor/browser/services/openerService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); registerSingleton(IContextViewService, ContextViewService, true); @@ -109,6 +111,7 @@ registerSingleton(IModelService, ModelServiceImpl, true); registerSingleton(ITextResourceConfigurationService, TextResourceConfigurationService); registerSingleton(IMenuService, MenuService, true); registerSingleton(IDownloadService, DownloadService, true); +registerSingleton(IOpenerService, OpenerService, true); //#endregion diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index c57bcf1742..f3a68e0927 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -49,7 +49,6 @@ import 'vs/workbench/services/extensionManagement/node/extensionManagementServic import 'vs/workbench/services/accessibility/node/accessibilityService'; import 'vs/workbench/services/remote/node/tunnelService'; import 'vs/workbench/services/backup/node/backupFileService'; -import 'vs/workbench/services/opener/electron-browser/openerService'; import 'vs/workbench/services/credentials/node/credentialsService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -70,7 +69,7 @@ import { IIssueService } from 'vs/platform/issue/node/issue'; import { IssueService } from 'vs/platform/issue/electron-browser/issueService'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspacesService } from 'vs/platform/workspaces/electron-browser/workspacesService'; -import { IMenubarService } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; import { IURLService } from 'vs/platform/url/common/url'; import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index f61b8a7e3d..85a92e6565 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -56,8 +56,6 @@ import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuS import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; -import { OpenerService } from 'vs/editor/browser/services/openerService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; registerSingleton(IRequestService, RequestService, true); registerSingleton(IExtensionManagementService, ExtensionManagementService); @@ -67,7 +65,6 @@ registerSingleton(IClipboardService, BrowserClipboardService, true); registerSingleton(IAccessibilityService, BrowserAccessibilityService, true); registerSingleton(ILifecycleService, BrowserLifecycleService); registerSingleton(IContextMenuService, ContextMenuService); -registerSingleton(IOpenerService, OpenerService, true); //#endregion diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index aa77a99bf1..4c107794c5 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -149,7 +149,7 @@ export class Application { await this.code.waitForElement('.monaco-workbench'); if (this.remote) { - await this.code.waitForElement('.monaco-workbench .statusbar-item[title="Editing on TestResolver"]'); + await this.code.waitForElement('.monaco-workbench .statusbar-item[id="status.host"]'); } // wait a bit, since focus might be stolen off widgets diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index c049a23a7d..5bd8fb3fe3 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -26,7 +26,7 @@ const vscodeToPuppeteerKey = { }; function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver { - const driver = { + const driver: IDriver = { _serviceBrand: undefined, getWindowIds: () => { return Promise.resolve([1]); @@ -98,6 +98,7 @@ export async function launch(_args: string[]): Promise { server.stdout.on('data', e => console.log('Server stdout: ' + e)); process.on('exit', teardown); process.on('SIGINT', teardown); + process.on('SIGTERM', teardown); endpoint = await waitForEndpoint(); } @@ -180,7 +181,7 @@ export interface IDriver { getTitle(windowId: number): Promise; isActiveElement(windowId: number, selector: string): Promise; getElements(windowId: number, selector: string, recursive?: boolean): Promise; - getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }>; + getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }>; typeInEditor(windowId: number, selector: string, text: string): Promise; getTerminalBuffer(windowId: number, selector: string): Promise; writeInTerminal(windowId: number, selector: string, text: string): Promise; diff --git a/tslint.json b/tslint.json index 6ec4d12ece..ff88a7d36e 100644 --- a/tslint.json +++ b/tslint.json @@ -5,11 +5,14 @@ "rules": { "no-arg": true, "no-construct": true, + "no-debugger": true, "no-duplicate-super": true, "no-duplicate-switch-case": true, "no-duplicate-variable": true, + // "no-for-in-array": true, // {{SQL CARBON EDIT}} @anthonydresser disable till we fix "no-eval": true, "no-redundant-jsdoc": true, + // "no-restricted-globals": true, // {{SQL CARBON EDIT}} @anthonydresser disable till we fix "no-sparse-arrays": true, "no-string-throw": true, "no-unsafe-finally": true, diff --git a/yarn.lock b/yarn.lock index 2272f3f400..588f3fbeb9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3938,10 +3938,10 @@ growl@1.9.2: resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= -gulp-atom-electron@^1.21.1: - version "1.21.1" - resolved "https://registry.yarnpkg.com/gulp-atom-electron/-/gulp-atom-electron-1.21.1.tgz#4017144bf659fbbf7d0644664fcc47c64efac0f0" - integrity sha512-UHEf2pZrJD/u+AAzKCbhdPXaKrReFDa+OEJjBCAdN2SHnD+dfZqSJAz/u2OD6YR/eREuUbQOCw+VWUwex20klQ== +gulp-atom-electron@^1.22.0: + version "1.22.0" + resolved "https://registry.yarnpkg.com/gulp-atom-electron/-/gulp-atom-electron-1.22.0.tgz#0e2f4fe7c7310145c6c81d5660d0277cd8338d27" + integrity sha512-K13Ze2+iRIvC1igl+BIbZdF8M2ZzEIM6LF+/j83oWNGNhe7+en77qT9y9rs6SipYeOyoInlbly+pzbFgvwpSHA== dependencies: event-stream "3.3.4" github-releases-ms "^0.5.0" @@ -9972,10 +9972,10 @@ vscode-chokidar@2.1.7: optionalDependencies: vscode-fsevents "1.2.12" -vscode-debugprotocol@1.35.0: - version "1.35.0" - resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.35.0.tgz#565140cd42945e30c6c85cafb38c631457d4a46c" - integrity sha512-+OMm11R1bGYbpIJ5eQIkwoDGFF4GvBz3Ztl6/VM+/RNNb2Gjk2c0Ku+oMmfhlTmTlPCpgHBsH4JqVCbUYhu5bA== +vscode-debugprotocol@1.36.0-pre.0: + version "1.36.0-pre.0" + resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.36.0-pre.0.tgz#4f998e143acae9e3ce13c308d4ad322f96841926" + integrity sha512-nQhImfsUJFfr73JqA2Uc1bjTvpgZRabqkkKQWUbmrrnTLCPKRCr9AgE44Ehb/vkLXNdo+vPnWjJXWnPGjT10mA== vscode-fsevents@1.2.12: version "1.2.12"