diff --git a/.github/commands.yml b/.github/commands.yml index f20079caab81afb73708b24bd3d18ab3f5cbd47e..c92792325293c5996c4d83d2a2138cf2463ebb07 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -105,7 +105,7 @@ { type: 'comment', name: 'a11ymas', - allowUsers: ['AccessibilityTestingTeam-TCS'], + allowUsers: ['AccessibilityTestingTeam-TCS', 'dixitsonali95', 'Mohini78', 'ChitrarupaSharma', 'mspatil110', 'umasarath52', 'v-umnaik'], action: 'updateLabels', addLabel: 'a11ymas' }, diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index 83eebc9d69d6b35bef782f540d28ef1bc5e8d3df..b0e919883c460914c48a71219ffaa6ea3680f808 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -19,6 +19,8 @@ const untar = require('gulp-untar'); const File = require('vinyl'); const fs = require('fs'); +const cp = require('child_process'); + const REPO_ROOT = path.dirname(__dirname); const noop = () => { return Promise.resolve(); }; @@ -115,3 +117,33 @@ function nodejs(platform, arch) { })) ); } + +function mixinServer(watch) { + const packageJSONPath = path.join(path.dirname(__dirname), 'package.json'); + function exec(cmdLine) { + console.log(cmdLine); + cp.execSync(cmdLine, { stdio: "inherit" }); + } + function checkout() { + const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString()); + exec('git fetch distro'); + exec(`git checkout ${packageJSON['distro']} -- src/vs/server resources/server`); + exec('git reset HEAD src/vs/server resources/server'); + } + checkout(); + if (watch) { + console.log('Enter watch mode (observing package.json)'); + const watcher = fs.watch(packageJSONPath); + watcher.addListener('change', () => { + try { + checkout(); + } catch (e) { + console.log(e); + } + }); + } + return Promise.resolve(); +} + +gulp.task(task.define('mixin-server', () => mixinServer(false))); +gulp.task(task.define('mixin-server-watch', () => mixinServer(true))); diff --git a/build/npm/install-server.js b/build/npm/install-server.js deleted file mode 100644 index ffeec1fa30adfa82b280177bb730610e4b7d9575..0000000000000000000000000000000000000000 --- a/build/npm/install-server.js +++ /dev/null @@ -1,15 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -const cp = require('child_process'); - -function exec(cmdLine) { - console.log(cmdLine); - cp.execSync(cmdLine, {stdio: "inherit"}); -} - -exec('git fetch distro'); -exec(`git checkout ${process.env['npm_package_distro']} -- src/vs/server resources/server`); -exec('git reset HEAD src/vs/server resources/server'); \ No newline at end of file diff --git a/extensions/shellscript/package.json b/extensions/shellscript/package.json index 1840366311000aaa17486c184ebacd29913ff4ba..a55af2b08ff96e4edceb0435659b5647f32fb55b 100644 --- a/extensions/shellscript/package.json +++ b/extensions/shellscript/package.json @@ -14,7 +14,7 @@ "id": "shellscript", "aliases": ["Shell Script", "shellscript", "bash", "sh", "zsh", "ksh"], "extensions": [".sh", ".bash", ".bashrc", ".bash_aliases", ".bash_profile", ".bash_login", ".ebuild", ".install", ".profile", ".bash_logout", ".zsh", ".zshrc", ".zprofile", ".zlogin", ".zlogout", ".zshenv", ".zsh-theme", ".ksh"], - "filenames": ["PKGBUILD"], + "filenames": ["APKBUILD", "PKGBUILD"], "firstLine": "^#!.*\\b(bash|zsh|sh|tcsh|ksh|ash|qsh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-", "configuration": "./language-configuration.json", "mimetypes": ["text/x-shellscript"] diff --git a/extensions/typescript-language-features/src/features/definitions.ts b/extensions/typescript-language-features/src/features/definitions.ts index 5fe72c2005f596fe66fefbdc80e26c00e7ad9313..fd3a1f10e79e6f4f6290591d98925e66cf3318a6 100644 --- a/extensions/typescript-language-features/src/features/definitions.ts +++ b/extensions/typescript-language-features/src/features/definitions.ts @@ -37,10 +37,18 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase return response.body.definitions .map((location): vscode.DefinitionLink => { const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); + if ((location as any).contextStart) { + return { + originSelectionRange: span, + targetRange: typeConverters.Range.fromLocations((location as any).contextStart, (location as any).contextEnd), + targetUri: target.uri, + targetSelectionRange: target.range, + }; + } return { originSelectionRange: span, targetRange: target.range, - targetUri: target.uri, + targetUri: target.uri }; }); } diff --git a/extensions/typescript-language-features/src/utils/typeConverters.ts b/extensions/typescript-language-features/src/utils/typeConverters.ts index eaa6a96c860c3abf018f49d21f4f771ff13352b0..37947b38810d5d139cdfeb3848d2982366203d74 100644 --- a/extensions/typescript-language-features/src/utils/typeConverters.ts +++ b/extensions/typescript-language-features/src/utils/typeConverters.ts @@ -13,9 +13,12 @@ import { ITypeScriptServiceClient } from '../typescriptService'; export namespace Range { export const fromTextSpan = (span: Proto.TextSpan): vscode.Range => + fromLocations(span.start, span.end); + + export const fromLocations = (start: Proto.Location, end: Proto.Location): vscode.Range => new vscode.Range( - Math.max(0, span.start.line - 1), Math.max(span.start.offset - 1, 0), - Math.max(0, span.end.line - 1), Math.max(0, span.end.offset - 1)); + Math.max(0, start.line - 1), Math.max(start.offset - 1, 0), + Math.max(0, end.line - 1), Math.max(0, end.offset - 1)); export const toFileRangeRequestArgs = (file: string, range: vscode.Range): Proto.FileRangeRequestArgs => ({ file, diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index dc700abdf1b89ad51fb7bbb714709c95a6ae02fb..67059e58c5069552449c5aa4f1088c39eadd9f8b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -513,28 +513,28 @@ suite('workspace-namespace', () => { }); }); - test('findFiles', () => { + (process.platform === 'win32' ? test.skip /* https://github.com/microsoft/vscode/issues/74898 */ : test)('findFiles', () => { return vscode.workspace.findFiles('**/*.png').then((res) => { assert.equal(res.length, 2); assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); }); }); - test('findFiles - exclude', () => { + (process.platform === 'win32' ? test.skip /* https://github.com/microsoft/vscode/issues/74898 */ : test)('findFiles - exclude', () => { return vscode.workspace.findFiles('**/*.png').then((res) => { assert.equal(res.length, 2); assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); }); }); - test('findFiles, exclude', () => { + (process.platform === 'win32' ? test.skip /* https://github.com/microsoft/vscode/issues/74898 */ : test)('findFiles, exclude', () => { return vscode.workspace.findFiles('**/*.png', '**/sub/**').then((res) => { assert.equal(res.length, 1); assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); }); }); - test('findFiles, cancellation', () => { + (process.platform === 'win32' ? test.skip /* https://github.com/microsoft/vscode/issues/74898 */ : test)('findFiles, cancellation', () => { const source = new vscode.CancellationTokenSource(); const token = source.token; // just to get an instance first @@ -545,7 +545,7 @@ suite('workspace-namespace', () => { }); }); - test('findTextInFiles', async () => { + (process.platform === 'win32' ? test.skip /* https://github.com/microsoft/vscode/issues/74898 */ : test)('findTextInFiles', async () => { const options: vscode.FindTextInFilesOptions = { include: '*.ts', previewOptions: { @@ -565,7 +565,7 @@ suite('workspace-namespace', () => { assert.equal(vscode.workspace.asRelativePath(match.uri), '10linefile.ts'); }); - test('findTextInFiles, cancellation', async () => { + (process.platform === 'win32' ? suite.skip /* https://github.com/microsoft/vscode/issues/74898 */ : suite)('findTextInFiles, cancellation', async () => { const results: vscode.TextSearchResult[] = []; const cancellation = new vscode.CancellationTokenSource(); cancellation.cancel(); diff --git a/package.json b/package.json index 88789b3c9e3e46b8a654aa777a4c72b392f21407..6916b4088ec60984d31521f6e0e36f9bb88535c8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.36.0", - "distro": "c1ec2234fb5dc62f2bf9689f41dc1cd0a507a581", + "distro": "26125d2b5b10bac3b3de09f4ee565ea4d5c1c7bc", "author": { "name": "Microsoft Corporation" }, @@ -24,10 +24,7 @@ "smoketest": "cd test/smoke && node test/index.js", "download-builtin-extensions": "node build/lib/builtInExtensions.js", "monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit", - "strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization", - "web": "node resources/server/bin-dev/code-web.js --port 9888", - "web-selfhost": "node resources/server/bin-dev/code-web.js --port 9777 --selfhost", - "install-server": "node build/npm/install-server.js" + "strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization" }, "dependencies": { "applicationinsights": "1.0.8", diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index b1a0f7e4f3e66532524e87ff0eaecabbe396238d..606fb108fe5b0308535b675355183e9ddb74f118 100644 --- a/resources/win32/bin/code.sh +++ b/resources/win32/bin/code.sh @@ -11,8 +11,18 @@ VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")" ELECTRON="$VSCODE_PATH/$NAME.exe" if grep -qi Microsoft /proc/version; then # in a wsl shell - WSL_BUILD=$(uname -r | sed -E 's/^.+-([0-9]+)-[Mm]icrosoft/\1/') - if [ $WSL_BUILD -ge 17063 ] 2> /dev/null; then + if ! [ -z "$WSL_DISTRO_NAME"]; then + # $WSL_DISTRO_NAME is available since WSL builds 18362, also for WSL2 + WSL_BUILD=18362 + else + WSL_BUILD=$(uname -r | sed -E 's/^.+-([0-9]+)-Microsoft/\1/') + if ! [ -z "$WSL_BUILD" ]; then + WSL_BUILD=0 + fi + fi + + if [ $WSL_BUILD -ge 17063 ]; then + # $WSL_DISTRO_NAME is available since WSL builds 18362, also for WSL2 # WSLPATH is available since WSL build 17046 # WSLENV is available since WSL build 17063 export WSLENV=ELECTRON_RUN_AS_NODE/w:$WSLENV diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 3d397ed204ee7c6e0e2374b3fa0b07a8459ded47..a9058442dce2871bc105dd1b2301221f888ea883 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -21,9 +21,9 @@ cd $ROOT ./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started ./scripts/code.sh $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started -mkdir -p $ROOT/extensions/emmet/test-fixtures -./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started -rm -rf $ROOT/extensions/emmet/test-fixtures +# mkdir -p $ROOT/extensions/emmet/test-fixtures +# ./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started +# rm -rf $ROOT/extensions/emmet/test-fixtures if [ -f ./resources/server/test/test-remote-integration.sh ]; then ./resources/server/test/test-remote-integration.sh diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 64fd5661495bbe3d602ce019697b30d208d9604b..265d69f85a261a531e2cd54f5c46a5e0ec29b0c5 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -804,8 +804,7 @@ export function createCSSRule(selector: string, cssText: string, style: HTMLStyl if (!style || !cssText) { return; } - - (style.sheet).insertRule(selector + '{' + cssText + '}', 0); + style.textContent = `${selector}{${cssText}}\n${style.textContent}`; } export function removeCSSRulesContainingSelector(ruleName: string, style: HTMLStyleElement = getSharedStyleSheet()): void { diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index e74092b1148411d58effdedec2358c24537037a2..0e69183da973d891e60dbe7562588298cfb68b77 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -18,6 +18,7 @@ import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Disposable, dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; import { asArray } from 'vs/base/common/arrays'; +import { ScanCodeUtils, ScanCode } from 'vs/base/common/scanCode'; const $ = DOM.$; @@ -777,6 +778,13 @@ export class MenuBar extends Disposable { return; } + // Prevent alt-key default if the menu is not hidden and we use alt to focus + if (modifierKeyStatus.event && !this.options.disableAltFocus) { + if (ScanCodeUtils.toEnum(modifierKeyStatus.event.code) === ScanCode.AltLeft) { + modifierKeyStatus.event.preventDefault(); + } + } + // Alt key pressed while menu is focused. This should return focus away from the menubar if (this.isFocused && modifierKeyStatus.lastKeyPressed === 'alt' && modifierKeyStatus.altKey) { this.setUnfocusedState(); @@ -892,6 +900,7 @@ interface IModifierKeyStatus { ctrlKey: boolean; lastKeyPressed?: ModifierKey; lastKeyReleased?: ModifierKey; + event?: KeyboardEvent; } @@ -930,6 +939,7 @@ class ModifierKeyEmitter extends Emitter { this._keyStatus.shiftKey = e.shiftKey; if (this._keyStatus.lastKeyPressed) { + this._keyStatus.event = e; this.fire(this._keyStatus); } })); @@ -954,6 +964,7 @@ class ModifierKeyEmitter extends Emitter { this._keyStatus.shiftKey = e.shiftKey; if (this._keyStatus.lastKeyReleased) { + this._keyStatus.event = e; this.fire(this._keyStatus); } })); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index fadc69ed298d618bcef5f157249b9637670bb09a..c3527a710b8d0dff6ffe64f9470126cfe16deddc 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -381,12 +381,14 @@ class TreeRenderer implements IListRenderer implements IListRenderer('line', { x1: x, y1: 0, x2: x, y2: height }); + const x = Math.floor((target.depth - i - 1) * this.indent * window.devicePixelRatio) + 2.5; + const line = $.SVG('line', { x1: x, y1: 0, x2: x, y2: virtualHeight }); if (this.activeParentNodes.has(parent)) { addClass(line, 'active'); diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index 03f75d4e6e28e496f295f9bd50d2d4a9bba34598..564d5505e416375870fd26c6942271ee42202bb0 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -14,10 +14,19 @@ height: 100%; position: absolute; top: 0; - left: 16px; + left: 14px; pointer-events: none; } +.hide-arrows .monaco-tl-indent { + left: 10px; +} + +/* TODO @misolori remove before shipping stable */ +body:not([data-exploration="icon-exploration"]) .monaco-tl-indent { + left: 16px; +} + .monaco-tl-indent > svg { overflow: visible; } diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 679b872a315c9b19e88b83aa81e8e48294253211..fedadf22ee6da3272c57234ce2fac50e32abf2a3 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -6,25 +6,25 @@ + + + + + + + + + + + - - - \ No newline at end of file diff --git a/src/vs/code/browser/workbench/workbench.js b/src/vs/code/browser/workbench/workbench.js index 3dfc2e31515e33ae54820d64c82c4fa33c1088e7..34f321f90df970aa92747212d3473393978be249 100644 --- a/src/vs/code/browser/workbench/workbench.js +++ b/src/vs/code/browser/workbench/workbench.js @@ -3,12 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -//@ts-check 'use strict'; (function () { - // @ts-ignore require.config({ baseUrl: `${window.location.origin}/out`, paths: { @@ -20,9 +18,9 @@ } }); - // @ts-ignore require(['vs/workbench/workbench.web.api'], function (api) { - // @ts-ignore - api.create(document.body, self.WORKBENCH_WEB_CONFIGURATION); + const options = JSON.parse(document.getElementById('vscode-workbench-web-configuration').getAttribute('data-settings')); + + api.create(document.body, options); }); })(); \ No newline at end of file diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index c175034f9699f22bdfe1c5bf87082e8aa4497e6c..9cfeed27dd05938d443e91b400d11fa6226920c8 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -55,7 +55,7 @@ export class OpenerService implements IOpenerService { if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https) || equalsIgnoreCase(scheme, Schemas.mailto)) { // open http or default mail application - dom.windowOpenNoOpener(encodeURI(resource.toString(true))); + dom.windowOpenNoOpener(resource.toString()); return Promise.resolve(true); } else if (equalsIgnoreCase(scheme, Schemas.command)) { diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 926d6a3da2fd011c2ec703dd80167ddc2671d39b..dc34e90fcfbdd5ea6a1c2cab119384e46dfda53c 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -225,11 +225,11 @@ export class OutlineItemComparator implements ITreeSorter { } else if (a instanceof OutlineElement && b instanceof OutlineElement) { if (this.type === OutlineSortOrder.ByKind) { - return a.symbol.kind - b.symbol.kind || a.symbol.name.localeCompare(b.symbol.name); + return a.symbol.kind - b.symbol.kind || a.symbol.name.localeCompare(b.symbol.name, undefined, { numeric: true }); } else if (this.type === OutlineSortOrder.ByName) { - return a.symbol.name.localeCompare(b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range); + return a.symbol.name.localeCompare(b.symbol.name, undefined, { numeric: true }) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range); } else if (this.type === OutlineSortOrder.ByPosition) { - return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || a.symbol.name.localeCompare(b.symbol.name); + return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || a.symbol.name.localeCompare(b.symbol.name, undefined, { numeric: true }); } } return 0; diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index 7c81a73fb159d3c04a4c1757e7fb679202c5e532..5c81fdeae574450fe89bcc0d77f41297162d57ad 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -10,7 +10,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { Range } from 'vs/editor/common/core/range'; +import { Range, IRange } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { DefinitionProviderRegistry, LocationLink } from 'vs/editor/common/modes'; import { ICodeEditor, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; @@ -150,7 +150,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC return; } - const previewValue = this.getPreviewValue(textEditorModel, startLineNumber); + const previewValue = this.getPreviewValue(textEditorModel, startLineNumber, result); let wordRange: Range; if (result.originSelectionRange) { @@ -170,8 +170,8 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC }).then(undefined, onUnexpectedError); } - private getPreviewValue(textEditorModel: ITextModel, startLineNumber: number) { - let rangeToUse = this.getPreviewRangeBasedOnBrackets(textEditorModel, startLineNumber); + private getPreviewValue(textEditorModel: ITextModel, startLineNumber: number, result: LocationLink) { + let rangeToUse = result.targetSelectionRange ? result.range : this.getPreviewRangeBasedOnBrackets(textEditorModel, startLineNumber); const numberOfLinesInRange = rangeToUse.endLineNumber - rangeToUse.startLineNumber; if (numberOfLinesInRange >= GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES) { rangeToUse = this.getPreviewRangeBasedOnIndentation(textEditorModel, startLineNumber); @@ -181,7 +181,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC return previewValue; } - private stripIndentationFromPreviewRange(textEditorModel: ITextModel, startLineNumber: number, previewRange: Range) { + private stripIndentationFromPreviewRange(textEditorModel: ITextModel, startLineNumber: number, previewRange: IRange) { const startIndent = textEditorModel.getLineFirstNonWhitespaceColumn(startLineNumber); let minIndent = startIndent; diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 8f2bef9143554a013a29c319d1938b85c68fbcab..968fa5d44cbf311444d12e7d57ffba91d2851516 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -25,7 +25,7 @@ import { basename } from 'vs/base/common/resources'; import { IAction } from 'vs/base/common/actions'; import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/referenceSearch/referencesWidget'; -import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import * as aria from 'vs/base/browser/ui/aria/aria'; import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; class MessageWidget { @@ -287,9 +287,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { this.editor.revealPositionInCenter(position, ScrollType.Smooth); - if (this.editor.getConfiguration().accessibilitySupport !== AccessibilitySupport.Disabled) { - this.focus(); - } + aria.alert(marker.message); } updateMarker(marker: IMarker): void { diff --git a/src/vs/platform/dialogs/browser/dialogService.ts b/src/vs/platform/dialogs/browser/dialogService.ts index d5cc82af884312ec6da9ee20906e1fb809d94063..5a906768029b29cfa9f7ecf5b04b26cbdc452b2a 100644 --- a/src/vs/platform/dialogs/browser/dialogService.ts +++ b/src/vs/platform/dialogs/browser/dialogService.ts @@ -9,7 +9,6 @@ import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { ILogService } from 'vs/platform/log/common/log'; import Severity from 'vs/base/common/severity'; import { Dialog } from 'vs/base/browser/ui/dialog/dialog'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachDialogStyler } from 'vs/platform/theme/common/styler'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -93,5 +92,3 @@ export class DialogService implements IDialogService { return choice; } } - -registerSingleton(IDialogService, DialogService, true); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 25cfdd43a2a2cdb5c1ae09360750343cebe914f2..bf1dbd5f54369885b57a9a042a94a213b3cb67c8 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -69,6 +69,7 @@ export interface ParsedArgs { 'driver-verbose'?: boolean; remote?: string; 'disable-user-env-probe'?: boolean; + 'enable-remote-auto-shutdown'?: boolean; } export const IEnvironmentService = createDecorator('environmentService'); diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 530b5071cb338aaaa26c4a0d0017caf6abf8dce2..fad5b86e37819be85a84761c236ad8baa8c9a552 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -104,6 +104,9 @@ export class EnvironmentService implements IEnvironmentService { return parseUserDataDir(this._args, process); } + @memoize + get webUserDataHome(): URI { return URI.file(parsePathArg(this._args['web-user-data-dir'], process) || this.userDataPath); } + get appNameLong(): string { return product.nameLong; } get appQuality(): string | undefined { return product.quality; } diff --git a/src/vs/platform/sign/browser/signService.ts b/src/vs/platform/sign/browser/signService.ts index 5c1919ba88fb32bbe4087c4faa75ba9c4933822a..501ab8939d4cebdf467650879e12be2c55384d4b 100644 --- a/src/vs/platform/sign/browser/signService.ts +++ b/src/vs/platform/sign/browser/signService.ts @@ -11,6 +11,6 @@ export class SignService implements ISignService { _serviceBrand: ServiceIdentifier; async sign(value: string): Promise { - return Promise.resolve((self).WORKBENCH_WEB_CONFIGURATION.connectionAuthToken); + return Promise.resolve(document.getElementById('vscode-remote-connection-token')!.getAttribute('data-settings')!); } } \ No newline at end of file diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index e0cbafd346350d56e2241dea25bc00907730902b..8fb66ec1a554c18393fad5ab2cd7a5afbd7b6fe1 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -36,6 +36,7 @@ class EditorWebviewZone implements IViewZone { readonly webview: Webview, ) { this.domNode = document.createElement('div'); + this.domNode.style.zIndex = '10'; // without this, the webview is not interactive this.afterLineNumber = range.startLineNumber; this.afterColumn = range.startColumn; this.heightInLines = range.endLineNumber - range.startLineNumber; @@ -92,7 +93,8 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { allowSvgs: false, extension: { id: extensionId, location: URI.revive(extensionLocation) } }, { - allowScripts: options.enableScripts + allowScripts: options.enableScripts, + localResourceRoots: options.localResourceRoots ? options.localResourceRoots.map(uri => URI.revive(uri)) : undefined }); const webviewZone = new EditorWebviewZone(editor, range, webview); diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts index 4c5b038c2313e5df95735e8047f8d3db3a245ec0..c9a212e48632f0ca3b85f04f1e272abb24c7437f 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.windowsService.openExternal(encodeURI(uri.toString(true))); + return this.windowsService.openExternal(uri.toString()); } private getLocalhostPort(uri: URI): number | undefined { diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 2da50e9c3f40f59a9a5d55185c9f0033ef786e33..4d78f9c67f3b52a56981cda452e9b27f3608a5e0 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -18,6 +18,7 @@ import { CustomCodeAction } from 'vs/workbench/api/common/extHostLanguageFeature import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand, OpenIssueReporter } from './apiCommands'; import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { IRange } from 'vs/editor/common/core/range'; export class ExtHostApiCommands { @@ -414,15 +415,21 @@ export class ExtHostApiCommands { }); } - private _executeSelectionRangeProvider(resource: URI, positions: types.Position[]): Promise { + private _executeSelectionRangeProvider(resource: URI, positions: types.Position[]): Promise { const pos = positions.map(typeConverters.Position.from); const args = { resource, position: pos[0], positions: pos }; - return this._commands.executeCommand('_executeSelectionRangeProvider', args).then(result => { - return result.map(oneResult => oneResult.map(typeConverters.SelectionRange.to)); + return this._commands.executeCommand('_executeSelectionRangeProvider', args).then(result => { + return result.map(ranges => { + let node: types.SelectionRange | undefined; + for (const range of ranges.reverse()) { + node = new types.SelectionRange(typeConverters.Range.to(range), node); + } + return node!; + }); }); } diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index 5173ebb176c26e27215216032af8805504f8a7c1..3d0840068b0bcafa7b5b61d12fc907f8921885c2 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -210,7 +210,7 @@ export class CommandsConverter { this._commands.registerCommand(true, this._delegatingCommandId, this._executeConvertedCommand, this); } - toInternal2(command: vscode.Command | undefined, disposables: DisposableStore): CommandDto | undefined { + toInternal(command: vscode.Command | undefined, disposables: DisposableStore): CommandDto | undefined { if (!command) { return undefined; @@ -240,40 +240,6 @@ export class CommandsConverter { return result; } - toInternal(command: vscode.Command): CommandDto; - toInternal(command: undefined): undefined; - toInternal(command: vscode.Command | undefined): CommandDto | undefined; - toInternal(command: vscode.Command | undefined): CommandDto | undefined { - - if (!command) { - return undefined; - } - - const result: CommandDto = { - $ident: undefined, - id: command.command, - title: command.title, - }; - - if (command.command && isNonEmptyArray(command.arguments)) { - // we have a contributed command with arguments. that - // means we don't want to send the arguments around - - const id = ++this._cachIdPool; - this._cache.set(id, command); - result.$ident = id; - - result.id = this._delegatingCommandId; - result.arguments = [id]; - } - - if (command.tooltip) { - result.tooltip = command.tooltip; - } - - return result; - } - fromInternal(command: modes.Command): vscode.Command | undefined { const id = ObjectIdentifier.of(command); diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index 4b76a7bece033da888d11974984dc1d100a94ea2..93b58afbd084f80cce34bc3310e97f1abcb3af5c 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -650,9 +650,9 @@ export class ExtHostCommentThread implements vscode.CommentThread { const label = this.label; const contextValue = this.contextValue; const comments = this._comments.map(cmt => { return convertToModeComment2(this, this._commentController, cmt, this._commandsConverter, this._commentsMap, this._acceptInputDisposables.value!); }); - const acceptInputCommand = this._acceptInputCommand ? this._commandsConverter.toInternal2(this._acceptInputCommand, this._acceptInputDisposables.value) : undefined; - const additionalCommands = (this._additionalCommands ? this._additionalCommands.map(x => this._commandsConverter.toInternal2(x, this._acceptInputDisposables.value!)) : []) as CommandDto[]; - const deleteCommand = this._deleteCommand ? this._commandsConverter.toInternal2(this._deleteCommand, this._acceptInputDisposables.value) : undefined; + const acceptInputCommand = this._acceptInputCommand ? this._commandsConverter.toInternal(this._acceptInputCommand, this._acceptInputDisposables.value) : undefined; + const additionalCommands = (this._additionalCommands ? this._additionalCommands.map(x => this._commandsConverter.toInternal(x, this._acceptInputDisposables.value!)) : []) as CommandDto[]; + const deleteCommand = this._deleteCommand ? this._commandsConverter.toInternal(this._deleteCommand, this._acceptInputDisposables.value) : undefined; const collapsibleState = convertToCollapsibleState(this._collapseState); this._proxy.$updateCommentThread( @@ -951,9 +951,9 @@ function convertToModeComment2(thread: ExtHostCommentThread, commentController: userName: vscodeComment.author ? vscodeComment.author.name : vscodeComment.userName, userIconPath: iconPath, isDraft: vscodeComment.isDraft, - selectCommand: vscodeComment.selectCommand ? commandsConverter.toInternal2(vscodeComment.selectCommand, disposables) : undefined, - editCommand: vscodeComment.editCommand ? commandsConverter.toInternal2(vscodeComment.editCommand, disposables) : undefined, - deleteCommand: vscodeComment.deleteCommand ? commandsConverter.toInternal2(vscodeComment.deleteCommand, disposables) : undefined, + selectCommand: vscodeComment.selectCommand ? commandsConverter.toInternal(vscodeComment.selectCommand, disposables) : undefined, + editCommand: vscodeComment.editCommand ? commandsConverter.toInternal(vscodeComment.editCommand, disposables) : undefined, + deleteCommand: vscodeComment.deleteCommand ? commandsConverter.toInternal(vscodeComment.deleteCommand, disposables) : undefined, label: vscodeComment.label, commentReactions: reactions ? reactions.map(reaction => convertToReaction2(commentController.reactionProvider, reaction)) : undefined }; @@ -971,7 +971,7 @@ function convertToComment(provider: vscode.DocumentCommentProvider | vscode.Work userIconPath: iconPath, canEdit: canEdit, canDelete: canDelete, - selectCommand: vscodeComment.command ? commandsConverter.toInternal2(vscodeComment.command, disposables) : undefined, + selectCommand: vscodeComment.command ? commandsConverter.toInternal(vscodeComment.command, disposables) : undefined, isDraft: vscodeComment.isDraft, commentReactions: vscodeComment.commentReactions ? vscodeComment.commentReactions.map(reaction => convertToReaction(provider, reaction)) : undefined }; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 661e98948c92250b79858fd2d31f48283a4e9927..4aefb1cc5d6f1d7ba9affc6b1975d3e74c55dbaa 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -133,7 +133,7 @@ class CodeLensAdapter { result.lenses.push({ cacheId: [cacheId, i], range: typeConvert.Range.from(lenses[i].range), - command: this._commands.toInternal2(lenses[i].command, disposables) + command: this._commands.toInternal(lenses[i].command, disposables) }); } @@ -167,7 +167,7 @@ class CodeLensAdapter { } newLens = newLens || lens; - symbol.command = this._commands.toInternal2(newLens.command || CodeLensAdapter._badCmd, disposables); + symbol.command = this._commands.toInternal(newLens.command || CodeLensAdapter._badCmd, disposables); return symbol; }); } @@ -368,7 +368,7 @@ class CodeActionAdapter { actions.push({ _isSynthetic: true, title: candidate.title, - command: this._commands.toInternal2(candidate, disposables), + command: this._commands.toInternal(candidate, disposables), }); } else { if (codeActionContext.only) { @@ -382,7 +382,7 @@ class CodeActionAdapter { // new school: convert code action actions.push({ title: candidate.title, - command: candidate.command && this._commands.toInternal2(candidate.command, disposables), + command: candidate.command && this._commands.toInternal(candidate.command, disposables), diagnostics: candidate.diagnostics && candidate.diagnostics.map(typeConvert.Diagnostic.from), edit: candidate.edit && typeConvert.WorkspaceEdit.from(candidate.edit), kind: candidate.kind && candidate.kind.value, @@ -735,7 +735,7 @@ class SuggestAdapter { i: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0, k: item.commitCharacters, l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from), - m: this._commands.toInternal2(item.command, disposables), + m: this._commands.toInternal(item.command, disposables), }; // 'insertText'-logic diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 5ae57de0204d528b45a8d76018d75c235cd9249e..675201e622fa5a5645263c90267188deb31ff301 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -427,7 +427,7 @@ class ExtHostSourceControl implements vscode.SourceControl { this._acceptInputCommand = acceptInputCommand; - const internal = this._commands.converter.toInternal2(acceptInputCommand, this._acceptInputDisposables.value); + const internal = this._commands.converter.toInternal(acceptInputCommand, this._acceptInputDisposables.value); this._proxy.$updateSourceControl(this.handle, { acceptInputCommand: internal }); } @@ -447,7 +447,7 @@ class ExtHostSourceControl implements vscode.SourceControl { this._statusBarCommands = statusBarCommands; - const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal2(c, this._statusBarDisposables.value!)) as CommandDto[]; + const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal(c, this._statusBarDisposables.value!)) as CommandDto[]; this._proxy.$updateSourceControl(this.handle, { statusBarCommands: internal }); } diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index ac0c9878b0e87c98ab851fc141f9452cc5423758..0524871953aa5683fc3e931c7705d9ca7ef7b6e2 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -452,7 +452,7 @@ class ExtHostTreeView extends Disposable { description: extensionTreeItem.description, resourceUri: extensionTreeItem.resourceUri, tooltip: typeof extensionTreeItem.tooltip === 'string' ? extensionTreeItem.tooltip : undefined, - command: extensionTreeItem.command ? this.commands.toInternal2(extensionTreeItem.command, disposable) : undefined, + command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command, disposable) : undefined, contextValue: extensionTreeItem.contextValue, icon, iconDark: this.getDarkIconPath(extensionTreeItem) || icon, diff --git a/src/vs/workbench/browser/media/icons.css b/src/vs/workbench/browser/media/icons.css index 1d000db29a08bbefc3730d3c46d0c5182074e45b..05465d73aa370af02dfe741d476260cd3dd61fbe 100644 --- a/src/vs/workbench/browser/media/icons.css +++ b/src/vs/workbench/browser/media/icons.css @@ -67,8 +67,13 @@ body[data-exploration^="icon-exploration"] .monaco-workbench .part > .title > .t body[data-exploration^="icon-exploration"] .monaco-workbench .part > .title > .title-actions .actions-container[aria-label="Source Control: Git actions"] .icon[data-title="git.refresh"], body[data-exploration^="icon-exploration"] .monaco-workbench .part > .title > .title-actions .actions-container[aria-label="Debug actions"] .icon, body[data-exploration^="icon-exploration"] .monaco-workbench .part > .title > .title-actions .actions-container[aria-label^="Extensions"] .icon, -body[data-exploration^="icon-exploration"] .scm-viewlet .monaco-list-row > .resource-group > .actions .action-label[data-title^="git."], -body[data-exploration^="icon-exploration"] .scm-viewlet .monaco-list-row > .resource > .name > .monaco-icon-label > .actions .action-label, +body[data-exploration^="icon-exploration"] .scm-viewlet .monaco-list-row > .resource > .name > .monaco-icon-label > .actions .action-label[data-title^="git.unstage"], +body[data-exploration^="icon-exploration"] .scm-viewlet .monaco-list-row > .resource > .name > .monaco-icon-label > .actions .action-label[data-title^="git.stage"], +body[data-exploration^="icon-exploration"] .scm-viewlet .monaco-list-row > .resource > .name > .monaco-icon-label > .actions .action-label[data-title^="git.openChange"], +body[data-exploration^="icon-exploration"] .scm-viewlet .monaco-list-row > .resource > .name > .monaco-icon-label > .actions .action-label[data-title^="git.clean"], +body[data-exploration^="icon-exploration"] .scm-viewlet .monaco-list-row > .resource-group > .actions .action-label[data-title="git.cleanAll"], +body[data-exploration^="icon-exploration"] .scm-viewlet .monaco-list-row > .resource-group > .actions .action-label[data-title="git.stageAll"], +body[data-exploration^="icon-exploration"] .scm-viewlet .monaco-list-row > .resource-group > .actions .action-label[data-title="git.unstageAll"], body[data-exploration^="icon-exploration"] .monaco-workbench .part > .content > .debug-viewlet .actions .action-label.icon, body[data-exploration^="icon-exploration"] .monaco-workbench .debug-toolbar .drag-area, body[data-exploration^="icon-exploration"] .monaco-workbench .debug-toolbar .action-label, diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 21cae306f4e9ff06e9e1cbdb3d90900b39f31f76..6590a90c12c949813773711dd13f1bec51fa572d 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -327,6 +327,7 @@ export class BreadcrumbsControl { // show picker let picker: BreadcrumbsPicker; + let pickerAnchor: { x: number; y: number }; let editor = this._getActiveCodeEditor(); let editorDecorations: string[] = []; let editorViewState: ICodeEditorViewState | undefined; @@ -393,34 +394,37 @@ export class BreadcrumbsControl { ); }, getAnchor: () => { - let maxInnerWidth = window.innerWidth - 8 /*a little less the full widget*/; - let maxHeight = Math.min(window.innerHeight * 0.7, 300); - - let pickerWidth = Math.min(maxInnerWidth, Math.max(240, maxInnerWidth / 4.17)); - let pickerArrowSize = 8; - let pickerArrowOffset: number; - - let data = dom.getDomNodePagePosition(event.node.firstChild as HTMLElement); - let y = data.top + data.height + pickerArrowSize; - if (y + maxHeight >= window.innerHeight) { - maxHeight = window.innerHeight - y - 30 /* room for shadow and status bar*/; - } - let x = data.left; - if (x + pickerWidth >= maxInnerWidth) { - x = maxInnerWidth - pickerWidth; - } - if (event.payload instanceof StandardMouseEvent) { - let maxPickerArrowOffset = pickerWidth - 2 * pickerArrowSize; - pickerArrowOffset = event.payload.posx - x; - if (pickerArrowOffset > maxPickerArrowOffset) { - x = Math.min(maxInnerWidth - pickerWidth, x + pickerArrowOffset - maxPickerArrowOffset); - pickerArrowOffset = maxPickerArrowOffset; + if (!pickerAnchor) { + let maxInnerWidth = window.innerWidth - 8 /*a little less the full widget*/; + let maxHeight = Math.min(window.innerHeight * 0.7, 300); + + let pickerWidth = Math.min(maxInnerWidth, Math.max(240, maxInnerWidth / 4.17)); + let pickerArrowSize = 8; + let pickerArrowOffset: number; + + let data = dom.getDomNodePagePosition(event.node.firstChild as HTMLElement); + let y = data.top + data.height + pickerArrowSize; + if (y + maxHeight >= window.innerHeight) { + maxHeight = window.innerHeight - y - 30 /* room for shadow and status bar*/; + } + let x = data.left; + if (x + pickerWidth >= maxInnerWidth) { + x = maxInnerWidth - pickerWidth; + } + if (event.payload instanceof StandardMouseEvent) { + let maxPickerArrowOffset = pickerWidth - 2 * pickerArrowSize; + pickerArrowOffset = event.payload.posx - x; + if (pickerArrowOffset > maxPickerArrowOffset) { + x = Math.min(maxInnerWidth - pickerWidth, x + pickerArrowOffset - maxPickerArrowOffset); + pickerArrowOffset = maxPickerArrowOffset; + } + } else { + pickerArrowOffset = (data.left + (data.width * 0.3)) - x; } - } else { - pickerArrowOffset = (data.left + (data.width * 0.3)) - x; + picker.show(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset)); + pickerAnchor = { x, y }; } - picker.show(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset)); - return { x, y }; + return pickerAnchor; }, onHide: (data) => { if (editor) { diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 40c05858aea6e8c5cc29a3e4b068dbe8f5f7d928..3c6b5806aaf9890e9264f3980cb2abb91b3be09f 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -34,6 +34,8 @@ 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'; export abstract class MenubarControl extends Disposable { @@ -106,8 +108,6 @@ export abstract class MenubarControl extends Disposable { this.menuUpdater = this._register(new RunOnceScheduler(() => this.doUpdateMenubar(false), 200)); this.notifyUserOfCustomMenubarAccessibility(); - - this.registerListeners(); } protected abstract doUpdateMenubar(firstTime: boolean): void; @@ -300,6 +300,8 @@ export class NativeMenubarControl extends MenubarControl { this.doUpdateMenubar(true); }); + + this.registerListeners(); } protected doUpdateMenubar(firstTime: boolean): void { @@ -457,7 +459,8 @@ export class CustomMenubarControl extends MenubarControl { @IPreferencesService preferencesService: IPreferencesService, @IEnvironmentService environmentService: IEnvironmentService, @IAccessibilityService accessibilityService: IAccessibilityService, - @IThemeService private readonly themeService: IThemeService + @IThemeService private readonly themeService: IThemeService, + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { super( @@ -482,6 +485,8 @@ export class CustomMenubarControl extends MenubarControl { this.recentlyOpened = recentlyOpened; }); + this.registerListeners(); + registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const menubarActiveWindowFgColor = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND); if (menubarActiveWindowFgColor) { @@ -642,7 +647,7 @@ export class CustomMenubarControl extends MenubarControl { enableMenuBarMnemonics = true; } - return enableMenuBarMnemonics; + return enableMenuBarMnemonics && (!isWeb || isFullscreen()); } private setupCustomMenubar(firstTime: boolean): void { @@ -750,6 +755,11 @@ export class CustomMenubarControl extends MenubarControl { this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, () => { this.menubar.blur(); })); + + // Mnemonics require fullscreen in web + if (isWeb) { + this._register(this.layoutService.onFullscreenChange(e => this.updateMenubar())); + } } get onVisibilityChange(): Event { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 07fe800bd64ef9522f048d17b3dbba95ada13c78..5b46af70bad683b9bafef45d51d1bc50c16b8d82 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -6,7 +6,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import * as nls from 'vs/nls'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform'; // Configuration (function registerConfiguration(): void { @@ -278,6 +278,34 @@ import { isMacintosh } from 'vs/base/common/platform'; 'type': 'string', 'default': isMacintosh ? '${activeEditorShort}${separator}${rootName}' : '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}', 'markdownDescription': windowTitleDescription + }, + 'window.menuBarVisibility': { + 'type': 'string', + 'enum': ['default', 'visible', 'toggle', 'hidden'], + 'enumDescriptions': [ + nls.localize('window.menuBarVisibility.default', "Menu is only hidden in full screen mode."), + nls.localize('window.menuBarVisibility.visible', "Menu is always visible even in full screen mode."), + nls.localize('window.menuBarVisibility.toggle', "Menu is hidden but can be displayed via Alt key."), + nls.localize('window.menuBarVisibility.hidden', "Menu is always hidden.") + ], + 'default': 'default', + 'scope': ConfigurationScope.APPLICATION, + 'description': nls.localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and a single press of the Alt key will show it. By default, the menu bar will be visible, unless the window is full screen."), + 'included': isWindows || isLinux || isWeb + }, + 'window.enableMenuBarMnemonics': { + 'type': 'boolean', + 'default': true, + 'scope': ConfigurationScope.APPLICATION, + 'description': nls.localize('enableMenuBarMnemonics', "If enabled, the main menus can be opened via Alt-key shortcuts. Disabling mnemonics allows to bind these Alt-key shortcuts to editor commands instead."), + 'included': isWindows || isLinux || isWeb + }, + 'window.disableCustomMenuBarAltFocus': { + 'type': 'boolean', + 'default': false, + 'scope': ConfigurationScope.APPLICATION, + 'markdownDescription': nls.localize('disableCustomMenuBarAltFocus', "If enabled, disables the ability to focus the menu bar with the Alt-key when not set to toggle."), + 'included': isWindows || isLinux || isWeb } } }); diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index af9817c980794af0ea18b5230976d70541b7ab6e..317b77e3e78ff8ebce7a4476a312ac9f7f02a04f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -73,8 +73,8 @@ export class ConfigurationManager implements IConfigurationManager { this.adapterDescriptorFactories = []; this.debuggers = []; this.toDispose = []; - this.registerListeners(lifecycleService); this.initLaunches(); + this.registerListeners(lifecycleService); const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); const previousSelectedLaunch = this.launches.filter(l => l.uri.toString() === previousSelectedRoot).pop(); this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 12347d6ea7e0c84f08cf3fe0ec5045cb5a95b4f9..fa261bc537bd6bba7a12c3002d2ca83b7fd8dc1c 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -4,74 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { join } from 'vs/base/common/path'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/contrib/output/common/output'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { OpenLogsFolderAction, SetLogLevelAction } from 'vs/workbench/contrib/logs/common/logsActions'; -import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; -import { dirname, joinPath } from 'vs/base/common/resources'; -import { IRemoteAgentService, RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { SetLogLevelAction } from 'vs/workbench/contrib/logs/common/logsActions'; -class LogOutputChannels extends Disposable implements IWorkbenchContribution { - - constructor( - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @ILogService logService: ILogService, - @IFileService private readonly fileService: IFileService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService - ) { - super(); - this.registerLogChannel(Constants.mainLogChannelId, nls.localize('mainLog', "Main"), URI.file(join(environmentService.logsPath, `main.log`))); - this.registerLogChannel(Constants.sharedLogChannelId, nls.localize('sharedLog', "Shared"), URI.file(join(environmentService.logsPath, `sharedprocess.log`))); - this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), URI.file(join(environmentService.logsPath, `renderer${environmentService.configuration.windowId}.log`))); - remoteAgentService.getEnvironment().then(remoteEnv => { - if (remoteEnv) { - const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); - outputChannelRegistry.registerChannel({ id: 'remoteExtensionLog', label: nls.localize('remoteExtensionLog', "Remote Server"), file: joinPath(remoteEnv.logsPath, `${RemoteExtensionLogFileName}.log`), log: true }); - } - }); - - const registerTelemetryChannel = (level: LogLevel) => { - if (level === LogLevel.Trace && !Registry.as(OutputExt.OutputChannels).getChannel(Constants.telemetryLogChannelId)) { - this.registerLogChannel(Constants.telemetryLogChannelId, nls.localize('telemetryLog', "Telemetry"), URI.file(join(environmentService.logsPath, `telemetry.log`))); - } - }; - registerTelemetryChannel(logService.getLevel()); - logService.onDidChangeLogLevel(registerTelemetryChannel); - - const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); - const devCategory = nls.localize('developer', "Developer"); - workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory); - workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.LABEL), 'Developer: Set Log Level...', devCategory); - } - - private async registerLogChannel(id: string, label: string, file: URI): Promise { - const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); - const exists = await this.fileService.exists(file); - if (exists) { - outputChannelRegistry.registerChannel({ id, label, file, log: true }); - return; - } - - const watcher = this.fileService.watch(dirname(file)); - const disposable = this.fileService.onFileChanges(e => { - if (e.contains(file, FileChangeType.ADDED)) { - watcher.dispose(); - disposable.dispose(); - outputChannelRegistry.registerChannel({ id, label, file, log: true }); - } - }); - } - -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored); \ No newline at end of file +const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); +const devCategory = nls.localize('developer', "Developer"); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.LABEL), 'Developer: Set Log Level...', devCategory); \ No newline at end of file diff --git a/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts new file mode 100644 index 0000000000000000000000000000000000000000..d8d381fa5252bc8e2e607ad4b4207a34dc2949ba --- /dev/null +++ b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { join } from 'vs/base/common/path'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/contrib/output/common/output'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; +import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { OpenLogsFolderAction } from 'vs/workbench/contrib/logs/common/logsActions'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; +import { dirname } from 'vs/base/common/resources'; + +class LogOutputChannels extends Disposable implements IWorkbenchContribution { + + constructor( + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @ILogService logService: ILogService, + @IFileService private readonly fileService: IFileService + ) { + super(); + this.registerLogChannel(Constants.mainLogChannelId, nls.localize('mainLog', "Main"), URI.file(join(environmentService.logsPath, `main.log`))); + this.registerLogChannel(Constants.sharedLogChannelId, nls.localize('sharedLog', "Shared"), URI.file(join(environmentService.logsPath, `sharedprocess.log`))); + this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), URI.file(join(environmentService.logsPath, `renderer${environmentService.configuration.windowId}.log`))); + + const registerTelemetryChannel = (level: LogLevel) => { + if (level === LogLevel.Trace && !Registry.as(OutputExt.OutputChannels).getChannel(Constants.telemetryLogChannelId)) { + this.registerLogChannel(Constants.telemetryLogChannelId, nls.localize('telemetryLog', "Telemetry"), URI.file(join(environmentService.logsPath, `telemetry.log`))); + } + }; + registerTelemetryChannel(logService.getLevel()); + logService.onDidChangeLogLevel(registerTelemetryChannel); + + const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); + const devCategory = nls.localize('developer', "Developer"); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory); + } + + private async registerLogChannel(id: string, label: string, file: URI): Promise { + const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); + const exists = await this.fileService.exists(file); + if (exists) { + outputChannelRegistry.registerChannel({ id, label, file, log: true }); + return; + } + + const watcher = this.fileService.watch(dirname(file)); + const disposable = this.fileService.onFileChanges(e => { + if (e.contains(file, FileChangeType.ADDED)) { + watcher.dispose(); + disposable.dispose(); + outputChannelRegistry.registerChannel({ id, label, file, log: true }); + } + }); + } + +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored); \ No newline at end of file diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 1bfb9d2439a3287cc5c503b36103d2f6d9e99281..f169fda95d143320afb5d1f2a8fb8426c65103a6 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -10,9 +10,12 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { isWeb, OperatingSystem } from 'vs/base/common/platform'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Schemas } from 'vs/base/common/network'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IRemoteAgentService, RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; import { ILogService } from 'vs/platform/log/common/log'; import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; +import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/contrib/output/common/output'; +import { localize } from 'vs/nls'; +import { joinPath } from 'vs/base/common/resources'; export class LabelContribution implements IWorkbenchContribution { constructor( @@ -54,6 +57,21 @@ class RemoteChannelsContribution implements IWorkbenchContribution { } } +class RemoteLogOutputChannels implements IWorkbenchContribution { + + constructor( + @IRemoteAgentService remoteAgentService: IRemoteAgentService + ) { + remoteAgentService.getEnvironment().then(remoteEnv => { + if (remoteEnv) { + const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); + outputChannelRegistry.registerChannel({ id: 'remoteExtensionLog', label: localize('remoteExtensionLog', "Remote Server"), file: joinPath(remoteEnv.logsPath, `${RemoteExtensionLogFileName}.log`), log: true }); + } + }); + } +} + const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(LabelContribution, LifecyclePhase.Starting); workbenchContributionsRegistry.registerWorkbenchContribution(RemoteChannelsContribution, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteLogOutputChannels, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/scm/browser/scmActivity.ts b/src/vs/workbench/contrib/scm/browser/scmActivity.ts index 2bcd79a66f87db7a3d713bb07799916bec42f6c0..de8d68429cfca679f364c0c8f0b77320213f2b00 100644 --- a/src/vs/workbench/contrib/scm/browser/scmActivity.ts +++ b/src/vs/workbench/contrib/scm/browser/scmActivity.ts @@ -189,20 +189,12 @@ export class StatusBarController implements IWorkbenchContribution { const disposables = new DisposableStore(); for (const c of commands) { - const statusId = `status.scm.${repository.provider.id}.${c.tooltip}`; // needs to be unique, but c.id is too random - let statusLabel: string; - if (c.tooltip) { - statusLabel = localize('status.scm', "Source Control ({0}): {1}", repository.provider.label, c.tooltip.replace('...', '')); - } else { - statusLabel = localize('status.scm.short', "Source Control ({0})", repository.provider.label); - } - disposables.add(this.statusbarService.addEntry({ text: c.title, tooltip: `${label} - ${c.tooltip}`, command: c.id, arguments: c.arguments - }, statusId, statusLabel, MainThreadStatusBarAlignment.LEFT, 10000)); + }, 'status.scm', localize('status.scm', "Source Control"), MainThreadStatusBarAlignment.LEFT, 10000)); } this.statusBarDisposable = disposables; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 8a62400e086eae943da012d018c247a98ce9b4d2..e2e01bbb8b6ceddb4ab75ca6e0b34723d6dd5724 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -23,7 +23,7 @@ import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/wor import { AllowWorkspaceShellTerminalCommand, ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, DisallowWorkspaceShellTerminalCommand, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; -import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, ITerminalService, TERMINAL_ACTION_CATEGORY } from 'vs/workbench/contrib/terminal/common/terminal'; import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; @@ -302,7 +302,7 @@ actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionTerm Registry.as(panel.Extensions.Panels).setDefaultPanelId(TERMINAL_PANEL_ID); // On mac cmd+` is reserved to cycle between windows, that's why the keybindings use WinCtrl -const category = nls.localize('terminalCategory', "Terminal"); +const category = TERMINAL_ACTION_CATEGORY; const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.LABEL), 'Terminal: Kill the Active Terminal Instance', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, CopyTerminalSelectionAction.LABEL, { diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index be1202ac526b557fe65fa3eb82d60324ab3ebbee..7c7d7824119f82df7d653dae9da4e0fcb6857211 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { RawContextKey, ContextKeyExpr, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -56,6 +57,8 @@ export const TerminalCursorStyle = { export const TERMINAL_CONFIG_SECTION = 'terminal.integrated'; +export const TERMINAL_ACTION_CATEGORY = nls.localize('terminalCategory', "Terminal"); + export const DEFAULT_LETTER_SPACING = 0; export const MINIMUM_LETTER_SPACING = -5; export const DEFAULT_LINE_HEIGHT = 1; diff --git a/src/vs/workbench/contrib/terminal/common/terminalCommands.ts b/src/vs/workbench/contrib/terminal/common/terminalCommands.ts index 925800419235edf95fe2c61b60e3c673a2de2a3e..8dd482ffbab7694d526c0b53f55df61c9b448cd4 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalCommands.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalCommands.ts @@ -22,6 +22,7 @@ export const enum TERMINAL_COMMAND_ID { MOVE_TO_LINE_START = 'workbench.action.terminal.moveToLineStart', MOVE_TO_LINE_END = 'workbench.action.terminal.moveToLineEnd', NEW = 'workbench.action.terminal.new', + NEW_LOCAL = 'workbench.action.terminal.newLocal', NEW_IN_ACTIVE_WORKSPACE = 'workbench.action.terminal.newInActiveWorkspace', SPLIT = 'workbench.action.terminal.split', SPLIT_IN_ACTIVE_WORKSPACE = 'workbench.action.terminal.splitInActiveWorkspace', diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts index 423284f97a5333250ab6d25e5ac60ccb90e5b8a3..12406462f44030632a468e5286844f6db62fa41f 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts @@ -13,6 +13,9 @@ import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/termi import { execFile } from 'child_process'; import { Emitter, Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { registerRemoteContributions } from 'vs/workbench/contrib/terminal/node/terminalRemote'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { isWeb } from 'vs/base/common/platform'; export class TerminalNativeService implements ITerminalNativeService { public _serviceBrand: any; @@ -27,9 +30,17 @@ export class TerminalNativeService implements ITerminalNativeService { constructor( @IFileService private readonly _fileService: IFileService, @IInstantiationService readonly instantiationService: IInstantiationService, + @IRemoteAgentService remoteAgentService: IRemoteAgentService ) { ipc.on('vscode:openFiles', (_event: any, request: IOpenFileRequest) => this._onOpenFileRequest.fire(request)); ipc.on('vscode:osResume', () => this._onOsResume.fire()); + + if (!isWeb) { + const connection = remoteAgentService.getConnection(); + if (connection && connection.remoteAuthority) { + registerRemoteContributions(); + } + } } public whenFileDeleted(path: URI): Promise { diff --git a/src/vs/workbench/contrib/terminal/node/terminalRemote.ts b/src/vs/workbench/contrib/terminal/node/terminalRemote.ts new file mode 100644 index 0000000000000000000000000000000000000000..e264cdfdd5d5db52f1108a28fb2da956df278ca7 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/node/terminalRemote.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; +import { Action } from 'vs/base/common/actions'; +import { URI } from 'vs/base/common/uri'; +import { homedir } from 'os'; + +export function registerRemoteContributions() { + const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); + actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewLocalTerminalAction, CreateNewLocalTerminalAction.ID, CreateNewLocalTerminalAction.LABEL), 'Terminal: Create New Integrated Terminal (Local)', TERMINAL_ACTION_CATEGORY); +} + +export class CreateNewLocalTerminalAction extends Action { + public static readonly ID = TERMINAL_COMMAND_ID.NEW_LOCAL; + public static readonly LABEL = nls.localize('workbench.action.terminal.newLocal', "Create New Integrated Terminal (Local)"); + + constructor( + id: string, label: string, + @ITerminalService private readonly terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): Promise { + const instance = this.terminalService.createTerminal({ cwd: URI.file(homedir()) }); + if (!instance) { + return Promise.resolve(undefined); + } + + // Append (Local) to the first title that comes back, the title will then become static + const disposable = instance.onTitleChanged(() => { + if (instance.title && instance.title.trim().length > 0) { + disposable.dispose(); + instance.setTitle(`${instance.title} (Local)`, false); + } + }); + + this.terminalService.setActiveInstance(instance); + return this.terminalService.showPanel(true); + } +} diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts index e0110601c82480bc4ef9529272c1c9d1357ef69c..3d2cf17654958fcb1f58fb1524588a6b2fc5c1d3 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts @@ -16,7 +16,7 @@ suite('Workbench - TerminalConfigHelper', () => { fixture = document.body; }); - test('TerminalConfigHelper - getFont fontFamily', function () { + test.skip('TerminalConfigHelper - getFont fontFamily', function () { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: 'bar' } }); diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index d41618e61927fbfc5d0434194571745e405cf533..971cbc0de23ef7e6747b971ef752be4749194082 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -123,16 +123,18 @@ navigator.serviceWorker.register('service-worker.js'); navigator.serviceWorker.ready.then(registration => { - host.onMessage('loaded-resource', event => { - registration.active.postMessage({ channel: 'loaded-resource', data: event.data.args }); - }); + function forwardFromHostToWorker(channel) { + host.onMessage(channel, event => { + registration.active.postMessage({ channel: channel, data: event.data.args }); + }); + } + forwardFromHostToWorker('did-load-resource'); + forwardFromHostToWorker('did-load-localhost'); }); navigator.serviceWorker.addEventListener('message', event => { - switch (event.data.channel) { - case 'load-resource': - host.postMessage('load-resource', { path: event.data.path }); - return; + if (['load-resource', 'load-localhost'].includes(event.data.channel)) { + host.postMessage(event.data.channel, event.data); } }); } diff --git a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js index 6c28c8ea02fae562eb15e0780507b8c783c96c43..d6154ba645d82d0b2c62411b6a98aa4b8e9ba078 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js +++ b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js @@ -8,25 +8,26 @@ const resourceRoot = '/vscode-resource'; /** + * @template T * @typedef {{ - * resolve: () => void, - * promise: Promise - * }} ResourcePathEntry + * resolve: (x: T) => void, + * promise: Promise + * }} RequestStoreEntry */ /** - * Map of requested paths to responses. + * @template T */ -const resourceRequestManager = new class ResourceRequestManager { +class RequestStore { constructor() { - /** @type {Map} */ + /** @type {Map>} */ this.map = new Map(); } /** * @param {string} webviewId * @param {string} path - * @return {ResourcePathEntry | undefined} + * @return {RequestStoreEntry | undefined} */ get(webviewId, path) { return this.map.get(this._key(webviewId, path)); @@ -35,19 +36,31 @@ const resourceRequestManager = new class ResourceRequestManager { /** * @param {string} webviewId * @param {string} path - * @return {boolean} */ - has(webviewId, path) { - return this.map.has(this._key(webviewId, path)); + create(webviewId, path) { + const existing = this.get(webviewId, path); + if (existing) { + return existing.promise; + } + let resolve; + const promise = new Promise(r => resolve = r); + this.map.set(this._key(webviewId, path), { resolve, promise }); + return promise; } /** * @param {string} webviewId * @param {string} path - * @param {ResourcePathEntry} entry + * @param {T} result + * @return {boolean} */ - set(webviewId, path, entry) { - this.map.set(this._key(webviewId, path), entry); + resolve(webviewId, path, result) { + const entry = this.get(webviewId, path); + if (!entry) { + return false; + } + entry.resolve(result); + return true; } /** @@ -58,31 +71,49 @@ const resourceRequestManager = new class ResourceRequestManager { _key(webviewId, path) { return `${webviewId}@@@${path}`; } -}(); +} + +/** + * Map of requested paths to responses. + * + * @type {RequestStore<{ body: any, mime: string } | undefined>} + */ +const resourceRequestStore = new RequestStore(); + +/** + * Map of requested localhost origins to optional redirects. + * + * @type {RequestStore} + */ +const localhostRequestStore = new RequestStore(); const notFoundResponse = new Response('Not Found', { status: 404, }); + self.addEventListener('message', (event) => { switch (event.data.channel) { - case 'loaded-resource': + case 'did-load-resource': { const webviewId = getWebviewIdForClient(event.source); const data = event.data.data; - const target = resourceRequestManager.get(webviewId, data.path); - if (!target) { - console.log('Loaded unknown resource', data.path); - return; + const response = data.status === 200 + ? { body: data.data, mime: data.mime } + : undefined; + + if (!resourceRequestStore.resolve(webviewId, data.path, response)) { + console.log('Could not resolve unknown resource', data.path); } + return; + } - if (data.status === 200) { - target.resolve(new Response(data.data, { - status: 200, - headers: { 'Content-Type': data.mime }, - })); - } else { - target.resolve(notFoundResponse.clone()); + case 'did-load-localhost': + { + const webviewId = getWebviewIdForClient(event.source); + const data = event.data.data; + if (!localhostRequestStore.resolve(webviewId, data.origin, data.location)) { + console.log('Could not resolve unknown localhost', data.origin); } return; } @@ -94,59 +125,123 @@ self.addEventListener('message', (event) => { self.addEventListener('fetch', (event) => { const requestUrl = new URL(event.request.url); - if (!requestUrl.pathname.startsWith(resourceRoot + '/')) { - return event.respondWith(fetch(event.request)); + // See if it's a resource request + if (requestUrl.origin === self.origin && requestUrl.pathname.startsWith(resourceRoot + '/')) { + return event.respondWith(processResourceRequest(event, requestUrl)); + } + + // See if it's a localhost request + if (requestUrl.origin !== self.origin && requestUrl.host.match(/^localhost:(\d+)$/)) { + return event.respondWith(processLocalhostRequest(event, requestUrl)); + } +}); + +self.addEventListener('install', (event) => { + event.waitUntil(self.skipWaiting()); // Activate worker immediately +}); + +self.addEventListener('activate', (event) => { + event.waitUntil(self.clients.claim()); // Become available to all pages +}); + +async function processResourceRequest(event, requestUrl) { + const client = await self.clients.get(event.clientId); + if (!client) { + console.log('Could not find inner client for request'); + return notFoundResponse.clone(); } - event.respondWith((async () => { - const client = await self.clients.get(event.clientId); - if (!client) { - console.log('Could not find inner client for request'); + const webviewId = getWebviewIdForClient(client); + const resourcePath = requestUrl.pathname.replace(resourceRoot, ''); + + function resolveResourceEntry(entry) { + if (!entry) { return notFoundResponse.clone(); } + return new Response(entry.body, { + status: 200, + headers: { 'Content-Type': entry.mime } + }); + } + + const parentClient = await getOuterIframeClient(webviewId); + if (!parentClient) { + console.log('Could not find parent client for request'); + return notFoundResponse.clone(); + } - const webviewId = getWebviewIdForClient(client); - const resourcePath = requestUrl.pathname.replace(resourceRoot, ''); + // Check if we've already resolved this request + const existing = resourceRequestStore.get(webviewId, resourcePath); + if (existing) { + return existing.promise.then(resolveResourceEntry); + } - const allClients = await self.clients.matchAll({ includeUncontrolled: true }); + parentClient.postMessage({ + channel: 'load-resource', + path: resourcePath + }); - // Check if we've already resolved this request - const existing = resourceRequestManager.get(webviewId, resourcePath); - if (existing) { - return existing.promise.then(r => r.clone()); - } + return resourceRequestStore.create(webviewId, resourcePath) + .then(resolveResourceEntry); +} - // Find parent iframe - for (const client of allClients) { - const clientUrl = new URL(client.url); - if (clientUrl.pathname === '/' && clientUrl.search.match(new RegExp('\\bid=' + webviewId))) { - client.postMessage({ - channel: 'load-resource', - path: resourcePath - }); - - let resolve; - const promise = new Promise(r => resolve = r); - resourceRequestManager.set(webviewId, resourcePath, { resolve, promise }); - return promise.then(r => r.clone()); - } +/** + * @param {*} event + * @param {URL} requestUrl + */ +async function processLocalhostRequest(event, requestUrl) { + const client = await self.clients.get(event.clientId); + if (!client) { + // This is expected when requesting resources on other localhost ports + // that are not spawned by vs code + return undefined; + } + const webviewId = getWebviewIdForClient(client); + const origin = requestUrl.origin; + + const resolveRedirect = redirectOrigin => { + if (!redirectOrigin) { + return fetch(event.request); } + const location = event.request.url.replace(new RegExp(`^${requestUrl.origin}(/|$)`), `${redirectOrigin}$1`); + return new Response(null, { + status: 302, + headers: { + Location: location + } + }); + }; + const parentClient = await getOuterIframeClient(webviewId); + if (!parentClient) { console.log('Could not find parent client for request'); return notFoundResponse.clone(); - })()); -}); + } -self.addEventListener('install', (event) => { - event.waitUntil(self.skipWaiting()); // Activate worker immediately -}); + // Check if we've already resolved this request + const existing = localhostRequestStore.get(webviewId, origin); + if (existing) { + return existing.promise.then(resolveRedirect); + } -self.addEventListener('activate', (event) => { - event.waitUntil(self.clients.claim()); // Become available to all pages -}); + parentClient.postMessage({ + channel: 'load-localhost', + origin: origin + }); + return localhostRequestStore.create(webviewId, origin) + .then(resolveRedirect); +} function getWebviewIdForClient(client) { const requesterClientUrl = new URL(client.url); return requesterClientUrl.search.match(/\bid=([a-z0-9-]+)/i)[1]; } + +async function getOuterIframeClient(webviewId) { + const allClients = await self.clients.matchAll({ includeUncontrolled: true }); + return allClients.find(client => { + const clientUrl = new URL(client.url); + return clientUrl.pathname === '/' && clientUrl.search.match(new RegExp('\\bid=' + webviewId)); + }); +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 4e2f29a1997680e864c90bbc938421fb2b83d016..98cfb4c089138065450e603c30b7f74dc9d830c8 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -15,6 +15,8 @@ import { addDisposableListener, addClass } from 'vs/base/browser/dom'; import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { loadLocalResource } from 'vs/workbench/contrib/webview/common/resourceLoader'; +import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; interface WebviewContent { readonly html: string; @@ -32,11 +34,14 @@ export class IFrameWebview extends Disposable implements Webview { private readonly id: string; + private readonly _portMappingManager: WebviewPortMappingManager; + constructor( private _options: WebviewOptions, contentOptions: WebviewContentOptions, @IThemeService themeService: IThemeService, @IEnvironmentService environmentService: IEnvironmentService, + @ITunnelService tunnelService: ITunnelService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly _configurationService: IConfigurationService, ) { @@ -45,6 +50,12 @@ export class IFrameWebview extends Disposable implements Webview { throw new Error('To use iframe based webviews, you must configure `environmentService.webviewEndpoint`'); } + this._portMappingManager = this._register(new WebviewPortMappingManager( + this._options.extension ? this._options.extension.location : undefined, + () => this.content.options.portMappings || [], + tunnelService + )); + this.content = { html: '', options: contentOptions, @@ -103,11 +114,16 @@ export class IFrameWebview extends Disposable implements Webview { case 'load-resource': { - const path = e.data.data.path; - const uri = URI.file(path); + const uri = URI.file(e.data.data.path); this.loadResource(uri); return; } + + case 'load-localhost': + { + this.localLocalhost(e.data.data.origin); + return; + } } })); @@ -289,7 +305,7 @@ export class IFrameWebview extends Disposable implements Webview { () => (this.content.options.localResourceRoots || [])); if (result.type === 'success') { - return this._send('loaded-resource', { + return this._send('did-load-resource', { status: 200, path: uri.path, mime: result.mimeType, @@ -300,10 +316,18 @@ export class IFrameWebview extends Disposable implements Webview { // noop } - return this._send('loaded-resource', { + return this._send('did-load-resource', { status: 404, path: uri.path }); } + + private async localLocalhost(origin: string) { + const redirect = await this._portMappingManager.getRedirect(origin); + return this._send('did-load-localhost', { + origin, + location: redirect + }); + } } diff --git a/src/vs/workbench/contrib/webview/common/portMapping.ts b/src/vs/workbench/contrib/webview/common/portMapping.ts new file mode 100644 index 0000000000000000000000000000000000000000..3a9b8ee056af1e796b9d177f6f73a2c443403817 --- /dev/null +++ b/src/vs/workbench/contrib/webview/common/portMapping.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import * as modes from 'vs/editor/common/modes'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; + +export class WebviewPortMappingManager extends Disposable { + + private readonly _tunnels = new Map>(); + + constructor( + private readonly extensionLocation: URI | undefined, + private readonly mappings: () => ReadonlyArray, + private readonly tunnelService: ITunnelService + ) { + super(); + } + + public async getRedirect(url: string): Promise { + const uri = URI.parse(url); + if (uri.scheme !== 'http' && uri.scheme !== 'https') { + return undefined; + } + + const localhostMatch = /^localhost:(\d+)$/.exec(uri.authority); + if (!localhostMatch) { + return undefined; + } + + const port = +localhostMatch[1]; + for (const mapping of this.mappings()) { + if (mapping.webviewPort === port) { + if (this.extensionLocation && this.extensionLocation.scheme === REMOTE_HOST_SCHEME) { + const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort); + if (tunnel) { + return url.replace( + new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}(/|$)`), + `${uri.scheme}://localhost:${tunnel.tunnelLocalPort}$1`); + } + } + + if (mapping.webviewPort !== mapping.extensionHostPort) { + return url.replace( + new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}(/|$)`), + `${uri.scheme}://localhost:${mapping.extensionHostPort}$1`); + } + } + } + + return undefined; + } + + dispose() { + super.dispose(); + + for (const tunnel of this._tunnels.values()) { + tunnel.then(tunnel => tunnel.dispose()); + } + this._tunnels.clear(); + } + + private getOrCreateTunnel(remotePort: number): Promise | undefined { + const existing = this._tunnels.get(remotePort); + if (existing) { + return existing; + } + const tunnel = this.tunnelService.openTunnel(remotePort); + if (tunnel) { + this._tunnels.set(remotePort, tunnel); + } + return tunnel; + } +} \ 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 b86e3120d0034bfa4a6c3a82f2b304c858ca429b..f0f599d10496d63542e9d690a1f9625ecc4c66e9 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -13,19 +13,17 @@ import { endsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; 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 { registerFileProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService'; import { WebviewFindWidget } from '../browser/webviewFindWidget'; -import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; export interface WebviewPortMapping { readonly port: number; @@ -141,88 +139,22 @@ class WebviewProtocolProvider extends Disposable { class WebviewPortMappingProvider extends Disposable { - private readonly _tunnels = new Map>(); + private readonly _manager: WebviewPortMappingManager; constructor( session: WebviewSession, extensionLocation: URI | undefined, mappings: () => ReadonlyArray, - private readonly tunnelService: ITunnelService, - extensionId: ExtensionIdentifier | undefined, - @ITelemetryService telemetryService: ITelemetryService + tunnelService: ITunnelService, ) { super(); + this._manager = this._register(new WebviewPortMappingManager(extensionLocation, mappings, tunnelService)); - let hasLogged = false; - - session.onBeforeRequest(async (details) => { - const uri = URI.parse(details.url); - if (uri.scheme !== 'http' && uri.scheme !== 'https') { - return undefined; - } - - const localhostMatch = /^localhost:(\d+)$/.exec(uri.authority); - if (localhostMatch) { - if (!hasLogged && extensionId) { - hasLogged = true; - - /* __GDPR__ - "webview.accessLocalhost" : { - "extension" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - telemetryService.publicLog('webview.accessLocalhost', { extension: extensionId.value }); - } - - const port = +localhostMatch[1]; - for (const mapping of mappings()) { - if (mapping.webviewPort === port) { - if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) { - const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort); - if (tunnel) { - return { - redirectURL: details.url.replace( - new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`), - `${uri.scheme}://localhost:${tunnel.tunnelLocalPort}/`) - }; - } - } - - if (mapping.webviewPort !== mapping.extensionHostPort) { - return { - redirectURL: details.url.replace( - new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`), - `${uri.scheme}://localhost:${mapping.extensionHostPort}/`) - }; - } - } - } - } - - return undefined; + session.onBeforeRequest(async details => { + const redirect = await this._manager.getRedirect(details.url); + return redirect ? { redirectURL: redirect } : undefined; }); } - - dispose() { - super.dispose(); - - for (const tunnel of this._tunnels.values()) { - tunnel.then(tunnel => tunnel.dispose()); - } - this._tunnels.clear(); - } - - private getOrCreateTunnel(remotePort: number): Promise | undefined { - const existing = this._tunnels.get(remotePort); - if (existing) { - return existing; - } - const tunnel = this.tunnelService.openTunnel(remotePort); - if (tunnel) { - this._tunnels.set(remotePort, tunnel); - } - return tunnel; - } } class SvgBlocker extends Disposable { @@ -370,10 +302,8 @@ export class WebviewElement extends Disposable implements Webview { contentOptions: WebviewContentOptions, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, - @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, @ITunnelService tunnelService: ITunnelService, - @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); @@ -420,8 +350,6 @@ export class WebviewElement extends Disposable implements Webview { _options.extension ? _options.extension.location : undefined, () => (this.content.options.portMappings || []), tunnelService, - _options.extension ? _options.extension.id : undefined, - telemetryService )); if (!this._options.allowSvgs) { diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 57ec2510560ecdccbcaec0c5e077fc2c7c036749..071c367c648f6d898685e476500c7b9eeac6f007 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -151,7 +151,7 @@ const extensionPacks: ExtensionSuggestion[] = [ // { name: localize('welcomePage.go', "Go"), id: 'lukehoban.go' }, { name: localize('welcomePage.php', "PHP"), id: 'felixfbecker.php-pack' }, { name: localize('welcomePage.azure', "Azure"), title: localize('welcomePage.showAzureExtensions', "Show Azure extensions"), id: 'workbench.extensions.action.showAzureExtensions', isCommand: true }, - { name: localize('welcomePage.docker', "Docker"), id: 'peterjausovec.vscode-docker' }, + { name: localize('welcomePage.docker', "Docker"), id: 'ms-azuretools.vscode-docker' }, ]; const keymapExtensions: ExtensionSuggestion[] = [ diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 52d70f2049b14902c6dd138109d512429cc7adfd..a38fbde646df11d7aa4baa3bda76fff8d1e7a4d5 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -591,34 +591,6 @@ import product from 'vs/platform/product/node/product'; 'default': false, 'description': nls.localize('closeWhenEmpty', "Controls whether closing the last editor should also close the window. This setting only applies for windows that do not show folders.") }, - 'window.menuBarVisibility': { - 'type': 'string', - 'enum': ['default', 'visible', 'toggle', 'hidden'], - 'enumDescriptions': [ - nls.localize('window.menuBarVisibility.default', "Menu is only hidden in full screen mode."), - nls.localize('window.menuBarVisibility.visible', "Menu is always visible even in full screen mode."), - nls.localize('window.menuBarVisibility.toggle', "Menu is hidden but can be displayed via Alt key."), - nls.localize('window.menuBarVisibility.hidden', "Menu is always hidden.") - ], - 'default': 'default', - 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and a single press of the Alt key will show it. By default, the menu bar will be visible, unless the window is full screen."), - 'included': isWindows || isLinux - }, - 'window.enableMenuBarMnemonics': { - 'type': 'boolean', - 'default': true, - 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('enableMenuBarMnemonics', "If enabled, the main menus can be opened via Alt-key shortcuts. Disabling mnemonics allows to bind these Alt-key shortcuts to editor commands instead."), - 'included': isWindows || isLinux - }, - 'window.disableCustomMenuBarAltFocus': { - 'type': 'boolean', - 'default': false, - 'scope': ConfigurationScope.APPLICATION, - 'markdownDescription': nls.localize('disableCustomMenuBarAltFocus', "If enabled, disables the ability to focus the menu bar with the Alt-key when not set to toggle."), - 'included': isWindows || isLinux - }, 'window.autoDetectHighContrast': { 'type': 'boolean', 'default': true, diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index c9a8760aa965a051acba363e69d82d8fa70bdb89..bcf4b31cef93792ae37ea25561f46369270fd759 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -19,8 +19,9 @@ import { AbstractVariableResolverService } from 'vs/workbench/services/configura import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IQuickInputService, IInputOptions, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; -import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { ConfiguredInput, IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IProcessEnvironment } from 'vs/base/common/platform'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export abstract class BaseConfigurationResolverService extends AbstractVariableResolverService { @@ -304,4 +305,6 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi ) { super(environmentService.configuration.userEnv, editorService, environmentService, configurationService, commandService, workspaceContextService, quickInputService); } -} \ No newline at end of file +} + +registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); \ 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 d224fa7383bd7070b0dc9f0504eef1cf2745e5f8..beb696c762b6efcf65bf5e5d6dbd6ad8a489666a 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -69,7 +69,7 @@ export class BrowserWorkbenchEnvironmentService implements IEnvironmentService { this.configuration.remoteAuthority = configuration.remoteAuthority; - this.appSettingsHome = joinPath(URI.revive(configuration.userDataUri), 'User'); + this.appSettingsHome = joinPath(URI.revive(JSON.parse(document.getElementById('vscode-remote-user-data-uri')!.getAttribute('data-settings')!)), 'User'); this.settingsResource = joinPath(this.appSettingsHome, 'settings.json'); this.keybindingsResource = joinPath(this.appSettingsHome, 'keybindings.json'); this.keyboardLayoutResource = joinPath(this.appSettingsHome, 'keyboardLayout.json'); diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index 7051709d15fd554ac7bdcf9da5fc3a42fb88dd2a..8c793ca7a8708640a1efcde8b7eefdd2bbd2a913 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -20,6 +20,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, ISearchComplete, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType, isFileMatch, isProgressMessage } from 'vs/workbench/services/search/common/search'; import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class SearchService extends Disposable implements ISearchService { _serviceBrand: any; @@ -421,3 +422,4 @@ export class RemoteSearchService extends SearchService { } } +registerSingleton(ISearchService, RemoteSearchService, true); diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 047d78070f36fc43b3f4e4b91cfb3172586bd3c4..8eb327c11c7aa5ea116f83b97ad97eb7a4e699ad 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -796,9 +796,9 @@ suite('ExtHostLanguageFeatureCommands', function () { })); await rpcProtocol.sync(); - let value = await commands.executeCommand('vscode.executeSelectionRangeProvider', model.uri, [new types.Position(0, 10)]); + let value = await commands.executeCommand('vscode.executeSelectionRangeProvider', model.uri, [new types.Position(0, 10)]); assert.equal(value.length, 1); - assert.ok(value[0].length >= 2); + assert.ok(value[0].parent); }); }); diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index ee92df6ca328a969219e2c8afbf9f0e1f775441e..a6c31adbe640ca6d71a3b393743dfa7463a77843 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -71,6 +71,8 @@ import { IRequestService } from 'vs/platform/request/node/request'; import { RequestService } from 'vs/platform/request/electron-browser/requestService'; import { LifecycleService } from 'vs/platform/lifecycle/electron-browser/lifecycleService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { DialogService } from 'vs/platform/dialogs/browser/dialogService'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { LocalizationsService } from 'vs/platform/localizations/electron-browser/localizationsService'; import { ISharedProcessService, SharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; @@ -90,8 +92,6 @@ import { IURLService } from 'vs/platform/url/common/url'; import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { TunnelService } from 'vs/workbench/services/remote/node/tunnelService'; -import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/electron-browser/configurationResolverService'; -import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; @@ -136,7 +136,9 @@ import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/notification/common/notificationService'; import 'vs/workbench/services/window/electron-browser/windowService'; import 'vs/workbench/services/telemetry/electron-browser/telemetryService'; +import 'vs/workbench/services/configurationResolver/electron-browser/configurationResolverService'; +registerSingleton(IDialogService, DialogService, true); registerSingleton(IMenuService, MenuService, true); registerSingleton(IListService, ListService, true); registerSingleton(IOpenerService, OpenerService, true); @@ -163,7 +165,6 @@ registerSingleton(IWorkspacesService, WorkspacesService); registerSingleton(IMenubarService, MenubarService); registerSingleton(IURLService, RelayURLService); registerSingleton(ITunnelService, TunnelService, true); -registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); registerSingleton(ICredentialsService, KeytarCredentialsService, true); //#endregion @@ -203,6 +204,7 @@ registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); // Logs import 'vs/workbench/contrib/logs/common/logs.contribution'; +import 'vs/workbench/contrib/logs/electron-browser/logs.contribution'; // Quick Open Handlers import 'vs/workbench/contrib/quickopen/browser/quickopen.contribution'; @@ -276,6 +278,7 @@ import { ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; registerSingleton(ITaskService, TaskService, true); // Remote +import 'vs/workbench/contrib/remote/common/remote.contribution'; import 'vs/workbench/contrib/remote/electron-browser/remote.contribution'; // Emmet diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 326b1f485c351bcfa932bde815f31de621b986a2..d8070d0ce4c01f48c6b1f6b713d4c38ec27817dc 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -6,16 +6,21 @@ import 'vs/workbench/workbench.web.main'; import { main } from 'vs/workbench/browser/web.main'; import { UriComponents } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; export interface IWorkbenchConstructionOptions { remoteAuthority: string; - userDataUri: UriComponents; - webviewEndpoint?: string; folderUri?: UriComponents; workspaceUri?: UriComponents; + + userData?: { + read(key: string): Promise; + write(key: string, value: string): Promise; + onDidChange: Event; + }; } function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise { diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 30e1a0f9daae37cff2fd811cb4f82750ed9ad48a..1b819ef81b27961408126ed0965b139e7c7dca5d 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -9,7 +9,6 @@ import 'vs/editor/editor.all'; import 'vs/workbench/api/browser/extensionHost.contribution'; -// import 'vs/workbench/electron-browser/main.contribution'; import 'vs/workbench/browser/workbench.contribution'; import 'vs/workbench/browser/web.main'; @@ -63,14 +62,15 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { TextResourceConfigurationService } from 'vs/editor/common/services/resourceConfigurationImpl'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { BrowserAccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; -import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; +// import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; // import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; // import { IRequestService } from 'vs/platform/request/node/request'; // import { RequestService } from 'vs/platform/request/electron-browser/requestService'; import { BrowserLifecycleService } from 'vs/platform/lifecycle/browser/lifecycleService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { DialogService } from 'vs/platform/dialogs/browser/dialogService'; // import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; // import { LocalizationsService } from 'vs/platform/localizations/electron-browser/localizationsService'; // import { ISharedProcessService, SharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; @@ -90,11 +90,8 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; // import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; // import { ITunnelService } from 'vs/platform/remote/common/tunnel'; // import { TunnelService } from 'vs/workbench/services/remote/node/tunnelService'; -import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; -import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; -import { ISearchService } from 'vs/workbench/services/search/common/search'; -import { RemoteSearchService } from 'vs/workbench/services/search/common/searchService'; -import 'vs/platform/dialogs/browser/dialogService'; +// import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +// import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; import 'vs/workbench/services/bulkEdit/browser/bulkEditService'; // import 'vs/workbench/services/integrity/node/integrityService'; import 'vs/workbench/services/keybinding/common/keybindingEditing'; @@ -102,7 +99,7 @@ import 'vs/workbench/services/textMate/browser/textMateService'; // import 'vs/workbench/services/workspace/electron-browser/workspaceEditingService'; // import 'vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler'; import 'vs/workbench/services/decorations/browser/decorationsService'; -// import 'vs/workbench/services/search/node/searchService'; +import 'vs/workbench/services/search/common/searchService'; import 'vs/workbench/services/progress/browser/progressService'; import 'vs/workbench/services/editor/browser/codeEditorService'; // import 'vs/workbench/services/extensions/electron-browser/extensionHostDebugService'; @@ -135,9 +132,13 @@ import 'vs/workbench/services/label/common/labelService'; import 'vs/workbench/services/notification/common/notificationService'; // import 'vs/workbench/services/window/electron-browser/windowService'; // import 'vs/workbench/services/telemetry/electron-browser/telemetryService'; +import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; import 'vs/workbench/browser/web.simpleservices'; +registerSingleton(IDialogService, DialogService, true); registerSingleton(IMenuService, MenuService, true); registerSingleton(IListService, ListService, true); registerSingleton(IOpenerService, OpenerService, true); @@ -163,9 +164,9 @@ registerSingleton(ILifecycleService, BrowserLifecycleService); // registerSingleton(IWorkspacesService, WorkspacesService); // registerSingleton(IMenubarService, MenubarService); // registerSingleton(IURLService, RelayURLService); -registerSingleton(ISearchService, RemoteSearchService, true); +// registerSingleton(ITunnelService, TunnelService, true); +// registerSingleton(ICredentialsService, KeytarCredentialsService, true); registerSingleton(IContextMenuService, ContextMenuService); -registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); //#endregion