diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index e18099fe67..b204fb78f5 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -10,6 +10,7 @@ "vscode": "^1.20.0" }, "main": "./out/extension", + "extensionKind": "ui", "categories": [ "Programming Languages" ], diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index 3806508775..ec445f89fd 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -23,8 +23,8 @@ "selection.background": "#ccccc7", "editor.selectionHighlightBackground": "#575b6180", "editor.selectionBackground": "#878b9180", - "editor.wordHighlightBackground": "#4a4a7680", - "editor.wordHighlightStrongBackground": "#6a6a9680", + "editor.wordHighlightBackground": "#4a4a7680", + "editor.wordHighlightStrongBackground": "#6a6a9680", "editor.lineHighlightBackground": "#3e3d32", "editorLineNumber.activeForeground": "#c2c2bf", "editorCursor.foreground": "#f8f8f0", diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index 4249da1107..3686307f7d 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -163,7 +163,7 @@ function darkenColor(color) { for (let i = 1; i < 7; i += 2) { let newVal = Math.round(parseInt('0x' + color.substr(i, 2), 16) * 0.9); let hex = newVal.toString(16); - if (hex.length == 1) { + if (hex.length === 1) { res += '0'; } res += hex; diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index 6cc47a93ae..58b59e79d0 100644 --- a/resources/win32/bin/code.sh +++ b/resources/win32/bin/code.sh @@ -5,6 +5,7 @@ COMMIT="@@COMMIT@@" APP_NAME="@@APPNAME@@" QUALITY="@@QUALITY@@" +NAME="@@NAME@@" set -e @@ -22,6 +23,8 @@ if grep -qi Microsoft /proc/version; then fi fi +VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")" + if [ -x "$(command -v cygpath)" ]; then CLI=$(cygpath -m "$VSCODE_PATH/resources/app/out/cli.js") else diff --git a/scripts/code.sh b/scripts/code.sh index ba4177c33e..8227916f95 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -59,19 +59,19 @@ function code-wsl() { # in a wsl shell local WIN_CODE_CLI_CMD=$(wslpath -w "$ROOT/scripts/code-cli.bat") - - local WSL_EXT_ID="ms-vscode.remote-wsl" - local WSL_EXT_WLOC=$(cmd.exe /c "$WIN_CODE_CLI_CMD" --locate-extension $WSL_EXT_ID) - if ! [ -z "$WSL_EXT_WLOC" ]; then - # replace \r\n with \n in WSL_EXT_WLOC - local WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode-dev.sh - $WSL_CODE "$ROOT" "$@" - exit $? + if ! [ -z "$WIN_CODE_CLI_CMD" ]; then + local WSL_EXT_ID="ms-vscode.remote-wsl" + local WSL_EXT_WLOC=$(cmd.exe /c "$WIN_CODE_CLI_CMD" --locate-extension $WSL_EXT_ID) + if ! [ -z "$WSL_EXT_WLOC" ]; then + # replace \r\n with \n in WSL_EXT_WLOC + local WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode-dev.sh + $WSL_CODE "$ROOT" "$@" + exit $? + fi fi } -if [ -z ${IN_WSL+x} ]; then - code "$@" -else +if ! [ -z ${IN_WSL+x} ]; then code-wsl "$@" -fi \ No newline at end of file +fi +code "$@" \ No newline at end of file diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 7172974334..d5884ce09e 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -20,6 +20,7 @@ export interface IDialogOptions { cancelId?: number; detail?: string; type?: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending'; + keyEventProcessor?: (event: StandardKeyboardEvent) => void; } export interface IDialogStyles extends IButtonStyles { @@ -103,19 +104,26 @@ export class Dialog extends Disposable { return; } + let eventHandled = false; if (this.buttonGroup) { if (evt.equals(KeyMod.Shift | KeyCode.Tab) || evt.equals(KeyCode.LeftArrow)) { focusedButton = focusedButton + this.buttonGroup.buttons.length - 1; focusedButton = focusedButton % this.buttonGroup.buttons.length; this.buttonGroup.buttons[focusedButton].focus(); + eventHandled = true; } else if (evt.equals(KeyCode.Tab) || evt.equals(KeyCode.RightArrow)) { focusedButton++; focusedButton = focusedButton % this.buttonGroup.buttons.length; this.buttonGroup.buttons[focusedButton].focus(); + eventHandled = true; } } - EventHelper.stop(e, true); + if (eventHandled) { + EventHelper.stop(e, true); + } else if (this.options.keyEventProcessor) { + this.options.keyEventProcessor(evt); + } })); this._register(domEvent(window, 'keyup', true)((e: KeyboardEvent) => { diff --git a/src/vs/base/browser/ui/sash/sash.css b/src/vs/base/browser/ui/sash/sash.css index 73916ddab7..de8ac93f4b 100644 --- a/src/vs/base/browser/ui/sash/sash.css +++ b/src/vs/base/browser/ui/sash/sash.css @@ -5,7 +5,7 @@ .monaco-sash { position: absolute; - z-index: 90; + z-index: 35; touch-action: none; } diff --git a/src/vs/code/electron-browser/issue/issueReporterPage.ts b/src/vs/code/electron-browser/issue/issueReporterPage.ts index e8300eabd2..566c7bc45c 100644 --- a/src/vs/code/electron-browser/issue/issueReporterPage.ts +++ b/src/vs/code/electron-browser/issue/issueReporterPage.ts @@ -63,7 +63,7 @@ export default (): string => `
@@ -85,7 +85,7 @@ export default (): string => `
@@ -97,7 +97,7 @@ export default (): string => `
@@ -107,7 +107,7 @@ export default (): string => `
@@ -117,7 +117,7 @@ export default (): string => `
diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts
index c1d7217028..9573f53e84 100644
--- a/src/vs/editor/browser/controller/textAreaHandler.ts
+++ b/src/vs/editor/browser/controller/textAreaHandler.ts
@@ -543,7 +543,7 @@ export class TextAreaHandler extends ViewPart {
}
// (in WebKit the textarea is 1px by 1px because it cannot handle input to a 0x0 textarea)
- // specifically, when doing Korean IME, setting the textare to 0x0 breaks IME badly.
+ // specifically, when doing Korean IME, setting the textarea to 0x0 breaks IME badly.
ta.setWidth(1);
ta.setHeight(1);
diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts
index 971c6071dc..9c3a61162a 100644
--- a/src/vs/editor/browser/controller/textAreaInput.ts
+++ b/src/vs/editor/browser/controller/textAreaInput.ts
@@ -344,7 +344,7 @@ export class TextAreaInput extends Disposable {
//
// The problems with the `selectionchange` event are:
// * the event is emitted when the textarea is focused programmatically -- textarea.focus()
- // * the event is emitted when the selection is changed in the textarea programatically -- textarea.setSelectionRange(...)
+ // * the event is emitted when the selection is changed in the textarea programmatically -- textarea.setSelectionRange(...)
// * the event is emitted when the value of the textarea is changed programmatically -- textarea.value = '...'
// * the event is emitted when tabbing into the textarea
// * the event is emitted asynchronously (sometimes with a delay as high as a few tens of ms)
diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts
index a36360468c..ac7034a626 100644
--- a/src/vs/editor/common/modes.ts
+++ b/src/vs/editor/common/modes.ts
@@ -1408,6 +1408,14 @@ export interface WorkspaceCommentProvider {
onDidChangeCommentThreads(): Event;
}
+/**
+ * @internal
+ */
+export interface IWebviewPortMapping {
+ webviewPort: number;
+ extensionHostPort: number;
+}
+
/**
* @internal
*/
@@ -1415,7 +1423,7 @@ export interface IWebviewOptions {
readonly enableScripts?: boolean;
readonly enableCommandUris?: boolean;
readonly localResourceRoots?: ReadonlyArray;
- readonly portMapping?: ReadonlyArray<{ port: number, resolvedPort: number }>;
+ readonly portMapping?: ReadonlyArray;
}
/**
diff --git a/src/vs/platform/dialogs/browser/dialogService.ts b/src/vs/platform/dialogs/browser/dialogService.ts
index c2fa9b5d34..057ed24c05 100644
--- a/src/vs/platform/dialogs/browser/dialogService.ts
+++ b/src/vs/platform/dialogs/browser/dialogService.ts
@@ -13,6 +13,8 @@ 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 { dispose, IDisposable } from 'vs/base/common/lifecycle';
+import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
+import { EventHelper } from 'vs/base/browser/dom';
export class DialogService implements IDialogService {
_serviceBrand: any;
@@ -76,7 +78,10 @@ export class DialogService implements IDialogService {
{
detail: options ? options.detail : undefined,
cancelId: options ? options.cancelId : undefined,
- type: this.getDialogType(severity)
+ type: this.getDialogType(severity),
+ keyEventProcessor: (event: StandardKeyboardEvent) => {
+ EventHelper.stop(event, true);
+ }
});
dialogDisposables.push(dialog);
diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts
index 08b22bc33e..478e1f220d 100644
--- a/src/vs/platform/environment/node/argv.ts
+++ b/src/vs/platform/environment/node/argv.ts
@@ -206,12 +206,14 @@ export function buildHelpMessage(productName: string, executableName: string, ve
help.push('');
help.push(`${localize('usage', "Usage")}: ${executableName} [${localize('options', "options")}][${localize('paths', 'paths')}...]`);
help.push('');
- if (os.platform() === 'win32') {
- help.push(localize('stdinWindows', "To read output from another program, append '-' (e.g. 'echo Hello World | {0} -')", executableName));
- } else {
- help.push(localize('stdinUnix', "To read from stdin, append '-' (e.g. 'ps aux | grep code | {0} -')", executableName));
+ if (isPipeSupported) {
+ if (os.platform() === 'win32') {
+ help.push(localize('stdinWindows', "To read output from another program, append '-' (e.g. 'echo Hello World | {0} -')", executableName));
+ } else {
+ help.push(localize('stdinUnix', "To read from stdin, append '-' (e.g. 'ps aux | grep code | {0} -')", executableName));
+ }
+ help.push('');
}
- help.push('');
for (let key in categories) {
let categoryOptions = options.filter(o => !!o.description && o.cat === key && isOptionSupported(o));
if (categoryOptions.length) {
diff --git a/src/vs/platform/extensions/node/extensionsUtil.ts b/src/vs/platform/extensions/node/extensionsUtil.ts
index 0d36fbe606..6314a3b766 100644
--- a/src/vs/platform/extensions/node/extensionsUtil.ts
+++ b/src/vs/platform/extensions/node/extensionsUtil.ts
@@ -12,13 +12,11 @@ import product from 'vs/platform/product/node/product';
export function isUIExtension(manifest: IExtensionManifest, uiContributions: string[], configurationService: IConfigurationService): boolean {
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
const configuredUIExtensions = configurationService.getValue('_workbench.uiExtensions') || [];
- if (configuredUIExtensions.length) {
- if (configuredUIExtensions.indexOf(extensionId) !== -1) {
- return true;
- }
- if (configuredUIExtensions.indexOf(`-${extensionId}`) !== -1) {
- return false;
- }
+ if (configuredUIExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) {
+ return true;
+ }
+ if (configuredUIExtensions.some(id => areSameExtensions({ id }, { id: `-${extensionId}` }))) {
+ return false;
}
switch (manifest.extensionKind) {
case 'ui': return true;
diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts
index b59202d4fa..522a148576 100644
--- a/src/vs/platform/quickinput/common/quickInput.ts
+++ b/src/vs/platform/quickinput/common/quickInput.ts
@@ -162,6 +162,16 @@ export interface IQuickPick extends IQuickInput {
readonly onDidAccept: Event;
+ ok: boolean;
+
+ readonly onDidCustom: Event;
+
+ customButton: boolean;
+
+ customLabel: string;
+
+ customHover: string;
+
buttons: ReadonlyArray;
readonly onDidTriggerButton: Event;
diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts
index 5e08bbea91..4136f3de64 100644
--- a/src/vs/platform/remote/common/remoteAgentConnection.ts
+++ b/src/vs/platform/remote/common/remoteAgentConnection.ts
@@ -7,6 +7,7 @@ import { Client, PersistentProtocol, ISocket } from 'vs/base/parts/ipc/common/ip
import { generateUuid } from 'vs/base/common/uuid';
import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment';
import { Disposable } from 'vs/base/common/lifecycle';
+import { Emitter } from 'vs/base/common/event';
export const enum ConnectionType {
Management = 1,
@@ -147,8 +148,38 @@ export async function connectRemoteAgentTunnel(options: IConnectionOptions, tunn
return protocol;
}
+export const enum PersistenConnectionEventType {
+ ConnectionLost,
+ ReconnectionWait,
+ ReconnectionRunning,
+ ReconnectionPermanentFailure,
+ ConnectionGain
+}
+export class ConnectionLostEvent {
+ public readonly type = PersistenConnectionEventType.ConnectionLost;
+}
+export class ReconnectionWaitEvent {
+ public readonly type = PersistenConnectionEventType.ReconnectionWait;
+ constructor(
+ public readonly durationSeconds: number
+ ) { }
+}
+export class ReconnectionRunningEvent {
+ public readonly type = PersistenConnectionEventType.ReconnectionRunning;
+}
+export class ConnectionGainEvent {
+ public readonly type = PersistenConnectionEventType.ConnectionGain;
+}
+export class ReconnectionPermanentFailureEvent {
+ public readonly type = PersistenConnectionEventType.ReconnectionPermanentFailure;
+}
+export type PersistenConnectionEvent = ConnectionLostEvent | ReconnectionWaitEvent | ReconnectionRunningEvent | ConnectionGainEvent | ReconnectionPermanentFailureEvent;
+
abstract class PersistentConnection extends Disposable {
+ private readonly _onDidStateChange = this._register(new Emitter());
+ public readonly onDidStateChange = this._onDidStateChange.event;
+
protected readonly _options: IConnectionOptions;
public readonly reconnectionToken: string;
public readonly protocol: PersistentProtocol;
diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts
index d2bd7caf17..ad89bba4be 100644
--- a/src/vs/vscode.d.ts
+++ b/src/vs/vscode.d.ts
@@ -5714,6 +5714,21 @@ declare module 'vscode' {
copy?(source: Uri, destination: Uri, options: { overwrite: boolean }): void | Thenable;
}
+ /**
+ * Defines a port mapping used for localhost inside the webview.
+ */
+ export interface WebviewPortMapping {
+ /**
+ * Localhost port to remap inside the webview.
+ */
+ readonly webviewPort: number;
+
+ /**
+ * Destination port. The `webviewPort` is resolved to this port.
+ */
+ readonly extensionHostPort: number;
+ }
+
/**
* Content settings for a webview.
*/
@@ -5740,6 +5755,18 @@ declare module 'vscode' {
* Pass in an empty array to disallow access to any local resources.
*/
readonly localResourceRoots?: ReadonlyArray;
+
+ /**
+ * Mappings of localhost ports used inside the webview.
+ *
+ * Port mapping allow webviews to transparently define how localhost ports are resolved. This can be used
+ * to allow using a static localhost port inside the webview that is resolved to random port that a service is
+ * running on.
+ *
+ * If a webview accesses localhost content, we recomend that you specify port mappings even if
+ * the `webviewPort` and `extensionHostPort` ports are the same.
+ */
+ readonly portMapping?: ReadonlyArray;
}
/**
diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts
index d41ca3b532..01b21ea2ea 100644
--- a/src/vs/vscode.proposed.d.ts
+++ b/src/vs/vscode.proposed.d.ts
@@ -1360,35 +1360,4 @@ declare module 'vscode' {
group?: string;
}
//#endregion
-
- //#region Webview Port mapping— mjbvz
- /**
- * Defines a port mapping used for localhost inside the webview.
- */
- export interface WebviewPortMapping {
- /**
- * Localhost port to remap inside the webview.
- */
- readonly port: number;
-
- /**
- * Destination port. The `port` is resolved to this port.
- */
- readonly resolvedPort: number;
- }
-
- export interface WebviewOptions {
- /**
- * Mappings of localhost ports used inside the webview.
- *
- * Port mapping allow webviews to transparently define how localhost ports are resolved. This can be used
- * to allow using a static localhost port inside the webview that is resolved to random port that a service is
- * running on.
- *
- * If a webview accesses localhost content, we recomend that you specify port mappings even if
- * the `port` and `resolvedPort` ports are the same.
- */
- readonly portMapping?: ReadonlyArray;
- }
- //#endregion
}
diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts
index b29154ec1f..dc590ebb11 100644
--- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts
+++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts
@@ -51,6 +51,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
if (activeInstance) {
this._proxy.$acceptActiveTerminalChanged(activeInstance.id);
}
+
+ this.terminalService.extHostReady(extHostContext.remoteAuthority);
}
public dispose(): void {
diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts
index 79c117267f..4999852c13 100644
--- a/src/vs/workbench/api/browser/mainThreadWindow.ts
+++ b/src/vs/workbench/api/browser/mainThreadWindow.ts
@@ -8,7 +8,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
-import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape } from '../common/extHost.protocol';
+import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape, IOpenUriOptions } from '../common/extHost.protocol';
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@@ -45,9 +45,9 @@ export class MainThreadWindow implements MainThreadWindowShape {
return this.windowService.isFocused();
}
- async $openUri(uriComponent: UriComponents): Promise {
+ async $openUri(uriComponent: UriComponents, options: IOpenUriOptions): Promise {
const uri = URI.revive(uriComponent);
- if (!!this.environmentService.configuration.remoteAuthority) {
+ if (options.allowTunneling && !!this.environmentService.configuration.remoteAuthority) {
if (uri.scheme === 'http' || uri.scheme === 'https') {
const port = this.getLocalhostPort(uri);
if (typeof port === 'number') {
diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts
index 59bbdd32f3..f849a83797 100644
--- a/src/vs/workbench/api/common/extHost.protocol.ts
+++ b/src/vs/workbench/api/common/extHost.protocol.ts
@@ -539,7 +539,7 @@ export interface ExtHostWebviewsShape {
$onMessage(handle: WebviewPanelHandle, message: any): void;
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, newState: WebviewPanelViewState): void;
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise;
- $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions): Promise;
+ $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise;
}
export interface MainThreadUrlsShape extends IDisposable {
@@ -689,9 +689,13 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Promise;
}
+export interface IOpenUriOptions {
+ readonly allowTunneling?: boolean;
+}
+
export interface MainThreadWindowShape extends IDisposable {
$getWindowVisibility(): Promise;
- $openUri(uri: UriComponents): Promise;
+ $openUri(uri: UriComponents, options: IOpenUriOptions): Promise;
}
// -- extension host
diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts
index 83893e8f52..03d782deac 100644
--- a/src/vs/workbench/api/common/extHostWebview.ts
+++ b/src/vs/workbench/api/common/extHostWebview.ts
@@ -11,6 +11,7 @@ import * as vscode from 'vscode';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState, WebviewInsetHandle } from './extHost.protocol';
import { Disposable } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
+import * as modes from 'vs/editor/common/modes';
type IconPath = URI | { light: URI, dark: URI };
@@ -58,7 +59,7 @@ export class ExtHostWebview implements vscode.Webview {
public set options(newOptions: vscode.WebviewOptions) {
this.assertNotDisposed();
- this._proxy.$setOptions(this._handle, newOptions);
+ this._proxy.$setOptions(this._handle, convertWebviewOptions(newOptions));
this._options = newOptions;
}
@@ -257,7 +258,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
};
const handle = ExtHostWebviews.newHandle();
- this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.identifier, extension.extensionLocation);
+ this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, convertWebviewOptions(options), extension.identifier, extension.extensionLocation);
const webview = new ExtHostWebview(handle, this._proxy, options);
const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview);
@@ -325,7 +326,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
title: string,
state: any,
position: EditorViewColumn,
- options: vscode.WebviewOptions & vscode.WebviewPanelOptions
+ options: modes.IWebviewOptions & modes.IWebviewPanelOptions
): Promise {
const serializer = this._serializers.get(viewType);
if (!serializer) {
@@ -342,3 +343,20 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
return this._webviewPanels.get(handle);
}
}
+
+function convertWebviewOptions(
+ options: vscode.WebviewPanelOptions & vscode.WebviewOptions
+): modes.IWebviewOptions {
+ return {
+ ...options,
+ portMapping: options.portMapping
+ ? options.portMapping.map((x): modes.IWebviewPortMapping => {
+ // Handle old proposed api
+ if ('port' in x) {
+ return { webviewPort: (x as any).port, extensionHostPort: (x as any).resolvedPort };
+ }
+ return { webviewPort: x.webviewPort, extensionHostPort: x.extensionHostPort };
+ })
+ : undefined,
+ };
+}
diff --git a/src/vs/workbench/api/common/extHostWindow.ts b/src/vs/workbench/api/common/extHostWindow.ts
index c15a5c76e5..b0f720493a 100644
--- a/src/vs/workbench/api/common/extHostWindow.ts
+++ b/src/vs/workbench/api/common/extHostWindow.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
-import { ExtHostWindowShape, MainContext, MainThreadWindowShape, IMainContext } from './extHost.protocol';
+import { ExtHostWindowShape, MainContext, MainThreadWindowShape, IMainContext, IOpenUriOptions } from './extHost.protocol';
import { WindowState } from 'vscode';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
@@ -38,7 +38,7 @@ export class ExtHostWindow implements ExtHostWindowShape {
this._onDidChangeWindowState.fire(this._state);
}
- openUri(stringOrUri: string | URI): Promise {
+ openUri(stringOrUri: string | URI, options: IOpenUriOptions): Promise {
if (typeof stringOrUri === 'string') {
try {
stringOrUri = URI.parse(stringOrUri);
@@ -51,6 +51,6 @@ export class ExtHostWindow implements ExtHostWindowShape {
} else if (stringOrUri.scheme === Schemas.command) {
return Promise.reject(`Invalid scheme '${stringOrUri.scheme}'`);
}
- return this._proxy.$openUri(stringOrUri);
+ return this._proxy.$openUri(stringOrUri, options);
}
}
diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts
index 1aeafb3c8c..d75c163c3a 100644
--- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts
+++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts
@@ -7,7 +7,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as map from 'vs/base/common/map';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
-import { IWebviewOptions } from 'vs/editor/common/modes';
+import * as modes from 'vs/editor/common/modes';
import { localize } from 'vs/nls';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -122,7 +122,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
$createWebviewCodeInset(
handle: WebviewInsetHandle,
symbolId: string,
- options: IWebviewOptions,
+ options: modes.IWebviewOptions,
extensionId: ExtensionIdentifier,
extensionLocation: UriComponents
): void {
@@ -189,7 +189,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
}
}
- public $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: IWebviewOptions): void {
+ public $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: modes.IWebviewOptions): void {
if (typeof handle === 'number') {
this.getWebviewElement(handle).options = reviveWebviewOptions(options as any /*todo@mat */);
} else {
diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts
index 218e920af8..f14f88a20d 100644
--- a/src/vs/workbench/api/node/extHost.api.impl.ts
+++ b/src/vs/workbench/api/node/extHost.api.impl.ts
@@ -257,7 +257,7 @@ export function createApiFactory(
return extHostClipboard;
},
openExternal(uri: URI) {
- return extHostWindow.openUri(uri);
+ return extHostWindow.openUri(uri, { allowTunneling: !!initData.remoteAuthority });
}
});
diff --git a/src/vs/workbench/api/node/extHostRequireInterceptor.ts b/src/vs/workbench/api/node/extHostRequireInterceptor.ts
index 0093594850..c73c005de9 100644
--- a/src/vs/workbench/api/node/extHostRequireInterceptor.ts
+++ b/src/vs/workbench/api/node/extHostRequireInterceptor.ts
@@ -202,9 +202,9 @@ export class OpenNodeModuleFactory implements INodeModuleFactory {
return this.callOriginal(target, options);
}
if (uri.scheme === 'http' || uri.scheme === 'https') {
- return mainThreadWindow.$openUri(uri);
+ return mainThreadWindow.$openUri(uri, { allowTunneling: true });
} else if (uri.scheme === 'mailto') {
- return mainThreadWindow.$openUri(uri);
+ return mainThreadWindow.$openUri(uri, {});
}
return this.callOriginal(target, options);
};
diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts
index 3b48e59cc1..b05365a715 100644
--- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts
+++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts
@@ -68,9 +68,11 @@ interface QuickInputUI {
visibleCount: CountBadge;
count: CountBadge;
message: HTMLElement;
+ customButton: Button;
progressBar: ProgressBar;
list: QuickInputList;
onDidAccept: Event;
+ onDidCustom: Event;
onDidTriggerButton: Event;
ignoreFocusOut: boolean;
keyMods: Writeable;
@@ -92,6 +94,7 @@ type Visibilities = {
message?: boolean;
list?: boolean;
ok?: boolean;
+ customButton?: boolean;
};
class QuickInput implements IQuickInput {
@@ -312,6 +315,7 @@ class QuickPick extends QuickInput implements IQuickPi
private _placeholder: string;
private onDidChangeValueEmitter = new Emitter();
private onDidAcceptEmitter = new Emitter();
+ private onDidCustomEmitter = new Emitter();
private _items: Array = [];
private itemsUpdated = false;
private _canSelectMany = false;
@@ -331,6 +335,10 @@ class QuickPick extends QuickInput implements IQuickPi
private _valueSelection: Readonly<[number, number]>;
private valueSelectionUpdated = true;
private _validationMessage: string;
+ private _ok: boolean;
+ private _customButton: boolean;
+ private _customButtonLabel: string;
+ private _customButtonHover: string;
quickNavigate: IQuickNavigateConfiguration;
@@ -339,6 +347,7 @@ class QuickPick extends QuickInput implements IQuickPi
this.disposables.push(
this.onDidChangeValueEmitter,
this.onDidAcceptEmitter,
+ this.onDidCustomEmitter,
this.onDidChangeActiveEmitter,
this.onDidChangeSelectionEmitter,
this.onDidTriggerItemButtonEmitter,
@@ -367,6 +376,8 @@ class QuickPick extends QuickInput implements IQuickPi
onDidAccept = this.onDidAcceptEmitter.event;
+ onDidCustom = this.onDidCustomEmitter.event;
+
get items() {
return this._items;
}
@@ -463,6 +474,42 @@ class QuickPick extends QuickInput implements IQuickPi
this.update();
}
+ get customButton() {
+ return this._customButton;
+ }
+
+ set customButton(showCustomButton: boolean) {
+ this._customButton = showCustomButton;
+ this.update();
+ }
+
+ get customLabel() {
+ return this._customButtonLabel;
+ }
+
+ set customLabel(label: string) {
+ this._customButtonLabel = label;
+ this.update();
+ }
+
+ get customHover() {
+ return this._customButtonHover;
+ }
+
+ set customHover(hover: string) {
+ this._customButtonHover = hover;
+ this.update();
+ }
+
+ get ok() {
+ return this._ok;
+ }
+
+ set ok(showOkButton: boolean) {
+ this._ok = showOkButton;
+ this.update();
+ }
+
public inputHasFocus(): boolean {
return this.visible ? this.ui.inputBox.hasFocus() : false;
}
@@ -547,6 +594,9 @@ class QuickPick extends QuickInput implements IQuickPi
}
this.onDidAcceptEmitter.fire(undefined);
}),
+ this.ui.onDidCustom(() => {
+ this.onDidCustomEmitter.fire(undefined);
+ }),
this.ui.list.onDidChangeFocus(focusedItems => {
if (this.activeItemsUpdated) {
return; // Expect another event.
@@ -697,12 +747,14 @@ class QuickPick extends QuickInput implements IQuickPi
this.ui.message.textContent = null;
this.ui.inputBox.showDecoration(Severity.Ignore);
}
+ this.ui.customButton.label = this.customLabel;
+ this.ui.customButton.element.title = this.customHover;
this.ui.list.matchOnDescription = this.matchOnDescription;
this.ui.list.matchOnDetail = this.matchOnDetail;
this.ui.list.matchOnLabel = this.matchOnLabel;
this.ui.setComboboxAccessibility(true);
this.ui.inputBox.setAttribute('aria-label', QuickPick.INPUT_BOX_ARIA_LABEL);
- this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: true, list: true, message: !!this.validationMessage } : { title: !!this.title || !!this.step, inputBox: true, visibleCount: true, list: true, message: !!this.validationMessage });
+ this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: true, list: true, message: !!this.validationMessage } : { title: !!this.title || !!this.step, inputBox: true, visibleCount: true, list: true, message: !!this.validationMessage, customButton: this.customButton, ok: this.ok });
}
}
@@ -848,6 +900,7 @@ export class QuickInputService extends Component implements IQuickInputService {
private countContainer: HTMLElement;
private okContainer: HTMLElement;
private ok: Button;
+ private customButtonContainer: HTMLElement;
private ui: QuickInputUI;
private comboboxAccessibility = false;
private enabled = true;
@@ -855,6 +908,7 @@ export class QuickInputService extends Component implements IQuickInputService {
private inQuickOpenContext: IContextKey;
private contexts: { [id: string]: IContextKey; } = Object.create(null);
private onDidAcceptEmitter = this._register(new Emitter());
+ private onDidCustomEmitter = this._register(new Emitter());
private onDidTriggerButtonEmitter = this._register(new Emitter());
private keyMods: Writeable = { ctrlCmd: false, alt: false };
@@ -1013,6 +1067,14 @@ export class QuickInputService extends Component implements IQuickInputService {
this.onDidAcceptEmitter.fire();
}));
+ this.customButtonContainer = dom.append(headerContainer, $('.quick-input-action'));
+ const customButton = new Button(this.customButtonContainer);
+ attachButtonStyler(customButton, this.themeService);
+ customButton.label = localize('custom', "Custom");
+ this._register(customButton.onDidClick(e => {
+ this.onDidCustomEmitter.fire();
+ }));
+
const message = dom.append(container, $(`#${this.idPrefix}message.quick-input-message`));
const progressBar = new ProgressBar(container);
@@ -1098,9 +1160,11 @@ export class QuickInputService extends Component implements IQuickInputService {
visibleCount,
count,
message,
+ customButton,
progressBar,
list,
onDidAccept: this.onDidAcceptEmitter.event,
+ onDidCustom: this.onDidCustomEmitter.event,
onDidTriggerButton: this.onDidTriggerButtonEmitter.event,
ignoreFocusOut: false,
keyMods: this.keyMods,
@@ -1330,6 +1394,7 @@ export class QuickInputService extends Component implements IQuickInputService {
this.visibleCountContainer.style.display = visibilities.visibleCount ? '' : 'none';
this.countContainer.style.display = visibilities.count ? '' : 'none';
this.okContainer.style.display = visibilities.ok ? '' : 'none';
+ this.customButtonContainer.style.display = visibilities.customButton ? '' : 'none';
this.ui.message.style.display = visibilities.message ? '' : 'none';
this.ui.list.display(!!visibilities.list);
this.ui.container.classList[visibilities.checkAll ? 'add' : 'remove']('show-checkboxes');
diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts
index 7b5e2d0aa8..e8c3f58fd5 100644
--- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts
+++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts
@@ -1060,10 +1060,10 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel {
const visibleViewDescriptors = this.viewsModel.visibleViewDescriptors;
const toSetVisible = this.viewsModel.viewDescriptors
- .filter(d => d instanceof RepositoryViewDescriptor && repositories.indexOf(d.repository) > -1 && visibleViewDescriptors.indexOf(d) === -1);
+ .filter((d): d is RepositoryViewDescriptor => d instanceof RepositoryViewDescriptor && repositories.indexOf(d.repository) > -1 && visibleViewDescriptors.indexOf(d) === -1);
const toSetInvisible = visibleViewDescriptors
- .filter(d => d instanceof RepositoryViewDescriptor && repositories.indexOf(d.repository) === -1);
+ .filter((d): d is RepositoryViewDescriptor => d instanceof RepositoryViewDescriptor && repositories.indexOf(d.repository) === -1);
let size: number | undefined;
const oneToOne = toSetVisible.length === 1 && toSetInvisible.length === 1;
@@ -1077,10 +1077,12 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel {
}
}
+ viewDescriptor.repository.setSelected(false);
this.viewsModel.setVisible(viewDescriptor.id, false);
}
for (const viewDescriptor of toSetVisible) {
+ viewDescriptor.repository.setSelected(true);
this.viewsModel.setVisible(viewDescriptor.id, true, size);
}
}
diff --git a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts
index e51010ba3a..8a4afe6625 100644
--- a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts
+++ b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts
@@ -1972,7 +1972,7 @@ class TaskService extends Disposable implements ITaskService {
return entries;
}
- private showQuickPick(tasks: Promise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry): Promise {
+ private showQuickPick(tasks: Promise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry, additionalEntries?: TaskQuickPickEntry[]): Promise {
let _createEntries = (): Promise => {
if (Array.isArray(tasks)) {
return Promise.resolve(this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry));
@@ -1984,6 +1984,9 @@ class TaskService extends Disposable implements ITaskService {
if ((entries.length === 0) && defaultEntry) {
entries.push(defaultEntry);
}
+ else if (entries.length > 1 && additionalEntries && additionalEntries.length > 0) {
+ entries.push(additionalEntries[0]);
+ }
return entries;
}), {
placeHolder,
@@ -1997,7 +2000,7 @@ class TaskService extends Disposable implements ITaskService {
this.openConfig(task);
}
}
- }).then(entry => entry ? entry.task : undefined);
+ });
}
private showIgnoredFoldersMessage(): Promise {
@@ -2057,7 +2060,8 @@ class TaskService extends Disposable implements ITaskService {
task: null
},
true).
- then((task) => {
+ then((entry) => {
+ let task: Task | undefined | null = entry ? entry.task : undefined;
if (task === undefined) {
return;
}
@@ -2137,7 +2141,8 @@ class TaskService extends Disposable implements ITaskService {
label: nls.localize('TaskService.noBuildTask', 'No build task to run found. Configure Build Task...'),
task: null
},
- true).then((task) => {
+ true).then((entry) => {
+ let task: Task | undefined | null = entry ? entry.task : undefined;
if (task === undefined) {
return;
}
@@ -2185,7 +2190,8 @@ class TaskService extends Disposable implements ITaskService {
label: nls.localize('TaskService.noTestTaskTerminal', 'No test task to run found. Configure Tasks...'),
task: null
}, true
- ).then((task) => {
+ ).then((entry) => {
+ let task: Task | undefined | null = entry ? entry.task : undefined;
if (task === undefined) {
return;
}
@@ -2206,15 +2212,29 @@ class TaskService extends Disposable implements ITaskService {
if (!this.canRunCommand()) {
return;
}
+ if (arg === 'terminateAll') {
+ this.terminateAll();
+ return;
+ }
let runQuickPick = (promise?: Promise) => {
this.showQuickPick(promise || this.getActiveTasks(),
nls.localize('TaskService.taskToTerminate', 'Select task to terminate'),
{
label: nls.localize('TaskService.noTaskRunning', 'No task is currently running'),
- task: null
+ task: undefined
},
- false, true
- ).then(task => {
+ false, true,
+ undefined,
+ [{
+ label: nls.localize('TaskService.terminateAllRunningTasks', 'All running tasks'),
+ id: 'terminateAll',
+ task: undefined
+ }]
+ ).then(entry => {
+ if (entry && entry.id === 'terminateAll') {
+ this.terminateAll();
+ }
+ let task: Task | undefined | null = entry ? entry.task : undefined;
if (task === undefined || task === null) {
return;
}
@@ -2270,7 +2290,8 @@ class TaskService extends Disposable implements ITaskService {
task: null
},
false, true
- ).then(task => {
+ ).then(entry => {
+ let task: Task | undefined | null = entry ? entry.task : undefined;
if (task === undefined || task === null) {
return;
}
@@ -2482,7 +2503,8 @@ class TaskService extends Disposable implements ITaskService {
this.showIgnoredFoldersMessage().then(() => {
this.showQuickPick(tasks,
nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), undefined, true, false, selectedEntry).
- then((task) => {
+ then((entry) => {
+ let task: Task | undefined | null = entry ? entry.task : undefined;
if ((task === undefined) || (task === null)) {
return;
}
@@ -2532,7 +2554,8 @@ class TaskService extends Disposable implements ITaskService {
this.showIgnoredFoldersMessage().then(() => {
this.showQuickPick(tasks,
- nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), undefined, true, false, selectedEntry).then((task) => {
+ nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), undefined, true, false, selectedEntry).then((entry) => {
+ let task: Task | undefined | null = entry ? entry.task : undefined;
if (!task) {
return;
}
@@ -2565,7 +2588,8 @@ class TaskService extends Disposable implements ITaskService {
task: null
},
false, true
- ).then((task) => {
+ ).then((entry) => {
+ let task: Task | undefined | null = entry ? entry.task : undefined;
if (task === undefined || task === null) {
return;
}
diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts
index c0e643a220..db4dd0e1e5 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts
@@ -433,12 +433,10 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneRightT
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Right', category);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneUpTerminalAction, ResizePaneUpTerminalAction.ID, ResizePaneUpTerminalAction.LABEL, {
primary: 0,
- linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow },
mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.UpArrow }
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Up', category);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneDownTerminalAction, ResizePaneDownTerminalAction.ID, ResizePaneDownTerminalAction.LABEL, {
primary: 0,
- linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow },
mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.DownArrow }
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Down', category);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToPreviousCommandAction, ScrollToPreviousCommandAction.ID, ScrollToPreviousCommandAction.LABEL, {
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts
index d392e301c2..c9359581df 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts
@@ -135,47 +135,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(hasRemoteAuthority ? REMOTE_HOST_SCHEME : undefined);
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows);
} else {
- if (!shellLaunchConfig.executable) {
- this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
- }
-
- const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
- const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentService.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd);
-
- // Compel type system as process.env should not have any undefined entries
- let env: platform.IProcessEnvironment = {};
-
- if (shellLaunchConfig.strictEnv) {
- // Only base the terminal process environment on this environment and add the
- // various mixins when strictEnv is false
- env = { ...shellLaunchConfig.env } as any;
- } else {
- // Merge process env with the env from config and from shellLaunchConfig
- env = { ...process.env } as any;
-
- // Resolve env vars from config and shell
- const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null;
- const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
- const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions();
- const envFromConfigValue = this._workspaceConfigurationService.inspect<{ [key: string]: string }>(`terminal.integrated.env.${platformKey}`);
- const allowedEnvFromConfig = (isWorkspaceShellAllowed ? envFromConfigValue.value : envFromConfigValue.user);
- const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...allowedEnvFromConfig }, lastActiveWorkspaceRoot);
- const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
- shellLaunchConfig.env = envFromShell;
-
- terminalEnvironment.mergeEnvironments(env, envFromConfig);
- terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env);
-
- // Sanitize the environment, removing any undesirable VS Code and Electron environment
- // variables
- sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI');
-
- // Adding other env keys necessary to create the process
- terminalEnvironment.addTerminalEnvironmentKeys(env, this._productService.version, platform.locale, this._configHelper.config.setLocaleVariables);
- }
-
- this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env);
- this._process = this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty);
+ this._process = this._launchProcess(shellLaunchConfig, cols, rows);
}
this.processState = ProcessState.LAUNCHING;
@@ -211,6 +171,50 @@ export class TerminalProcessManager implements ITerminalProcessManager {
}, LAUNCHING_DURATION);
}
+ private _launchProcess(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number): ITerminalChildProcess {
+ if (!shellLaunchConfig.executable) {
+ this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
+ }
+
+ const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
+ const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentService.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd);
+
+ // Compel type system as process.env should not have any undefined entries
+ let env: platform.IProcessEnvironment = {};
+
+ if (shellLaunchConfig.strictEnv) {
+ // Only base the terminal process environment on this environment and add the
+ // various mixins when strictEnv is false
+ env = { ...shellLaunchConfig.env } as any;
+ } else {
+ // Merge process env with the env from config and from shellLaunchConfig
+ env = { ...process.env } as any;
+
+ // Resolve env vars from config and shell
+ const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null;
+ const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
+ const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions();
+ const envFromConfigValue = this._workspaceConfigurationService.inspect<{ [key: string]: string }>(`terminal.integrated.env.${platformKey}`);
+ const allowedEnvFromConfig = (isWorkspaceShellAllowed ? envFromConfigValue.value : envFromConfigValue.user);
+ const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...allowedEnvFromConfig }, lastActiveWorkspaceRoot);
+ const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
+ shellLaunchConfig.env = envFromShell;
+
+ terminalEnvironment.mergeEnvironments(env, envFromConfig);
+ terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env);
+
+ // Sanitize the environment, removing any undesirable VS Code and Electron environment
+ // variables
+ sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI');
+
+ // Adding other env keys necessary to create the process
+ terminalEnvironment.addTerminalEnvironmentKeys(env, this._productService.version, platform.locale, this._configHelper.config.setLocaleVariables);
+ }
+
+ this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env);
+ return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty);
+ }
+
public setDimensions(cols: number, rows: number): void {
if (!this._process) {
return;
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts
index bbdcbc410c..74e36a1f5f 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts
@@ -22,6 +22,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { IFileService } from 'vs/platform/files/common/files';
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal';
+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
export abstract class TerminalService extends CommonTerminalService implements ITerminalService {
protected _configHelper: IBrowserTerminalConfigHelper;
@@ -38,8 +39,9 @@ export abstract class TerminalService extends CommonTerminalService implements I
@IWorkbenchEnvironmentService private _environmentService: IWorkbenchEnvironmentService,
@IExtensionService extensionService: IExtensionService,
@IFileService fileService: IFileService,
+ @IRemoteAgentService remoteAgentService: IRemoteAgentService
) {
- super(contextKeyService, panelService, lifecycleService, storageService, notificationService, dialogService, extensionService, fileService);
+ super(contextKeyService, panelService, lifecycleService, storageService, notificationService, dialogService, extensionService, fileService, remoteAgentService);
}
protected abstract _getDefaultShell(p: platform.Platform): string;
diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts
index d0caa558bb..00d6133180 100644
--- a/src/vs/workbench/contrib/terminal/common/terminal.ts
+++ b/src/vs/workbench/contrib/terminal/common/terminal.ts
@@ -267,6 +267,7 @@ export interface ITerminalService {
*/
preparePathForTerminalAsync(path: string, executable: string | undefined, title: string): Promise;
+ extHostReady(remoteAuthority: string): void;
requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void;
}
diff --git a/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts
index 22caa9726c..11f9c4996c 100644
--- a/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts
+++ b/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts
@@ -7,7 +7,6 @@ import { Event, Emitter } from 'vs/base/common/event';
import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal';
import { IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
-import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerminalProcessExtHostProxy {
private _disposables: IDisposable[] = [];
@@ -44,15 +43,10 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm
activeWorkspaceRootUri: URI,
cols: number,
rows: number,
- @ITerminalService private readonly _terminalService: ITerminalService,
- @IExtensionService private readonly _extensionService: IExtensionService
+ @ITerminalService private readonly _terminalService: ITerminalService
) {
- this._extensionService.whenInstalledExtensionsRegistered().then(() => {
- // TODO: MainThreadTerminalService is not ready at this point, fix this
- setTimeout(() => {
- this._terminalService.requestExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows);
- }, 0);
- });
+ this._terminalService.requestExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows);
+ setTimeout(() => this._onProcessTitleChanged.fire('Starting...'), 0);
}
public dispose(): void {
diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts
index 8956cbf541..a52c03a7c5 100644
--- a/src/vs/workbench/contrib/terminal/common/terminalService.ts
+++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts
@@ -19,6 +19,8 @@ import { IFileService } from 'vs/platform/files/common/files';
import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { isWindows } from 'vs/base/common/platform';
import { basename } from 'vs/base/common/path';
+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
+import { timeout } from 'vs/base/common/async';
export abstract class TerminalService implements ITerminalService {
public _serviceBrand: any;
@@ -32,7 +34,7 @@ export abstract class TerminalService implements ITerminalService {
return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), []);
}
private _findState: FindReplaceState;
-
+ private _extHostsReady: { [authority: string]: boolean } = {};
private _activeTabIndex: number;
public get activeTabIndex(): number { return this._activeTabIndex; }
@@ -65,12 +67,13 @@ export abstract class TerminalService implements ITerminalService {
constructor(
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IPanelService protected readonly _panelService: IPanelService,
- @ILifecycleService lifecycleService: ILifecycleService,
+ @ILifecycleService readonly lifecycleService: ILifecycleService,
@IStorageService protected readonly _storageService: IStorageService,
@INotificationService protected readonly _notificationService: INotificationService,
@IDialogService private readonly _dialogService: IDialogService,
@IExtensionService private readonly _extensionService: IExtensionService,
- @IFileService protected readonly _fileService: IFileService
+ @IFileService protected readonly _fileService: IFileService,
+ @IRemoteAgentService readonly _remoteAgentService: IRemoteAgentService
) {
this._activeTabIndex = 0;
this._isShuttingDown = false;
@@ -116,15 +119,22 @@ export abstract class TerminalService implements ITerminalService {
}
public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void {
- // Ensure extension host is ready before requesting a process
- this._extensionService.whenInstalledExtensionsRegistered().then(() => {
- // TODO: MainThreadTerminalService is not ready at this point, fix this
- setTimeout(() => {
- this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows });
- }, 500);
+ this._extensionService.whenInstalledExtensionsRegistered().then(async () => {
+ // Wait for the remoteAuthority to be ready (and listening for events) before proceeding
+ const conn = this._remoteAgentService.getConnection();
+ const remoteAuthority = conn ? conn.remoteAuthority : 'null';
+ let retries = 0;
+ while (!this._extHostsReady[remoteAuthority] && ++retries < 50) {
+ await timeout(100);
+ }
+ this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows });
});
}
+ public extHostReady(remoteAuthority: string): void {
+ this._extHostsReady[remoteAuthority] = true;
+ }
+
private _onBeforeShutdown(): boolean | Promise {
if (this.terminalInstances.length === 0) {
// No terminal instances, don't veto
diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts
index 39a60adf28..5dc59893c9 100644
--- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts
+++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts
@@ -28,6 +28,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { execFile } from 'child_process';
import { URI } from 'vs/base/common/uri';
+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
export class TerminalService extends BrowserTerminalService implements ITerminalService {
public get configHelper(): ITerminalConfigHelper { return this._configHelper; }
@@ -45,9 +46,10 @@ export class TerminalService extends BrowserTerminalService implements ITerminal
@IDialogService dialogService: IDialogService,
@IExtensionService extensionService: IExtensionService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
- @IFileService fileService: IFileService
+ @IFileService fileService: IFileService,
+ @IRemoteAgentService remoteAgentService: IRemoteAgentService
) {
- super(contextKeyService, panelService, layoutService, lifecycleService, storageService, notificationService, dialogService, instantiationService, environmentService, extensionService, fileService);
+ super(contextKeyService, panelService, layoutService, lifecycleService, storageService, notificationService, dialogService, instantiationService, environmentService, extensionService, fileService, remoteAgentService);
this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, linuxDistro);
ipc.on('vscode:openFiles', (_event: any, request: IOpenFileRequest) => {
diff --git a/src/vs/workbench/contrib/webview/common/webview.ts b/src/vs/workbench/contrib/webview/common/webview.ts
index 208d174467..3358c27dc9 100644
--- a/src/vs/workbench/contrib/webview/common/webview.ts
+++ b/src/vs/workbench/contrib/webview/common/webview.ts
@@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
+import * as modes from 'vs/editor/common/modes';
/**
* Set when the find widget in a webview is visible.
@@ -28,11 +29,6 @@ export interface IWebviewService {
): Webview;
}
-export interface WebviewPortMapping {
- readonly port: number;
- readonly resolvedPort: number;
-}
-
export interface WebviewOptions {
readonly allowSvgs?: boolean;
readonly extension?: {
@@ -46,7 +42,7 @@ export interface WebviewContentOptions {
readonly allowScripts?: boolean;
readonly svgWhiteList?: string[];
readonly localResourceRoots?: ReadonlyArray;
- readonly portMappings?: ReadonlyArray;
+ readonly portMappings?: ReadonlyArray;
}
export interface Webview {
diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts
index c2cef4a649..3da48618aa 100644
--- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts
+++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts
@@ -11,21 +11,22 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { isMacintosh } from 'vs/base/common/platform';
import { endsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
+import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions';
+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 * as colorRegistry from 'vs/platform/theme/common/colorRegistry';
import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService';
+import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview';
import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols';
-import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
-import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
-import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService';
import { WebviewFindWidget } from '../browser/webviewFindWidget';
-import { WebviewContentOptions, WebviewPortMapping, WebviewOptions, Webview } from 'vs/workbench/contrib/webview/common/webview';
-import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
-import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
-import { IEditorOptions, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
export interface WebviewPortMapping {
readonly port: number;
@@ -153,7 +154,7 @@ class WebviewPortMappingProvider extends Disposable {
constructor(
session: WebviewSession,
extensionLocation: URI | undefined,
- mappings: () => ReadonlyArray,
+ mappings: () => ReadonlyArray,
private readonly tunnelService: ITunnelService,
extensionId: ExtensionIdentifier | undefined,
@ITelemetryService telemetryService: ITelemetryService
@@ -183,23 +184,23 @@ class WebviewPortMappingProvider extends Disposable {
const port = +localhostMatch[1];
for (const mapping of mappings()) {
- if (mapping.port === port) {
+ if (mapping.webviewPort === port) {
if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) {
- const tunnel = await this.getOrCreateTunnel(mapping.resolvedPort);
+ const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort);
if (tunnel) {
return {
redirectURL: details.url.replace(
- new RegExp(`^${uri.scheme}://localhost:${mapping.port}/`),
+ new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`),
`${uri.scheme}://localhost:${tunnel.tunnelLocalPort}/`)
};
}
}
- if (mapping.port !== mapping.resolvedPort) {
+ if (mapping.webviewPort !== mapping.extensionHostPort) {
return {
redirectURL: details.url.replace(
- new RegExp(`^${uri.scheme}://localhost:${mapping.port}/`),
- `${uri.scheme}://localhost:${mapping.resolvedPort}/`)
+ new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`),
+ `${uri.scheme}://localhost:${mapping.extensionHostPort}/`)
};
}
}
@@ -416,7 +417,7 @@ export class WebviewElement extends Disposable implements Webview {
this._register(new WebviewPortMappingProvider(
session,
_options.extension ? _options.extension.location : undefined,
- () => (this._contentOptions.portMappings || [{ port: 3000, resolvedPort: 4000 }]),
+ () => (this._contentOptions.portMappings || []),
tunnelService,
_options.extension ? _options.extension.id : undefined,
telemetryService
diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts
index 85ab8dd3d2..0a43d18e62 100644
--- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts
+++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts
@@ -72,8 +72,8 @@ export class FileDialogService implements IFileDialogService {
}
}
- // ...then fallback to default folder path
- return this.defaultFolderPath(schemeFilter);
+ // ...then fallback to default file path
+ return this.defaultFilePath(schemeFilter);
}
private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions {
diff --git a/src/vs/workbench/services/dialogs/browser/media/dark/accept.svg b/src/vs/workbench/services/dialogs/browser/media/dark/accept.svg
deleted file mode 100644
index c225b2f597..0000000000
--- a/src/vs/workbench/services/dialogs/browser/media/dark/accept.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/vs/workbench/services/dialogs/browser/media/dark/folder.svg b/src/vs/workbench/services/dialogs/browser/media/dark/folder.svg
deleted file mode 100644
index 3d64ae71db..0000000000
--- a/src/vs/workbench/services/dialogs/browser/media/dark/folder.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/vs/workbench/services/dialogs/browser/media/light/accept.svg b/src/vs/workbench/services/dialogs/browser/media/light/accept.svg
deleted file mode 100644
index c300a55adb..0000000000
--- a/src/vs/workbench/services/dialogs/browser/media/light/accept.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/vs/workbench/services/dialogs/browser/media/light/folder.svg b/src/vs/workbench/services/dialogs/browser/media/light/folder.svg
deleted file mode 100644
index 13b18d1801..0000000000
--- a/src/vs/workbench/services/dialogs/browser/media/light/folder.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts
index 7de4708647..fb23847896 100644
--- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts
+++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import * as resources from 'vs/base/common/resources';
import * as objects from 'vs/base/common/objects';
import { IFileService, IFileStat, FileKind } from 'vs/platform/files/common/files';
-import { IQuickInputService, IQuickPickItem, IQuickPick, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput';
+import { IQuickInputService, IQuickPickItem, IQuickPick } from 'vs/platform/quickinput/common/quickInput';
import { URI } from 'vs/base/common/uri';
import { isWindows } from 'vs/base/common/platform';
import { ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
@@ -23,20 +23,26 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { RemoteFileDialogContext } from 'vs/workbench/common/contextkeys';
-import { equalsIgnoreCase } from 'vs/base/common/strings';
+import { equalsIgnoreCase, format } from 'vs/base/common/strings';
+import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
+import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
interface FileQuickPickItem extends IQuickPickItem {
uri: URI;
isFolder: boolean;
}
+enum UpdateResult {
+ Updated,
+ NotUpdated,
+ InvalidPath
+}
+
// Reference: https://en.wikipedia.org/wiki/Filename
const INVALID_FILE_CHARS = isWindows ? /[\\/:\*\?"<>\|]/g : /[\\/]/g;
const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i;
export class RemoteFileDialog {
- private acceptButton: IQuickInputButton;
- private fallbackListItem: FileQuickPickItem | undefined;
private options: IOpenDialogOptions;
private currentFolder: URI;
private filePickBox: IQuickPick;
@@ -52,6 +58,7 @@ export class RemoteFileDialog {
private autoCompletePathSegment: string;
private activeItem: FileQuickPickItem;
private userHome: URI;
+ private badPath: string | undefined;
constructor(
@IFileService private readonly fileService: IFileService,
@@ -64,8 +71,8 @@ export class RemoteFileDialog {
@IModeService private readonly modeService: IModeService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
- @IContextKeyService contextKeyService: IContextKeyService
-
+ @IKeybindingService private readonly keybindingService: IKeybindingService,
+ @IContextKeyService contextKeyService: IContextKeyService,
) {
this.remoteAuthority = this.environmentService.configuration.remoteAuthority;
this.contextKey = RemoteFileDialogContext.bindTo(contextKeyService);
@@ -79,13 +86,6 @@ export class RemoteFileDialog {
return Promise.resolve(undefined);
}
this.options = newOptions;
-
- const openFileString = nls.localize('remoteFileDialog.localFileFallback', '(Open Local File)');
- const openFolderString = nls.localize('remoteFileDialog.localFolderFallback', '(Open Local Folder)');
- const openFileFolderString = nls.localize('remoteFileDialog.localFileFolderFallback', '(Open Local File or Folder)');
- let fallbackLabel = options.canSelectFiles ? (options.canSelectFolders ? openFileFolderString : openFileString) : openFolderString;
- this.fallbackListItem = this.getFallbackFileSystem(fallbackLabel);
-
return this.pickResource();
}
@@ -100,7 +100,6 @@ export class RemoteFileDialog {
this.options = newOptions;
this.options.canSelectFolders = true;
this.options.canSelectFiles = true;
- this.fallbackListItem = this.getFallbackFileSystem(nls.localize('remoteFileDialog.localSaveFallback', '(Save Local File)'));
return new Promise((resolve) => {
this.pickResource(true).then(folderUri => {
@@ -129,20 +128,13 @@ export class RemoteFileDialog {
private remoteUriFrom(path: string): URI {
path = path.replace(/\\/g, '/');
- return resources.toLocalResource(URI.from({ scheme: this.scheme, path }), this.remoteAuthority);
+ return resources.toLocalResource(URI.from({ scheme: this.scheme, path }), this.scheme === Schemas.file ? undefined : this.remoteAuthority);
}
private getScheme(defaultUri: URI | undefined, available: string[] | undefined): string {
return defaultUri ? defaultUri.scheme : (available ? available[0] : Schemas.file);
}
- private getFallbackFileSystem(label: string): FileQuickPickItem | undefined {
- if (this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) {
- return { label: label, uri: URI.from({ scheme: this.options.availableFileSystems[1] }), isFolder: true };
- }
- return undefined;
- }
-
private async getUserHome(): Promise {
if (this.scheme !== Schemas.file) {
const env = await this.remoteAgentService.getEnvironment();
@@ -182,33 +174,34 @@ export class RemoteFileDialog {
}
}
}
- this.acceptButton = { iconPath: this.getDialogIcons('accept'), tooltip: this.options.title };
return new Promise(async (resolve) => {
this.filePickBox = this.quickInputService.createQuickPick();
this.filePickBox.matchOnLabel = false;
this.filePickBox.autoFocusOnList = false;
+ this.filePickBox.ok = true;
+ if (this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) {
+ this.filePickBox.customButton = true;
+ this.filePickBox.customLabel = nls.localize('remoteFileDialog.local', 'Show Local');
+ const action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderAction : OpenLocalFileAction) : OpenLocalFolderAction;
+ const keybinding = this.keybindingService.lookupKeybinding(action.ID);
+ if (keybinding) {
+ const label = keybinding.getLabel();
+ if (label) {
+ this.filePickBox.customHover = format('{0} ({1})', action.LABEL, label);
+ }
+ }
+ }
let isResolving = false;
let isAcceptHandled = false;
this.currentFolder = homedir;
this.userEnteredPathSegment = '';
this.autoCompletePathSegment = '';
- this.filePickBox.buttons = [this.acceptButton];
- this.filePickBox.onDidTriggerButton(_ => {
- // accept button
- const resolveValue = this.addPostfix(this.remoteUriFrom(this.filePickBox.value));
- this.validate(resolveValue).then(validated => {
- if (validated) {
- isResolving = true;
- this.filePickBox.hide();
- doResolve(this, resolveValue);
- }
- });
- });
this.filePickBox.title = this.options.title;
- this.filePickBox.value = this.pathFromUri(this.currentFolder);
+ this.filePickBox.value = this.pathFromUri(this.currentFolder, true);
+ this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
this.filePickBox.items = [];
function doResolve(dialog: RemoteFileDialog, uri: URI | undefined) {
@@ -217,6 +210,28 @@ export class RemoteFileDialog {
dialog.filePickBox.dispose();
}
+ this.filePickBox.onDidCustom(() => {
+ if (isAcceptHandled || this.filePickBox.busy) {
+ return undefined; // {{SQL CARBON EDIT}} @todo anthonydresser return to return; when we do strict null checks
+ }
+
+ isAcceptHandled = true;
+ isResolving = true;
+ if (this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) {
+ this.options.availableFileSystems.shift();
+ }
+ this.options.defaultUri = undefined;
+ if (this.requiresTrailing) {
+ return this.fileDialogService.showSaveDialog(this.options).then(result => {
+ doResolve(this, result);
+ });
+ } else {
+ return this.fileDialogService.showOpenDialog(this.options).then(result => {
+ doResolve(this, result ? result[0] : undefined);
+ });
+ }
+ });
+
this.filePickBox.onDidAccept(_ => {
if (isAcceptHandled || this.filePickBox.busy) {
return;
@@ -247,15 +262,16 @@ export class RemoteFileDialog {
this.filePickBox.onDidChangeValue(async value => {
// onDidChangeValue can also be triggered by the auto complete, so if it looks like the auto complete, don't do anything
if (this.isChangeFromUser()) {
- if (value !== this.constructFullUserPath()) {
+ // If the user has just entered more bad path, don't change anything
+ if (value !== this.constructFullUserPath() && !this.isBadSubpath(value)) {
this.filePickBox.validationMessage = undefined;
this.shouldOverwriteFile = false;
const valueUri = this.remoteUriFrom(this.trimTrailingSlash(this.filePickBox.value));
- let isUpdate = false;
+ let updated: UpdateResult = UpdateResult.NotUpdated;
if (!resources.isEqual(this.remoteUriFrom(this.trimTrailingSlash(this.pathFromUri(this.currentFolder))), valueUri, true)) {
- isUpdate = await this.tryUpdateItems(value, this.remoteUriFrom(this.filePickBox.value));
+ updated = await this.tryUpdateItems(value, this.remoteUriFrom(this.filePickBox.value));
}
- if (!isUpdate) {
+ if (updated === UpdateResult.NotUpdated) {
this.setActiveItems(value);
}
} else {
@@ -281,6 +297,10 @@ export class RemoteFileDialog {
});
}
+ private isBadSubpath(value: string) {
+ return this.badPath && (value.length > this.badPath.length) && equalsIgnoreCase(value.substring(0, this.badPath.length), this.badPath);
+ }
+
private isChangeFromUser(): boolean {
if ((this.filePickBox.value === this.pathAppend(this.currentFolder, this.userEnteredPathSegment + this.autoCompletePathSegment))
&& (this.activeItem === (this.filePickBox.activeItems ? this.filePickBox.activeItems[0] : undefined))) {
@@ -294,24 +314,6 @@ export class RemoteFileDialog {
}
private async onDidAccept(): Promise {
- // Check if Open Local has been selected
- const selectedItems: ReadonlyArray = this.filePickBox.selectedItems;
- if (selectedItems && (selectedItems.length > 0) && (selectedItems[0] === this.fallbackListItem)) {
- if (this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) {
- this.options.availableFileSystems.shift();
- }
- this.options.defaultUri = undefined;
- if (this.requiresTrailing) {
- return this.fileDialogService.showSaveDialog(this.options).then(result => {
- return result;
- });
- } else {
- return this.fileDialogService.showOpenDialog(this.options).then(result => {
- return result ? result[0] : undefined;
- });
- }
- }
-
let resolveValue: URI | undefined;
let navigateValue: URI | undefined;
const trimmedPickBoxValue = ((this.filePickBox.value.length > 1) && this.endsWithSlash(this.filePickBox.value)) ? this.filePickBox.value.substr(0, this.filePickBox.value.length - 1) : this.filePickBox.value;
@@ -360,10 +362,11 @@ export class RemoteFileDialog {
return Promise.resolve(undefined);
}
- private async tryUpdateItems(value: string, valueUri: URI): Promise {
+ private async tryUpdateItems(value: string, valueUri: URI): Promise {
if (value[value.length - 1] === '~') {
await this.updateItems(this.userHome);
- return true;
+ this.badPath = undefined;
+ return UpdateResult.Updated;
} else if (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true))) {
let stat: IFileStat | undefined;
try {
@@ -373,7 +376,14 @@ export class RemoteFileDialog {
}
if (stat && stat.isDirectory && (resources.basename(valueUri) !== '.') && this.endsWithSlash(value)) {
await this.updateItems(valueUri);
- return true;
+ return UpdateResult.Updated;
+ } else if (this.endsWithSlash(value)) {
+ // The input box contains a path that doesn't exist on the system.
+ this.filePickBox.validationMessage = nls.localize('remoteFileDialog.badPath', 'The path does not exist.');
+ // Save this bad path. It can take too long to to a stat on every user entered character, but once a user enters a bad path they are likely
+ // to keep typing more bad path. We can compare against this bad path and see if the user entered path starts with it.
+ this.badPath = value;
+ return UpdateResult.InvalidPath;
} else {
const inputUriDirname = resources.dirname(valueUri);
if (!resources.isEqual(this.remoteUriFrom(this.trimTrailingSlash(this.pathFromUri(this.currentFolder))), inputUriDirname, true)) {
@@ -385,12 +395,14 @@ export class RemoteFileDialog {
}
if (statWithoutTrailing && statWithoutTrailing.isDirectory && (resources.basename(valueUri) !== '.')) {
await this.updateItems(inputUriDirname, resources.basename(valueUri));
- return true;
+ this.badPath = undefined;
+ return UpdateResult.Updated;
}
}
}
}
- return false;
+ this.badPath = undefined;
+ return UpdateResult.NotUpdated;
}
private setActiveItems(value: string) {
@@ -428,7 +440,7 @@ export class RemoteFileDialog {
this.autoCompletePathSegment = '';
return false;
}
- const itemBasename = (quickPickItem.label === '..') ? quickPickItem.label : resources.basename(quickPickItem.uri);
+ const itemBasename = quickPickItem.label;
// Either force the autocomplete, or the old value should be one smaller than the new value and match the new value.
if (!force && (itemBasename.length >= startingBasename.length) && equalsIgnoreCase(itemBasename.substr(0, startingBasename.length), startingBasename)) {
this.userEnteredPathSegment = startingBasename;
@@ -582,8 +594,11 @@ export class RemoteFileDialog {
if (this.allowFolderSelection) {
this.filePickBox.activeItems = [];
}
- this.filePickBox.valueSelection = [0, this.filePickBox.value.length];
- this.insertText(newValue, newValue);
+ if (!equalsIgnoreCase(this.filePickBox.value, newValue)) {
+ this.filePickBox.valueSelection = [0, this.filePickBox.value.length];
+ this.insertText(newValue, newValue);
+ }
+ this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
this.filePickBox.busy = false;
});
}
@@ -687,10 +702,6 @@ export class RemoteFileDialog {
if (backDir) {
sorted.unshift(backDir);
}
-
- if (this.fallbackListItem) {
- sorted.push(this.fallbackListItem);
- }
return sorted;
}
@@ -724,11 +735,4 @@ export class RemoteFileDialog {
return undefined;
}
}
-
- private getDialogIcons(name: string): { light: URI, dark: URI } {
- return {
- dark: URI.parse(require.toUrl(`vs/workbench/services/dialogs/browser/media/dark/${name}.svg`)),
- light: URI.parse(require.toUrl(`vs/workbench/services/dialogs/browser/media/light/${name}.svg`))
- };
- }
}
diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts
index 9e2a180d90..f38eb6b508 100644
--- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts
+++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts
@@ -12,8 +12,8 @@ import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/c
import { Registry } from 'vs/platform/registry/common/platform';
import { IMessage } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
+import { values } from 'vs/base/common/map';
-const hasOwnProperty = Object.hasOwnProperty;
const schemaRegistry = Registry.as(Extensions.JSONContribution);
export type ExtensionKind = 'workspace' | 'ui' | undefined;
@@ -370,18 +370,14 @@ export interface IExtensionPointDescriptor {
export class ExtensionsRegistryImpl {
- private _extensionPoints: { [extPoint: string]: ExtensionPoint; };
-
- constructor() {
- this._extensionPoints = {};
- }
+ private readonly _extensionPoints = new Map>();
public registerExtensionPoint(desc: IExtensionPointDescriptor): IExtensionPoint {
- if (hasOwnProperty.call(this._extensionPoints, desc.extensionPoint)) {
+ if (this._extensionPoints.has(desc.extensionPoint)) {
throw new Error('Duplicate extension point: ' + desc.extensionPoint);
}
- let result = new ExtensionPoint(desc.extensionPoint, desc.defaultExtensionKind);
- this._extensionPoints[desc.extensionPoint] = result;
+ const result = new ExtensionPoint(desc.extensionPoint, desc.defaultExtensionKind);
+ this._extensionPoints.set(desc.extensionPoint, result);
schema.properties['contributes'].properties[desc.extensionPoint] = desc.jsonSchema;
schemaRegistry.registerSchema(schemaId, schema);
@@ -390,11 +386,7 @@ export class ExtensionsRegistryImpl {
}
public getExtensionPoints(): ExtensionPoint[] {
- return Object.keys(this._extensionPoints).map(point => this._extensionPoints[point]);
- }
-
- public getExtensionPointsMap(): { [extPoint: string]: ExtensionPoint; } {
- return this._extensionPoints;
+ return values(this._extensionPoints);
}
}
diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts
index 925f36b1c3..31d2fd53e1 100644
--- a/src/vs/workbench/services/progress/browser/progressService2.ts
+++ b/src/vs/workbench/services/progress/browser/progressService2.ts
@@ -20,6 +20,9 @@ import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { Dialog } from 'vs/base/browser/ui/dialog/dialog';
import { attachDialogStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
+import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
+import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
+import { EventHelper } from 'vs/base/browser/dom';
export class ProgressService2 implements IProgressService2 {
@@ -34,7 +37,8 @@ export class ProgressService2 implements IProgressService2 {
@INotificationService private readonly _notificationService: INotificationService,
@IStatusbarService private readonly _statusbarService: IStatusbarService,
@ILayoutService private readonly _layoutService: ILayoutService,
- @IThemeService private readonly _themeService: IThemeService
+ @IThemeService private readonly _themeService: IThemeService,
+ @IKeybindingService private readonly _keybindingService: IKeybindingService
) { }
withProgress(options: IProgressOptions, task: (progress: IProgress) => Promise, onDidCancel?: () => void): Promise {
@@ -276,6 +280,10 @@ export class ProgressService2 implements IProgressService2 {
private _withDialogProgress, R = unknown>(options: IProgressOptions, task: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P {
const disposables: IDisposable[] = [];
+ const allowableCommands = [
+ 'workbench.action.quit',
+ 'workbench.action.reloadWindow'
+ ];
let dialog: Dialog;
@@ -284,7 +292,17 @@ export class ProgressService2 implements IProgressService2 {
this._layoutService.container,
message,
[options.cancellable ? localize('cancel', "Cancel") : localize('dismiss', "Dismiss")],
- { type: 'pending' }
+ {
+ type: 'pending',
+ keyEventProcessor: (event: StandardKeyboardEvent) => {
+ const resolved = this._keybindingService.softDispatch(event, this._layoutService.container);
+ if (resolved && resolved.commandId) {
+ if (allowableCommands.indexOf(resolved.commandId) === -1) {
+ EventHelper.stop(event, true);
+ }
+ }
+ }
+ }
);
disposables.push(dialog);
diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts
index 2c74e2260b..4a9290dd22 100644
--- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts
+++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts
@@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { IChannel, IServerChannel, getDelayedChannel } from 'vs/base/parts/ipc/common/ipc';
import { Client } from 'vs/base/parts/ipc/common/ipc.net';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
-import { connectRemoteAgentManagement, IConnectionOptions, IWebSocketFactory } from 'vs/platform/remote/common/remoteAgentConnection';
+import { connectRemoteAgentManagement, IConnectionOptions, IWebSocketFactory, PersistenConnectionEvent } from 'vs/platform/remote/common/remoteAgentConnection';
import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
@@ -73,15 +73,18 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon
private readonly _onReconnecting = this._register(new Emitter());
public readonly onReconnecting = this._onReconnecting.event;
+ private readonly _onDidStateChange = this._register(new Emitter());
+ public readonly onDidStateChange = this._onDidStateChange.event;
+
readonly remoteAuthority: string;
private _connection: Promise> | null;
constructor(
remoteAuthority: string,
- private _commit: string | undefined,
- private _webSocketFactory: IWebSocketFactory,
- private _environmentService: IEnvironmentService,
- private _remoteAuthorityResolverService: IRemoteAuthorityResolverService
+ private readonly _commit: string | undefined,
+ private readonly _webSocketFactory: IWebSocketFactory,
+ private readonly _environmentService: IEnvironmentService,
+ private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService
) {
super();
this.remoteAuthority = remoteAuthority;
@@ -121,8 +124,8 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon
}
}
};
- const connection = await connectRemoteAgentManagement(options, this.remoteAuthority, `renderer`);
- this._register(connection);
+ const connection = this._register(await connectRemoteAgentManagement(options, this.remoteAuthority, `renderer`));
+ this._register(connection.onDidStateChange(e => this._onDidStateChange.fire(e)));
return connection.client;
}
}
diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts
index b76336ee5f..787366ebdf 100644
--- a/src/vs/workbench/services/remote/common/remoteAgentService.ts
+++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts
@@ -8,6 +8,7 @@ import { RemoteAgentConnectionContext, IRemoteAgentEnvironment } from 'vs/platfo
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
import { Event } from 'vs/base/common/event';
+import { PersistenConnectionEvent } from 'vs/platform/remote/common/remoteAgentConnection';
export const RemoteExtensionLogFileName = 'remoteagent';
@@ -25,7 +26,9 @@ export interface IRemoteAgentService {
export interface IRemoteAgentConnection {
readonly remoteAuthority: string;
+ readonly onReconnecting: Event;
+ readonly onDidStateChange: Event;
+
getChannel(channelName: string): T;
registerChannel>(channelName: string, channel: T): void;
- onReconnecting: Event;
}