Merge from vscode 6e530127a1bb8ffbd1bfb77dc680c321dc0d71f5 (#6844)

This commit is contained in:
Anthony Dresser
2019-08-20 21:07:47 -07:00
committed by GitHub
parent 1f00249646
commit ecb80f14f0
221 changed files with 3140 additions and 1552 deletions

View File

@@ -8,7 +8,7 @@ import * as modes from 'vs/editor/common/modes';
import { MainContext, MainThreadEditorInsetsShape, IExtHostContext, ExtHostEditorInsetsShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from '../common/extHostCustomers';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/common/webview';
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';

View File

@@ -5,24 +5,43 @@
import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers';
import { URI, UriComponents } from 'vs/base/common/uri';
import { MainThreadDiagnosticsShape, MainContext, IExtHostContext } from '../common/extHost.protocol';
import { MainThreadDiagnosticsShape, MainContext, IExtHostContext, ExtHostDiagnosticsShape, ExtHostContext } from '../common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { IDisposable } from 'vs/base/common/lifecycle';
@extHostNamedCustomer(MainContext.MainThreadDiagnostics)
export class MainThreadDiagnostics implements MainThreadDiagnosticsShape {
private readonly _activeOwners = new Set<string>();
private readonly _proxy: ExtHostDiagnosticsShape;
private readonly _markerService: IMarkerService;
private readonly _markerListener: IDisposable;
constructor(
extHostContext: IExtHostContext,
@IMarkerService markerService: IMarkerService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDiagnostics);
this._markerService = markerService;
this._markerListener = this._markerService.onMarkerChanged(this._forwardMarkers, this);
}
dispose(): void {
this._markerListener.dispose();
this._activeOwners.forEach(owner => this._markerService.changeAll(owner, []));
this._activeOwners.clear();
}
private _forwardMarkers(resources: URI[]): void {
const data: [UriComponents, IMarkerData[]][] = [];
for (const resource of resources) {
data.push([
resource,
this._markerService.read({ resource }).filter(marker => !this._activeOwners.has(marker.owner))
]);
}
this._proxy.$acceptMarkersChange(data);
}
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {

View File

@@ -11,6 +11,7 @@ import { distinct } from 'vs/base/common/arrays';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { isUndefinedOrNull, isNumber } from 'vs/base/common/types';
import { Registry } from 'vs/platform/registry/common/platform';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadTreeViews)
export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape {
@@ -21,25 +22,28 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
constructor(
extHostContext: IExtHostContext,
@IViewsService private readonly viewsService: IViewsService,
@INotificationService private readonly notificationService: INotificationService
@INotificationService private readonly notificationService: INotificationService,
@IExtensionService private readonly extensionService: IExtensionService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
}
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void {
const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService);
this._dataProviders.set(treeViewId, dataProvider);
const viewer = this.getTreeView(treeViewId);
if (viewer) {
viewer.dataProvider = dataProvider;
viewer.showCollapseAllAction = !!options.showCollapseAll;
viewer.canSelectMany = !!options.canSelectMany;
this.registerListeners(treeViewId, viewer);
this._proxy.$setVisible(treeViewId, viewer.visible);
} else {
this.notificationService.error('No view is registered with id: ' + treeViewId);
}
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService);
this._dataProviders.set(treeViewId, dataProvider);
const viewer = this.getTreeView(treeViewId);
if (viewer) {
viewer.dataProvider = dataProvider;
viewer.showCollapseAllAction = !!options.showCollapseAll;
viewer.canSelectMany = !!options.canSelectMany;
this.registerListeners(treeViewId, viewer);
this._proxy.$setVisible(treeViewId, viewer.visible);
} else {
this.notificationService.error('No view is registered with id: ' + treeViewId);
}
});
}
$reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void> {

View File

@@ -5,30 +5,62 @@
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as map from 'vs/base/common/map';
import { startsWith } from 'vs/base/common/strings';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { localize } from 'vs/nls';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IProductService } from 'vs/platform/product/common/product';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewPanelViewStateData } from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { extHostNamedCustomer } from '../common/extHostCustomers';
import { IProductService } from 'vs/platform/product/common/product';
import { startsWith } from 'vs/base/common/strings';
import { Webview } from 'vs/workbench/contrib/webview/common/webview';
interface OldMainThreadWebviewState {
readonly viewType: string;
state: any;
}
/**
* Bi-directional map between webview handles and inputs.
*/
class WebviewHandleStore {
private readonly _handlesToInputs = new Map<string, WebviewEditorInput>();
private readonly _inputsToHandles = new Map<WebviewEditorInput, string>();
public add(handle: string, input: WebviewEditorInput): void {
this._handlesToInputs.set(handle, input);
this._inputsToHandles.set(input, handle);
}
public getHandleForInput(input: WebviewEditorInput): string | undefined {
return this._inputsToHandles.get(input);
}
public getInputForHandle(handle: string): WebviewEditorInput | undefined {
return this._handlesToInputs.get(handle);
}
public delete(handle: string): void {
const input = this.getInputForHandle(handle);
this._handlesToInputs.delete(handle);
if (input) {
this._inputsToHandles.delete(input);
}
}
public get size(): number {
return this._handlesToInputs.size;
}
}
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape {
@@ -43,12 +75,9 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
private static revivalPool = 0;
private readonly _proxy: ExtHostWebviewsShape;
private readonly _webviewEditorInputs = new Map<string, WebviewEditorInput>();
private readonly _webviews = new Map<string, Webview>();
private readonly _webviewEditorInputs = new WebviewHandleStore();
private readonly _revivers = new Map<string, IDisposable>();
private _activeWebview: WebviewPanelHandle | undefined = undefined;
constructor(
context: IExtHostContext,
@IExtensionService extensionService: IExtensionService,
@@ -62,8 +91,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
super();
this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews);
this._register(_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this));
this._register(_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this));
this._register(_editorService.onDidActiveEditorChange(this.updateWebviewViewStates, this));
this._register(_editorService.onDidVisibleEditorsChange(this.updateWebviewViewStates, this));
// This reviver's only job is to activate webview extensions
// This should trigger the real reviver to be registered from the extension host side.
@@ -104,8 +133,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
});
this.hookupWebviewEventDelegate(handle, webview);
this._webviewEditorInputs.set(handle, webview);
this._webviews.set(handle, webview.webview);
this._webviewEditorInputs.add(handle, webview);
/* __GDPR__
"webviews:createWebviewPanel" : {
@@ -175,8 +203,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
}
const handle = `revival-${MainThreadWebviews.revivalPool++}`;
this._webviewEditorInputs.set(handle, webviewEditorInput);
this._webviews.set(handle, webviewEditorInput.webview);
this._webviewEditorInputs.add(handle, webviewEditorInput);
this.hookupWebviewEventDelegate(handle, webviewEditorInput);
let state = undefined;
@@ -234,7 +261,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
input.onDispose(() => {
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
this._webviewEditorInputs.delete(handle);
this._webviews.delete(handle);
});
});
input.webview.onDidUpdateState((newState: any) => {
@@ -246,70 +272,35 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
});
}
private onActiveEditorChanged() {
const activeEditor = this._editorService.activeControl;
let newActiveWebview: { input: WebviewEditorInput, handle: WebviewPanelHandle } | undefined = undefined;
if (activeEditor && activeEditor.input instanceof WebviewEditorInput) {
for (const handle of map.keys(this._webviewEditorInputs)) {
const input = this._webviewEditorInputs.get(handle)!;
if (input.matches(activeEditor.input)) {
newActiveWebview = { input, handle };
break;
}
}
}
if (newActiveWebview && newActiveWebview.handle === this._activeWebview) {
// Webview itself unchanged but position may have changed
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || 0)
});
private updateWebviewViewStates() {
if (!this._webviewEditorInputs.size) {
return;
}
// Broadcast view state update for currently active
if (typeof this._activeWebview !== 'undefined') {
const oldActiveWebview = this._webviewEditorInputs.get(this._activeWebview);
if (oldActiveWebview) {
this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, {
active: false,
visible: this._editorService.visibleControls.some(editor => !!editor.input && editor.input.matches(oldActiveWebview)),
position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || 0),
});
}
}
const activeInput = this._editorService.activeControl && this._editorService.activeControl.input;
const viewStates: WebviewPanelViewStateData = {};
for (const group of this._editorGroupService.groups) {
for (const input of group.editors) {
if (!(input instanceof WebviewEditorInput)) {
continue;
}
// Then for newly active
if (newActiveWebview) {
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, activeEditor ? activeEditor.group : ACTIVE_GROUP),
});
this._activeWebview = newActiveWebview.handle;
} else {
this._activeWebview = undefined;
}
}
input.updateGroup(group.id);
private onVisibleEditorsChanged(): void {
this._webviewEditorInputs.forEach((input, handle) => {
for (const workbenchEditor of this._editorService.visibleControls) {
if (workbenchEditor.input && workbenchEditor.input.matches(input)) {
const editorPosition = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group!);
input.updateGroup(workbenchEditor.group!.id);
this._proxy.$onDidChangeWebviewPanelViewState(handle, {
active: handle === this._activeWebview,
visible: true,
position: editorPosition
});
break;
const handle = this._webviewEditorInputs.getHandleForInput(input);
if (handle) {
viewStates[handle] = {
visible: input === group.activeEditor,
active: input === activeInput,
position: editorGroupToViewColumn(this._editorGroupService, group.id || 0),
};
}
}
});
}
if (Object.keys(viewStates).length) {
this._proxy.$onDidChangeWebviewPanelViewStates(viewStates);
}
}
private onDidClickLink(handle: WebviewPanelHandle, link: URI): void {
@@ -342,28 +333,19 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
}
private tryGetWebviewEditorInput(handle: WebviewPanelHandle): WebviewEditorInput | undefined {
return this._webviewEditorInputs.get(handle);
return this._webviewEditorInputs.getInputForHandle(handle);
}
private getWebview(handle: WebviewPanelHandle): Webview {
const webview = this.tryGetWebview(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
}
return webview;
}
private tryGetWebview(handle: WebviewPanelHandle): Webview | undefined {
return this._webviews.get(handle);
return this.getWebviewEditorInput(handle).webview;
}
private static getDeserializationFailedContents(viewType: string) {
return `<!DOCTYPE html>
<html>
<head>
<base href="https://code.visualstudio.com/raw/">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https: data:; media-src https:; script-src 'none'; style-src vscode-resource: https: 'unsafe-inline'; child-src 'none'; frame-src 'none';">
<meta http-equiv="Content-Security-Policy" content="default-src 'none';">
</head>
<body>${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)}</body>
</html>`;

View File

@@ -59,7 +59,7 @@ export class MainThreadWindow implements MainThreadWindowShape {
}
}
return this.openerService.openExternal(uri);
return this.openerService.open(uri, { openExternal: true });
}
private getOrCreateTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {

View File

@@ -175,6 +175,11 @@ CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async functio
return windowService.addRecentlyOpened([recent]);
});
CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function (accessor: ServicesAccessor) {
const windowService = accessor.get(IWindowService);
return windowService.getRecentlyOpened();
});
export class SetEditorLayoutAPICommand {
public static ID = 'vscode.setEditorLayout';
public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Promise<any> {
@@ -205,4 +210,4 @@ CommandsRegistry.registerCommand({
}
}]
}
});
});

View File

@@ -39,13 +39,14 @@ const configurationEntrySchema: IJSONSchema = {
},
scope: {
type: 'string',
enum: ['application', 'machine', 'window', 'resource'],
enum: ['application', 'machine', 'window', 'resource', 'machine-overridable'],
default: 'window',
enumDescriptions: [
nls.localize('scope.application.description', "Application specific configuration, which can be configured only in the user settings."),
nls.localize('scope.machine.description', "Machine specific configuration, which can be configured only in the user settings when the extension is running locally, or only in the remote settings when the extension is running remotely."),
nls.localize('scope.window.description', "Window specific configuration, which can be configured in the user, remote or workspace settings."),
nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the user, remote, workspace or folder settings.")
nls.localize('scope.application.description', "Configuration that can be configured only in the user settings."),
nls.localize('scope.machine.description', "Configuration that can be configured only in the user settings when the extension is running locally, or only in the remote settings when the extension is running remotely."),
nls.localize('scope.window.description', "Configuration that can be configured in the user, remote or workspace settings."),
nls.localize('scope.resource.description', "Configuration that can be configured in the user, remote, workspace or folder settings."),
nls.localize('scope.machine-overridable.description', "Machine configuration that can be configured also in workspace or folder settings.")
],
description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `application`, `machine`, `window` and `resource`.")
},
@@ -215,6 +216,8 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten
propertyConfiguration.scope = ConfigurationScope.MACHINE;
} else if (propertyConfiguration.scope.toString() === 'resource') {
propertyConfiguration.scope = ConfigurationScope.RESOURCE;
} else if (propertyConfiguration.scope.toString() === 'machine-overridable') {
propertyConfiguration.scope = ConfigurationScope.MACHINE_OVERRIDABLE;
} else {
propertyConfiguration.scope = ConfigurationScope.WINDOW;
}

View File

@@ -553,15 +553,17 @@ export interface MainThreadWebviewsShape extends IDisposable {
$unregisterSerializer(viewType: string): void;
}
export interface WebviewPanelViewState {
readonly active: boolean;
readonly visible: boolean;
readonly position: EditorViewColumn;
export interface WebviewPanelViewStateData {
[handle: string]: {
readonly active: boolean;
readonly visible: boolean;
readonly position: EditorViewColumn;
};
}
export interface ExtHostWebviewsShape {
$onMessage(handle: WebviewPanelHandle, message: any): void;
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, newState: WebviewPanelViewState): void;
$onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void;
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void>;
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
}
@@ -745,7 +747,7 @@ export interface ExtHostConfigurationShape {
}
export interface ExtHostDiagnosticsShape {
$acceptMarkersChange(data: [UriComponents, IMarkerData[]][]): void;
}
export interface ExtHostDocumentContentProvidersShape {

View File

@@ -10,7 +10,7 @@ import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor';
import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors';
import * as vscode from 'vscode';
import { ExtHostEditorInsetsShape, MainThreadEditorInsetsShape } from './extHost.protocol';
import { toWebviewResource, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import { generateUuid } from 'vs/base/common/uuid';
export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
@@ -65,8 +65,8 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
private _html: string = '';
private _options: vscode.WebviewOptions = Object.create(null);
toWebviewResource(resource: vscode.Uri): vscode.Uri {
return toWebviewResource(that._initData, this._uuid, resource);
asWebviewUri(resource: vscode.Uri): vscode.Uri {
return asWebviewUri(that._initData, this._uuid, resource);
}
get cspSource(): string {

View File

@@ -5,7 +5,7 @@
import { localize } from 'vs/nls';
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as vscode from 'vscode';
import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol';
import { DiagnosticSeverity } from './extHostTypes';
@@ -20,12 +20,12 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
private readonly _owner: string;
private readonly _maxDiagnosticsPerFile: number;
private readonly _onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>;
private readonly _proxy: MainThreadDiagnosticsShape;
private readonly _proxy: MainThreadDiagnosticsShape | undefined;
private _isDisposed = false;
private _data = new Map<string, vscode.Diagnostic[]>();
constructor(name: string, owner: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) {
constructor(name: string, owner: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape | undefined, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) {
this._name = name;
this._owner = owner;
this._maxDiagnosticsPerFile = maxDiagnosticsPerFile;
@@ -36,7 +36,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
dispose(): void {
if (!this._isDisposed) {
this._onDidChangeDiagnostics.fire(keys(this._data));
this._proxy.$clear(this._owner);
if (this._proxy) {
this._proxy.$clear(this._owner);
}
this._data = undefined!;
this._isDisposed = true;
}
@@ -112,6 +114,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
this._onDidChangeDiagnostics.fire(toSync);
// compute change and send to main side
if (!this._proxy) {
return;
}
const entries: [URI, IMarkerData[]][] = [];
for (let uri of toSync) {
let marker: IMarkerData[] = [];
@@ -149,7 +154,6 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
entries.push([uri, marker]);
}
this._proxy.$changeMany(this._owner, entries);
}
@@ -157,14 +161,18 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
this._checkDisposed();
this._onDidChangeDiagnostics.fire([uri]);
this._data.delete(uri.toString());
this._proxy.$changeMany(this._owner, [[uri, undefined]]);
if (this._proxy) {
this._proxy.$changeMany(this._owner, [[uri, undefined]]);
}
}
clear(): void {
this._checkDisposed();
this._onDidChangeDiagnostics.fire(keys(this._data));
this._data.clear();
this._proxy.$clear(this._owner);
if (this._proxy) {
this._proxy.$clear(this._owner);
}
}
forEach(callback: (uri: URI, diagnostics: ReadonlyArray<vscode.Diagnostic>, collection: DiagnosticCollection) => any, thisArg?: any): void {
@@ -311,4 +319,20 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
});
return res;
}
private _mirrorCollection: vscode.DiagnosticCollection | undefined;
$acceptMarkersChange(data: [UriComponents, IMarkerData[]][]): void {
if (!this._mirrorCollection) {
const name = '_generated_mirror';
const collection = new DiagnosticCollection(name, name, ExtHostDiagnostics._maxDiagnosticsPerFile, undefined, this._onDidChangeDiagnostics);
this._collections.set(name, collection);
this._mirrorCollection = collection;
}
for (const [uri, markers] of data) {
this._mirrorCollection.set(URI.revive(uri), markers.map(converter.Diagnostic.to));
}
}
}

View File

@@ -24,12 +24,12 @@ import { IExtensionApiFactory as sqlIApiFactory } from 'sql/workbench/api/common
interface LoadFunction {
(request: string, parent: { filename: string; }, isMain: any): any;
(request: string): any;
}
export interface INodeModuleFactory { //{{SQL CARBON EDIT}} export interface
readonly nodeModuleName: string | string[];
load(request: string, parent: URI, isMain: any, original: LoadFunction): any;
load(request: string, parent: URI, original: LoadFunction): any;
alternativeModuleName?(name: string): string | undefined;
}
@@ -250,7 +250,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory {
};
}
public load(request: string, parent: URI, isMain: any, original: LoadFunction): any {
public load(request: string, parent: URI, original: LoadFunction): any {
// get extension id from filename and api for extension
const extension = this._extensionPaths.findSubstr(parent.fsPath);
if (extension) {
@@ -258,7 +258,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory {
this.sendShimmingTelemetry();
}
this._original = original(request, { filename: parent.fsPath }, isMain);
this._original = original(request);
return this._impl;
}

View File

@@ -113,6 +113,15 @@ export namespace DiagnosticTag {
}
return undefined;
}
export function to(value: MarkerTag): vscode.DiagnosticTag | undefined {
switch (value) {
case MarkerTag.Unnecessary:
return types.DiagnosticTag.Unnecessary;
case MarkerTag.Deprecated:
return types.DiagnosticTag.Deprecated;
}
return undefined;
}
}
export namespace Diagnostic {
@@ -127,6 +136,15 @@ export namespace Diagnostic {
tags: Array.isArray(value.tags) ? coalesce(value.tags.map(DiagnosticTag.from)) : undefined,
};
}
export function to(value: IMarkerData): vscode.Diagnostic {
const res = new types.Diagnostic(Range.to(value), value.message, DiagnosticSeverity.to(value.severity));
res.source = value.source;
res.code = value.code;
res.relatedInformation = value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.to);
res.tags = value.tags && coalesce(value.tags.map(DiagnosticTag.to));
return res;
}
}
export namespace DiagnosticRelatedInformation {

View File

@@ -5,15 +5,15 @@
import { Emitter, Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import * as modes from 'vs/editor/common/modes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import * as vscode from 'vscode';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol';
import { Disposable } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as modes from 'vs/editor/common/modes';
import { WebviewInitData, toWebviewResource } from 'vs/workbench/api/common/shared/webview';
import { generateUuid } from 'vs/base/common/uuid';
type IconPath = URI | { light: URI, dark: URI };
@@ -35,8 +35,8 @@ export class ExtHostWebview implements vscode.Webview {
this._onMessageEmitter.dispose();
}
public toWebviewResource(resource: vscode.Uri): vscode.Uri {
return toWebviewResource(this._initData, this._handle, resource);
public asWebviewUri(resource: vscode.Uri): vscode.Uri {
return asWebviewUri(this._initData, this._handle, resource);
}
public get cspSource(): string {
@@ -89,11 +89,12 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
private readonly _options: vscode.WebviewPanelOptions;
private readonly _webview: ExtHostWebview;
private _isDisposed: boolean = false;
private _viewColumn: vscode.ViewColumn | undefined;
private _visible: boolean = true;
private _active: boolean = true;
_isDisposed: boolean = false;
readonly _onDisposeEmitter = new Emitter<void>();
public readonly onDidDispose: Event<void> = this._onDisposeEmitter.event;
@@ -297,21 +298,21 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
}
public $onDidChangeWebviewPanelViewState(
handle: WebviewPanelHandle,
newState: WebviewPanelViewState
): void {
const panel = this.getWebviewPanel(handle);
if (!panel) {
return;
}
public $onDidChangeWebviewPanelViewStates(newStates: WebviewPanelViewStateData): void {
for (const handle of Object.keys(newStates)) {
const panel = this.getWebviewPanel(handle);
if (!panel || panel._isDisposed) {
continue;
}
const viewColumn = typeConverters.ViewColumn.to(newState.position);
if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) {
panel._setActive(newState.active);
panel._setVisible(newState.visible);
panel._setViewColumn(viewColumn);
panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel });
const newState = newStates[handle];
const viewColumn = typeConverters.ViewColumn.to(newState.position);
if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) {
panel._setActive(newState.active);
panel._setVisible(newState.visible);
panel._setViewColumn(viewColumn);
panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel });
}
}
}

View File

@@ -11,7 +11,7 @@ export interface WebviewInitData {
readonly webviewCspSource: string;
}
export function toWebviewResource(
export function asWebviewUri(
initData: WebviewInitData,
uuid: string,
resource: vscode.Uri

View File

@@ -31,7 +31,11 @@ class NodeModuleRequireInterceptor extends RequireInterceptor {
if (!that._factories.has(request)) {
return original.apply(this, arguments);
}
return that._factories.get(request)!.load(request, URI.file(parent.filename), isMain, original);
return that._factories.get(request)!.load(
request,
URI.file(parent.filename),
request => original.apply(this, [request, parent, isMain])
);
};
}
}

View File

@@ -497,6 +497,7 @@ export class ExtHostTerminalService implements IExtHostTerminalService, ExtHostT
const terminalConfig = configProvider.getConfiguration('terminal.integrated');
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), lastActiveWorkspace ? lastActiveWorkspace : undefined, this._variableResolver, activeWorkspaceRootUri, terminalConfig.cwd, this._logService);
shellLaunchConfig.cwd = initialCwd;
const envFromConfig = this._apiInspectConfigToPlain(configProvider.getConfiguration('terminal.integrated').inspect<ITerminalEnvironment>(`env.${platformKey}`));
const baseEnv = terminalConfig.get<boolean>('inheritEnv', true) ? process.env as platform.IProcessEnvironment : await this._getNonInheritedEnv();

View File

@@ -89,7 +89,7 @@ class WorkerRequireInterceptor extends RequireInterceptor {
}
if (this._factories.has(request)) {
return this._factories.get(request)!.load(request, parent, false, () => { throw new Error(); });
return this._factories.get(request)!.load(request, parent, () => { throw new Error('CANNOT LOAD MODULE from here.'); });
}
return undefined;
}

View File

@@ -113,7 +113,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array<ID
if (e.dataTransfer && e.dataTransfer.files) {
for (let i = 0; i < e.dataTransfer.files.length; i++) {
const file = e.dataTransfer.files[i];
if (file && file.path && !resources.some(r => r.resource.fsPath === file.path) /* prevent duplicates */) {
if (file && file.path /* Electron only */ && !resources.some(r => r.resource.fsPath === file.path) /* prevent duplicates */) {
try {
resources.push({ resource: URI.file(file.path), isExternal: true });
} catch (error) {

View File

@@ -317,40 +317,34 @@ class ResourceLabelWidget extends IconLabel {
}
notifyFormattersChange(): void {
if (this.label && this.label.resource) {
this.setFile(this.label.resource, this.options);
}
this.render(false);
}
setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void {
const hasResourceChanged = this.hasResourceChanged(label, options);
const hasPathLabelChanged = this.hasPathLabelChanged(label, options);
const clearIconCache = this.clearIconCache(label, options);
this.label = label;
this.options = options;
if (hasResourceChanged) {
if (hasPathLabelChanged) {
this.computedPathLabel = undefined; // reset path label due to resource change
}
this.render(hasResourceChanged);
this.render(clearIconCache);
}
private hasResourceChanged(label: IResourceLabelProps, options?: IResourceLabelOptions): boolean {
const newResource = label ? label.resource : undefined;
private clearIconCache(newLabel: IResourceLabelProps, newOptions?: IResourceLabelOptions): boolean {
const newResource = newLabel ? newLabel.resource : undefined;
const oldResource = this.label ? this.label.resource : undefined;
const newFileKind = options ? options.fileKind : undefined;
const newFileKind = newOptions ? newOptions.fileKind : undefined;
const oldFileKind = this.options ? this.options.fileKind : undefined;
if (newFileKind !== oldFileKind) {
return true; // same resource but different kind (file, folder)
}
if (newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource)) {
return true;
}
if (newResource && oldResource) {
return newResource.toString() !== oldResource.toString();
}
@@ -362,6 +356,12 @@ class ResourceLabelWidget extends IconLabel {
return true;
}
private hasPathLabelChanged(newLabel: IResourceLabelProps, newOptions?: IResourceLabelOptions): boolean {
const newResource = newLabel ? newLabel.resource : undefined;
return !!newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource);
}
setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void {
this.setResource({
resource: toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }),
@@ -460,6 +460,7 @@ class ResourceLabelWidget extends IconLabel {
}
iconLabelOptions.extraClasses = this.computedIconClasses.slice(0);
}
if (this.options && this.options.extraClasses) {
iconLabelOptions.extraClasses!.push(...this.options.extraClasses);
}

View File

@@ -9,7 +9,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { size } from 'vs/base/common/collections';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { isEqual, dirname } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -36,14 +36,14 @@ type FileInfo = { path: FileElement[], folder?: IWorkspaceFolder };
export class EditorBreadcrumbsModel {
private readonly _disposables: IDisposable[] = [];
private readonly _disposables = new DisposableStore();
private readonly _fileInfo: FileInfo;
private readonly _cfgFilePath: BreadcrumbsConfig<'on' | 'off' | 'last'>;
private readonly _cfgSymbolPath: BreadcrumbsConfig<'on' | 'off' | 'last'>;
private _outlineElements: Array<OutlineModel | OutlineGroup | OutlineElement> = [];
private _outlineDisposables: IDisposable[] = [];
private _outlineDisposables = new DisposableStore();
private _onDidUpdate = new Emitter<this>();
readonly onDidUpdate: Event<this> = this._onDidUpdate.event;
@@ -58,8 +58,8 @@ export class EditorBreadcrumbsModel {
this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(configurationService);
this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(configurationService);
this._disposables.push(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this)));
this._disposables.push(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this)));
this._disposables.add(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this)));
this._disposables.add(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this)));
this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(this._uri, workspaceService);
this._bindToEditor();
@@ -69,7 +69,7 @@ export class EditorBreadcrumbsModel {
dispose(): void {
this._cfgFilePath.dispose();
this._cfgSymbolPath.dispose();
dispose(this._disposables);
this._disposables.dispose();
}
isRelative(): boolean {
@@ -133,20 +133,27 @@ export class EditorBreadcrumbsModel {
if (!this._editor) {
return;
}
// update as model changes
this._disposables.push(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline()));
this._disposables.push(this._editor.onDidChangeModel(_ => this._updateOutline()));
this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._updateOutline()));
this._disposables.push(Event.debounce(this._editor.onDidChangeModelContent, _ => _, 350)(_ => this._updateOutline(true)));
// update as language, model, providers changes
this._disposables.add(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline()));
this._disposables.add(this._editor.onDidChangeModel(_ => this._updateOutline()));
this._disposables.add(this._editor.onDidChangeModelLanguage(_ => this._updateOutline()));
// update soon'ish as model content change
const updateSoon = new TimeoutTimer();
this._disposables.add(updateSoon);
this._disposables.add(this._editor.onDidChangeModelContent(_ => {
const timeout = OutlineModel.getRequestDelay(this._editor!.getModel());
updateSoon.cancelAndSet(() => this._updateOutline(true), timeout);
}));
this._updateOutline();
// stop when editor dies
this._disposables.push(this._editor.onDidDispose(() => this._outlineDisposables = dispose(this._outlineDisposables)));
this._disposables.add(this._editor.onDidDispose(() => this._outlineDisposables.clear()));
}
private _updateOutline(didChangeContent?: boolean): void {
this._outlineDisposables = dispose(this._outlineDisposables);
this._outlineDisposables.clear();
if (!didChangeContent) {
this._updateOutlineElements([]);
}
@@ -162,7 +169,7 @@ export class EditorBreadcrumbsModel {
const versionIdThen = buffer.getVersionId();
const timeout = new TimeoutTimer();
this._outlineDisposables.push({
this._outlineDisposables.add({
dispose: () => {
source.cancel();
source.dispose();
@@ -180,7 +187,7 @@ export class EditorBreadcrumbsModel {
model = model.adopt();
this._updateOutlineElements(this._getOutlineElements(model, editor.getPosition()));
this._outlineDisposables.push(editor.onDidChangeCursorPosition(_ => {
this._outlineDisposables.add(editor.onDidChangeCursorPosition(_ => {
timeout.cancelAndSet(() => {
if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId() && editor.getModel()) {
this._updateOutlineElements(this._getOutlineElements(model, editor.getPosition()));

View File

@@ -17,6 +17,10 @@
text-decoration-line: underline;
}
.monaco-workbench .monaco-breadcrumb-item.shows-symbol-icon .symbol-icon.block {
padding-right: 6px;
}
/* todo@joh move somewhere else */
.monaco-workbench .monaco-breadcrumbs-picker .arrow {

View File

@@ -66,4 +66,4 @@
.vs .monaco-workbench > .notifications-center > .notifications-center-header .hide-all-notifications-action {
background-image: url('tree-expanded-light.svg');
}
}

View File

@@ -91,6 +91,7 @@
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-toolbar-container {
display: none;
height: 22px;
}
.monaco-workbench .notifications-list-container .notification-list-item:hover .notification-list-item-toolbar-container,
@@ -142,4 +143,4 @@
.monaco-workbench .notifications-list-container .progress-bit {
height: 2px;
bottom: 0;
}
}

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { IMenubarMenu, IMenubarMenuItemAction, IMenubarMenuItemSubmenu, IMenubarKeybinding, IMenubarService, IMenubarData, MenubarMenuItem } from 'vs/platform/menubar/common/menubar';
import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService';
import { IWindowService, MenuBarVisibility, IWindowsService, getTitleBarStyle, IURIToOpen } from 'vs/platform/windows/common/windows';
@@ -33,7 +32,6 @@ import { attachMenuStyler } from 'vs/platform/theme/common/styler';
import { assign } from 'vs/base/common/objects';
import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { isFullscreen } from 'vs/base/browser/browser';
@@ -134,7 +132,7 @@ export abstract class MenubarControl extends Disposable {
this.menuUpdater.schedule();
}
protected calculateActionLabel(action: IAction | IMenubarMenuItemAction): string {
protected calculateActionLabel(action: { id: string; label: string; }): string {
let label = action.label;
switch (action.id) {
default:
@@ -252,195 +250,6 @@ export abstract class MenubarControl extends Disposable {
}
}
export class NativeMenubarControl extends MenubarControl {
constructor(
@IMenuService menuService: IMenuService,
@IWindowService windowService: IWindowService,
@IWindowsService windowsService: IWindowsService,
@IContextKeyService contextKeyService: IContextKeyService,
@IKeybindingService keybindingService: IKeybindingService,
@IConfigurationService configurationService: IConfigurationService,
@ILabelService labelService: ILabelService,
@IUpdateService updateService: IUpdateService,
@IStorageService storageService: IStorageService,
@INotificationService notificationService: INotificationService,
@IPreferencesService preferencesService: IPreferencesService,
@IEnvironmentService environmentService: IEnvironmentService,
@IAccessibilityService accessibilityService: IAccessibilityService,
@IMenubarService private readonly menubarService: IMenubarService
) {
super(
menuService,
windowService,
windowsService,
contextKeyService,
keybindingService,
configurationService,
labelService,
updateService,
storageService,
notificationService,
preferencesService,
environmentService,
accessibilityService);
if (isMacintosh && !isWeb) {
this.menus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService));
this.topLevelTitles['Preferences'] = nls.localize('mPreferences', "Preferences");
}
for (const topLevelMenuName of Object.keys(this.topLevelTitles)) {
const menu = this.menus[topLevelMenuName];
if (menu) {
this._register(menu.onDidChange(() => this.updateMenubar()));
}
}
this.windowService.getRecentlyOpened().then((recentlyOpened) => {
this.recentlyOpened = recentlyOpened;
this.doUpdateMenubar(true);
});
this.registerListeners();
}
protected doUpdateMenubar(firstTime: boolean): void {
// Send menus to main process to be rendered by Electron
const menubarData = { menus: {}, keybindings: {} };
if (this.getMenubarMenus(menubarData)) {
this.menubarService.updateMenubar(this.windowService.windowId, menubarData);
}
}
private getMenubarMenus(menubarData: IMenubarData): boolean {
if (!menubarData) {
return false;
}
menubarData.keybindings = this.getAdditionalKeybindings();
for (const topLevelMenuName of Object.keys(this.topLevelTitles)) {
const menu = this.menus[topLevelMenuName];
if (menu) {
const menubarMenu: IMenubarMenu = { items: [] };
this.populateMenuItems(menu, menubarMenu, menubarData.keybindings);
if (menubarMenu.items.length === 0) {
// Menus are incomplete
return false;
}
menubarData.menus[topLevelMenuName] = menubarMenu;
}
}
return true;
}
private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding | undefined }) {
let groups = menu.getActions();
for (let group of groups) {
const [, actions] = group;
actions.forEach(menuItem => {
if (menuItem instanceof SubmenuItemAction) {
const submenu = { items: [] };
if (!this.menus[menuItem.item.submenu]) {
this.menus[menuItem.item.submenu] = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService);
this._register(this.menus[menuItem.item.submenu]!.onDidChange(() => this.updateMenubar()));
}
const menuToDispose = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService);
this.populateMenuItems(menuToDispose, submenu, keybindings);
let menubarSubmenuItem: IMenubarMenuItemSubmenu = {
id: menuItem.id,
label: menuItem.label,
submenu: submenu
};
menuToPopulate.items.push(menubarSubmenuItem);
menuToDispose.dispose();
} else {
if (menuItem.id === 'workbench.action.openRecent') {
const actions = this.getOpenRecentActions().map(this.transformOpenRecentAction);
menuToPopulate.items.push(...actions);
}
let menubarMenuItem: IMenubarMenuItemAction = {
id: menuItem.id,
label: menuItem.label
};
if (menuItem.checked) {
menubarMenuItem.checked = true;
}
if (!menuItem.enabled) {
menubarMenuItem.enabled = false;
}
menubarMenuItem.label = this.calculateActionLabel(menubarMenuItem);
keybindings[menuItem.id] = this.getMenubarKeybinding(menuItem.id);
menuToPopulate.items.push(menubarMenuItem);
}
});
menuToPopulate.items.push({ id: 'vscode.menubar.separator' });
}
if (menuToPopulate.items.length > 0) {
menuToPopulate.items.pop();
}
}
private transformOpenRecentAction(action: Separator | (IAction & { uri: URI })): MenubarMenuItem {
if (action instanceof Separator) {
return { id: 'vscode.menubar.separator' };
}
return {
id: action.id,
uri: action.uri,
enabled: action.enabled,
label: action.label
};
}
private getAdditionalKeybindings(): { [id: string]: IMenubarKeybinding } {
const keybindings: { [id: string]: IMenubarKeybinding } = {};
if (isMacintosh) {
const keybinding = this.getMenubarKeybinding('workbench.action.quit');
if (keybinding) {
keybindings['workbench.action.quit'] = keybinding;
}
}
return keybindings;
}
private getMenubarKeybinding(id: string): IMenubarKeybinding | undefined {
const binding = this.keybindingService.lookupKeybinding(id);
if (!binding) {
return undefined;
}
// first try to resolve a native accelerator
const electronAccelerator = binding.getElectronAccelerator();
if (electronAccelerator) {
return { label: electronAccelerator, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) };
}
// we need this fallback to support keybindings that cannot show in electron menus (e.g. chords)
const acceleratorLabel = binding.getLabel();
if (acceleratorLabel) {
return { label: acceleratorLabel, isNative: false, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) };
}
return undefined;
}
}
export class CustomMenubarControl extends MenubarControl {
private menubar: MenuBar;
private container: HTMLElement;

View File

@@ -27,7 +27,7 @@ import { URI } from 'vs/base/common/uri';
import { Color } from 'vs/base/common/color';
import { trim } from 'vs/base/common/strings';
import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { MenubarControl, NativeMenubarControl, CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { template, getBaseLabel } from 'vs/base/common/labels';
import { ILabelService } from 'vs/platform/label/common/label';
@@ -65,7 +65,7 @@ export class TitlebarPart extends Part implements ITitleService {
private windowControls: HTMLElement;
private maxRestoreControl: HTMLElement;
private appIcon: HTMLElement;
private menubarPart: MenubarControl;
private customMenubar: CustomMenubarControl | undefined;
private menubar: HTMLElement;
private resizer: HTMLElement;
private lastLayoutDimensions: Dimension;
@@ -332,19 +332,16 @@ export class TitlebarPart extends Part implements ITitleService {
})));
}
// Menubar: the menubar part which is responsible for populating both the custom and native menubars
if ((isMacintosh && !isWeb) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') {
this.menubarPart = this.instantiationService.createInstance(NativeMenubarControl);
} else {
const customMenubarControl = this.instantiationService.createInstance(CustomMenubarControl);
this.menubarPart = customMenubarControl;
// Menubar: install a custom menu bar depending on configuration
if (getTitleBarStyle(this.configurationService, this.environmentService) !== 'native' && (!isMacintosh || isWeb)) {
this.customMenubar = this._register(this.instantiationService.createInstance(CustomMenubarControl));
this.menubar = append(this.element, $('div.menubar'));
this.menubar.setAttribute('role', 'menubar');
customMenubarControl.create(this.menubar);
this.customMenubar.create(this.menubar);
this._register(customMenubarControl.onVisibilityChange(e => this.onMenubarVisibilityChanged(e)));
this._register(customMenubarControl.onFocusStateChange(e => this.onMenubarFocusChanged(e)));
this._register(this.customMenubar.onVisibilityChange(e => this.onMenubarVisibilityChanged(e)));
this._register(this.customMenubar.onFocusStateChange(e => this.onMenubarFocusChanged(e)));
}
// Title
@@ -547,7 +544,7 @@ export class TitlebarPart extends Part implements ITitleService {
}
private adjustTitleMarginToCenter(): void {
if (this.menubarPart instanceof CustomMenubarControl) {
if (this.customMenubar) {
const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10;
const rightMarker = this.element.clientWidth - (this.windowControls ? this.windowControls.clientWidth : 0) - 10;
@@ -588,9 +585,9 @@ export class TitlebarPart extends Part implements ITitleService {
runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter());
if (this.menubarPart instanceof CustomMenubarControl) {
if (this.customMenubar) {
const menubarDimension = new Dimension(0, dimension.height);
this.menubarPart.layout(menubarDimension);
this.customMenubar.layout(menubarDimension);
}
}
}

View File

@@ -69,7 +69,9 @@ export class CustomTreeViewPanel extends ViewletPanel {
}
renderBody(container: HTMLElement): void {
this.treeView.show(container);
if (this.treeView instanceof CustomTreeView) {
this.treeView.show(container);
}
}
layoutBody(height: number, width: number): void {

View File

@@ -204,7 +204,7 @@ class CodeRendererMain extends Disposable {
version: '1.38.0-unknown',
nameLong: 'Unknown',
extensionAllowedProposedApi: [],
}, ...{ urlProtocol: '', enableTelemetry: false }
}, ...{ urlProtocol: '' }
};
return { _serviceBrand: undefined, ...productConfiguration };
}

View File

@@ -50,12 +50,12 @@ export class Workbench extends Layout {
private readonly _onBeforeShutdown = this._register(new Emitter<BeforeShutdownEvent>());
readonly onBeforeShutdown: Event<BeforeShutdownEvent> = this._onBeforeShutdown.event;
private readonly _onShutdown = this._register(new Emitter<void>());
readonly onShutdown: Event<void> = this._onShutdown.event;
private readonly _onWillShutdown = this._register(new Emitter<WillShutdownEvent>());
readonly onWillShutdown: Event<WillShutdownEvent> = this._onWillShutdown.event;
private readonly _onShutdown = this._register(new Emitter<void>());
readonly onShutdown: Event<void> = this._onShutdown.event;
constructor(
parent: HTMLElement,
private readonly serviceCollection: ServiceCollection,
@@ -429,7 +429,7 @@ export class Workbench extends Layout {
// Restore Editor Center Mode
if (this.state.editor.restoreCentered) {
this.centerEditorLayout(true);
this.centerEditorLayout(true, true);
}
// Emit a warning after 10s if restore does not complete

View File

@@ -334,8 +334,6 @@ export interface ITreeView extends IDisposable {
layout(height: number, width: number): void;
show(container: HTMLElement): void;
getOptimalWidth(): number;
reveal(item: ITreeItem): Promise<void>;

View File

@@ -169,7 +169,7 @@ class SelectionToReplAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
const debugService = accessor.get(IDebugService);
const panelService = accessor.get(IPanelService);
const viewModel = debugService.getViewModel();
@@ -179,9 +179,8 @@ class SelectionToReplAction extends EditorAction {
}
const text = editor.getModel().getValueInRange(editor.getSelection());
return session.addReplExpression(viewModel.focusedStackFrame!, text)
.then(() => panelService.openPanel(REPL_ID, true))
.then(_ => undefined);
await session.addReplExpression(viewModel.focusedStackFrame!, text);
await panelService.openPanel(REPL_ID, true);
}
}

View File

@@ -404,7 +404,7 @@ export class DebugService implements IDebugService {
.then(() => false);
}
return launch && launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false);
return !!launch && launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false);
});
}
@@ -542,6 +542,10 @@ export class DebugService implements IDebugService {
if (this.layoutService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getValue<IDebugConfiguration>('debug').openExplorerOnEnd) {
this.viewletService.openViewlet(EXPLORER_VIEWLET_ID);
}
// Data breakpoints that can not be persisted should be cleared when a session ends
const dataBreakpoints = this.model.getDataBreakpoints().filter(dbp => !dbp.canPersist);
dataBreakpoints.forEach(dbp => this.model.removeDataBreakpoints(dbp.getId()));
}
}));

View File

@@ -31,6 +31,8 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
import { onUnexpectedError } from 'vs/base/common/errors';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView';
export class DebugSession implements IDebugSession {
@@ -66,7 +68,8 @@ export class DebugSession implements IDebugSession {
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@INotificationService private readonly notificationService: INotificationService,
@IProductService private readonly productService: IProductService,
@IWindowsService private readonly windowsService: IWindowsService
@IWindowsService private readonly windowsService: IWindowsService,
@IOpenerService private readonly openerService: IOpenerService
) {
this.id = generateUuid();
this.repl = new ReplModel(this);
@@ -167,7 +170,7 @@ export class DebugSession implements IDebugSession {
return dbgr.createDebugAdapter(this).then(debugAdapter => {
this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService);
this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService, this.openerService);
return this.raw.start().then(() => {
@@ -552,7 +555,8 @@ export class DebugSession implements IDebugSession {
insertText: item.text || item.label,
kind: completionKindFromString(item.type || 'property'),
filterText: (item.start && item.length) ? text.substr(item.start, item.length).concat(item.label) : undefined,
range: Range.fromPositions(position.delta(0, -(item.length || overwriteBefore)), position)
range: Range.fromPositions(position.delta(0, -(item.length || overwriteBefore)), position),
sortText: item.sortText
});
}
});
@@ -903,12 +907,13 @@ export class DebugSession implements IDebugSession {
this._onDidChangeREPLElements.fire();
}
addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void> {
async addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void> {
const viewModel = this.debugService.getViewModel();
return this.repl.addReplExpression(stackFrame, name)
.then(() => this._onDidChangeREPLElements.fire())
// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
.then(() => this.debugService.focusStackFrame(viewModel.focusedStackFrame, viewModel.focusedThread, viewModel.focusedSession));
await this.repl.addReplExpression(stackFrame, name);
this._onDidChangeREPLElements.fire();
// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
this.debugService.focusStackFrame(viewModel.focusedStackFrame, viewModel.focusedThread, viewModel.focusedSession);
variableSetEmitter.fire();
}
appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void {

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import strings = require('vs/base/common/strings');
import * as strings from 'vs/base/common/strings';
import { isAbsolute } from 'vs/base/common/path';
import { URI as uri } from 'vs/base/common/uri';
import { isMacintosh } from 'vs/base/common/platform';

View File

@@ -17,6 +17,7 @@ import { IWindowsService } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { env as processEnv } from 'vs/base/common/process';
import { IOpenerService } from 'vs/platform/opener/common/opener';
/**
* This interface represents a single command line argument split into a "prefix" and a "path" half.
@@ -74,7 +75,8 @@ export class RawDebugSession {
dbgr: IDebugger,
private readonly telemetryService: ITelemetryService,
public readonly customTelemetryService: ITelemetryService | undefined,
private readonly windowsService: IWindowsService
private readonly windowsService: IWindowsService,
private readonly openerService: IOpenerService
) {
this.debugAdapter = debugAdapter;
@@ -652,7 +654,7 @@ export class RawDebugSession {
const label = error.urlLabel ? error.urlLabel : nls.localize('moreInfo', "More Info");
return createErrorWithActions(userMessage, {
actions: [new Action('debug.moreInfo', label, undefined, true, () => {
window.open(error.url);
this.openerService.open(URI.parse(error.url));
return Promise.resolve(null);
})]
});

View File

@@ -62,7 +62,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { VariablesRenderer, variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView';
import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView';
const $ = dom.$;
@@ -104,6 +104,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
private scopedInstantiationService!: IInstantiationService;
private replElementsChangeListener: IDisposable | undefined;
private styleElement: HTMLStyleElement | undefined;
private completionItemProvider: IDisposable | undefined;
constructor(
@IDebugService private readonly debugService: IDebugService,
@@ -131,7 +132,33 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
this._register(this.debugService.getViewModel().onDidFocusSession(session => {
if (session) {
sessionsToIgnore.delete(session);
if (this.completionItemProvider) {
this.completionItemProvider.dispose();
}
if (session.capabilities.supportsCompletionsRequest) {
this.completionItemProvider = CompletionProviderRegistry.register({ scheme: DEBUG_SCHEME, pattern: '**/replinput', hasAccessToAllModels: true }, {
triggerCharacters: session.capabilities.completionTriggerCharacters || ['.'],
provideCompletionItems: async (_: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken): Promise<CompletionList> => {
// Disable history navigation because up and down are used to navigate through the suggest widget
this.historyNavigationEnablement.set(false);
const model = this.replInput.getModel();
if (model) {
const word = model.getWordAtPosition(position);
const overwriteBefore = word ? word.word.length : 0;
const text = model.getLineContent(position.lineNumber);
const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame;
const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined;
const suggestions = await session.completions(frameId, text, position, overwriteBefore);
return { suggestions };
}
return Promise.resolve({ suggestions: [] });
}
});
}
}
this.selectSession();
}));
this._register(this.debugService.onWillNewSession(newSession => {
@@ -274,7 +301,6 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
revealLastElement(this.tree);
this.history.add(this.replInput.getValue());
this.replInput.setValue('');
variableSetEmitter.fire();
const shouldRelayout = this.replInputHeight > Repl.REPL_INPUT_INITIAL_HEIGHT;
this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT;
if (shouldRelayout) {
@@ -418,6 +444,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
private createReplInput(container: HTMLElement): void {
this.replInputContainer = dom.append(container, $('.repl-input-wrapper'));
this.replInputContainer.title = nls.localize('debugConsole', "Debug Console");
const { scopedContextKeyService, historyNavigationEnablement } = createAndBindHistoryNavigationWidgetScopedContextKeyService(this.contextKeyService, { target: this.replInputContainer, historyNavigator: this });
this.historyNavigationEnablement = historyNavigationEnablement;
@@ -430,34 +457,6 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
options.readOnly = true;
this.replInput = this.scopedInstantiationService.createInstance(CodeEditorWidget, this.replInputContainer, options, getSimpleCodeEditorWidgetOptions());
CompletionProviderRegistry.register({ scheme: DEBUG_SCHEME, pattern: '**/replinput', hasAccessToAllModels: true }, {
triggerCharacters: ['.'],
provideCompletionItems: (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken): Promise<CompletionList> => {
// Disable history navigation because up and down are used to navigate through the suggest widget
this.historyNavigationEnablement.set(false);
const focusedSession = this.debugService.getViewModel().focusedSession;
if (focusedSession && focusedSession.capabilities.supportsCompletionsRequest) {
const model = this.replInput.getModel();
if (model) {
const word = model.getWordAtPosition(position);
const overwriteBefore = word ? word.word.length : 0;
const text = model.getLineContent(position.lineNumber);
const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame;
const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined;
return focusedSession.completions(frameId, text, position, overwriteBefore).then(suggestions => {
return { suggestions };
}, err => {
return { suggestions: [] };
});
}
}
return Promise.resolve({ suggestions: [] });
}
});
this._register(this.replInput.onDidScrollChange(e => {
if (!e.scrollHeightChanged) {
return;

View File

@@ -168,7 +168,7 @@ declare module DebugProtocol {
category?: string;
/** The output to report. */
output: string;
/** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. */
/** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31 - 1). */
variablesReference?: number;
/** An optional source location where the output was produced. */
source?: Source;
@@ -284,9 +284,9 @@ declare module DebugProtocol {
/** Response to 'runInTerminal' request. */
export interface RunInTerminalResponse extends Response {
body: {
/** The process ID. */
/** The process ID. The value should be less than or equal to 2147483647 (2^31 - 1). */
processId?: number;
/** The process ID of the terminal shell. */
/** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31 - 1). */
shellProcessId?: number;
};
}
@@ -757,7 +757,7 @@ declare module DebugProtocol {
}
/** Pause request; value of command field is 'pause'.
The request suspenses the debuggee.
The request suspends the debuggee.
The debug adapter first sends the response and then a 'stopped' event (with reason 'pause') after the thread has been paused successfully.
*/
export interface PauseRequest extends Request {
@@ -842,7 +842,7 @@ declare module DebugProtocol {
export interface VariablesArguments {
/** The Variable reference. */
variablesReference: number;
/** Optional filter to limit the child variables to either named or indexed. If ommited, both types are fetched. */
/** Optional filter to limit the child variables to either named or indexed. If omitted, both types are fetched. */
filter?: 'indexed' | 'named';
/** The index of the first variable to return; if omitted children start at 0. */
start?: number;
@@ -887,14 +887,14 @@ declare module DebugProtocol {
value: string;
/** The type of the new value. Typically shown in the UI when hovering over the value. */
type?: string;
/** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */
/** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */
variablesReference?: number;
/** The number of named child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1).
*/
namedVariables?: number;
/** The number of indexed child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1).
*/
indexedVariables?: number;
};
@@ -1041,14 +1041,14 @@ declare module DebugProtocol {
type?: string;
/** Properties of a evaluate result that can be used to determine how to render the result in the UI. */
presentationHint?: VariablePresentationHint;
/** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */
/** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */
variablesReference: number;
/** The number of named child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1).
*/
namedVariables?: number;
/** The number of indexed child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1).
*/
indexedVariables?: number;
/** Memory reference to a location appropriate for this result. For pointer type eval results, this is generally a reference to the memory address contained in the pointer. */
@@ -1086,14 +1086,14 @@ declare module DebugProtocol {
type?: string;
/** Properties of a value that can be used to determine how to render the result in the UI. */
presentationHint?: VariablePresentationHint;
/** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */
/** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */
variablesReference?: number;
/** The number of named child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1).
*/
namedVariables?: number;
/** The number of indexed child variables.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks.
The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1).
*/
indexedVariables?: number;
};
@@ -1294,6 +1294,8 @@ declare module DebugProtocol {
supportsStepInTargetsRequest?: boolean;
/** The debug adapter supports the 'completions' request. */
supportsCompletionsRequest?: boolean;
/** The set of characters that should trigger completion in a REPL. If not specified, the UI should assume the '.' character. */
completionTriggerCharacters?: string[];
/** The debug adapter supports the 'modules' request. */
supportsModulesRequest?: boolean;
/** The set of additional module information exposed by the debug adapter. */
@@ -1433,7 +1435,7 @@ declare module DebugProtocol {
name?: string;
/** The path of the source to be shown in the UI. It is only used to locate and load the content of the source if no sourceReference is specified (or its value is 0). */
path?: string;
/** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. */
/** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. The value should be less than or equal to 2147483647 (2^31 - 1). */
sourceReference?: number;
/** An optional hint for how to present the source in the UI. A value of 'deemphasize' can be used to indicate that the source is not available or that it is skipped on stepping. */
presentationHint?: 'normal' | 'emphasize' | 'deemphasize';
@@ -1668,6 +1670,8 @@ declare module DebugProtocol {
label: string;
/** If text is not falsy then it is inserted instead of the label. */
text?: string;
/** A string that should be used when comparing this item with other items. When `falsy` the label is used. */
sortText?: string;
/** The item's type. Typically the client uses this information to render the item in the UI with an icon. */
type?: CompletionItemType;
/** This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added.
@@ -1729,7 +1733,7 @@ declare module DebugProtocol {
/** This enumeration defines all possible conditions when a thrown exception should result in a break.
never: never breaks,
always: always breaks,
unhandled: breaks when excpetion unhandled,
unhandled: breaks when exception unhandled,
userUnhandled: breaks if the exception is not handled by user code.
*/
export type ExceptionBreakMode = 'never' | 'always' | 'unhandled' | 'userUnhandled';
@@ -1766,7 +1770,7 @@ declare module DebugProtocol {
instructionBytes?: string;
/** Text representing the instruction and its operands, in an implementation-defined format. */
instruction: string;
/** Name of the symbol that correponds with the location of this instruction, if any. */
/** Name of the symbol that corresponds with the location of this instruction, if any. */
symbol?: string;
/** Source location that corresponds to this instruction, if any. Should always be set (if available) on the first instruction returned, but can be omitted afterwards if this instruction maps to the same source file as the previous instruction. */
location?: Source;

View File

@@ -13,9 +13,10 @@ import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
import { IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug';
import { NullOpenerService } from 'vs/platform/opener/common/opener';
function createMockSession(model: DebugModel, name = 'mockSession', parentSession?: DebugSession | undefined): DebugSession {
return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, parentSession, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, parentSession, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService);
}
suite('Debug - Model', () => {
@@ -427,7 +428,7 @@ suite('Debug - Model', () => {
// Repl output
test('repl output', () => {
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService);
const repl = new ReplModel(session);
repl.appendToRepl('first line\n', severity.Error);
repl.appendToRepl('second line ', severity.Error);

View File

@@ -11,6 +11,8 @@ import { IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/exten
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { Disposable } from 'vs/base/common/lifecycle';
import { language } from 'vs/base/common/platform';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { URI } from 'vs/base/common/uri';
export class ExperimentalPrompts extends Disposable implements IWorkbenchContribution {
@@ -18,7 +20,8 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib
@IExperimentService private readonly experimentService: IExperimentService,
@IViewletService private readonly viewletService: IViewletService,
@INotificationService private readonly notificationService: INotificationService,
@ITelemetryService private readonly telemetryService: ITelemetryService
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IOpenerService private readonly openerService: IOpenerService
) {
super();
@@ -65,7 +68,7 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib
run: () => {
logTelemetry(commandText);
if (command.externalLink) {
window.open(command.externalLink);
this.openerService.open(URI.parse(command.externalLink));
} else if (command.curatedExtensionsKey && Array.isArray(command.curatedExtensionsList)) {
this.viewletService.openViewlet('workbench.view.extensions', true)
.then(viewlet => viewlet as IExtensionsViewlet)

View File

@@ -50,11 +50,12 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry';
import { isUndefined } from 'vs/base/common/types';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/common/webview';
import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { renderDashboardContributions } from 'sql/workbench/parts/extensions/browser/contributionRenders'; // {{SQL CARBON EDIT}}
import { generateUuid } from 'vs/base/common/uuid';
import { platform } from 'vs/base/common/process';
import { URI } from 'vs/base/common/uri';
function removeEmbeddedSVGs(documentContent: string): string {
const newDocument = new DOMParser().parseFromString(documentContent, 'text/html');
@@ -186,7 +187,7 @@ export class ExtensionEditor extends BaseEditor {
@IStorageService storageService: IStorageService,
@IExtensionService private readonly extensionService: IExtensionService,
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
@IWebviewService private readonly webviewService: IWebviewService,
@IWebviewService private readonly webviewService: IWebviewService
) {
super(ExtensionEditor.ID, telemetryService, themeService, storageService);
this.extensionReadme = null;
@@ -355,8 +356,8 @@ export class ExtensionEditor extends BaseEditor {
toggleClass(template.publisher, 'clickable', !!extension.url);
toggleClass(template.rating, 'clickable', !!extension.url);
if (extension.url) {
this.transientDisposables.add(this.onClick(template.name, () => window.open(extension.url)));
this.transientDisposables.add(this.onClick(template.rating, () => window.open(`${extension.url}#review-details`)));
this.transientDisposables.add(this.onClick(template.name, () => this.openerService.open(URI.parse(extension.url!))));
this.transientDisposables.add(this.onClick(template.rating, () => this.openerService.open(URI.parse(`${extension.url}#review-details`))));
this.transientDisposables.add(this.onClick(template.publisher, () => {
this.viewletService.openViewlet(VIEWLET_ID, true)
.then(viewlet => viewlet as IExtensionsViewlet)
@@ -364,7 +365,7 @@ export class ExtensionEditor extends BaseEditor {
}));
if (extension.licenseUrl) {
this.transientDisposables.add(this.onClick(template.license, () => window.open(extension.licenseUrl)));
this.transientDisposables.add(this.onClick(template.license, () => this.openerService.open(URI.parse(extension.licenseUrl!))));
template.license.style.display = 'initial';
} else {
template.license.style.display = 'none';
@@ -384,7 +385,7 @@ export class ExtensionEditor extends BaseEditor {
// {{SQL CARBON EDIT}} - End
if (extension.repository) {
this.transientDisposables.add(this.onClick(template.repository, () => window.open(extension.repository)));
this.transientDisposables.add(this.onClick(template.repository, () => this.openerService.open(URI.parse(extension.repository!))));
template.repository.style.display = 'initial';
}
else {

View File

@@ -534,8 +534,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return false;
}
const id = recommendationsToSuggest[0];
const tip = this._importantExeBasedRecommendations[id];
const extensionId = recommendationsToSuggest[0];
const tip = this._importantExeBasedRecommendations[extensionId];
const message = localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.friendlyName!, tip.exeFriendlyName || basename(tip.windowsPath!));
this.notificationService.prompt(Severity.Info, message,
@@ -548,8 +548,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'install', extensionId: name });
this.instantiationService.createInstance(InstallRecommendedExtensionAction, id).run();
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'install', extensionId });
this.instantiationService.createInstance(InstallRecommendedExtensionAction, extensionId).run();
}
}, {
label: localize('showRecommendations', "Show Recommendations"),
@@ -560,7 +560,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'show', extensionId: name });
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'show', extensionId });
const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations"));
recommendationsAction.run();
@@ -570,14 +570,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
label: choiceNever,
isSecondary: true,
run: () => {
this.addToImportantRecommendationsIgnore(id);
this.addToImportantRecommendationsIgnore(extensionId);
/* __GDPR__
"exeExtensionRecommendations:popup" : {
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name });
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId });
this.notificationService.prompt(
Severity.Info,
localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"),
@@ -600,7 +600,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'cancelled', extensionId: name });
this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'cancelled', extensionId });
}
}
);

View File

@@ -17,6 +17,7 @@ import { join } from 'vs/base/common/path';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { IOpenerService } from 'vs/platform/opener/common/opener';
abstract class RepoInfo {
abstract get base(): string;
@@ -117,6 +118,7 @@ class ReportExtensionSlowAction extends Action {
readonly repoInfo: RepoInfo,
readonly profile: IExtensionHostProfile,
@IDialogService private readonly _dialogService: IDialogService,
@IOpenerService private readonly _openerService: IOpenerService
) {
super('report.slow', localize('cmd.report', "Report Issue"));
}
@@ -140,7 +142,7 @@ class ReportExtensionSlowAction extends Action {
- VSCode version: \`${pkg.version}\`\n\n${message}`);
const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues/new/?body=${body}&title=${title}`;
window.open(url);
this._openerService.open(URI.parse(url));
this._dialogService.show(
Severity.Info,
@@ -158,6 +160,7 @@ class ShowExtensionSlowAction extends Action {
readonly repoInfo: RepoInfo,
readonly profile: IExtensionHostProfile,
@IDialogService private readonly _dialogService: IDialogService,
@IOpenerService private readonly _openerService: IOpenerService
) {
super('show.slow', localize('cmd.show', "Show Issues"));
}
@@ -172,7 +175,7 @@ class ShowExtensionSlowAction extends Action {
// show issues
const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues?utf8=✓&q=is%3Aissue+state%3Aopen+%22Extension+causes+high+cpu+load%22`;
window.open(url);
this._openerService.open(URI.parse(url));
this._dialogService.show(
Severity.Info,

View File

@@ -44,6 +44,8 @@ import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/pl
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { URI } from 'vs/base/common/uri';
export const IExtensionHostProfileService = createDecorator<IExtensionHostProfileService>('extensionHostProfileService');
export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey<string>('profileSessionState', 'none');
@@ -120,7 +122,8 @@ export class RuntimeExtensionsEditor extends BaseEditor {
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
@IStorageService storageService: IStorageService,
@ILabelService private readonly _labelService: ILabelService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IOpenerService private readonly _openerService: IOpenerService
) {
super(RuntimeExtensionsEditor.ID, telemetryService, themeService, storageService);
@@ -312,7 +315,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
data.actionbar.push(this._instantiationService.createInstance(SlowExtensionAction, element.description, element.unresponsiveProfile), { icon: true, label: true });
}
if (isNonEmptyArray(element.status.runtimeErrors)) {
data.actionbar.push(new ReportExtensionIssueAction(element), { icon: true, label: true });
data.actionbar.push(new ReportExtensionIssueAction(element, this._openerService), { icon: true, label: true });
}
let title: string;
@@ -416,7 +419,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
const actions: IAction[] = [];
actions.push(new ReportExtensionIssueAction(e.element));
actions.push(new ReportExtensionIssueAction(e.element, this._openerService));
actions.push(new Separator());
if (e.element.marketplaceInfo) {
@@ -480,7 +483,7 @@ export class ReportExtensionIssueAction extends Action {
marketplaceInfo: IExtension;
status?: IExtensionsStatus;
unresponsiveProfile?: IExtensionHostProfile
}) {
}, @IOpenerService private readonly openerService: IOpenerService) {
super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue');
this.enabled = extension.marketplaceInfo
&& extension.marketplaceInfo.type === ExtensionType.User
@@ -490,7 +493,7 @@ export class ReportExtensionIssueAction extends Action {
}
async run(): Promise<void> {
window.open(this._url);
this.openerService.open(URI.parse(this._url));
}
private static _generateNewIssueUrl(extension: {

View File

@@ -20,6 +20,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { IProductService } from 'vs/platform/product/common/product';
import { IOpenerService } from 'vs/platform/opener/common/opener';
export interface IFeedback {
feedback: string;
@@ -27,7 +28,7 @@ export interface IFeedback {
}
export interface IFeedbackDelegate {
submitFeedback(feedback: IFeedback): void;
submitFeedback(feedback: IFeedback, openerService: IOpenerService): void;
getCharacterLimit(sentiment: number): number;
}
@@ -66,7 +67,8 @@ export class FeedbackDropdown extends Dropdown {
@IIntegrityService private readonly integrityService: IIntegrityService,
@IThemeService private readonly themeService: IThemeService,
@IStatusbarService private readonly statusbarService: IStatusbarService,
@IProductService productService: IProductService
@IProductService productService: IProductService,
@IOpenerService private readonly openerService: IOpenerService
) {
super(container, options);
@@ -415,7 +417,7 @@ export class FeedbackDropdown extends Dropdown {
this.feedbackDelegate.submitFeedback({
feedback: this.feedbackDescriptionInput.value,
sentiment: this.sentiment
});
}, this.openerService);
this.hide();
}

View File

@@ -12,6 +12,8 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStatusbarService, StatusbarAlignment, IStatusbarEntry, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar';
import { localize } from 'vs/nls';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { URI } from 'vs/base/common/uri';
class TwitterFeedbackService implements IFeedbackDelegate {
@@ -23,11 +25,11 @@ class TwitterFeedbackService implements IFeedbackDelegate {
return TwitterFeedbackService.HASHTAGS.join(',');
}
submitFeedback(feedback: IFeedback): void {
submitFeedback(feedback: IFeedback, openerService: IOpenerService): void {
const queryString = `?${feedback.sentiment === 1 ? `hashtags=${this.combineHashTagsAsString()}&` : null}ref_src=twsrc%5Etfw&related=twitterapi%2Ctwitter&text=${encodeURIComponent(feedback.feedback)}&tw_p=tweetbutton&via=${TwitterFeedbackService.VIA_NAME}`;
const url = TwitterFeedbackService.TWITTER_URL + queryString;
window.open(url);
openerService.open(URI.parse(url));
}
getCharacterLimit(sentiment: number): number {

View File

@@ -38,7 +38,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
BinaryFileEditor.ID,
{
openInternal: (input, options) => this.openInternal(input, options),
openExternal: resource => this.openerService.openExternal(resource)
openExternal: resource => this.openerService.open(resource, { openExternal: true })
},
telemetryService,
themeService,

View File

@@ -59,7 +59,6 @@ export class EmptyView extends ViewletPanel {
container.appendChild(titleContainer);
this.titleElement = document.createElement('span');
this.titleElement.textContent = name;
titleContainer.appendChild(this.titleElement);
}

View File

@@ -203,17 +203,20 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
switch (verbosity) {
case Verbosity.SHORT:
title = this.shortTitle;
// already decorated by getName()
break;
default:
case Verbosity.MEDIUM:
title = this.mediumTitle;
title = this.decorateLabel(title);
break;
case Verbosity.LONG:
title = this.longTitle;
title = this.decorateLabel(title);
break;
}
return this.decorateLabel(title);
return title;
}
private decorateLabel(label: string): string {

View File

@@ -122,7 +122,8 @@ class RequestOracle {
let handle: any;
let contentListener = codeEditor.onDidChangeModelContent(event => {
clearTimeout(handle);
handle = setTimeout(() => this._callback(codeEditor!, event), 350);
const timeout = OutlineModel.getRequestDelay(codeEditor!.getModel());
handle = setTimeout(() => this._callback(codeEditor!, event), timeout);
});
let modeListener = codeEditor.onDidChangeModelLanguage(_ => {
this._callback(codeEditor!, undefined);

View File

@@ -17,6 +17,7 @@ import { PerfviewInput } from 'vs/workbench/contrib/performance/electron-browser
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { URI } from 'vs/base/common/uri';
import { IOpenerService } from 'vs/platform/opener/common/opener';
export class StartupProfiler implements IWorkbenchContribution {
@@ -28,6 +29,7 @@ export class StartupProfiler implements IWorkbenchContribution {
@IClipboardService private readonly _clipboardService: IClipboardService,
@ILifecycleService lifecycleService: ILifecycleService,
@IExtensionService extensionService: IExtensionService,
@IOpenerService private readonly _openerService: IOpenerService
) {
// wait for everything to be ready
Promise.all([
@@ -116,6 +118,6 @@ export class StartupProfiler implements IWorkbenchContribution {
const baseUrl = product.reportIssueUrl;
const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&';
window.open(`${baseUrl}${queryStringPrefix}body=${encodeURIComponent(body)}`);
this._openerService.open(URI.parse(`${baseUrl}${queryStringPrefix}body=${encodeURIComponent(body)}`));
}
}

View File

@@ -789,7 +789,7 @@ class KeybindingItemRenderer implements IListRenderer<IKeybindingItemEntry, Keyb
get templateId(): string { return KEYBINDING_ENTRY_TEMPLATE_ID; }
constructor(
private keybindingsEditor: IKeybindingsEditor,
private keybindingsEditor: KeybindingsEditor,
private instantiationService: IInstantiationService
) { }

View File

@@ -9,12 +9,12 @@ import { isArray, withUndefinedAsNull } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets';
import { ITOCEntry, knownAcronyms, knownTermMappings } from 'vs/workbench/contrib/preferences/browser/settingsLayout';
import { MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences';
import { IExtensionSetting, ISearchResult, ISetting, SettingValueType } from 'vs/workbench/services/preferences/common/preferences';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { FOLDER_SCOPES, WORKSPACE_SCOPES, REMOTE_MACHINE_SCOPES, LOCAL_MACHINE_SCOPES } from 'vs/workbench/services/configuration/common/configuration';
export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices';
@@ -227,20 +227,24 @@ export class SettingsTreeSettingElement extends SettingsTreeElement {
matchesScope(scope: SettingsTarget, isRemote: boolean): boolean {
const configTarget = URI.isUri(scope) ? ConfigurationTarget.WORKSPACE_FOLDER : scope;
if (!this.setting.scope) {
return true;
}
if (configTarget === ConfigurationTarget.WORKSPACE_FOLDER) {
return this.setting.scope === ConfigurationScope.RESOURCE;
return FOLDER_SCOPES.indexOf(this.setting.scope) !== -1;
}
if (configTarget === ConfigurationTarget.WORKSPACE) {
return this.setting.scope === ConfigurationScope.WINDOW || this.setting.scope === ConfigurationScope.RESOURCE;
return WORKSPACE_SCOPES.indexOf(this.setting.scope) !== -1;
}
if (configTarget === ConfigurationTarget.USER_REMOTE) {
return this.setting.scope === ConfigurationScope.MACHINE || this.setting.scope === ConfigurationScope.WINDOW || this.setting.scope === ConfigurationScope.RESOURCE;
return REMOTE_MACHINE_SCOPES.indexOf(this.setting.scope) !== -1;
}
if (configTarget === ConfigurationTarget.USER_LOCAL && isRemote) {
return this.setting.scope !== ConfigurationScope.MACHINE;
return LOCAL_MACHINE_SCOPES.indexOf(this.setting.scope) !== -1;
}
return true;

View File

@@ -55,7 +55,6 @@ export interface IKeybindingsEditor extends IEditor {
focusKeybindings(): void;
recordSearchKeys(): void;
toggleSortByPrecedence(): void;
layoutColumns(columns: HTMLElement[]): void;
selectKeybinding(keybindingEntry: IKeybindingItemEntry): void;
defineKeybinding(keybindingEntry: IKeybindingItemEntry): Promise<void>;
defineWhenExpression(keybindingEntry: IKeybindingItemEntry): void;

View File

@@ -99,18 +99,17 @@ class GotoLineEntry extends EditorQuickOpenEntry {
if (this.editorService.activeTextEditorWidget && this.invalidRange(maxLineNumber)) {
const position = this.editorService.activeTextEditorWidget.getPosition();
if (position) {
const currentLine = position.lineNumber;
if (maxLineNumber > 0) {
return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}. Type a line number between 1 and {1} to navigate to.", currentLine, maxLineNumber);
return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}, Column: {1}. Type a line number between 1 and {2} to navigate to.", position.lineNumber, position.column, maxLineNumber);
}
return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine);
return nls.localize('gotoLineLabelEmpty', "Current Line: {0}, Column: {1}. Type a line number to navigate to.", position.lineNumber, position.column);
}
}
// Input valid, indicate action
return this.column ? nls.localize('gotoLineColumnLabel', "Go to line {0} and character {1}.", this.line, this.column) : nls.localize('gotoLineLabel', "Go to line {0}.", this.line);
return this.column ? nls.localize('gotoLineColumnLabel', "Go to line {0} and column {1}.", this.line, this.column) : nls.localize('gotoLineLabel', "Go to line {0}.", this.line);
}
private invalidRange(maxLineNumber: number = this.getMaxLineNumber()): boolean {
@@ -229,8 +228,7 @@ export class GotoLineHandler extends QuickOpenHandler {
if (this.editorService.activeTextEditorWidget) {
const position = this.editorService.activeTextEditorWidget.getPosition();
if (position) {
const currentLine = position.lineNumber;
return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine);
return nls.localize('gotoLineLabelEmpty', "Current Line: {0}, Column: {1}. Type a line number to navigate to.", position.lineNumber, position.column);
}
}

View File

@@ -60,6 +60,20 @@
display: inline-flex;
}
.search-view .search-widget .replace-input {
position: relative;
display: flex;
display: -webkit-flex;
vertical-align: middle;
width: auto !important;
}
.search-view .search-widget .replace-input > .controls {
position: absolute;
top: 3px;
right: 2px;
}
.search-view .search-widget .replace-container.disabled {
display: none;
}

View File

@@ -7,14 +7,11 @@ import * as DOM from 'vs/base/browser/dom';
import { Action } from 'vs/base/common/actions';
import { INavigator } from 'vs/base/common/iterator';
import { createKeybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { Schemas } from 'vs/base/common/network';
import { normalize } from 'vs/base/common/path';
import { isWindows, OS } from 'vs/base/common/platform';
import { repeat } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ILabelService } from 'vs/platform/label/common/label';
import { ICommandHandler } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -667,14 +664,11 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction {
}
}
function uriToClipboardString(resource: URI): string {
return resource.scheme === Schemas.file ? normalize(normalizeDriveLetter(resource.fsPath)) : resource.toString();
}
export const copyPathCommand: ICommandHandler = async (accessor, fileMatch: FileMatch | FolderMatch) => {
const clipboardService = accessor.get(IClipboardService);
const labelService = accessor.get(ILabelService);
const text = uriToClipboardString(fileMatch.resource);
const text = labelService.getUriLabel(fileMatch.resource, { noPrefix: true });
await clipboardService.writeText(text);
};
@@ -706,25 +700,26 @@ function matchToString(match: Match, indent = 0): string {
}
const lineDelimiter = isWindows ? '\r\n' : '\n';
function fileMatchToString(fileMatch: FileMatch, maxMatches: number): { text: string, count: number } {
function fileMatchToString(fileMatch: FileMatch, maxMatches: number, labelService: ILabelService): { text: string, count: number } {
const matchTextRows = fileMatch.matches()
.sort(searchMatchComparer)
.slice(0, maxMatches)
.map(match => matchToString(match, 2));
const uriString = labelService.getUriLabel(fileMatch.resource, { noPrefix: true });
return {
text: `${uriToClipboardString(fileMatch.resource)}${lineDelimiter}${matchTextRows.join(lineDelimiter)}`,
text: `${uriString}${lineDelimiter}${matchTextRows.join(lineDelimiter)}`,
count: matchTextRows.length
};
}
function folderMatchToString(folderMatch: FolderMatch | BaseFolderMatch, maxMatches: number): { text: string, count: number } {
function folderMatchToString(folderMatch: FolderMatch | BaseFolderMatch, maxMatches: number, labelService: ILabelService): { text: string, count: number } {
const fileResults: string[] = [];
let numMatches = 0;
const matches = folderMatch.matches().sort(searchMatchComparer);
for (let i = 0; i < folderMatch.fileCount() && numMatches < maxMatches; i++) {
const fileResult = fileMatchToString(matches[i], maxMatches - numMatches);
const fileResult = fileMatchToString(matches[i], maxMatches - numMatches, labelService);
numMatches += fileResult.count;
fileResults.push(fileResult.text);
}
@@ -738,14 +733,15 @@ function folderMatchToString(folderMatch: FolderMatch | BaseFolderMatch, maxMatc
const maxClipboardMatches = 1e4;
export const copyMatchCommand: ICommandHandler = async (accessor, match: RenderableMatch) => {
const clipboardService = accessor.get(IClipboardService);
const labelService = accessor.get(ILabelService);
let text: string | undefined;
if (match instanceof Match) {
text = matchToString(match);
} else if (match instanceof FileMatch) {
text = fileMatchToString(match, maxClipboardMatches).text;
text = fileMatchToString(match, maxClipboardMatches, labelService).text;
} else if (match instanceof BaseFolderMatch) {
text = folderMatchToString(match, maxClipboardMatches).text;
text = folderMatchToString(match, maxClipboardMatches, labelService).text;
}
if (text) {
@@ -753,12 +749,12 @@ export const copyMatchCommand: ICommandHandler = async (accessor, match: Rendera
}
};
function allFolderMatchesToString(folderMatches: Array<FolderMatch | BaseFolderMatch>, maxMatches: number): string {
function allFolderMatchesToString(folderMatches: Array<FolderMatch | BaseFolderMatch>, maxMatches: number, labelService: ILabelService): string {
const folderResults: string[] = [];
let numMatches = 0;
folderMatches = folderMatches.sort(searchMatchComparer);
for (let i = 0; i < folderMatches.length && numMatches < maxMatches; i++) {
const folderResult = folderMatchToString(folderMatches[i], maxMatches - numMatches);
const folderResult = folderMatchToString(folderMatches[i], maxMatches - numMatches, labelService);
if (folderResult.count) {
numMatches += folderResult.count;
folderResults.push(folderResult.text);
@@ -772,12 +768,13 @@ export const copyAllCommand: ICommandHandler = async (accessor) => {
const viewletService = accessor.get(IViewletService);
const panelService = accessor.get(IPanelService);
const clipboardService = accessor.get(IClipboardService);
const labelService = accessor.get(ILabelService);
const searchView = getSearchView(viewletService, panelService);
if (searchView) {
const root = searchView.searchResult;
const text = allFolderMatchesToString(root.folderMatches(), maxClipboardMatches);
const text = allFolderMatchesToString(root.folderMatches(), maxClipboardMatches, labelService);
await clipboardService.writeText(text);
}
};

View File

@@ -61,6 +61,7 @@ import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/v
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { Memento, MementoObject } from 'vs/workbench/common/memento';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IOpenerService } from 'vs/platform/opener/common/opener';
const $ = dom.$;
@@ -153,6 +154,7 @@ export class SearchView extends ViewletPanel {
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@IKeybindingService keybindingService: IKeybindingService,
@IStorageService storageService: IStorageService,
@IOpenerService private readonly openerService: IOpenerService
) {
super({ ...(options as IViewletPanelOptions), id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService);
@@ -364,6 +366,7 @@ export class SearchView extends ViewletPanel {
const searchHistory = history.search || this.viewletState['query.searchHistory'] || [];
const replaceHistory = history.replace || this.viewletState['query.replaceHistory'] || [];
const showReplace = typeof this.viewletState['view.showReplace'] === 'boolean' ? this.viewletState['view.showReplace'] : true;
const preserveCase = this.viewletState['query.preserveCase'] === true;
this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, container, <ISearchWidgetOptions>{
value: contentPattern,
@@ -372,7 +375,8 @@ export class SearchView extends ViewletPanel {
isCaseSensitive: isCaseSensitive,
isWholeWords: isWholeWords,
searchHistory: searchHistory,
replaceHistory: replaceHistory
replaceHistory: replaceHistory,
preserveCase: preserveCase
}));
if (showReplace) {
@@ -390,6 +394,12 @@ export class SearchView extends ViewletPanel {
this.viewModel.replaceActive = state;
this.refreshTree();
}));
this._register(this.searchWidget.onPreserveCaseChange((state) => {
this.viewModel.preserveCase = state;
this.refreshTree();
}));
this._register(this.searchWidget.onReplaceValueChanged((value) => {
this.viewModel.replaceString = this.searchWidget.getReplaceValue();
this.delayedRefresh.trigger(() => this.refreshTree());
@@ -1465,7 +1475,7 @@ export class SearchView extends ViewletPanel {
private onLearnMore = (e: MouseEvent): void => {
dom.EventHelper.stop(e, false);
window.open('https://go.microsoft.com/fwlink/?linkid=853977');
this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=853977'));
}
private updateSearchResultCount(disregardExcludesAndIgnores?: boolean): void {
@@ -1641,6 +1651,7 @@ export class SearchView extends ViewletPanel {
const patternExcludes = this.inputPatternExcludes.getValue().trim();
const patternIncludes = this.inputPatternIncludes.getValue().trim();
const useExcludesAndIgnoreFiles = this.inputPatternExcludes.useExcludesAndIgnoreFiles();
const preserveCase = this.viewModel.preserveCase;
this.viewletState['query.contentPattern'] = contentPattern;
this.viewletState['query.regex'] = isRegex;
@@ -1649,6 +1660,7 @@ export class SearchView extends ViewletPanel {
this.viewletState['query.folderExclusions'] = patternExcludes;
this.viewletState['query.folderIncludes'] = patternIncludes;
this.viewletState['query.useExcludesAndIgnoreFiles'] = useExcludesAndIgnoreFiles;
this.viewletState['query.preserveCase'] = preserveCase;
const isReplaceShown = this.searchAndReplaceWidget.isReplaceShown();
this.viewletState['view.showReplace'] = isReplaceShown;

View File

@@ -33,6 +33,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
export interface ISearchWidgetOptions {
value?: string;
@@ -42,6 +43,7 @@ export interface ISearchWidgetOptions {
isWholeWords?: boolean;
searchHistory?: string[];
replaceHistory?: string[];
preserveCase?: boolean;
}
class ReplaceAllAction extends Action {
@@ -97,6 +99,7 @@ export class SearchWidget extends Widget {
replaceInputFocusTracker: dom.IFocusTracker;
private replaceInputBoxFocused: IContextKey<boolean>;
private _replaceHistoryDelayer: Delayer<void>;
private _preserveCase: Checkbox;
private ignoreGlobalFindBufferOnNextFocus = false;
private previousGlobalFindBufferValue: string;
@@ -113,6 +116,9 @@ export class SearchWidget extends Widget {
private _onReplaceStateChange = this._register(new Emitter<boolean>());
readonly onReplaceStateChange: Event<boolean> = this._onReplaceStateChange.event;
private _onPreserveCaseChange = this._register(new Emitter<boolean>());
readonly onPreserveCaseChange: Event<boolean> = this._onPreserveCaseChange.event;
private _onReplaceValueChanged = this._register(new Emitter<void>());
readonly onReplaceValueChanged: Event<void> = this._onReplaceValueChanged.event;
@@ -333,13 +339,34 @@ export class SearchWidget extends Widget {
private renderReplaceInput(parent: HTMLElement, options: ISearchWidgetOptions): void {
this.replaceContainer = dom.append(parent, dom.$('.replace-container.disabled'));
const replaceBox = dom.append(this.replaceContainer, dom.$('.input-box'));
const replaceBox = dom.append(this.replaceContainer, dom.$('.replace-input'));
this.replaceInput = this._register(new ContextScopedHistoryInputBox(replaceBox, this.contextViewService, {
ariaLabel: nls.localize('label.Replace', 'Replace: Type replace term and press Enter to preview or Escape to cancel'),
placeholder: nls.localize('search.replace.placeHolder', "Replace"),
history: options.replaceHistory || [],
flexibleHeight: true
}, this.contextKeyService));
this._preserveCase = this._register(new Checkbox({
actionClassName: 'monaco-preserve-case',
title: nls.localize('label.preserveCaseCheckbox', "Preserve Case"),
isChecked: !!options.preserveCase,
}));
this._register(this._preserveCase.onChange(viaKeyboard => {
if (!viaKeyboard) {
this.replaceInput.focus();
this._onPreserveCaseChange.fire(this._preserveCase.checked);
}
}));
let controls = document.createElement('div');
controls.className = 'controls';
controls.style.display = 'block';
controls.appendChild(this._preserveCase.domNode);
replaceBox.appendChild(controls);
this._register(attachInputBoxStyler(this.replaceInput, this.themeService));
this.onkeydown(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent));
this.replaceInput.value = options.replaceValue || '';

View File

@@ -103,17 +103,17 @@ export class Match {
}
const fullMatchText = this.fullMatchText();
let replaceString = searchModel.replacePattern.getReplaceString(fullMatchText);
let replaceString = searchModel.replacePattern.getReplaceString(fullMatchText, searchModel.preserveCase);
// If match string is not matching then regex pattern has a lookahead expression
if (replaceString === null) {
const fullMatchTextWithTrailingContent = this.fullMatchText(true);
replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithTrailingContent);
replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithTrailingContent, searchModel.preserveCase);
// Search/find normalize line endings - check whether \r prevents regex from matching
if (replaceString === null) {
const fullMatchTextWithoutCR = fullMatchTextWithTrailingContent.replace(/\r\n/g, '\n');
replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithoutCR);
replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithoutCR, searchModel.preserveCase);
}
}
@@ -895,6 +895,7 @@ export class SearchModel extends Disposable {
private _replaceActive: boolean = false;
private _replaceString: string | null = null;
private _replacePattern: ReplacePattern | null = null;
private _preserveCase: boolean = false;
private readonly _onReplaceTermChanged: Emitter<void> = this._register(new Emitter<void>());
readonly onReplaceTermChanged: Event<void> = this._onReplaceTermChanged.event;
@@ -926,6 +927,14 @@ export class SearchModel extends Disposable {
return this._replaceString || '';
}
set preserveCase(value: boolean) {
this._preserveCase = value;
}
get preserveCase(): boolean {
return this._preserveCase;
}
set replaceString(replaceString: string) {
this._replaceString = replaceString;
if (this._searchQuery) {

View File

@@ -16,6 +16,8 @@ import { ISurveyData } from 'vs/platform/product/common/product';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Severity, INotificationService } from 'vs/platform/notification/common/notification';
import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { URI } from 'vs/base/common/uri';
class LanguageSurvey {
@@ -25,7 +27,8 @@ class LanguageSurvey {
notificationService: INotificationService,
telemetryService: ITelemetryService,
modelService: IModelService,
textFileService: ITextFileService
textFileService: ITextFileService,
openerService: IOpenerService
) {
const SESSION_COUNT_KEY = `${data.surveyId}.sessionCount`;
const LAST_SESSION_DATE_KEY = `${data.surveyId}.lastSessionDate`;
@@ -94,7 +97,7 @@ class LanguageSurvey {
run: () => {
telemetryService.publicLog(`${data.surveyId}.survey/takeShortSurvey`);
telemetryService.getTelemetryInfo().then(info => {
window.open(`${data.surveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`);
openerService.open(URI.parse(`${data.surveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`));
storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL);
storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL);
});
@@ -126,11 +129,12 @@ class LanguageSurveysContribution implements IWorkbenchContribution {
@INotificationService notificationService: INotificationService,
@ITelemetryService telemetryService: ITelemetryService,
@IModelService modelService: IModelService,
@ITextFileService textFileService: ITextFileService
@ITextFileService textFileService: ITextFileService,
@IOpenerService openerService: IOpenerService
) {
product.surveys
.filter(surveyData => surveyData.surveyId && surveyData.editCount && surveyData.languageId && surveyData.surveyUrl && surveyData.userProbability)
.map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService));
.map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService, openerService));
}
}

View File

@@ -13,6 +13,8 @@ import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/node/product';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Severity, INotificationService } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { URI } from 'vs/base/common/uri';
const PROBABILITY = 0.15;
const SESSION_COUNT_KEY = 'nps/sessionCount';
@@ -25,7 +27,8 @@ class NPSContribution implements IWorkbenchContribution {
constructor(
@IStorageService storageService: IStorageService,
@INotificationService notificationService: INotificationService,
@ITelemetryService telemetryService: ITelemetryService
@ITelemetryService telemetryService: ITelemetryService,
@IOpenerService openerService: IOpenerService
) {
const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, '');
if (skipVersion) {
@@ -64,7 +67,7 @@ class NPSContribution implements IWorkbenchContribution {
label: nls.localize('takeSurvey', "Take Survey"),
run: () => {
telemetryService.getTelemetryInfo().then(info => {
window.open(`${product.npsSurveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`);
openerService.open(URI.parse(`${product.npsSurveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`));
storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL);
storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL);
});
@@ -87,4 +90,4 @@ class NPSContribution implements IWorkbenchContribution {
if (language === 'en' && product.npsSurveyUrl) {
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(NPSContribution, LifecyclePhase.Restored);
}
}

View File

@@ -1620,7 +1620,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
nls.localize('TaskService.noWorkspace', "Tasks are only available on a workspace folder."),
[{
label: nls.localize('TaskService.learnMore', "Learn More"),
run: () => window.open('https://code.visualstudio.com/docs/editor/tasks')
run: () => this.openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks'))
}]
);
return false;

View File

@@ -298,6 +298,11 @@ configurationRegistry.registerConfiguration({
description: nls.localize('terminal.integrated.experimentalRefreshOnResume', "An experimental setting that will refresh the terminal renderer when the system is resumed."),
type: 'boolean',
default: false
},
'terminal.integrated.experimentalUseTitleEvent': {
description: nls.localize('terminal.integrated.experimentalUseTitleEvent', "An experimental setting that will use the terminal title event for the dropdown title. This setting will only apply to new terminals."),
type: 'boolean',
default: false
}
}
});

View File

@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import { Action, IAction } from 'vs/base/common/actions';
import { EndOfLinePreference } from 'vs/editor/common/model';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { TogglePanelAction } from 'vs/workbench/browser/panel';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
@@ -1034,7 +1034,7 @@ export class RenameTerminalAction extends Action {
prompt: nls.localize('workbench.action.terminal.rename.prompt', "Enter terminal name"),
}).then(name => {
if (name) {
terminalInstance.setTitle(name, false);
terminalInstance.setTitle(name, TitleEventSource.Api);
}
});
}

View File

@@ -25,7 +25,7 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { PANEL_BACKGROUND } from 'vs/workbench/common/theme';
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager';
import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode } from 'vs/workbench/contrib/terminal/common/terminal';
import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
@@ -973,11 +973,22 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._processManager.onProcessResolvedShellLaunchConfig(e => this._setResolvedShellLaunchConfig(e));
if (this._shellLaunchConfig.name) {
this.setTitle(this._shellLaunchConfig.name, false);
this.setTitle(this._shellLaunchConfig.name, TitleEventSource.Api);
} else {
// Only listen for process title changes when a name is not provided
this.setTitle(this._shellLaunchConfig.executable, true);
this._messageTitleDisposable = this._processManager.onProcessTitle(title => this.setTitle(title ? title : '', true));
if (this._configHelper.config.experimentalUseTitleEvent) {
this._processManager.ptyProcessReady.then(() => {
this._terminalInstanceService.getDefaultShellAndArgs(false).then(e => {
this.setTitle(e.shell, TitleEventSource.Sequence);
});
this._xtermReadyPromise.then(xterm => {
this._messageTitleDisposable = xterm.onTitleChange(e => this._onTitleChange(e));
});
});
} else {
this.setTitle(this._shellLaunchConfig.executable, TitleEventSource.Process);
this._messageTitleDisposable = this._processManager.onProcessTitle(title => this.setTitle(title ? title : '', TitleEventSource.Process));
}
}
if (platform.isWindows) {
@@ -1149,7 +1160,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._createProcess();
if (oldTitle !== this._title) {
this.setTitle(this._title, true);
this.setTitle(this._title, TitleEventSource.Process);
}
this._processManager.onProcessData(data => this._onProcessData(data));
@@ -1168,6 +1179,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._sendLineData(buffer, buffer.baseY + buffer.cursorY);
}
private _onTitleChange(title: string): void {
if (this.isTitleSetByProcess) {
this.setTitle(title, TitleEventSource.Sequence);
}
}
private _sendLineData(buffer: IBuffer, lineIndex: number): void {
let line = buffer.getLine(lineIndex);
if (!line) {
@@ -1348,23 +1365,26 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._processManager.ptyProcessReady.then(() => this._processManager.setDimensions(cols, rows));
}
public setTitle(title: string | undefined, eventFromProcess: boolean): void {
public setTitle(title: string | undefined, eventSource: TitleEventSource): void {
if (!title) {
return;
}
if (eventFromProcess) {
title = path.basename(title);
if (platform.isWindows) {
// Remove the .exe extension
title = title.split('.exe')[0];
}
} else {
// If the title has not been set by the API or the rename command, unregister the handler that
// automatically updates the terminal name
dispose(this._messageTitleDisposable);
this._messageTitleDisposable = undefined;
dispose(this._windowsShellHelper);
this._windowsShellHelper = undefined;
switch (eventSource) {
case TitleEventSource.Process:
title = path.basename(title);
if (platform.isWindows) {
// Remove the .exe extension
title = title.split('.exe')[0];
}
break;
case TitleEventSource.Api:
// If the title has not been set by the API or the rename command, unregister the handler that
// automatically updates the terminal name
dispose(this._messageTitleDisposable);
this._messageTitleDisposable = undefined;
dispose(this._windowsShellHelper);
this._windowsShellHelper = undefined;
break;
}
const didTitleChange = title !== this._title;
this._title = title;

View File

@@ -276,7 +276,7 @@ export class TerminalPanel extends Panel {
const resources = e.dataTransfer.getData(DataTransfers.RESOURCES);
if (resources) {
path = URI.parse(JSON.parse(resources)[0]).fsPath;
} else if (e.dataTransfer.files.length > 0) {
} else if (e.dataTransfer.files.length > 0 && e.dataTransfer.files[0].path /* Electron only */) {
// Check if the file was dragged from the filesystem
path = URI.file(e.dataTransfer.files[0].path).fsPath;
}

View File

@@ -117,6 +117,7 @@ export interface ITerminalConfiguration {
splitCwd: 'workspaceRoot' | 'initial' | 'inherited';
windowsEnableConpty: boolean;
experimentalRefreshOnResume: boolean;
experimentalUseTitleEvent: boolean;
}
export interface ITerminalConfigHelper {
@@ -636,7 +637,7 @@ export interface ITerminalInstance {
/**
* Sets the title of the terminal instance.
*/
setTitle(title: string, eventFromProcess: boolean): void;
setTitle(title: string, eventSource: TitleEventSource): void;
waitForTitle(): Promise<string>;
@@ -769,6 +770,15 @@ export enum LinuxDistro {
Unknown
}
export enum TitleEventSource {
/** From the API or the rename command that overrides any other type */
Api,
/** From the process name property*/
Process,
/** From the VT sequence */
Sequence
}
export interface IWindowsShellHelper extends IDisposable {
getShellName(): Promise<string>;
}

View File

@@ -62,7 +62,7 @@ export function addTerminalEnvironmentKeys(env: platform.IProcessEnvironment, ve
env['COLORTERM'] = 'truecolor';
}
function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnvironment | NodeJS.ProcessEnv | undefined) {
function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnvironment | undefined) {
if (!other) {
return;
}

View File

@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { TERMINAL_ACTION_CATEGORY, ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal';
import { TERMINAL_ACTION_CATEGORY, ITerminalService, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands';
import { Action } from 'vs/base/common/actions';
import { URI } from 'vs/base/common/uri';
@@ -39,7 +39,7 @@ export class CreateNewLocalTerminalAction extends Action {
const disposable = instance.onTitleChanged(() => {
if (instance.title && instance.title.trim().length > 0) {
disposable.dispose();
instance.setTitle(`${instance.title} (Local)`, false);
instance.setTitle(`${instance.title} (Local)`, TitleEventSource.Api);
}
});

View File

@@ -5,9 +5,9 @@
import * as platform from 'vs/base/common/platform';
import { Emitter, Event } from 'vs/base/common/event';
import { ITerminalInstance, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalInstance, IWindowsShellHelper, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { Terminal as XTermTerminal } from 'xterm';
import WindowsProcessTreeType = require('windows-process-tree');
import * as WindowsProcessTreeType from 'windows-process-tree';
import { Disposable } from 'vs/base/common/lifecycle';
const SHELL_EXECUTABLES = [
@@ -80,7 +80,7 @@ export class WindowsShellHelper extends Disposable implements IWindowsShellHelpe
if (platform.isWindows && this._terminalInstance.isTitleSetByProcess) {
this.getShellName().then(title => {
if (!this._isDisposed) {
this._terminalInstance.setTitle(title, true);
this._terminalInstance.setTitle(title, TitleEventSource.Process);
}
});
}

View File

@@ -4,13 +4,16 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { IURLService } from 'vs/platform/url/common/url';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { URI } from 'vs/base/common/uri';
import { Action } from 'vs/base/common/actions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
export class OpenUrlAction extends Action {
@@ -34,5 +37,97 @@ export class OpenUrlAction extends Action {
}
}
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions)
.registerWorkbenchAction(new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), 'Open URL', localize('developer', "Developer"));
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions).registerWorkbenchAction(
new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL),
'Open URL',
localize('developer', 'Developer')
);
const VSCODE_DOMAIN = 'https://code.visualstudio.com';
const configureTrustedDomainsHandler = (
quickInputService: IQuickInputService,
storageService: IStorageService,
domainToConfigure?: string
) => {
let trustedDomains: string[] = [VSCODE_DOMAIN];
try {
const trustedDomainsSrc = storageService.get('http.trustedDomains', StorageScope.GLOBAL);
if (trustedDomainsSrc) {
trustedDomains = JSON.parse(trustedDomainsSrc);
}
} catch (err) { }
const domainQuickPickItems: IQuickPickItem[] = trustedDomains
.filter(d => d !== '*')
.map(d => {
return {
type: 'item',
label: d,
id: d,
picked: true,
};
});
const specialQuickPickItems: IQuickPickItem[] = [
{
type: 'item',
label: localize('openAllLinksWithoutPrompt', 'Open all links without prompt'),
id: '*',
picked: trustedDomains.indexOf('*') !== -1
}
];
let domainToConfigureItem: IQuickPickItem | undefined = undefined;
if (domainToConfigure && trustedDomains.indexOf(domainToConfigure) === -1) {
domainToConfigureItem = {
type: 'item',
label: domainToConfigure,
id: domainToConfigure,
picked: true,
description: localize('trustDomainAndOpenLink', 'Trust domain and open link')
};
specialQuickPickItems.push(<IQuickPickItem>domainToConfigureItem);
}
const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] = domainQuickPickItems.length === 0
? specialQuickPickItems
: [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems];
return quickInputService.pick(quickPickItems, {
canPickMany: true,
activeItem: domainToConfigureItem
}).then(result => {
if (result) {
const pickedDomains = result.map(r => r.id);
storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL);
return pickedDomains;
}
return [];
});
};
const configureTrustedDomainCommand = {
id: 'workbench.action.configureTrustedDomains',
description: {
description: localize('configureTrustedDomains', 'Configure Trusted Domains'),
args: [{ name: 'domainToConfigure', schema: { type: 'string' } }]
},
handler: (accessor: ServicesAccessor, domainToConfigure?: string) => {
const quickInputService = accessor.get(IQuickInputService);
const storageService = accessor.get(IStorageService);
return configureTrustedDomainsHandler(quickInputService, storageService, domainToConfigure);
}
};
CommandsRegistry.registerCommand(configureTrustedDomainCommand);
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: configureTrustedDomainCommand.id,
title: configureTrustedDomainCommand.description.description
}
});

View File

@@ -6,7 +6,7 @@
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IWebviewService, Webview, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview';
import { IWebviewService, Webview, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { memoize } from 'vs/base/common/decorators';

View File

@@ -289,6 +289,11 @@
const csp = newDocument.querySelector('meta[http-equiv="Content-Security-Policy"]');
if (!csp) {
host.postMessage('no-csp-found');
} else {
// Rewrite vscode-resource in csp
if (data.endpoint) {
csp.setAttribute('content', csp.getAttribute('content').replace(/vscode-resource:/g, data.endpoint));
}
}
// set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off

View File

@@ -15,7 +15,7 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/common/webview';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview';
import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand } from '../browser/webviewCommands';
import { WebviewEditor } from '../browser/webviewEditor';
import { WebviewEditorInput } from '../browser/webviewEditorInput';

View File

@@ -15,7 +15,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions } from 'vs/workbench/common/editor';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/common/webview';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';

View File

@@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { EditorInput, EditorModel, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor';
import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/common/webview';
import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview';
import { UnownedDisposable as Unowned } from 'vs/base/common/lifecycle';
class WebviewIconsManager {

View File

@@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { GroupIdentifier } from 'vs/workbench/common/editor';
import { IWebviewService, WebviewOptions, WebviewContentOptions } from 'vs/workbench/contrib/webview/common/webview';
import { IWebviewService, WebviewOptions, WebviewContentOptions } from 'vs/workbench/contrib/webview/browser/webview';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService';
import { RevivedWebviewEditorInput, WebviewEditorInput } from './webviewEditorInput';

View File

@@ -5,7 +5,7 @@
import { Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview';
import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
@@ -202,7 +202,8 @@ export class IFrameWebview extends Disposable implements Webview {
this._send('content', {
contents: this.content.html,
options: this.content.options,
state: this.content.state
state: this.content.state,
endpoint: this.endpoint,
});
}

View File

@@ -5,7 +5,7 @@
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement';
import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview';
import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
import { DynamicWebviewEditorOverlay } from './dynamicWebviewEditorOverlay';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';

View File

@@ -13,7 +13,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor';
import { IWebviewService, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/common/webview';
import { IWebviewService, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview';
import * as webviewCommands from 'vs/workbench/contrib/webview/electron-browser/webviewCommands';
import { ElectronWebviewService } from 'vs/workbench/contrib/webview/electron-browser/webviewService';
@@ -89,4 +89,4 @@ function registerWebViewCommands(editorId: string): void {
}
}
registerWebViewCommands(WebviewEditor.ID);
registerWebViewCommands(WebviewEditor.ID);

View File

@@ -9,7 +9,7 @@ import { Command, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/common/webview';
import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview';
export class OpenWebviewDeveloperToolsAction extends Action {
static readonly ID = 'workbench.action.webview.openDeveloperTools';
@@ -101,4 +101,4 @@ function withActiveWebviewBasedWebview(accessor: ServicesAccessor, f: (webview:
}
});
}
}
}

View File

@@ -20,7 +20,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping';
import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing';
import { Webview, WebviewContentOptions, WebviewOptions, WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/webview';
import { Webview, WebviewContentOptions, WebviewOptions, WebviewResourceScheme } from 'vs/workbench/contrib/webview/browser/webview';
import { registerFileProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols';
import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService';
import { WebviewFindWidget } from '../browser/webviewFindWidget';
@@ -327,12 +327,19 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
{
const rawEvent = event.args[0];
const bounds = this._webview.getBoundingClientRect();
window.dispatchEvent(new MouseEvent(rawEvent.type, {
...rawEvent,
clientX: rawEvent.clientX + bounds.left,
clientY: rawEvent.clientY + bounds.top,
}));
return;
try {
window.dispatchEvent(new MouseEvent(rawEvent.type, {
...rawEvent,
clientX: rawEvent.clientX + bounds.left,
clientY: rawEvent.clientY + bounds.top,
}));
return;
}
catch (TypeError) {
// CustomEvent was treated as MouseEvent so don't do anything - https://github.com/microsoft/vscode/issues/78915
return;
}
}
case 'did-set-content':
@@ -489,7 +496,11 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
if (!this._webview) {
return;
}
this._webview.focus();
try {
this._webview.focus();
} catch {
// noop
}
this._send('focus');
// Handle focus change programmatically (do not rely on event from <webview>)

View File

@@ -7,7 +7,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { DynamicWebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay';
import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement';
import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview';
import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
export class ElectronWebviewService implements IWebviewService {
@@ -38,4 +38,4 @@ export class ElectronWebviewService implements IWebviewService {
): WebviewEditorOverlay {
return this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions);
}
}
}

View File

@@ -9,6 +9,8 @@ import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import * as platform from 'vs/base/common/platform';
import product from 'vs/platform/product/node/product';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { URI } from 'vs/base/common/uri';
export class GettingStarted implements IWorkbenchContribution {
@@ -20,7 +22,8 @@ export class GettingStarted implements IWorkbenchContribution {
constructor(
@IStorageService private readonly storageService: IStorageService,
@IEnvironmentService environmentService: IEnvironmentService,
@ITelemetryService private readonly telemetryService: ITelemetryService
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IOpenerService private readonly openerService: IOpenerService
) {
this.appName = product.nameLong;
@@ -50,7 +53,7 @@ export class GettingStarted implements IWorkbenchContribution {
if (platform.isLinux && platform.isRootUser()) {
return;
}
window.open(url);
this.openerService.open(URI.parse(url));
}
private handleWelcome(): void {

View File

@@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri';
import * as strings from 'vs/base/common/strings';
import { ICommandService } from 'vs/platform/commands/common/commands';
import * as arrays from 'vs/base/common/arrays';
import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput';
import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';

View File

@@ -8,7 +8,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { Action } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput';
import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput';
import { Schemas } from 'vs/base/common/network';
import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor';

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput';
import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput';
import { WalkThroughPart } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart';
import { WalkThroughArrowUp, WalkThroughArrowDown, WalkThroughPageUp, WalkThroughPageDown } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions';
import { WalkThroughContentProvider, WalkThroughSnippetContentProvider } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider';
@@ -48,13 +48,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughPageUp);
KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughPageDown);
// {{SQL CARBON EDIT}} - Disable unused menu item
// MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
// group: '1_welcome',
// command: {
// id: 'workbench.action.showInteractivePlayground',
// title: localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "I&&nteractive Playground")
// },
// order: 2
// });
// {{SQL CARBON EDIT}} - End
/*MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { {{SQL CARBON EDIT}} remove menu item
group: '1_welcome',
command: {
id: 'workbench.action.showInteractivePlayground',
title: localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "I&&nteractive Playground")
},
order: 2
});*/

View File

@@ -12,7 +12,7 @@ import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/com
import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput';
import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import * as marked from 'vs/base/common/marked/marked';
import { IModelService } from 'vs/editor/common/services/modelService';

View File

@@ -8,6 +8,8 @@ import * as nls from 'vs/nls';
import product from 'vs/platform/product/node/product';
import { isMacintosh, isLinux, language } from 'vs/base/common/platform';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { URI } from 'vs/base/common/uri';
export class KeybindingsReferenceAction extends Action {
@@ -19,13 +21,14 @@ export class KeybindingsReferenceAction extends Action {
constructor(
id: string,
label: string
label: string,
@IOpenerService private readonly openerService: IOpenerService
) {
super(id, label);
}
run(): Promise<void> {
window.open(KeybindingsReferenceAction.URL);
this.openerService.open(URI.parse(KeybindingsReferenceAction.URL));
return Promise.resolve();
}
@@ -41,13 +44,14 @@ export class OpenDocumentationUrlAction extends Action {
constructor(
id: string,
label: string
label: string,
@IOpenerService private readonly openerService: IOpenerService
) {
super(id, label);
}
run(): Promise<void> {
window.open(OpenDocumentationUrlAction.URL);
this.openerService.open(URI.parse(OpenDocumentationUrlAction.URL));
return Promise.resolve();
}
@@ -63,13 +67,14 @@ export class OpenIntroductoryVideosUrlAction extends Action {
constructor(
id: string,
label: string
label: string,
@IOpenerService private readonly openerService: IOpenerService
) {
super(id, label);
}
run(): Promise<void> {
window.open(OpenIntroductoryVideosUrlAction.URL);
this.openerService.open(URI.parse(OpenIntroductoryVideosUrlAction.URL));
return Promise.resolve();
}
@@ -85,13 +90,14 @@ export class OpenTipsAndTricksUrlAction extends Action {
constructor(
id: string,
label: string
label: string,
@IOpenerService private readonly openerService: IOpenerService
) {
super(id, label);
}
run(): Promise<void> {
window.open(OpenTipsAndTricksUrlAction.URL);
this.openerService.open(URI.parse(OpenTipsAndTricksUrlAction.URL));
return Promise.resolve();
}
}
@@ -107,6 +113,7 @@ export class OpenNewsletterSignupUrlAction extends Action {
constructor(
id: string,
label: string,
@IOpenerService private readonly openerService: IOpenerService,
@ITelemetryService telemetryService: ITelemetryService
) {
super(id, label);
@@ -116,7 +123,7 @@ export class OpenNewsletterSignupUrlAction extends Action {
async run(): Promise<void> {
const info = await this.telemetryService.getTelemetryInfo();
window.open(`${OpenNewsletterSignupUrlAction.URL}?machineId=${encodeURIComponent(info.machineId)}`);
this.openerService.open(URI.parse(`${OpenNewsletterSignupUrlAction.URL}?machineId=${encodeURIComponent(info.machineId)}`));
}
}
@@ -127,14 +134,15 @@ export class OpenTwitterUrlAction extends Action {
constructor(
id: string,
label: string
label: string,
@IOpenerService private readonly openerService: IOpenerService
) {
super(id, label);
}
run(): Promise<void> {
if (product.twitterUrl) {
window.open(product.twitterUrl);
this.openerService.open(URI.parse(product.twitterUrl));
}
return Promise.resolve();
@@ -148,14 +156,15 @@ export class OpenRequestFeatureUrlAction extends Action {
constructor(
id: string,
label: string
label: string,
@IOpenerService private readonly openerService: IOpenerService
) {
super(id, label);
}
run(): Promise<void> {
if (product.requestFeatureUrl) {
window.open(product.requestFeatureUrl);
this.openerService.open(URI.parse(product.requestFeatureUrl));
}
return Promise.resolve();
@@ -169,7 +178,8 @@ export class OpenLicenseUrlAction extends Action {
constructor(
id: string,
label: string
label: string,
@IOpenerService private readonly openerService: IOpenerService
) {
super(id, label);
}
@@ -178,9 +188,9 @@ export class OpenLicenseUrlAction extends Action {
if (product.licenseUrl) {
if (language) {
const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?';
window.open(`${product.licenseUrl}${queryArgChar}lang=${language}`);
this.openerService.open(URI.parse(`${product.licenseUrl}${queryArgChar}lang=${language}`));
} else {
window.open(product.licenseUrl);
this.openerService.open(URI.parse(product.licenseUrl));
}
}
@@ -195,7 +205,8 @@ export class OpenPrivacyStatementUrlAction extends Action {
constructor(
id: string,
label: string
label: string,
@IOpenerService private readonly openerService: IOpenerService
) {
super(id, label);
}
@@ -204,9 +215,9 @@ export class OpenPrivacyStatementUrlAction extends Action {
if (product.privacyStatementUrl) {
if (language) {
const queryArgChar = product.privacyStatementUrl.indexOf('?') > 0 ? '&' : '?';
window.open(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`);
this.openerService.open(URI.parse(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`));
} else {
window.open(product.privacyStatementUrl);
this.openerService.open(URI.parse(product.privacyStatementUrl));
}
}

View File

@@ -14,7 +14,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { toResource, IUntitledResourceInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor';
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IRunKeybindingInWindowRequest } from 'vs/platform/windows/common/windows';
import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IRunKeybindingInWindowRequest, getTitleBarStyle } from 'vs/platform/windows/common/windows';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
@@ -24,7 +24,7 @@ import { IResourceInput } from 'vs/platform/editor/common/editor';
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService';
import { ipcRenderer as ipc, webFrame, crashReporter, Event } from 'electron';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction } from 'vs/platform/actions/common/actions';
import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { RunOnceScheduler } from 'vs/base/common/async';
@@ -32,7 +32,7 @@ import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecyc
import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity';
import { isRootUser, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform';
import { isRootUser, isWindows, isMacintosh, isLinux, isWeb } from 'vs/base/common/platform';
import product from 'vs/platform/product/node/product';
import pkg from 'vs/platform/product/node/package';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -45,6 +45,17 @@ import { coalesce } from 'vs/base/common/arrays';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { isEqual } from 'vs/base/common/resources';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { MenubarControl } from '../browser/parts/titlebar/menubarControl';
import { ILabelService } from 'vs/platform/label/common/label';
import { IUpdateService } from 'vs/platform/update/common/update';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IPreferencesService } from '../services/preferences/common/preferences';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IMenubarService, IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/node/menubar';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { Schemas } from 'vs/base/common/network';
const TextInputActions: IAction[] = [
new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))),
@@ -91,7 +102,9 @@ export class ElectronWindow extends Disposable {
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@ITextFileService private readonly textFileService: ITextFileService
@ITextFileService private readonly textFileService: ITextFileService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IOpenerService private readonly openerService: IOpenerService
) {
super();
@@ -297,13 +310,13 @@ export class ElectronWindow extends Disposable {
private create(): void {
// Handle window.open() calls
const $this = this;
window.open = function (url: string, target: string, features: string, replace: boolean): Window | null {
$this.windowsService.openExternal(url);
// Native menu controller
if (isMacintosh || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') {
this._register(this.instantiationService.createInstance(NativeMenubarControl));
}
return null;
};
// Handle open calls
this.setupOpenHandlers();
// Emit event when vscode is ready
this.lifecycleService.when(LifecyclePhase.Ready).then(() => ipc.send('vscode:workbenchReady', this.windowService.windowId));
@@ -341,6 +354,39 @@ export class ElectronWindow extends Disposable {
}
}
private setupOpenHandlers(): void {
// Block window.open() calls
const $this = this;
window.open = function (): Window | null {
throw new Error('Prevented call to window.open(). Use IOpenerService instead!');
};
// Handle internal open() calls
this.openerService.registerOpener({
async open(resource: URI, options?: { openToSide?: boolean; openExternal?: boolean; } | undefined): Promise<boolean> {
// If either the caller wants to open externally or the
// scheme is one where we prefer to open externally
// we handle this resource by delegating the opening to
// the main process to prevent window focus issues.
const scheme = resource.scheme.toLowerCase();
const preferOpenExternal = (scheme === Schemas.mailto || scheme === Schemas.http || scheme === Schemas.https);
if ((options && options.openExternal) || preferOpenExternal) {
const success = await $this.windowsService.openExternal(encodeURI(resource.toString(true)));
if (!success && resource.scheme === Schemas.file) {
// if opening failed, and this is a file, we can still try to reveal it
await $this.windowsService.showItemInFolder(resource);
}
return true;
}
return false; // not handled by us
}
});
}
private updateTouchbarMenu(): void {
if (!isMacintosh) {
return; // macOS only
@@ -538,3 +584,192 @@ export class ElectronWindow extends Disposable {
return this.editorService.openEditors(resources);
}
}
class NativeMenubarControl extends MenubarControl {
constructor(
@IMenuService menuService: IMenuService,
@IWindowService windowService: IWindowService,
@IWindowsService windowsService: IWindowsService,
@IContextKeyService contextKeyService: IContextKeyService,
@IKeybindingService keybindingService: IKeybindingService,
@IConfigurationService configurationService: IConfigurationService,
@ILabelService labelService: ILabelService,
@IUpdateService updateService: IUpdateService,
@IStorageService storageService: IStorageService,
@INotificationService notificationService: INotificationService,
@IPreferencesService preferencesService: IPreferencesService,
@IEnvironmentService environmentService: IEnvironmentService,
@IAccessibilityService accessibilityService: IAccessibilityService,
@IMenubarService private readonly menubarService: IMenubarService
) {
super(
menuService,
windowService,
windowsService,
contextKeyService,
keybindingService,
configurationService,
labelService,
updateService,
storageService,
notificationService,
preferencesService,
environmentService,
accessibilityService);
if (isMacintosh && !isWeb) {
this.menus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService));
this.topLevelTitles['Preferences'] = nls.localize('mPreferences', "Preferences");
}
for (const topLevelMenuName of Object.keys(this.topLevelTitles)) {
const menu = this.menus[topLevelMenuName];
if (menu) {
this._register(menu.onDidChange(() => this.updateMenubar()));
}
}
this.windowService.getRecentlyOpened().then((recentlyOpened) => {
this.recentlyOpened = recentlyOpened;
this.doUpdateMenubar(true);
});
this.registerListeners();
}
protected doUpdateMenubar(firstTime: boolean): void {
// Send menus to main process to be rendered by Electron
const menubarData = { menus: {}, keybindings: {} };
if (this.getMenubarMenus(menubarData)) {
this.menubarService.updateMenubar(this.windowService.windowId, menubarData);
}
}
private getMenubarMenus(menubarData: IMenubarData): boolean {
if (!menubarData) {
return false;
}
menubarData.keybindings = this.getAdditionalKeybindings();
for (const topLevelMenuName of Object.keys(this.topLevelTitles)) {
const menu = this.menus[topLevelMenuName];
if (menu) {
const menubarMenu: IMenubarMenu = { items: [] };
this.populateMenuItems(menu, menubarMenu, menubarData.keybindings);
if (menubarMenu.items.length === 0) {
return false; // Menus are incomplete
}
menubarData.menus[topLevelMenuName] = menubarMenu;
}
}
return true;
}
private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding | undefined }) {
let groups = menu.getActions();
for (let group of groups) {
const [, actions] = group;
actions.forEach(menuItem => {
if (menuItem instanceof SubmenuItemAction) {
const submenu = { items: [] };
if (!this.menus[menuItem.item.submenu]) {
this.menus[menuItem.item.submenu] = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService);
this._register(this.menus[menuItem.item.submenu]!.onDidChange(() => this.updateMenubar()));
}
const menuToDispose = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService);
this.populateMenuItems(menuToDispose, submenu, keybindings);
let menubarSubmenuItem: IMenubarMenuItemSubmenu = {
id: menuItem.id,
label: menuItem.label,
submenu: submenu
};
menuToPopulate.items.push(menubarSubmenuItem);
menuToDispose.dispose();
} else {
if (menuItem.id === 'workbench.action.openRecent') {
const actions = this.getOpenRecentActions().map(this.transformOpenRecentAction);
menuToPopulate.items.push(...actions);
}
let menubarMenuItem: IMenubarMenuItemAction = {
id: menuItem.id,
label: menuItem.label
};
if (menuItem.checked) {
menubarMenuItem.checked = true;
}
if (!menuItem.enabled) {
menubarMenuItem.enabled = false;
}
menubarMenuItem.label = this.calculateActionLabel(menubarMenuItem);
keybindings[menuItem.id] = this.getMenubarKeybinding(menuItem.id);
menuToPopulate.items.push(menubarMenuItem);
}
});
menuToPopulate.items.push({ id: 'vscode.menubar.separator' });
}
if (menuToPopulate.items.length > 0) {
menuToPopulate.items.pop();
}
}
private transformOpenRecentAction(action: Separator | (IAction & { uri: URI })): MenubarMenuItem {
if (action instanceof Separator) {
return { id: 'vscode.menubar.separator' };
}
return {
id: action.id,
uri: action.uri,
enabled: action.enabled,
label: action.label
};
}
private getAdditionalKeybindings(): { [id: string]: IMenubarKeybinding } {
const keybindings: { [id: string]: IMenubarKeybinding } = {};
if (isMacintosh) {
const keybinding = this.getMenubarKeybinding('workbench.action.quit');
if (keybinding) {
keybindings['workbench.action.quit'] = keybinding;
}
}
return keybindings;
}
private getMenubarKeybinding(id: string): IMenubarKeybinding | undefined {
const binding = this.keybindingService.lookupKeybinding(id);
if (!binding) {
return undefined;
}
// first try to resolve a native accelerator
const electronAccelerator = binding.getElectronAccelerator();
if (electronAccelerator) {
return { label: electronAccelerator, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) };
}
// we need this fallback to support keybindings that cannot show in electron menus (e.g. chords)
const acceleratorLabel = binding.getLabel();
if (acceleratorLabel) {
return { label: acceleratorLabel, isNative: false, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) };
}
return undefined;
}
}

View File

@@ -17,9 +17,9 @@ export const folderSettingsSchemaId = 'vscode://schemas/settings/folder';
export const launchSchemaId = 'vscode://schemas/launch';
export const LOCAL_MACHINE_SCOPES = [ConfigurationScope.APPLICATION, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE];
export const REMOTE_MACHINE_SCOPES = [ConfigurationScope.MACHINE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE];
export const WORKSPACE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE];
export const FOLDER_SCOPES = [ConfigurationScope.RESOURCE];
export const REMOTE_MACHINE_SCOPES = [ConfigurationScope.MACHINE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.MACHINE_OVERRIDABLE];
export const WORKSPACE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.MACHINE_OVERRIDABLE];
export const FOLDER_SCOPES = [ConfigurationScope.RESOURCE, ConfigurationScope.MACHINE_OVERRIDABLE];
export const TASKS_CONFIGURATION_KEY = 'tasks';
export const LAUNCH_CONFIGURATION_KEY = 'launch';
@@ -36,4 +36,4 @@ export interface IConfigurationCache {
write(key: ConfigurationKey, content: string): Promise<void>;
remove(key: ConfigurationKey): Promise<void>;
}
}

View File

@@ -15,6 +15,7 @@ import { joinPath } from 'vs/base/common/resources';
import { Schemas } from 'vs/base/common/network';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api';
import { generateUuid } from 'vs/base/common/uuid';
export class BrowserWindowConfiguration implements IWindowConfiguration {
@@ -80,6 +81,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
this.appNameLong = 'Visual Studio Code - Web';
this.configuration.remoteAuthority = options.remoteAuthority;
this.configuration.machineId = generateUuid();
this.userRoamingDataHome = URI.file('/User').with({ scheme: Schemas.userData });
this.settingsResource = joinPath(this.userRoamingDataHome, 'settings.json');
this.keybindingsResource = joinPath(this.userRoamingDataHome, 'keybindings.json');

View File

@@ -8,7 +8,7 @@ import {
IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionType, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, isLanguagePackExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { Disposable } from 'vs/base/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -17,6 +17,8 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens
import { localize } from 'vs/nls';
import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IProductService } from 'vs/platform/product/common/product';
import { Schemas } from 'vs/base/common/network';
import { IDownloadService } from 'vs/platform/download/common/download';
export class ExtensionManagementService extends Disposable implements IExtensionManagementService {
@@ -34,6 +36,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@IConfigurationService protected readonly configurationService: IConfigurationService,
@IProductService protected readonly productService: IProductService,
@IDownloadService protected readonly downloadService: IDownloadService,
) {
super();
if (this.extensionManagementServerService.localExtensionManagementServer) {
@@ -142,15 +145,43 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
async install(vsix: URI): Promise<ILocalExtension> {
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
const manifest = await this.getManifest(vsix);
if (isLanguagePackExtension(manifest)) {
// Install on both servers
const [local] = await Promise.all(this.servers.map(server => this.installVSIX(vsix, server)));
return local;
}
if (isUIExtension(manifest, this.productService, this.configurationService)) {
// Install only on local server
return this.installVSIX(vsix, this.extensionManagementServerService.localExtensionManagementServer);
}
// Install only on remote server
return this.installVSIX(vsix, this.extensionManagementServerService.remoteExtensionManagementServer);
}
if (this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix);
return this.installVSIX(vsix, this.extensionManagementServerService.localExtensionManagementServer);
}
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix);
return this.installVSIX(vsix, this.extensionManagementServerService.remoteExtensionManagementServer);
}
return Promise.reject('No Servers to Install');
}
protected installVSIX(vsix: URI, server: IExtensionManagementServer): Promise<ILocalExtension> {
return server.extensionManagementService.install(vsix);
}
getManifest(vsix: URI): Promise<IExtensionManifest> {
if (vsix.scheme === Schemas.file && this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getManifest(vsix);
}
if (vsix.scheme === Schemas.vscodeRemote && this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.getManifest(vsix);
}
return Promise.reject('No Servers');
}
async installFromGallery(gallery: IGalleryExtension): Promise<ILocalExtension> {
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);

View File

@@ -3,35 +3,25 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { tmpdir } from 'os';
import { generateUuid } from 'vs/base/common/uuid';
import { ILocalExtension, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { ExtensionManagementService as BaseExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { Schemas } from 'vs/base/common/network';
import * as path from 'vs/base/common/path';
export class ExtensionManagementService extends BaseExtensionManagementService {
async install(vsix: URI): Promise<ILocalExtension> {
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
const manifest = await getManifest(vsix.fsPath);
if (isLanguagePackExtension(manifest)) {
// Install on both servers
const [local] = await Promise.all(this.servers.map(server => server.extensionManagementService.install(vsix)));
return local;
}
if (isUIExtension(manifest, this.productService, this.configurationService)) {
// Install only on local server
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix);
}
// Install only on remote server
return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix);
protected async installVSIX(vsix: URI, server: IExtensionManagementServer): Promise<ILocalExtension> {
if (vsix.scheme === Schemas.vscodeRemote && server === this.extensionManagementServerService.localExtensionManagementServer) {
const downloadedLocation = URI.file(path.join(tmpdir(), generateUuid()));
await this.downloadService.download(vsix, downloadedLocation);
vsix = downloadedLocation;
}
if (this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix);
}
return Promise.reject('No Servers to Install');
return server.extensionManagementService.install(vsix);
}
}

View File

@@ -27,6 +27,7 @@ import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browse
import { Schemas } from 'vs/base/common/network';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions';
import { DeltaExtensionsResult } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
@@ -81,21 +82,21 @@ export class ExtensionService extends AbstractExtensionService implements IExten
};
}
protected _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] {
protected _createExtensionHosts(_isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] {
const result: ExtensionHostProcessManager[] = [];
const remoteAgentConnection = this._remoteAgentService.getConnection()!;
const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService)));
const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService)));
const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.parse('empty:value')); //todo@joh
const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents);
const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents);
result.push(webHostProcessManager);
const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents);
result.push(remoteExtHostProcessManager);
const remoteAgentConnection = this._remoteAgentService.getConnection();
if (remoteAgentConnection) {
const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService)));
const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents);
result.push(remoteExtHostProcessManager);
}
return result;
}
@@ -103,27 +104,35 @@ export class ExtensionService extends AbstractExtensionService implements IExten
protected async _scanAndHandleExtensions(): Promise<void> {
// fetch the remote environment
let [remoteEnv, localExtensions] = await Promise.all([
<Promise<IRemoteAgentEnvironment>>this._remoteAgentService.getEnvironment(),
this._remoteAgentService.getEnvironment(),
this._staticExtensions.getExtensions()
]);
let result: DeltaExtensionsResult;
// local: only enabled and web'ish extension
localExtensions = localExtensions.filter(ext => this._isEnabled(ext) && isWebExtension(ext, this._configService));
this._checkEnableProposedApi(localExtensions);
// remote: only enabled and none-web'ish extension
remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService));
this._checkEnableProposedApi(remoteEnv.extensions);
if (!remoteEnv) {
result = this._registry.deltaExtensions(localExtensions, []);
// in case of overlap, the remote wins
const isRemoteExtension = new Set<string>();
remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier)));
localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier)));
} else {
// remote: only enabled and none-web'ish extension
remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService));
this._checkEnableProposedApi(remoteEnv.extensions);
// save for remote extension's init data
this._remoteExtensionsEnvironmentData = remoteEnv;
// in case of overlap, the remote wins
const isRemoteExtension = new Set<string>();
remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier)));
localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier)));
// save for remote extension's init data
this._remoteExtensionsEnvironmentData = remoteEnv;
result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []);
}
const result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []);
if (result.removedDueToLooping.length > 0) {
this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', ')));
}

