Merge from vscode cfc1ab4c5f816765b91fb7ead3c3427a7c8581a3

This commit is contained in:
ADS Merger
2020-03-11 04:19:23 +00:00
parent 16fab722d5
commit 4c3e48773d
880 changed files with 20441 additions and 11232 deletions

View File

@@ -89,7 +89,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
const disposables = new DisposableStore();
const webview = this._webviewService.createWebview('' + handle, {
const webview = this._webviewService.createWebviewElement('' + handle, {
enableFindWidget: false,
}, {
allowScripts: options.enableScripts,

View File

@@ -75,7 +75,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
}
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined> {
return Promise.resolve(this._proxy.$runInTerminal(args));
return this._proxy.$runInTerminal(args);
}
// RPC methods (MainThreadDebugServiceShape)

View File

@@ -5,7 +5,6 @@
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { values } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor, isCodeEditor, isDiffEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
@@ -22,7 +21,7 @@ import { MainThreadTextEditors } from 'vs/workbench/api/browser/mainThreadEditor
import { ExtHostContext, ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IExtHostContext, IModelAddedData, ITextEditorAddData, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { EditorViewColumn, editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { IEditor as IWorkbenchEditor } from 'vs/workbench/common/editor';
import { IEditorPane } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
@@ -110,8 +109,8 @@ class DocumentAndEditorState {
static compute(before: DocumentAndEditorState | undefined, after: DocumentAndEditorState): DocumentAndEditorStateDelta {
if (!before) {
return new DocumentAndEditorStateDelta(
[], values(after.documents),
[], values(after.textEditors),
[], [...after.documents.values()],
[], [...after.textEditors.values()],
undefined, after.activeEditor
);
}
@@ -291,11 +290,11 @@ class MainThreadDocumentAndEditorStateComputer {
}
private _getActiveEditorFromEditorPart(): IEditor | undefined {
let result = this._editorService.activeTextEditorWidget;
if (isDiffEditor(result)) {
result = result.getModifiedEditor();
let activeTextEditorControl = this._editorService.activeTextEditorControl;
if (isDiffEditor(activeTextEditorControl)) {
activeTextEditorControl = activeTextEditorControl.getModifiedEditor();
}
return result;
return activeTextEditorControl;
}
}
@@ -436,17 +435,17 @@ export class MainThreadDocumentsAndEditors {
}
private _findEditorPosition(editor: MainThreadTextEditor): EditorViewColumn | undefined {
for (const workbenchEditor of this._editorService.visibleControls) {
if (editor.matches(workbenchEditor)) {
return editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group);
for (const editorPane of this._editorService.visibleEditorPanes) {
if (editor.matches(editorPane)) {
return editorGroupToViewColumn(this._editorGroupService, editorPane.group);
}
}
return undefined;
}
findTextEditorIdFor(inputEditor: IWorkbenchEditor): string | undefined {
findTextEditorIdFor(editorPane: IEditorPane): string | undefined {
for (const [id, editor] of this._textEditors) {
if (editor.matches(inputEditor)) {
if (editor.matches(editorPane)) {
return id;
}
}

View File

@@ -14,7 +14,7 @@ import { ISingleEditOperation, ITextModel, ITextModelUpdateOptions, IIdentifiedS
import { IModelService } from 'vs/editor/common/services/modelService';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, IUndoStopOptions, TextEditorRevealType } from 'vs/workbench/api/common/extHost.protocol';
import { IEditor } from 'vs/workbench/common/editor';
import { IEditorPane } from 'vs/workbench/common/editor';
import { withNullAsUndefined } from 'vs/base/common/types';
import { equals } from 'vs/base/common/arrays';
@@ -413,7 +413,7 @@ export class MainThreadTextEditor {
return false;
}
public matches(editor: IEditor): boolean {
public matches(editor: IEditorPane): boolean {
if (!editor) {
return false;
}

View File

@@ -15,7 +15,7 @@ import { ISelection } from 'vs/editor/common/core/selection';
import { IDecorationOptions, IDecorationRenderOptions, ILineChange } from 'vs/editor/common/editorCommon';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IEditorOptions, ITextEditorOptions, IResourceInput, EditorActivation } from 'vs/platform/editor/common/editor';
import { IEditorOptions, ITextEditorOptions, IResourceEditorInput, EditorActivation } from 'vs/platform/editor/common/editor';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors';
@@ -101,10 +101,10 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
private _getTextEditorPositionData(): ITextEditorPositionData {
const result: ITextEditorPositionData = Object.create(null);
for (let workbenchEditor of this._editorService.visibleControls) {
const id = this._documentsAndEditors.findTextEditorIdFor(workbenchEditor);
for (let editorPane of this._editorService.visibleEditorPanes) {
const id = this._documentsAndEditors.findTextEditorIdFor(editorPane);
if (id) {
result[id] = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group);
result[id] = editorGroupToViewColumn(this._editorGroupService, editorPane.group);
}
}
return result;
@@ -124,7 +124,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
activation: options.preserveFocus ? EditorActivation.RESTORE : undefined
};
const input: IResourceInput = {
const input: IResourceEditorInput = {
resource: uri,
options: editorOptions
};
@@ -151,10 +151,10 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
async $tryHideEditor(id: string): Promise<void> {
const mainThreadEditor = this._documentsAndEditors.getEditor(id);
if (mainThreadEditor) {
const editors = this._editorService.visibleControls;
for (let editor of editors) {
if (mainThreadEditor.matches(editor)) {
return editor.group.closeEditor(editor.input);
const editorPanes = this._editorService.visibleEditorPanes;
for (let editorPane of editorPanes) {
if (mainThreadEditor.matches(editorPane)) {
return editorPane.group.closeEditor(editorPane.input);
}
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import { ITextModel, ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import * as search from 'vs/workbench/contrib/search/common/search';
@@ -367,8 +367,21 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- semantic tokens
$registerDocumentSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void {
this._registrations.set(handle, modes.DocumentSemanticTokensProviderRegistry.register(selector, new MainThreadDocumentSemanticTokensProvider(this._proxy, handle, legend)));
$registerDocumentSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend, eventHandle: number | undefined): void {
let event: Event<void> | undefined = undefined;
if (typeof eventHandle === 'number') {
const emitter = new Emitter<void>();
this._registrations.set(eventHandle, emitter);
event = emitter.event;
}
this._registrations.set(handle, modes.DocumentSemanticTokensProviderRegistry.register(selector, new MainThreadDocumentSemanticTokensProvider(this._proxy, handle, legend, event)));
}
$emitDocumentSemanticTokensEvent(eventHandle: number): void {
const obj = this._registrations.get(eventHandle);
if (obj instanceof Emitter) {
obj.fire(undefined);
}
}
$registerDocumentRangeSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void {
@@ -662,6 +675,7 @@ export class MainThreadDocumentSemanticTokensProvider implements modes.DocumentS
private readonly _proxy: ExtHostLanguageFeaturesShape,
private readonly _handle: number,
private readonly _legend: modes.SemanticTokensLegend,
public readonly onDidChange: Event<void> | undefined,
) {
}

View File

@@ -8,6 +8,7 @@ import { MainThreadStatusBarShape, MainContext, IExtHostContext } from '../commo
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { dispose } from 'vs/base/common/lifecycle';
import { Command } from 'vs/editor/common/modes';
@extHostNamedCustomer(MainContext.MainThreadStatusBar)
export class MainThreadStatusBar implements MainThreadStatusBarShape {
@@ -24,7 +25,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
this.entries.clear();
}
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: string | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined): void {
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined): void {
const entry: IStatusbarEntry = { text, tooltip, command, color };
if (typeof priority === 'undefined') {

View File

@@ -33,6 +33,7 @@ import {
RunOptionsDTO
} from 'vs/workbench/api/common/shared/tasks';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
namespace TaskExecutionDTO {
export function from(value: TaskExecution): TaskExecutionDTO {
@@ -604,7 +605,7 @@ export class MainThreadTask implements MainThreadTaskShape {
return URI.parse(`${info.scheme}://${info.authority}${path}`);
},
context: this._extHostContext,
resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet): Promise<ResolvedVariables> => {
resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet, target: ConfigurationTarget): Promise<ResolvedVariables> => {
const vars: string[] = [];
toResolve.variables.forEach(item => vars.push(item));
return Promise.resolve(this._proxy.$resolveVariables(workspaceFolder.uri, { process: toResolve.process, variables: vars })).then(values => {
@@ -613,7 +614,7 @@ export class MainThreadTask implements MainThreadTaskShape {
partiallyResolvedVars.push(entry.value);
});
return new Promise<ResolvedVariables>((resolve, reject) => {
this._configurationResolverService.resolveWithInteraction(workspaceFolder, partiallyResolvedVars, 'tasks').then(resolvedVars => {
this._configurationResolverService.resolveWithInteraction(workspaceFolder, partiallyResolvedVars, 'tasks', undefined, target).then(resolvedVars => {
const result: ResolvedVariables = {
process: undefined,
variables: new Map<string, string>()

View File

@@ -22,8 +22,8 @@ export class MainThreadTheming implements MainThreadThemingShape {
this._themeService = themeService;
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTheming);
this._themeChangeListener = this._themeService.onThemeChange(e => {
this._proxy.$onColorThemeChange(this._themeService.getTheme().type);
this._themeChangeListener = this._themeService.onDidColorThemeChange(e => {
this._proxy.$onColorThemeChange(this._themeService.getColorTheme().type);
});
}

View File

@@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { MainContext, MainThreadTimelineShape, IExtHostContext, ExtHostTimelineShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, ITimelineService } from 'vs/workbench/contrib/timeline/common/timeline';
import { TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, ITimelineService, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
@extHostNamedCustomer(MainContext.MainThreadTimeline)
export class MainThreadTimeline implements MainThreadTimelineShape {
@@ -39,7 +39,7 @@ export class MainThreadTimeline implements MainThreadTimelineShape {
this._timelineService.registerTimelineProvider({
...provider,
onDidChange: onDidChange.event,
provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }) {
provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions) {
return proxy.$getTimeline(provider.id, uri, options, token, internalOptions);
},
dispose() {

View File

@@ -6,7 +6,7 @@
import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { IRemoteExplorerService, MakeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { ITunnelProvider, ITunnelService, TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { Disposable } from 'vs/base/common/lifecycle';
import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
@@ -51,6 +51,10 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
this.remoteExplorerService.registerCandidateFinder(() => this._proxy.$findCandidatePorts());
}
async $tunnelServiceReady(): Promise<void> {
return this.remoteExplorerService.restore();
}
async $setTunnelProvider(): Promise<void> {
const tunnelProvider: ITunnelProvider = {
forwardPort: (tunnelOptions: TunnelOptions) => {
@@ -60,9 +64,12 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
return {
tunnelRemotePort: tunnel.remoteAddress.port,
tunnelRemoteHost: tunnel.remoteAddress.host,
localAddress: tunnel.localAddress,
dispose: () => {
this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port });
localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : MakeAddress(tunnel.localAddress.host, tunnel.localAddress.port),
tunnelLocalPort: typeof tunnel.localAddress !== 'string' ? tunnel.localAddress.port : undefined,
dispose: (silent: boolean) => {
if (!silent) {
this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port });
}
}
};
});

View File

@@ -3,32 +3,38 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createCancelablePromise } from 'vs/base/common/async';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable, IReference, dispose } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { basename } from 'vs/base/common/path';
import { isWeb } from 'vs/base/common/platform';
import { startsWith } from 'vs/base/common/strings';
import { escape } 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 { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILabelService } from 'vs/platform/label/common/label';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IProductService } from 'vs/platform/product/common/productService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { IEditorInput } from 'vs/workbench/common/editor';
import { IEditorInput, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { CustomEditorInput, ModelType } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
import { WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview';
import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/customTextEditorModel';
import { WebviewExtensionDescription, WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { extHostNamedCustomer } from '../common/extHostCustomers';
/**
@@ -74,12 +80,17 @@ class WebviewViewTypeTransformer {
}
public toExternal(viewType: string): string | undefined {
return startsWith(viewType, this.prefix)
return viewType.startsWith(this.prefix)
? viewType.substr(this.prefix.length)
: undefined;
}
}
const enum ModelType {
Custom,
Text,
}
const webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-');
@extHostNamedCustomer(extHostProtocol.MainContext.MainThreadWebviews)
@@ -97,7 +108,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
private readonly _webviewInputs = new WebviewInputStore();
private readonly _revivers = new Map<string, IDisposable>();
private readonly _editorProviders = new Map<string, IDisposable>();
private readonly _customEditorModels = new Map<string, { referenceCount: number }>();
private readonly _webviewFromDiffEditorHandles = new Set<string>();
constructor(
context: extHostProtocol.IExtHostContext,
@@ -109,13 +120,24 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
@IProductService private readonly _productService: IProductService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService,
@IFileService private readonly _fileService: IFileService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
super();
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews);
this._register(_editorService.onDidActiveEditorChange(this.updateWebviewViewStates, this));
this._register(_editorService.onDidVisibleEditorsChange(this.updateWebviewViewStates, this));
this._register(_editorService.onDidActiveEditorChange(() => {
const activeInput = this._editorService.activeEditor;
if (activeInput instanceof DiffEditorInput && activeInput.master instanceof WebviewInput && activeInput.details instanceof WebviewInput) {
this.registerWebviewFromDiffEditorListeners(activeInput);
}
this.updateWebviewViewStates(activeInput);
}));
this._register(_editorService.onDidVisibleEditorsChange(() => {
this.updateWebviewViewStates(this._editorService.activeEditor);
}));
// This reviver's only job is to activate webview panel extensions
// This should trigger the real reviver to be registered from the extension host side.
@@ -264,7 +286,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
return this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options);
}
public registerEditorProvider(
private registerEditorProvider(
modelType: ModelType,
extensionData: extHostProtocol.WebviewExtensionDescription,
viewType: string,
@@ -287,16 +309,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
webviewInput.webview.options = options;
webviewInput.webview.extension = extension;
webviewInput.modelType = modelType;
const resource = webviewInput.resource;
if (modelType === ModelType.Custom) {
const model = await this.retainCustomEditorModel(webviewInput, resource, viewType);
webviewInput.onDisposeWebview(() => {
this.releaseCustomEditorModel(model);
});
}
const modelRef = await this.getOrCreateCustomEditorModel(modelType, webviewInput, resource, viewType);
webviewInput.webview.onDispose(() => {
modelRef.dispose();
});
try {
await this._proxy.$resolveWebviewEditor(
@@ -328,71 +347,27 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._customEditorService.models.disposeAllModelsForView(viewType);
}
private async retainCustomEditorModel(webviewInput: WebviewInput, resource: URI, viewType: string) {
const model = await this._customEditorService.models.resolve(webviewInput.resource, webviewInput.viewType);
const key = viewType + resource.toString();
const existingEntry = this._customEditorModels.get(key);
if (existingEntry) {
++existingEntry.referenceCount;
// no need to hook up listeners again
return model;
}
this._customEditorModels.set(key, { referenceCount: 1 });
const { editable } = await this._proxy.$createWebviewCustomEditorDocument(resource, viewType);
if (editable) {
model.onUndo(() => {
this._proxy.$undo(resource, viewType);
});
model.onRedo(() => {
this._proxy.$redo(resource, viewType);
});
model.onWillSave(e => {
e.waitUntil(this._proxy.$onSave(resource.toJSON(), viewType));
});
private async getOrCreateCustomEditorModel(
modelType: ModelType,
webviewInput: WebviewInput,
resource: URI,
viewType: string,
): Promise<IReference<ICustomEditorModel>> {
const existingModel = this._customEditorService.models.tryRetain(webviewInput.resource, webviewInput.viewType);
if (existingModel) {
return existingModel;
}
// Save as should always be implemented even if the model is readonly
model.onWillSaveAs(e => {
if (editable) {
e.waitUntil(this._proxy.$onSaveAs(e.resource.toJSON(), viewType, e.targetResource.toJSON()));
} else {
// Since the editor is readonly, just copy the file over
e.waitUntil(this._fileService.copy(e.resource, e.targetResource, false /* overwrite */));
}
});
const model = modelType === ModelType.Text
? CustomTextEditorModel.create(this._instantiationService, viewType, resource)
: MainThreadCustomEditorModel.create(this._instantiationService, this._proxy, viewType, resource);
model.onBackup(() => {
return createCancelablePromise(token =>
this._proxy.$backup(model.resource.toJSON(), viewType, token));
});
return model;
return this._customEditorService.models.add(resource, viewType, model);
}
private async releaseCustomEditorModel(model: ICustomEditorModel) {
const key = model.viewType + model.resource;
const entry = this._customEditorModels.get(key);
if (!entry) {
throw new Error('Model not found');
}
--entry.referenceCount;
if (entry.referenceCount <= 0) {
this._proxy.$disposeWebviewCustomEditorDocument(model.resource, model.viewType);
this._customEditorService.models.disposeModel(model);
this._customEditorModels.delete(key);
}
}
public $onDidChangeCustomDocumentState(resource: UriComponents, viewType: string, state: { dirty: boolean }) {
const model = this._customEditorService.models.get(URI.revive(resource), viewType);
if (!model) {
public async $onDidChangeCustomDocumentState(resource: UriComponents, viewType: string, state: { dirty: boolean }) {
const model = await this._customEditorService.models.get(URI.revive(resource), viewType);
if (!model || !(model instanceof MainThreadCustomEditorModel)) {
throw new Error('Could not find model for webview editor');
}
model.setDirty(state.dirty);
@@ -408,19 +383,39 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
input.onDispose(() => {
disposables.dispose();
});
input.onDisposeWebview(() => {
input.webview.onDispose(() => {
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
this._webviewInputs.delete(handle);
});
});
}
private updateWebviewViewStates() {
private registerWebviewFromDiffEditorListeners(diffEditorInput: DiffEditorInput): void {
const master = diffEditorInput.master as WebviewInput;
const details = diffEditorInput.details as WebviewInput;
if (this._webviewFromDiffEditorHandles.has(master.id) || this._webviewFromDiffEditorHandles.has(details.id)) {
return;
}
this._webviewFromDiffEditorHandles.add(master.id);
this._webviewFromDiffEditorHandles.add(details.id);
const disposables = new DisposableStore();
disposables.add(master.webview.onDidFocus(() => this.updateWebviewViewStates(master)));
disposables.add(details.webview.onDidFocus(() => this.updateWebviewViewStates(details)));
disposables.add(diffEditorInput.onDispose(() => {
this._webviewFromDiffEditorHandles.delete(master.id);
this._webviewFromDiffEditorHandles.delete(details.id);
dispose(disposables);
}));
}
private updateWebviewViewStates(activeEditorInput: IEditorInput | undefined) {
if (!this._webviewInputs.size) {
return;
}
const activeInput = this._editorService.activeControl && this._editorService.activeControl.input;
const viewStates: extHostProtocol.WebviewPanelViewStateData = {};
const updateViewStatesForInput = (group: IEditorGroup, topLevelInput: IEditorInput, editorInput: IEditorInput) => {
@@ -434,7 +429,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
if (handle) {
viewStates[handle] = {
visible: topLevelInput === group.activeEditor,
active: topLevelInput === activeInput,
active: editorInput === activeEditorInput,
position: editorGroupToViewColumn(this._editorGroupService, group.id),
};
}
@@ -476,7 +471,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
private getWebviewInput(handle: extHostProtocol.WebviewPanelHandle): WebviewInput {
const webview = this.tryGetWebviewInput(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
throw new Error(`Unknown webview handle:${handle}`);
}
return webview;
}
@@ -492,7 +487,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none';">
</head>
<body>${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)}</body>
<body>${localize('errorMessage', "An error occurred while restoring view:{0}", escape(viewType))}</body>
</html>`;
}
}
@@ -511,13 +506,179 @@ function reviveWebviewOptions(options: modes.IWebviewOptions): WebviewInputOptio
function reviveWebviewIcon(
value: { light: UriComponents, dark: UriComponents; } | undefined
): { light: URI, dark: URI; } | undefined {
if (!value) {
return undefined;
): WebviewIcons | undefined {
return value
? { light: URI.revive(value.light), dark: URI.revive(value.dark) }
: undefined;
}
namespace HotExitState {
export const enum Type {
Allowed,
NotAllowed,
Pending,
}
return {
light: URI.revive(value.light),
dark: URI.revive(value.dark)
};
export const Allowed = Object.freeze({ type: Type.Allowed } as const);
export const NotAllowed = Object.freeze({ type: Type.NotAllowed } as const);
export class Pending {
readonly type = Type.Pending;
constructor(
public readonly operation: CancelablePromise<void>,
) { }
}
export type State = typeof Allowed | typeof NotAllowed | Pending;
}
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
private _hotExitState: HotExitState.State = HotExitState.Allowed;
private _dirty = false;
public static async create(
instantiationService: IInstantiationService,
proxy: extHostProtocol.ExtHostWebviewsShape,
viewType: string,
resource: URI
) {
const { editable } = await proxy.$createWebviewCustomEditorDocument(resource, viewType);
return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, editable);
}
constructor(
private readonly _proxy: extHostProtocol.ExtHostWebviewsShape,
private readonly _viewType: string,
private readonly _resource: URI,
private readonly _editable: boolean,
@IWorkingCopyService workingCopyService: IWorkingCopyService,
@ILabelService private readonly _labelService: ILabelService,
@IFileService private readonly _fileService: IFileService,
) {
super();
this._register(workingCopyService.registerWorkingCopy(this));
}
dispose() {
this._proxy.$disposeWebviewCustomEditorDocument(this.resource, this._viewType);
super.dispose();
}
//#region IWorkingCopy
public get resource() { return this._resource; }
public get name() {
return basename(this._labelService.getUriLabel(this._resource));
}
public get capabilities(): WorkingCopyCapabilities {
return 0;
}
public isDirty(): boolean {
return this._dirty;
}
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeDirty: Event<void> = this._onDidChangeDirty.event;
private readonly _onDidChangeContent: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
//#endregion
public get viewType() {
return this._viewType;
}
public setDirty(dirty: boolean): void {
this._onDidChangeContent.fire();
if (this._dirty !== dirty) {
this._dirty = dirty;
this._onDidChangeDirty.fire();
}
}
public async revert(_options?: IRevertOptions) {
if (this._editable) {
this._proxy.$revert(this.resource, this.viewType);
}
}
public undo() {
if (this._editable) {
this._proxy.$undo(this.resource, this.viewType);
}
}
public redo() {
if (this._editable) {
this._proxy.$redo(this.resource, this.viewType);
}
}
public async save(_options?: ISaveOptions): Promise<boolean> {
if (!this._editable) {
return false;
}
await createCancelablePromise(token => this._proxy.$onSave(this.resource, this.viewType, token));
this.setDirty(false);
return true;
}
public async saveAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise<boolean> {
if (this._editable) {
await this._proxy.$onSaveAs(this.resource, this.viewType, targetResource);
this.setDirty(false);
return true;
} else {
// Since the editor is readonly, just copy the file over
await this._fileService.copy(resource, targetResource, false /* overwrite */);
return true;
}
}
public async backup(): Promise<IWorkingCopyBackup> {
const backupData: IWorkingCopyBackup = {
meta: {
viewType: this.viewType,
}
};
if (!this._editable) {
return backupData;
}
if (this._hotExitState.type === HotExitState.Type.Pending) {
this._hotExitState.operation.cancel();
}
const pendingState = new HotExitState.Pending(
createCancelablePromise(token =>
this._proxy.$backup(this.resource.toJSON(), this.viewType, token)));
this._hotExitState = pendingState;
try {
await pendingState.operation;
// Make sure state has not changed in the meantime
if (this._hotExitState === pendingState) {
this._hotExitState = HotExitState.Allowed;
}
} catch (e) {
// Make sure state has not changed in the meantime
if (this._hotExitState === pendingState) {
this._hotExitState = HotExitState.NotAllowed;
}
}
if (this._hotExitState === HotExitState.Allowed) {
return backupData;
}
throw new Error('Cannot back up in this state');
}
}

View File

@@ -12,7 +12,7 @@ import { isNative } from 'vs/base/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILabelService } from 'vs/platform/label/common/label';
import { IFileMatch, IPatternInfo, ISearchProgressItem, ISearchService } from 'vs/workbench/services/search/common/search';
import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, WorkbenchState, IWorkspace, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -138,7 +138,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
}
const query = this._queryBuilder.file(
includeFolder ? [includeFolder] : workspace.folders.map(f => f.uri),
includeFolder ? [toWorkspaceFolder(includeFolder)] : workspace.folders,
{
maxResults: withNullAsUndefined(maxResults),
disregardExcludeSettings: (excludePatternOrDisregardExcludes === false) || undefined,
@@ -190,7 +190,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
$checkExists(folders: UriComponents[], includes: string[], token: CancellationToken): Promise<boolean> {
const queryBuilder = this._instantiationService.createInstance(QueryBuilder);
const query = queryBuilder.file(folders.map(folder => URI.revive(folder)), {
const query = queryBuilder.file(folders.map(folder => toWorkspaceFolder(URI.revive(folder))), {
_reason: 'checkExists',
includePattern: includes.join(', '),
expandPatterns: true,

View File

@@ -9,7 +9,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as resources from 'vs/base/common/resources';
import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation } from 'vs/workbench/common/views';
import { CustomTreeViewPane, CustomTreeView } from 'vs/workbench/browser/parts/views/customView';
import { TreeViewPane, CustomTreeView } from 'vs/workbench/browser/parts/views/treeView';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { coalesce, } from 'vs/base/common/arrays';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
@@ -396,7 +396,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
const viewDescriptor = <ICustomViewDescriptor>{
id: item.id,
name: item.name,
ctorDescriptor: new SyncDescriptor(CustomTreeViewPane),
ctorDescriptor: new SyncDescriptor(TreeViewPane),
when: ContextKeyExpr.deserialize(item.when),
canToggleVisibility: true,
canMoveView: true,