View File

@@ -62,6 +62,7 @@ function patchProcess(allowExit: boolean) {
}
} as (code?: number) => never;
// override Electron's process.crash() method
process.crash = function () {
const err = new Error('An extension called process.crash() and this was prevented.');
console.warn(err.stack);

View File

@@ -16,6 +16,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { INotificationService, Severity, NeverShowAgainScope } from 'vs/platform/notification/common/notification';
import { localize } from 'vs/nls';
import { FileService } from 'vs/platform/files/common/fileService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
export class WorkspaceWatcher extends Disposable {
@@ -25,7 +26,8 @@ export class WorkspaceWatcher extends Disposable {
@IFileService private readonly fileService: FileService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@INotificationService private readonly notificationService: INotificationService
@INotificationService private readonly notificationService: INotificationService,
@IOpenerService private readonly openerService: IOpenerService
) {
super();
@@ -77,7 +79,7 @@ export class WorkspaceWatcher extends Disposable {
localize('netVersionError', "The Microsoft .NET Framework 4.5 is required. Please follow the link to install it."),
[{
label: localize('installNet', "Download .NET Framework 4.5"),
run: () => window.open('https://go.microsoft.com/fwlink/?LinkId=786533')
run: () => this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?LinkId=786533'))
}],
{
sticky: true,
@@ -93,7 +95,7 @@ export class WorkspaceWatcher extends Disposable {
localize('enospcError', "Unable to watch for file changes in this large workspace. Please follow the instructions link to resolve this issue."),
[{
label: localize('learnMore', "Instructions"),
run: () => window.open('https://go.microsoft.com/fwlink/?linkid=867693')
run: () => this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=867693'))
}],
{
sticky: true,

View File

@@ -15,6 +15,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { IOpenerService } from 'vs/platform/opener/common/opener';
interface IStorageData {
dontShowPrompt: boolean;
@@ -64,7 +65,8 @@ export class IntegrityServiceImpl implements IIntegrityService {
constructor(
@INotificationService private readonly notificationService: INotificationService,
@IStorageService storageService: IStorageService,
@ILifecycleService private readonly lifecycleService: ILifecycleService
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IOpenerService private readonly openerService: IOpenerService
) {
this._storage = new IntegrityStorage(storageService);
@@ -91,7 +93,7 @@ export class IntegrityServiceImpl implements IIntegrityService {
[
{
label: nls.localize('integrity.moreInformation', "More Information"),
run: () => window.open(URI.parse(product.checksumFailMoreInfoUrl).toString(true))
run: () => this.openerService.open(URI.parse(product.checksumFailMoreInfoUrl))
},
{
label: nls.localize('integrity.dontShowAgain', "Don't Show Again"),

View File

@@ -45,7 +45,7 @@ import * as objects from 'vs/base/common/objects';
import { IKeymapService } from 'vs/workbench/services/keybinding/common/keymapInfo';
import { getDispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig';
import { isArray } from 'vs/base/common/types';
import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard';
import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/browser/navigatorKeyboard';
import { ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/base/common/scanCode';
interface ContributedKeyBinding {

Some files were not shown because too many files have changed in this diff Show More