Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)

* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973

* disable strict null check
This commit is contained in:
Anthony Dresser
2019-07-15 22:35:46 -07:00
committed by GitHub
parent f720ec642f
commit 0b7e7ddbf9
2406 changed files with 59140 additions and 35464 deletions

View File

@@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
// --- other interested parties
import { JSONValidationExtensionPoint } from 'vs/workbench/api/common/jsonValidationExtensionPoint';
import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorExtensionPoint';
import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint';
// --- mainThread participants
import './mainThreadCodeInsets';
import './mainThreadClipboard';
import './mainThreadCommands';
import './mainThreadConfiguration';
import './mainThreadConsole';
// import './mainThreadDebugService'; {{SQL CARBON EDIT}} @anthonydresser comment out debug service
import './mainThreadDecorations';
import './mainThreadDiagnostics';
import './mainThreadDialogs';
import './mainThreadDocumentContentProviders';
import './mainThreadDocuments';
import './mainThreadDocumentsAndEditors';
import './mainThreadEditor';
import './mainThreadEditors';
import './mainThreadErrors';
import './mainThreadExtensionService';
import './mainThreadFileSystem';
import './mainThreadFileSystemEventService';
import './mainThreadKeytar';
import './mainThreadLanguageFeatures';
import './mainThreadLanguages';
import './mainThreadLogService';
import './mainThreadMessageService';
import './mainThreadOutputService';
import './mainThreadProgress';
import './mainThreadQuickOpen';
import './mainThreadSaveParticipant';
import './mainThreadSCM';
import './mainThreadSearch';
import './mainThreadStatusBar';
import './mainThreadStorage';
import './mainThreadTelemetry';
import './mainThreadTerminalService';
import './mainThreadTreeViews';
import './mainThreadUrls';
import './mainThreadWindow';
import './mainThreadWebview';
import './mainThreadWorkspace';
import './mainThreadComments';
// import './mainThreadTask'; {{SQL CARBON EDIT}} @anthonydresser comment out task
import './mainThreadLabelService';
import 'vs/workbench/api/common/apiCommands';
export class ExtensionPoints implements IWorkbenchContribution {
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
// Classes that handle extension points...
this.instantiationService.createInstance(JSONValidationExtensionPoint);
this.instantiationService.createInstance(ColorExtensionPoint);
this.instantiationService.createInstance(LanguageConfigurationFileHandler);
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, LifecyclePhase.Starting);

View File

@@ -8,7 +8,7 @@ import { MainContext, MainThreadClipboardShape } from '../common/extHost.protoco
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
@extHostNamedCustomer(MainContext.MainThreadClipboard)
export class MainThreadCommands implements MainThreadClipboardShape {
export class MainThreadClipboard implements MainThreadClipboardShape {
constructor(
_context: any,
@@ -20,11 +20,10 @@ export class MainThreadCommands implements MainThreadClipboardShape {
}
$readText(): Promise<string> {
return Promise.resolve(this._clipboardService.readText());
return this._clipboardService.readText();
}
$writeText(value: string): Promise<void> {
this._clipboardService.writeText(value);
return Promise.resolve();
return this._clipboardService.writeText(value);
}
}

View File

@@ -0,0 +1,148 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { UriComponents, URI } from 'vs/base/common/uri';
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, Webview } from 'vs/workbench/contrib/webview/common/webview';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
// todo@joh move these things back into something like contrib/insets
class EditorWebviewZone implements IViewZone {
readonly domNode: HTMLElement;
readonly afterLineNumber: number;
readonly afterColumn: number;
readonly heightInLines: number;
private _id: number;
// suppressMouseDown?: boolean | undefined;
// heightInPx?: number | undefined;
// minWidthInPx?: number | undefined;
// marginDomNode?: HTMLElement | null | undefined;
// onDomNodeTop?: ((top: number) => void) | undefined;
// onComputedHeight?: ((height: number) => void) | undefined;
constructor(
readonly editor: IActiveCodeEditor,
readonly line: number,
readonly height: number,
readonly webview: Webview,
) {
this.domNode = document.createElement('div');
this.domNode.style.zIndex = '10'; // without this, the webview is not interactive
this.afterLineNumber = line;
this.afterColumn = 1;
this.heightInLines = height;
editor.changeViewZones(accessor => this._id = accessor.addZone(this));
webview.mountTo(this.domNode);
}
dispose(): void {
this.editor.changeViewZones(accessor => accessor.removeZone(this._id));
}
}
@extHostNamedCustomer(MainContext.MainThreadEditorInsets)
export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
private readonly _proxy: ExtHostEditorInsetsShape;
private readonly _disposables = new DisposableStore();
private readonly _insets = new Map<number, EditorWebviewZone>();
constructor(
context: IExtHostContext,
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@IWebviewService private readonly _webviewService: IWebviewService,
) {
this._proxy = context.getProxy(ExtHostContext.ExtHostEditorInsets);
}
dispose(): void {
this._disposables.dispose();
}
async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void> {
let editor: IActiveCodeEditor | undefined;
id = id.substr(0, id.indexOf(',')); //todo@joh HACK
for (const candidate of this._editorService.listCodeEditors()) {
if (candidate.getId() === id && candidate.hasModel() && candidate.getModel()!.uri.toString() === URI.revive(uri).toString()) {
editor = candidate;
break;
}
}
if (!editor) {
setTimeout(() => this._proxy.$onDidDispose(handle));
return;
}
const disposables = new DisposableStore();
const webview = this._webviewService.createWebview('' + handle, {
enableFindWidget: false,
allowSvgs: false,
extension: { id: extensionId, location: URI.revive(extensionLocation) }
}, {
allowScripts: options.enableScripts,
localResourceRoots: options.localResourceRoots ? options.localResourceRoots.map(uri => URI.revive(uri)) : undefined
});
const webviewZone = new EditorWebviewZone(editor, line, height, webview);
const remove = () => {
disposables.dispose();
this._proxy.$onDidDispose(handle);
this._insets.delete(handle);
};
disposables.add(editor.onDidChangeModel(remove));
disposables.add(editor.onDidDispose(remove));
disposables.add(webviewZone);
disposables.add(webview);
disposables.add(webview.onMessage(msg => this._proxy.$onDidReceiveMessage(handle, msg)));
this._insets.set(handle, webviewZone);
}
$disposeEditorInset(handle: number): void {
const inset = this.getInset(handle);
this._insets.delete(handle);
inset.dispose();
}
$setHtml(handle: number, value: string): void {
const inset = this.getInset(handle);
inset.webview.html = value;
}
$setOptions(handle: number, options: modes.IWebviewOptions): void {
const inset = this.getInset(handle);
inset.webview.options = options;
}
async $postMessage(handle: number, value: any): Promise<boolean> {
const inset = this.getInset(handle);
inset.webview.sendMessage(value);
return true;
}
private getInset(handle: number): EditorWebviewZone {
const inset = this._insets.get(handle);
if (!inset) {
throw new Error('Unknown inset');
}
return inset;
}
}

View File

@@ -12,7 +12,7 @@ import { revive } from 'vs/base/common/marshalling';
@extHostNamedCustomer(MainContext.MainThreadCommands)
export class MainThreadCommands implements MainThreadCommandsShape {
private readonly _disposables = new Map<string, IDisposable>();
private readonly _commandRegistrations = new Map<string, IDisposable>();
private readonly _generateCommandsDocumentationRegistration: IDisposable;
private readonly _proxy: ExtHostCommandsShape;
@@ -26,8 +26,8 @@ export class MainThreadCommands implements MainThreadCommandsShape {
}
dispose() {
this._disposables.forEach(value => value.dispose());
this._disposables.clear();
this._commandRegistrations.forEach(value => value.dispose());
this._commandRegistrations.clear();
this._generateCommandsDocumentationRegistration.dispose();
}
@@ -36,10 +36,9 @@ export class MainThreadCommands implements MainThreadCommandsShape {
return this._proxy.$getContributedCommandHandlerDescriptions().then(result => {
// add local commands
const commands = CommandsRegistry.getCommands();
for (let id in commands) {
let { description } = commands[id];
if (description) {
result[id] = description;
for (const [id, command] of commands) {
if (command.description) {
result[id] = command.description;
}
}
@@ -53,7 +52,7 @@ export class MainThreadCommands implements MainThreadCommandsShape {
}
$registerCommand(id: string): void {
this._disposables.set(
this._commandRegistrations.set(
id,
CommandsRegistry.registerCommand(id, (accessor, ...args) => {
return this._proxy.$executeContributedCommand(id, ...args).then(result => {
@@ -64,10 +63,10 @@ export class MainThreadCommands implements MainThreadCommandsShape {
}
$unregisterCommand(id: string): void {
const command = this._disposables.get(id);
const command = this._commandRegistrations.get(id);
if (command) {
command.dispose();
this._disposables.delete(id);
this._commandRegistrations.delete(id);
}
}
@@ -79,7 +78,7 @@ export class MainThreadCommands implements MainThreadCommandsShape {
}
$getCommands(): Promise<string[]> {
return Promise.resolve(Object.keys(CommandsRegistry.getCommands()));
return Promise.resolve([...CommandsRegistry.getCommands().keys()]);
}
}

View File

@@ -3,85 +3,25 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ICodeEditor, isCodeEditor, isDiffEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
import * as modes from 'vs/editor/common/modes';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { keys } from 'vs/base/common/map';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape, CommentProviderFeatures } from '../common/extHost.protocol';
import { ICommentService, ICommentInfo } from 'vs/workbench/contrib/comments/browser/commentService';
import { COMMENTS_PANEL_ID, CommentsPanel, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsPanel';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { generateUuid } from 'vs/base/common/uuid';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommentsConfiguration } from 'vs/workbench/contrib/comments/browser/comments.contribution';
import { IRange } from 'vs/editor/common/core/range';
import * as modes from 'vs/editor/common/modes';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
import { IRange, Range } from 'vs/editor/common/core/range';
import { Emitter, Event } from 'vs/base/common/event';
import { CancellationToken } from 'vs/base/common/cancellation';
export class MainThreadDocumentCommentProvider implements modes.DocumentCommentProvider {
private readonly _proxy: ExtHostCommentsShape;
private readonly _handle: number;
private readonly _features: CommentProviderFeatures;
get startDraftLabel(): string | undefined { return this._features.startDraftLabel; }
get deleteDraftLabel(): string | undefined { return this._features.deleteDraftLabel; }
get finishDraftLabel(): string | undefined { return this._features.finishDraftLabel; }
get reactionGroup(): modes.CommentReaction[] | undefined { return this._features.reactionGroup; }
constructor(proxy: ExtHostCommentsShape, handle: number, features: CommentProviderFeatures) {
this._proxy = proxy;
this._handle = handle;
this._features = features;
}
async provideDocumentComments(uri: URI, token: CancellationToken) {
return this._proxy.$provideDocumentComments(this._handle, uri);
}
async createNewCommentThread(uri: URI, range: Range, text: string, token: CancellationToken) {
return this._proxy.$createNewCommentThread(this._handle, uri, range, text);
}
async replyToCommentThread(uri: URI, range: Range, thread: modes.CommentThread, text: string, token: CancellationToken) {
return this._proxy.$replyToCommentThread(this._handle, uri, range, thread, text);
}
async editComment(uri: URI, comment: modes.Comment, text: string, token: CancellationToken) {
return this._proxy.$editComment(this._handle, uri, comment, text);
}
async deleteComment(uri: URI, comment: modes.Comment, token: CancellationToken) {
return this._proxy.$deleteComment(this._handle, uri, comment);
}
async startDraft(uri: URI, token: CancellationToken): Promise<void> {
return this._proxy.$startDraft(this._handle, uri);
}
async deleteDraft(uri: URI, token: CancellationToken): Promise<void> {
return this._proxy.$deleteDraft(this._handle, uri);
}
async finishDraft(uri: URI, token: CancellationToken): Promise<void> {
return this._proxy.$finishDraft(this._handle, uri);
}
async addReaction(uri: URI, comment: modes.Comment, reaction: modes.CommentReaction, token: CancellationToken): Promise<void> {
return this._proxy.$addReaction(this._handle, uri, comment, reaction);
}
async deleteReaction(uri: URI, comment: modes.Comment, reaction: modes.CommentReaction, token: CancellationToken): Promise<void> {
return this._proxy.$deleteReaction(this._handle, uri, comment, reaction);
}
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'vs/workbench/browser/panel';
import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService';
import { CommentsPanel, COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsPanel';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape } from '../common/extHost.protocol';
// onDidChangeCommentThreads = null;
}
export class MainThreadCommentThread implements modes.CommentThread2 {
export class MainThreadCommentThread implements modes.CommentThread {
private _input?: modes.CommentInput;
get input(): modes.CommentInput | undefined {
return this._input;
@@ -117,7 +57,7 @@ export class MainThreadCommentThread implements modes.CommentThread2 {
}
private _onDidChangeLabel = new Emitter<string>();
get onDidChangeLabel(): Event<string> { return this._onDidChangeLabel.event; }
readonly onDidChangeLabel: Event<string> = this._onDidChangeLabel.event;
private _comments: modes.Comment[] | undefined;
@@ -133,42 +73,6 @@ export class MainThreadCommentThread implements modes.CommentThread2 {
private _onDidChangeComments = new Emitter<modes.Comment[] | undefined>();
get onDidChangeComments(): Event<modes.Comment[] | undefined> { return this._onDidChangeComments.event; }
private _acceptInputCommand: modes.Command | undefined;
set acceptInputCommand(newCommand: modes.Command | undefined) {
this._acceptInputCommand = newCommand;
this._onDidChangeAcceptInputCommand.fire(this._acceptInputCommand);
}
get acceptInputCommand(): modes.Command | undefined {
return this._acceptInputCommand!;
}
private _onDidChangeAcceptInputCommand = new Emitter<modes.Command | undefined>();
get onDidChangeAcceptInputCommand(): Event<modes.Command | undefined> { return this._onDidChangeAcceptInputCommand.event; }
private _additionalCommands: modes.Command[] | undefined;
set additionalCommands(newCommands: modes.Command[] | undefined) {
this._additionalCommands = newCommands;
this._onDidChangeAdditionalCommands.fire(this._additionalCommands);
}
get additionalCommands(): modes.Command[] | undefined {
return this._additionalCommands;
}
private _onDidChangeAdditionalCommands = new Emitter<modes.Command[] | undefined>();
get onDidChangeAdditionalCommands(): Event<modes.Command[] | undefined> { return this._onDidChangeAdditionalCommands.event; }
private _deleteCommand: modes.Command | undefined;
set deleteCommand(newCommand: modes.Command | undefined) {
this._deleteCommand = newCommand;
}
get deleteCommand(): modes.Command | undefined {
return this._deleteCommand;
}
set range(range: IRange) {
this._range = range;
this._onDidChangeRange.fire(this._range);
@@ -216,24 +120,16 @@ export class MainThreadCommentThread implements modes.CommentThread2 {
label: string,
contextValue: string | undefined,
comments: modes.Comment[],
acceptInputCommand: modes.Command | undefined,
additionalCommands: modes.Command[],
deleteCommand: modes.Command | undefined,
collapsibleState: modes.CommentThreadCollapsibleState) {
this._range = range;
this._label = label;
this._contextValue = contextValue;
this._comments = comments;
this._acceptInputCommand = acceptInputCommand;
this._additionalCommands = additionalCommands;
this._deleteCommand = deleteCommand;
this._collapsibleState = collapsibleState;
}
dispose() {
this._isDisposed = true;
this._onDidChangeAcceptInputCommand.dispose();
this._onDidChangeAdditionalCommands.dispose();
this._onDidChangeCollasibleState.dispose();
this._onDidChangeComments.dispose();
this._onDidChangeInput.dispose();
@@ -284,6 +180,9 @@ export class MainThreadCommentController {
private readonly _threads: Map<number, MainThreadCommentThread> = new Map<number, MainThreadCommentThread>();
public activeCommentThread?: MainThreadCommentThread;
get features(): CommentProviderFeatures {
return this._features;
}
constructor(
private readonly _proxy: ExtHostCommentsShape,
@@ -299,15 +198,16 @@ export class MainThreadCommentController {
this._features = features;
}
createCommentThread(commentThreadHandle: number,
createCommentThread(extensionId: string,
commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange,
): modes.CommentThread2 {
): modes.CommentThread {
let thread = new MainThreadCommentThread(
commentThreadHandle,
this.handle,
'',
extensionId,
threadId,
URI.revive(resource).toString(),
range
@@ -315,17 +215,11 @@ export class MainThreadCommentController {
this._threads.set(commentThreadHandle, thread);
// As we create comment thread from template and then restore from the newly created maint thread comment thread,
// we postpone the update event to avoid duplication.
// This can be actually removed once we are on the new API.
setTimeout(() => {
this._commentService.updateComments(this._uniqueId, {
added: [thread],
removed: [],
changed: [],
draftMode: modes.DraftMode.NotSupported
});
}, 0);
this._commentService.updateComments(this._uniqueId, {
added: [thread],
removed: [],
changed: []
});
return thread;
}
@@ -337,18 +231,14 @@ export class MainThreadCommentController {
label: string,
contextValue: string | undefined,
comments: modes.Comment[],
acceptInputCommand: modes.Command | undefined,
additionalCommands: modes.Command[],
deleteCommand: modes.Command | undefined,
collapsibleState: modes.CommentThreadCollapsibleState): void {
let thread = this.getKnownThread(commentThreadHandle);
thread.batchUpdate(range, label, contextValue, comments, acceptInputCommand, additionalCommands, deleteCommand, collapsibleState);
thread.batchUpdate(range, label, contextValue, comments, collapsibleState);
this._commentService.updateComments(this._uniqueId, {
added: [],
removed: [],
changed: [thread],
draftMode: modes.DraftMode.NotSupported
changed: [thread]
});
}
@@ -359,8 +249,7 @@ export class MainThreadCommentController {
this._commentService.updateComments(this._uniqueId, {
added: [],
removed: [thread],
changed: [],
draftMode: modes.DraftMode.NotSupported
changed: []
});
thread.dispose();
@@ -393,7 +282,7 @@ export class MainThreadCommentController {
}
async getDocumentComments(resource: URI, token: CancellationToken) {
let ret: modes.CommentThread2[] = [];
let ret: modes.CommentThread[] = [];
for (let thread of keys(this._threads)) {
const commentThread = this._threads.get(thread)!;
if (commentThread.resource === resource.toString()) {
@@ -402,26 +291,15 @@ export class MainThreadCommentController {
}
let commentingRanges = await this._proxy.$provideCommentingRanges(this.handle, resource, token);
let staticContribution = await this._proxy.$checkStaticContribution(this.handle);
return <ICommentInfo>{
owner: this._uniqueId,
label: this.label,
threads: ret,
commentingRanges: commentingRanges ? {
commentingRanges: {
resource: resource,
ranges: commentingRanges,
newCommentThreadCallback: staticContribution ? undefined : async (uri: UriComponents, range: IRange) => {
let threadHandle = await this._proxy.$createNewCommentWidgetCallback(this.handle, uri, range, token);
// if (threadHandle !== undefined) { {{SQL CARBON EDIT}} @anthonydresser this never happens but throws error because of strict null checks
// return this.getKnownThread(threadHandle);
// }
return undefined; // {{SQL CARBON EDIT}} @anthonydresser revert back after strict-null-check
}
} : [],
draftMode: modes.DraftMode.NotSupported
ranges: commentingRanges || []
}
};
}
@@ -434,7 +312,7 @@ export class MainThreadCommentController {
return this._features.reactionGroup;
}
async toggleReaction(uri: URI, thread: modes.CommentThread2, comment: modes.Comment, reaction: modes.CommentReaction, token: CancellationToken): Promise<void> {
async toggleReaction(uri: URI, thread: modes.CommentThread, comment: modes.Comment, reaction: modes.CommentReaction, token: CancellationToken): Promise<void> {
return this._proxy.$toggleReaction(this._handle, thread.commentThreadHandle, uri, comment, reaction);
}
@@ -451,6 +329,10 @@ export class MainThreadCommentController {
this._proxy.$createCommentThreadTemplate(this.handle, resource, range);
}
async updateCommentThreadTemplate(threadHandle: number, range: IRange) {
await this._proxy.$updateCommentThreadTemplate(this.handle, threadHandle, range);
}
toJSON(): any {
return {
$mid: 6,
@@ -461,28 +343,38 @@ export class MainThreadCommentController {
@extHostNamedCustomer(MainContext.MainThreadComments)
export class MainThreadComments extends Disposable implements MainThreadCommentsShape {
private _disposables: IDisposable[];
private _activeCommentThreadDisposables: IDisposable[];
private readonly _proxy: ExtHostCommentsShape;
private _documentProviders = new Map<number, IDisposable>();
private _workspaceProviders = new Map<number, IDisposable>();
private _handlers = new Map<number, string>();
private _commentControllers = new Map<number, MainThreadCommentController>();
private _activeCommentThread?: MainThreadCommentThread;
private readonly _activeCommentThreadDisposables = this._register(new DisposableStore());
private _openPanelListener: IDisposable | null;
constructor(
extHostContext: IExtHostContext,
@IEditorService private readonly _editorService: IEditorService,
@ICommentService private readonly _commentService: ICommentService,
@IPanelService private readonly _panelService: IPanelService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IPanelService private readonly _panelService: IPanelService
) {
super();
this._disposables = [];
this._activeCommentThreadDisposables = [];
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments);
this._register(this._commentService.onDidChangeActiveCommentThread(async thread => {
let handle = (thread as MainThreadCommentThread).controllerHandle;
let controller = this._commentControllers.get(handle);
if (!controller) {
return;
}
this._activeCommentThreadDisposables.clear();
this._activeCommentThread = thread as MainThreadCommentThread;
controller.activeCommentThread = this._activeCommentThread;
}));
}
$registerCommentController(handle: number, id: string, label: string): void {
@@ -525,15 +417,16 @@ export class MainThreadComments extends Disposable implements MainThreadComments
commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange
): modes.CommentThread2 | undefined {
range: IRange,
extensionId: ExtensionIdentifier
): modes.CommentThread | undefined {
let provider = this._commentControllers.get(handle);
if (!provider) {
return undefined;
}
return provider.createCommentThread(commentThreadHandle, threadId, resource, range);
return provider.createCommentThread(extensionId.value, commentThreadHandle, threadId, resource, range);
}
$updateCommentThread(handle: number,
@@ -544,9 +437,6 @@ export class MainThreadComments extends Disposable implements MainThreadComments
label: string,
contextValue: string | undefined,
comments: modes.Comment[],
acceptInputCommand: modes.Command | undefined,
additionalCommands: modes.Command[],
deleteCommand: modes.Command,
collapsibleState: modes.CommentThreadCollapsibleState): void {
let provider = this._commentControllers.get(handle);
@@ -554,7 +444,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
return undefined;
}
return provider.updateCommentThread(commentThreadHandle, threadId, resource, range, label, contextValue, comments, acceptInputCommand, additionalCommands, deleteCommand, collapsibleState);
return provider.updateCommentThread(commentThreadHandle, threadId, resource, range, label, contextValue, comments, collapsibleState);
}
$deleteCommentThread(handle: number, commentThreadHandle: number) {
@@ -567,26 +457,6 @@ export class MainThreadComments extends Disposable implements MainThreadComments
return provider.deleteCommentThread(commentThreadHandle);
}
$setInputValue(handle: number, input: string) {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateInput(input);
}
$registerDocumentCommentProvider(handle: number, features: CommentProviderFeatures): void {
this._documentProviders.set(handle, undefined);
const handler = new MainThreadDocumentCommentProvider(this._proxy, handle, features);
const providerId = generateUuid();
this._handlers.set(handle, providerId);
this._commentService.registerDataProvider(providerId, handler);
}
private registerPanel(commentsPanelAlreadyConstructed: boolean) {
if (!commentsPanelAlreadyConstructed) {
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescriptor(
@@ -608,15 +478,6 @@ export class MainThreadComments extends Disposable implements MainThreadComments
if (!commentsPanelAlreadyConstructed && !this._openPanelListener) {
this._openPanelListener = this._panelService.onDidPanelOpen(e => {
if (e.panel.getId() === COMMENTS_PANEL_ID) {
keys(this._workspaceProviders).forEach(handle => {
this._proxy.$provideWorkspaceComments(handle).then(commentThreads => {
if (commentThreads) {
const providerId = this.getHandler(handle);
this._commentService.setWorkspaceComments(providerId, commentThreads);
}
});
});
keys(this._commentControllers).forEach(handle => {
let threads = this._commentControllers.get(handle)!.getAllComments();
@@ -642,119 +503,14 @@ export class MainThreadComments extends Disposable implements MainThreadComments
return this._handlers.get(handle)!;
}
$registerWorkspaceCommentProvider(handle: number, extensionId: ExtensionIdentifier): void {
this._workspaceProviders.set(handle, undefined);
const providerId = generateUuid();
this._handlers.set(handle, providerId);
const commentsPanelAlreadyConstructed = this._panelService.getPanels().some(panel => panel.id === COMMENTS_PANEL_ID);
if (!commentsPanelAlreadyConstructed) {
this.registerPanel(commentsPanelAlreadyConstructed);
}
const openPanel = this._configurationService.getValue<ICommentsConfiguration>('comments').openPanel;
if (openPanel === 'neverOpen') {
this.registerOpenPanelListener(commentsPanelAlreadyConstructed);
}
if (openPanel === 'openOnSessionStart') {
this._panelService.openPanel(COMMENTS_PANEL_ID);
}
this._proxy.$provideWorkspaceComments(handle).then(commentThreads => {
if (commentThreads) {
if (openPanel === 'openOnSessionStartWithComments' && commentThreads.length) {
if (commentThreads.length) {
this._panelService.openPanel(COMMENTS_PANEL_ID);
} else {
this.registerOpenPanelListener(commentsPanelAlreadyConstructed);
}
}
this._commentService.setWorkspaceComments(providerId, commentThreads);
}
});
/* __GDPR__
"comments:registerWorkspaceCommentProvider" : {
"extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._telemetryService.publicLog('comments:registerWorkspaceCommentProvider', {
extensionId: extensionId.value
});
}
$unregisterDocumentCommentProvider(handle: number): void {
this._documentProviders.delete(handle);
const handlerId = this.getHandler(handle);
this._commentService.unregisterDataProvider(handlerId);
this._handlers.delete(handle);
}
$unregisterWorkspaceCommentProvider(handle: number): void {
this._workspaceProviders.delete(handle);
if (this._workspaceProviders.size === 0) {
Registry.as<PanelRegistry>(PanelExtensions.Panels).deregisterPanel(COMMENTS_PANEL_ID);
if (this._openPanelListener) {
this._openPanelListener.dispose();
this._openPanelListener = null;
}
}
const handlerId = this.getHandler(handle);
this._commentService.removeWorkspaceComments(handlerId);
this._handlers.delete(handle);
}
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent) {
// notify comment service
const providerId = this.getHandler(handle);
this._commentService.updateComments(providerId, event);
}
getVisibleEditors(): ICodeEditor[] {
let ret: ICodeEditor[] = [];
this._editorService.visibleControls.forEach(control => {
if (isCodeEditor(control.getControl())) {
ret.push(control.getControl() as ICodeEditor);
}
if (isDiffEditor(control.getControl())) {
let diffEditor = control.getControl() as IDiffEditor;
ret.push(diffEditor.getOriginalEditor(), diffEditor.getModifiedEditor());
}
});
return ret;
}
async provideWorkspaceComments(): Promise<modes.CommentThread[]> {
const result: modes.CommentThread[] = [];
for (const handle of keys(this._workspaceProviders)) {
const result = await this._proxy.$provideWorkspaceComments(handle);
if (Array.isArray(result)) {
result.push(...result);
}
}
return result;
}
async provideDocumentComments(resource: URI): Promise<Array<modes.CommentInfo | null>> {
const result: Array<modes.CommentInfo | null> = [];
for (const handle of keys(this._documentProviders)) {
result.push(await this._proxy.$provideDocumentComments(handle, resource));
}
return result;
}
dispose(): void {
this._disposables = dispose(this._disposables);
this._activeCommentThreadDisposables = dispose(this._activeCommentThreadDisposables);
super.dispose();
this._workspaceProviders.forEach(value => dispose(value));
this._workspaceProviders.clear();
this._documentProviders.forEach(value => dispose(value));

View File

@@ -33,7 +33,7 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
}
private _getConfigurationData(): IConfigurationInitData {
const configurationData: IConfigurationInitData = { ...(this.configurationService.getConfigurationData()!), configurationScopes: {} };
const configurationData: IConfigurationInitData = { ...(this.configurationService.getConfigurationData()!), configurationScopes: [] };
// Send configurations scopes only in development mode.
if (!this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment) {
configurationData.configurationScopes = getScopes();

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { URI as uri } from 'vs/base/common/uri';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, ITerminalSettings, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory } from 'vs/workbench/contrib/debug/common/debug';
import {
@@ -20,7 +20,7 @@ import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/contrib/debug/
export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterFactory {
private readonly _proxy: ExtHostDebugServiceShape;
private _toDispose: IDisposable[];
private readonly _toDispose = new DisposableStore();
private _breakpointEventsActive: boolean;
private readonly _debugAdapters: Map<number, ExtensionHostDebugAdapter>;
private _debugAdaptersHandleCounter = 1;
@@ -33,19 +33,18 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
@IDebugService private readonly debugService: IDebugService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDebugService);
this._toDispose = [];
this._toDispose.push(debugService.onDidNewSession(session => {
this._toDispose.add(debugService.onDidNewSession(session => {
this._proxy.$acceptDebugSessionStarted(this.getSessionDto(session));
}));
// Need to start listening early to new session events because a custom event can come while a session is initialising
this._toDispose.push(debugService.onWillNewSession(session => {
this._toDispose.push(session.onDidCustomEvent(event => this._proxy.$acceptDebugSessionCustomEvent(this.getSessionDto(session), event)));
this._toDispose.add(debugService.onWillNewSession(session => {
this._toDispose.add(session.onDidCustomEvent(event => this._proxy.$acceptDebugSessionCustomEvent(this.getSessionDto(session), event)));
}));
this._toDispose.push(debugService.onDidEndSession(session => {
this._toDispose.add(debugService.onDidEndSession(session => {
this._proxy.$acceptDebugSessionTerminated(this.getSessionDto(session));
this._sessions.delete(session.getId());
}));
this._toDispose.push(debugService.getViewModel().onDidFocusSession(session => {
this._toDispose.add(debugService.getViewModel().onDidFocusSession(session => {
this._proxy.$acceptDebugSessionActiveChanged(this.getSessionDto(session));
}));
@@ -56,7 +55,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
}
public dispose(): void {
this._toDispose = dispose(this._toDispose);
this._toDispose.dispose();
}
// interface IDebugAdapterProvider
@@ -79,7 +78,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
// RPC methods (MainThreadDebugServiceShape)
public $registerDebugTypes(debugTypes: string[]) {
this._toDispose.push(this.debugService.getConfigurationManager().registerDebugAdapterFactory(debugTypes, this));
this._toDispose.add(this.debugService.getConfigurationManager().registerDebugAdapterFactory(debugTypes, this));
}
public $startBreakpointEvents(): void {
@@ -88,7 +87,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
this._breakpointEventsActive = true;
// set up a handler to send more
this._toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(e => {
this._toDispose.add(this.debugService.getModel().onDidChangeBreakpoints(e => {
// Ignore session only breakpoint events since they should only reflect in the UI
if (e && !e.sessionOnly) {
const delta: IBreakpointsDeltaDto = {};
@@ -170,7 +169,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
};
}
this._debugConfigurationProviders.set(handle, provider);
this._toDispose.push(this.debugService.getConfigurationManager().registerDebugConfigurationProvider(provider));
this._toDispose.add(this.debugService.getConfigurationManager().registerDebugConfigurationProvider(provider));
return Promise.resolve(undefined);
}
@@ -192,7 +191,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
}
};
this._debugAdapterDescriptorFactories.set(handle, provider);
this._toDispose.push(this.debugService.getConfigurationManager().registerDebugAdapterDescriptorFactory(provider));
this._toDispose.add(this.debugService.getConfigurationManager().registerDebugAdapterDescriptorFactory(provider));
return Promise.resolve(undefined);
}

View File

@@ -45,7 +45,7 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon
return this._proxy.$provideTextDocumentContent(handle, uri).then(value => {
if (typeof value === 'string') {
const firstLineText = value.substr(0, 1 + value.search(/\r?\n/));
const languageSelection = this._modeService.createByFilepathOrFirstLine(uri.fsPath, firstLineText);
const languageSelection = this._modeService.createByFilepathOrFirstLine(uri, firstLineText);
return this._modelService.createModel(value, languageSelection, uri);
}
return null;

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { IDisposable, IReference, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, IReference, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ITextModel } from 'vs/editor/common/model';
@@ -73,10 +73,10 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
private readonly _untitledEditorService: IUntitledEditorService;
private readonly _environmentService: IWorkbenchEnvironmentService;
private _toDispose: IDisposable[];
private readonly _toDispose = new DisposableStore();
private _modelToDisposeMap: { [modelUrl: string]: IDisposable; };
private readonly _proxy: ExtHostDocumentsShape;
private readonly _modelIsSynced: { [modelId: string]: boolean; };
private readonly _modelIsSynced = new Set<string>();
private _modelReferenceCollection = new BoundModelReferenceCollection();
constructor(
@@ -98,25 +98,23 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
this._environmentService = environmentService;
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocuments);
this._modelIsSynced = {};
this._toDispose = [];
this._toDispose.push(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this)));
this._toDispose.push(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this)));
this._toDispose.push(this._modelReferenceCollection);
this._toDispose.push(modelService.onModelModeChanged(this._onModelModeChanged, this));
this._toDispose.add(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this)));
this._toDispose.add(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this)));
this._toDispose.add(this._modelReferenceCollection);
this._toDispose.add(modelService.onModelModeChanged(this._onModelModeChanged, this));
this._toDispose.push(textFileService.models.onModelSaved(e => {
this._toDispose.add(textFileService.models.onModelSaved(e => {
if (this._shouldHandleFileEvent(e)) {
this._proxy.$acceptModelSaved(e.resource);
}
}));
this._toDispose.push(textFileService.models.onModelReverted(e => {
this._toDispose.add(textFileService.models.onModelReverted(e => {
if (this._shouldHandleFileEvent(e)) {
this._proxy.$acceptDirtyStateChanged(e.resource, false);
}
}));
this._toDispose.push(textFileService.models.onModelDirty(e => {
this._toDispose.add(textFileService.models.onModelDirty(e => {
if (this._shouldHandleFileEvent(e)) {
this._proxy.$acceptDirtyStateChanged(e.resource, true);
}
@@ -130,7 +128,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
this._modelToDisposeMap[modelUrl].dispose();
});
this._modelToDisposeMap = Object.create(null);
this._toDispose = dispose(this._toDispose);
this._toDispose.dispose();
}
private _shouldHandleFileEvent(e: TextFileModelChangeEvent): boolean {
@@ -145,7 +143,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
return;
}
const modelUrl = model.uri;
this._modelIsSynced[modelUrl.toString()] = true;
this._modelIsSynced.add(modelUrl.toString());
this._modelToDisposeMap[modelUrl.toString()] = model.onDidChangeContent((e) => {
this._proxy.$acceptModelChanged(modelUrl, e, this._textFileService.isDirty(modelUrl));
});
@@ -154,7 +152,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
private _onModelModeChanged(event: { model: ITextModel; oldModeId: string; }): void {
let { model, oldModeId } = event;
const modelUrl = model.uri;
if (!this._modelIsSynced[modelUrl.toString()]) {
if (!this._modelIsSynced.has(modelUrl.toString())) {
return;
}
this._proxy.$acceptModelModeChanged(model.uri, oldModeId, model.getLanguageIdentifier().language);
@@ -162,10 +160,10 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
private _onModelRemoved(modelUrl: URI): void {
const strModelUrl = modelUrl.toString();
if (!this._modelIsSynced[strModelUrl]) {
if (!this._modelIsSynced.has(strModelUrl)) {
return;
}
delete this._modelIsSynced[strModelUrl];
this._modelIsSynced.delete(strModelUrl);
this._modelToDisposeMap[strModelUrl].dispose();
delete this._modelToDisposeMap[strModelUrl];
}
@@ -196,7 +194,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
return promise.then(success => {
if (!success) {
return Promise.reject(new Error('cannot open ' + uri.toString()));
} else if (!this._modelIsSynced[uri.toString()]) {
} else if (!this._modelIsSynced.has(uri.toString())) {
return Promise.reject(new Error('cannot open ' + uri.toString() + '. Detail: Files above 50MB cannot be synchronized with extensions.'));
} else {
return undefined;
@@ -237,7 +235,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
}).then(model => {
const resource = model.getResource();
if (!this._modelIsSynced[resource.toString()]) {
if (!this._modelIsSynced.has(resource.toString())) {
throw new Error(`expected URI ${resource.toString()} to have come to LIFE`);
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
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';
@@ -144,7 +144,7 @@ const enum ActiveEditorOrder {
class MainThreadDocumentAndEditorStateComputer {
private _toDispose: IDisposable[] = [];
private readonly _toDispose = new DisposableStore();
private _toDisposeOnEditorRemove = new Map<string, IDisposable>();
private _currentState: DocumentAndEditorState;
private _activeEditorOrder: ActiveEditorOrder = ActiveEditorOrder.Editor;
@@ -172,15 +172,15 @@ class MainThreadDocumentAndEditorStateComputer {
}
dispose(): void {
this._toDispose = dispose(this._toDispose);
this._toDispose.dispose();
}
private _onDidAddEditor(e: ICodeEditor): void {
this._toDisposeOnEditorRemove.set(e.getId(), combinedDisposable([
this._toDisposeOnEditorRemove.set(e.getId(), combinedDisposable(
e.onDidChangeModel(() => this._updateState()),
e.onDidFocusEditorText(() => this._updateState()),
e.onDidFocusEditorWidget(() => this._updateState(e))
]));
));
this._updateState();
}
@@ -304,10 +304,9 @@ class MainThreadDocumentAndEditorStateComputer {
@extHostCustomer
export class MainThreadDocumentsAndEditors {
private _toDispose: IDisposable[];
private readonly _toDispose = new DisposableStore();
private readonly _proxy: ExtHostDocumentsAndEditorsShape;
private readonly _stateComputer: MainThreadDocumentAndEditorStateComputer;
private _textEditors = <{ [id: string]: MainThreadTextEditor }>Object.create(null);
private readonly _textEditors = new Map<string, MainThreadTextEditor>();
private _onTextEditorAdd = new Emitter<MainThreadTextEditor[]>();
private _onTextEditorRemove = new Emitter<string[]>();
@@ -336,28 +335,23 @@ export class MainThreadDocumentsAndEditors {
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentsAndEditors);
const mainThreadDocuments = new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService, environmentService);
const mainThreadDocuments = this._toDispose.add(new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService, environmentService));
extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments);
const mainThreadTextEditors = new MainThreadTextEditors(this, extHostContext, codeEditorService, bulkEditService, this._editorService, this._editorGroupService);
const mainThreadTextEditors = this._toDispose.add(new MainThreadTextEditors(this, extHostContext, codeEditorService, bulkEditService, this._editorService, this._editorGroupService));
extHostContext.set(MainContext.MainThreadTextEditors, mainThreadTextEditors);
// It is expected that the ctor of the state computer calls our `_onDelta`.
this._stateComputer = new MainThreadDocumentAndEditorStateComputer(delta => this._onDelta(delta), _modelService, codeEditorService, this._editorService, panelService);
this._toDispose.add(new MainThreadDocumentAndEditorStateComputer(delta => this._onDelta(delta), _modelService, codeEditorService, this._editorService, panelService));
this._toDispose = [
mainThreadDocuments,
mainThreadTextEditors,
this._stateComputer,
this._onTextEditorAdd,
this._onTextEditorRemove,
this._onDocumentAdd,
this._onDocumentRemove,
];
this._toDispose.add(this._onTextEditorAdd);
this._toDispose.add(this._onTextEditorRemove);
this._toDispose.add(this._onDocumentAdd);
this._toDispose.add(this._onDocumentRemove);
}
dispose(): void {
this._toDispose = dispose(this._toDispose);
this._toDispose.dispose();
}
private _onDelta(delta: DocumentAndEditorStateDelta): void {
@@ -374,16 +368,16 @@ export class MainThreadDocumentsAndEditors {
const mainThreadEditor = new MainThreadTextEditor(apiEditor.id, apiEditor.editor.getModel(),
apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._modelService);
this._textEditors[apiEditor.id] = mainThreadEditor;
this._textEditors.set(apiEditor.id, mainThreadEditor);
addedEditors.push(mainThreadEditor);
}
// removed editors
for (const { id } of delta.removedEditors) {
const mainThreadEditor = this._textEditors[id];
const mainThreadEditor = this._textEditors.get(id);
if (mainThreadEditor) {
mainThreadEditor.dispose();
delete this._textEditors[id];
this._textEditors.delete(id);
removedEditors.push(id);
}
}
@@ -454,16 +448,16 @@ export class MainThreadDocumentsAndEditors {
return undefined;
}
findTextEditorIdFor(editor: IWorkbenchEditor): string | undefined {
for (const id in this._textEditors) {
if (this._textEditors[id].matches(editor)) {
findTextEditorIdFor(inputEditor: IWorkbenchEditor): string | undefined {
for (const [id, editor] of this._textEditors) {
if (editor.matches(inputEditor)) {
return id;
}
}
return undefined;
}
getEditor(id: string): MainThreadTextEditor {
return this._textEditors[id];
getEditor(id: string): MainThreadTextEditor | undefined {
return this._textEditors.get(id);
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { RenderLineNumbersType, TextEditorCursorStyle, cursorStyleToString } from 'vs/editor/common/config/editorOptions';
import { IRange, Range } from 'vs/editor/common/core/range';
@@ -173,10 +173,10 @@ export class MainThreadTextEditor {
private readonly _id: string;
private _model: ITextModel;
private readonly _modelService: IModelService;
private _modelListeners: IDisposable[];
private readonly _modelListeners = new DisposableStore();
private _codeEditor: ICodeEditor | null;
private readonly _focusTracker: IFocusTracker;
private _codeEditorListeners: IDisposable[];
private readonly _codeEditorListeners = new DisposableStore();
private _properties: MainThreadTextEditorProperties;
private readonly _onPropertiesChanged: Emitter<IEditorPropertiesChangeData>;
@@ -193,12 +193,10 @@ export class MainThreadTextEditor {
this._codeEditor = null;
this._focusTracker = focusTracker;
this._modelService = modelService;
this._codeEditorListeners = [];
this._onPropertiesChanged = new Emitter<IEditorPropertiesChangeData>();
this._modelListeners = [];
this._modelListeners.push(this._model.onDidChangeOptions((e) => {
this._modelListeners.add(this._model.onDidChangeOptions((e) => {
this._updatePropertiesNow(null);
}));
@@ -208,9 +206,9 @@ export class MainThreadTextEditor {
public dispose(): void {
this._model = null!;
this._modelListeners = dispose(this._modelListeners);
this._modelListeners.dispose();
this._codeEditor = null;
this._codeEditorListeners = dispose(this._codeEditorListeners);
this._codeEditorListeners.dispose();
}
private _updatePropertiesNow(selectionChangeSource: string | null): void {
@@ -249,36 +247,36 @@ export class MainThreadTextEditor {
// Nothing to do...
return;
}
this._codeEditorListeners = dispose(this._codeEditorListeners);
this._codeEditorListeners.clear();
this._codeEditor = codeEditor;
if (this._codeEditor) {
// Catch early the case that this code editor gets a different model set and disassociate from this model
this._codeEditorListeners.push(this._codeEditor.onDidChangeModel(() => {
this._codeEditorListeners.add(this._codeEditor.onDidChangeModel(() => {
this.setCodeEditor(null);
}));
this._codeEditorListeners.push(this._codeEditor.onDidFocusEditorWidget(() => {
this._codeEditorListeners.add(this._codeEditor.onDidFocusEditorWidget(() => {
this._focusTracker.onGainedFocus();
}));
this._codeEditorListeners.push(this._codeEditor.onDidBlurEditorWidget(() => {
this._codeEditorListeners.add(this._codeEditor.onDidBlurEditorWidget(() => {
this._focusTracker.onLostFocus();
}));
this._codeEditorListeners.push(this._codeEditor.onDidChangeCursorSelection((e) => {
this._codeEditorListeners.add(this._codeEditor.onDidChangeCursorSelection((e) => {
// selection
this._updatePropertiesNow(e.source);
}));
this._codeEditorListeners.push(this._codeEditor.onDidChangeConfiguration(() => {
this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration(() => {
// options
this._updatePropertiesNow(null);
}));
this._codeEditorListeners.push(this._codeEditor.onDidLayoutChange(() => {
this._codeEditorListeners.add(this._codeEditor.onDidLayoutChange(() => {
// visibleRanges
this._updatePropertiesNow(null);
}));
this._codeEditorListeners.push(this._codeEditor.onDidScrollChange(() => {
this._codeEditorListeners.add(this._codeEditor.onDidScrollChange(() => {
// visibleRanges
this._updatePropertiesNow(null);
}));
@@ -469,7 +467,7 @@ export class MainThreadTextEditor {
return true;
}
insertSnippet(template: string, ranges: IRange[], opts: IUndoStopOptions) {
insertSnippet(template: string, ranges: readonly IRange[], opts: IUndoStopOptions) {
if (!this._codeEditor) {
return false;
@@ -486,7 +484,7 @@ export class MainThreadTextEditor {
this._codeEditor.focus();
// make modifications
snippetController.insert(template, 0, 0, opts.undoStopBefore, opts.undoStopAfter);
snippetController.insert(template, { overwriteBefore: 0, overwriteAfter: 0, undoStopBefore: opts.undoStopBefore, undoStopAfter: opts.undoStopAfter });
return true;
}

View File

@@ -5,7 +5,7 @@
import { localize } from 'vs/nls';
import { disposed } from 'vs/base/common/errors';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { equals as objectEquals } from 'vs/base/common/objects';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
@@ -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 } from 'vs/platform/editor/common/editor';
import { IEditorOptions, ITextEditorOptions, IResourceInput } 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';
@@ -32,7 +32,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
private readonly _instanceId: string;
private readonly _proxy: ExtHostEditorsShape;
private readonly _documentsAndEditors: MainThreadDocumentsAndEditors;
private _toDispose: IDisposable[];
private readonly _toDispose = new DisposableStore();
private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; };
private _editorPositionData: ITextEditorPositionData | null;
private _registeredDecorationTypes: { [decorationType: string]: boolean; };
@@ -48,16 +48,16 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
this._instanceId = String(++MainThreadTextEditors.INSTANCE_COUNT);
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditors);
this._documentsAndEditors = documentsAndEditors;
this._toDispose = [];
this._textEditorsListenersMap = Object.create(null);
this._editorPositionData = null;
this._toDispose.push(documentsAndEditors.onTextEditorAdd(editors => editors.forEach(this._onTextEditorAdd, this)));
this._toDispose.push(documentsAndEditors.onTextEditorRemove(editors => editors.forEach(this._onTextEditorRemove, this)));
this._toDispose.add(documentsAndEditors.onTextEditorAdd(editors => editors.forEach(this._onTextEditorAdd, this)));
this._toDispose.add(documentsAndEditors.onTextEditorRemove(editors => editors.forEach(this._onTextEditorRemove, this)));
this._toDispose.push(this._editorService.onDidVisibleEditorsChange(() => this._updateActiveAndVisibleTextEditors()));
this._toDispose.push(this._editorGroupService.onDidRemoveGroup(() => this._updateActiveAndVisibleTextEditors()));
this._toDispose.push(this._editorGroupService.onDidMoveGroup(() => this._updateActiveAndVisibleTextEditors()));
this._toDispose.add(this._editorService.onDidVisibleEditorsChange(() => this._updateActiveAndVisibleTextEditors()));
this._toDispose.add(this._editorGroupService.onDidRemoveGroup(() => this._updateActiveAndVisibleTextEditors()));
this._toDispose.add(this._editorGroupService.onDidMoveGroup(() => this._updateActiveAndVisibleTextEditors()));
this._registeredDecorationTypes = Object.create(null);
}
@@ -67,7 +67,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
dispose(this._textEditorsListenersMap[editorId]);
});
this._textEditorsListenersMap = Object.create(null);
this._toDispose = dispose(this._toDispose);
this._toDispose.dispose();
for (let decorationType in this._registeredDecorationTypes) {
this._codeEditorService.removeDecorationType(decorationType);
}
@@ -112,7 +112,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
// --- from extension host process
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string | undefined> {
async $tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string | undefined> {
const uri = URI.revive(resource);
const editorOptions: ITextEditorOptions = {
@@ -121,91 +121,95 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
selection: options.selection
};
const input = {
const input: IResourceInput = {
resource: uri,
options: editorOptions
};
return this._editorService.openEditor(input, viewColumnToEditorGroup(this._editorGroupService, options.position)).then(editor => {
if (!editor) {
return undefined;
}
return this._documentsAndEditors.findTextEditorIdFor(editor);
});
const editor = await this._editorService.openEditor(input, viewColumnToEditorGroup(this._editorGroupService, options.position));
if (!editor) {
return undefined;
}
return this._documentsAndEditors.findTextEditorIdFor(editor);
}
$tryShowEditor(id: string, position?: EditorViewColumn): Promise<void> {
async $tryShowEditor(id: string, position?: EditorViewColumn): Promise<void> {
const mainThreadEditor = this._documentsAndEditors.getEditor(id);
if (mainThreadEditor) {
const model = mainThreadEditor.getModel();
return this._editorService.openEditor({
await this._editorService.openEditor({
resource: model.uri,
options: { preserveFocus: false }
}, viewColumnToEditorGroup(this._editorGroupService, position)).then(() => { return; });
}, viewColumnToEditorGroup(this._editorGroupService, position));
return;
}
return Promise.resolve();
}
$tryHideEditor(id: string): Promise<void> {
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).then(() => { return; });
return editor.group.closeEditor(editor.input);
}
}
}
return Promise.resolve();
}
$trySetSelections(id: string, selections: ISelection[]): Promise<void> {
if (!this._documentsAndEditors.getEditor(id)) {
const editor = this._documentsAndEditors.getEditor(id);
if (!editor) {
return Promise.reject(disposed(`TextEditor(${id})`));
}
this._documentsAndEditors.getEditor(id).setSelections(selections);
editor.setSelections(selections);
return Promise.resolve(undefined);
}
$trySetDecorations(id: string, key: string, ranges: IDecorationOptions[]): Promise<void> {
key = `${this._instanceId}-${key}`;
if (!this._documentsAndEditors.getEditor(id)) {
const editor = this._documentsAndEditors.getEditor(id);
if (!editor) {
return Promise.reject(disposed(`TextEditor(${id})`));
}
this._documentsAndEditors.getEditor(id).setDecorations(key, ranges);
editor.setDecorations(key, ranges);
return Promise.resolve(undefined);
}
$trySetDecorationsFast(id: string, key: string, ranges: number[]): Promise<void> {
key = `${this._instanceId}-${key}`;
if (!this._documentsAndEditors.getEditor(id)) {
const editor = this._documentsAndEditors.getEditor(id);
if (!editor) {
return Promise.reject(disposed(`TextEditor(${id})`));
}
this._documentsAndEditors.getEditor(id).setDecorationsFast(key, ranges);
editor.setDecorationsFast(key, ranges);
return Promise.resolve(undefined);
}
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): Promise<void> {
if (!this._documentsAndEditors.getEditor(id)) {
const editor = this._documentsAndEditors.getEditor(id);
if (!editor) {
return Promise.reject(disposed(`TextEditor(${id})`));
}
this._documentsAndEditors.getEditor(id).revealRange(range, revealType);
editor.revealRange(range, revealType);
return Promise.resolve();
}
$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): Promise<void> {
if (!this._documentsAndEditors.getEditor(id)) {
const editor = this._documentsAndEditors.getEditor(id);
if (!editor) {
return Promise.reject(disposed(`TextEditor(${id})`));
}
this._documentsAndEditors.getEditor(id).setConfiguration(options);
editor.setConfiguration(options);
return Promise.resolve(undefined);
}
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): Promise<boolean> {
if (!this._documentsAndEditors.getEditor(id)) {
const editor = this._documentsAndEditors.getEditor(id);
if (!editor) {
return Promise.reject(disposed(`TextEditor(${id})`));
}
return Promise.resolve(this._documentsAndEditors.getEditor(id).applyEdits(modelVersionId, edits, opts));
return Promise.resolve(editor.applyEdits(modelVersionId, edits, opts));
}
$tryApplyWorkspaceEdit(dto: WorkspaceEditDto): Promise<boolean> {
@@ -213,11 +217,12 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
return this._bulkEditService.apply({ edits }, undefined).then(() => true, err => false);
}
$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): Promise<boolean> {
if (!this._documentsAndEditors.getEditor(id)) {
$tryInsertSnippet(id: string, template: string, ranges: readonly IRange[], opts: IUndoStopOptions): Promise<boolean> {
const editor = this._documentsAndEditors.getEditor(id);
if (!editor) {
return Promise.reject(disposed(`TextEditor(${id})`));
}
return Promise.resolve(this._documentsAndEditors.getEditor(id).insertSnippet(template, ranges, opts));
return Promise.resolve(editor.insertSnippet(template, ranges, opts));
}
$registerTextEditorDecorationType(key: string, options: IDecorationRenderOptions): void {

View File

@@ -5,11 +5,10 @@
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions } from 'vs/platform/files/common/files';
import { URI, UriComponents } from 'vs/base/common/uri';
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions, IFileStat, FileOperationError, FileOperationResult, FileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../common/extHost.protocol';
import { ResourceLabelFormatter, ILabelService } from 'vs/platform/label/common/label';
import { VSBuffer } from 'vs/base/common/buffer';
@extHostNamedCustomer(MainContext.MainThreadFileSystem)
@@ -17,12 +16,10 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
private readonly _proxy: ExtHostFileSystemShape;
private readonly _fileProvider = new Map<number, RemoteFileSystemProvider>();
private readonly _resourceLabelFormatters = new Map<number, IDisposable>();
constructor(
extHostContext: IExtHostContext,
@IFileService private readonly _fileService: IFileService,
@ILabelService private readonly _labelService: ILabelService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystem);
}
@@ -41,18 +38,6 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
this._fileProvider.delete(handle);
}
$registerResourceLabelFormatter(handle: number, formatter: ResourceLabelFormatter): void {
// Dynamicily registered formatters should have priority over those contributed via package.json
formatter.priority = true;
const disposable = this._labelService.registerFormatter(formatter);
this._resourceLabelFormatters.set(handle, disposable);
}
$unregisterResourceLabelFormatter(handle: number): void {
dispose(this._resourceLabelFormatters.get(handle));
this._resourceLabelFormatters.delete(handle);
}
$onFileSystemChange(handle: number, changes: IFileChangeDto[]): void {
const fileProvider = this._fileProvider.get(handle);
if (!fileProvider) {
@@ -60,6 +45,80 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
}
fileProvider.$onFileSystemChange(changes);
}
// --- consumer fs, vscode.workspace.fs
$stat(uri: UriComponents): Promise<IStat> {
return this._fileService.resolve(URI.revive(uri), { resolveMetadata: true }).then(stat => {
return {
ctime: 0,
mtime: stat.mtime,
size: stat.size,
type: MainThreadFileSystem._getFileType(stat)
};
}).catch(MainThreadFileSystem._handleError);
}
$readdir(uri: UriComponents): Promise<[string, FileType][]> {
return this._fileService.resolve(URI.revive(uri), { resolveMetadata: false }).then(stat => {
if (!stat.isDirectory) {
const err = new Error(stat.name);
err.name = FileSystemProviderErrorCode.FileNotADirectory;
throw err;
}
return !stat.children ? [] : stat.children.map(child => [child.name, MainThreadFileSystem._getFileType(child)]);
}).catch(MainThreadFileSystem._handleError);
}
private static _getFileType(stat: IFileStat): FileType {
return (stat.isDirectory ? FileType.Directory : FileType.File) + (stat.isSymbolicLink ? FileType.SymbolicLink : 0);
}
$readFile(uri: UriComponents): Promise<VSBuffer> {
return this._fileService.readFile(URI.revive(uri)).then(file => file.value).catch(MainThreadFileSystem._handleError);
}
$writeFile(uri: UriComponents, content: VSBuffer): Promise<void> {
return this._fileService.writeFile(URI.revive(uri), content).catch(MainThreadFileSystem._handleError);
}
$rename(source: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void> {
return this._fileService.move(URI.revive(source), URI.revive(target), opts.overwrite).catch(MainThreadFileSystem._handleError);
}
$copy(source: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void> {
return this._fileService.copy(URI.revive(source), URI.revive(target), opts.overwrite).catch(MainThreadFileSystem._handleError);
}
$mkdir(uri: UriComponents): Promise<void> {
return this._fileService.createFolder(URI.revive(uri)).catch(MainThreadFileSystem._handleError);
}
$delete(uri: UriComponents, opts: FileDeleteOptions): Promise<void> {
return this._fileService.del(URI.revive(uri), opts).catch(MainThreadFileSystem._handleError);
}
private static _handleError(err: any): never {
if (err instanceof FileOperationError) {
switch (err.fileOperationResult) {
case FileOperationResult.FILE_NOT_FOUND:
err.name = FileSystemProviderErrorCode.FileNotFound;
break;
case FileOperationResult.FILE_IS_DIRECTORY:
err.name = FileSystemProviderErrorCode.FileIsADirectory;
break;
case FileOperationResult.FILE_PERMISSION_DENIED:
err.name = FileSystemProviderErrorCode.NoPermissions;
break;
case FileOperationResult.FILE_MOVE_CONFLICT:
err.name = FileSystemProviderErrorCode.FileExists;
break;
}
}
throw err;
}
}
class RemoteFileSystemProvider implements IFileSystemProvider {

View File

@@ -1,25 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtHostContext, IExtHostContext } from '../common/extHost.protocol';
import { Disposable } from 'vs/base/common/lifecycle';
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { IHeapService } from 'vs/workbench/services/heap/common/heap';
@extHostCustomer
export class MainThreadHeapService extends Disposable {
constructor(
extHostContext: IExtHostContext,
@IHeapService heapService: IHeapService,
) {
super();
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostHeapService);
this._register(heapService.onGarbageCollection((ids) => {
// send to ext host
proxy.$onGarbageCollection(ids);
}));
}
}

View File

@@ -5,27 +5,19 @@
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { MainContext, MainThreadKeytarShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
interface IKeytarModule {
getPassword(service: string, account: string): Promise<string | null>;
setPassword(service: string, account: string, password: string): Promise<void>;
deletePassword(service: string, account: string): Promise<boolean>;
findPassword(service: string): Promise<string | null>;
}
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { optional } from 'vs/platform/instantiation/common/instantiation';
@extHostNamedCustomer(MainContext.MainThreadKeytar)
export class MainThreadKeytar implements MainThreadKeytarShape {
private _keytar: IKeytarModule | null;
private readonly _credentialsService?: ICredentialsService;
constructor(
extHostContext: IExtHostContext
_extHostContext: IExtHostContext,
@optional(ICredentialsService) credentialsService: ICredentialsService,
) {
try {
this._keytar = <IKeytarModule>require.__$__nodeRequire('keytar');
} catch (e) {
this._keytar = null;
}
this._credentialsService = credentialsService;
}
dispose(): void {
@@ -33,28 +25,28 @@ export class MainThreadKeytar implements MainThreadKeytarShape {
}
async $getPassword(service: string, account: string): Promise<string | null> {
if (this._keytar) {
return this._keytar.getPassword(service, account);
if (this._credentialsService) {
return this._credentialsService.getPassword(service, account);
}
return null;
}
async $setPassword(service: string, account: string, password: string): Promise<void> {
if (this._keytar) {
return this._keytar.setPassword(service, account, password);
if (this._credentialsService) {
return this._credentialsService.setPassword(service, account, password);
}
}
async $deletePassword(service: string, account: string): Promise<boolean> {
if (this._keytar) {
return this._keytar.deletePassword(service, account);
if (this._credentialsService) {
return this._credentialsService.deletePassword(service, account);
}
return false;
}
async $findPassword(service: string): Promise<string | null> {
if (this._keytar) {
return this._keytar.findPassword(service);
if (this._credentialsService) {
return this._credentialsService.findPassword(service);
}
return null;
}

View File

@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainContext, MainThreadLabelServiceShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ResourceLabelFormatter, ILabelService } from 'vs/platform/label/common/label';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
@extHostNamedCustomer(MainContext.MainThreadLabelService)
export class MainThreadLabelService implements MainThreadLabelServiceShape {
private readonly _resourceLabelFormatters = new Map<number, IDisposable>();
constructor(
_: IExtHostContext,
@ILabelService private readonly _labelService: ILabelService
) { }
$registerResourceLabelFormatter(handle: number, formatter: ResourceLabelFormatter): void {
// Dynamicily registered formatters should have priority over those contributed via package.json
formatter.priority = true;
const disposable = this._labelService.registerFormatter(formatter);
this._resourceLabelFormatters.set(handle, disposable);
}
$unregisterResourceLabelFormatter(handle: number): void {
dispose(this._resourceLabelFormatters.get(handle));
this._resourceLabelFormatters.delete(handle);
}
dispose(): void {
// noop
}
}

View File

@@ -11,48 +11,44 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto, LinkDto, CallHierarchyDto, SuggestDataDto } from '../common/extHost.protocol';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CallHierarchyDto, SuggestDataDto, CodeActionDto } from '../common/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { URI } from 'vs/base/common/uri';
import { Selection } from 'vs/editor/common/core/selection';
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { IHeapService } from 'vs/workbench/services/heap/common/heap';
import { mixin } from 'vs/base/common/objects';
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape {
private readonly _proxy: ExtHostLanguageFeaturesShape;
private readonly _heapService: IHeapService;
private readonly _modeService: IModeService;
private readonly _registrations: { [handle: number]: IDisposable; } = Object.create(null);
private readonly _registrations = new Map<number, IDisposable>();
constructor(
extHostContext: IExtHostContext,
@IHeapService heapService: IHeapService,
@IModeService modeService: IModeService,
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostLanguageFeatures);
this._heapService = heapService;
this._modeService = modeService;
}
dispose(): void {
for (const key in this._registrations) {
this._registrations[key].dispose();
for (const registration of this._registrations.values()) {
registration.dispose();
}
this._registrations.clear();
}
$unregister(handle: number): void {
const registration = this._registrations[handle];
const registration = this._registrations.get(handle);
if (registration) {
registration.dispose();
delete this._registrations[handle];
this._registrations.delete(handle);
}
}
@@ -101,7 +97,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
private static _reviveCodeActionDto(data: CodeActionDto[] | undefined): modes.CodeAction[] {
private static _reviveCodeActionDto(data: ReadonlyArray<CodeActionDto>): modes.CodeAction[] {
if (data) {
data.forEach(code => reviveWorkspaceEditDto(code.edit));
}
@@ -127,12 +123,12 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- outline
$registerDocumentSymbolProvider(handle: number, selector: ISerializedDocumentFilter[], displayName: string): void {
this._registrations[handle] = modes.DocumentSymbolProviderRegistry.register(selector, <modes.DocumentSymbolProvider>{
this._registrations.set(handle, modes.DocumentSymbolProviderRegistry.register(selector, <modes.DocumentSymbolProvider>{
displayName,
provideDocumentSymbols: (model: ITextModel, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined> => {
return this._proxy.$provideDocumentSymbols(handle, model.uri, token);
}
});
}));
}
// --- code lens
@@ -140,190 +136,161 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void {
const provider = <modes.CodeLensProvider>{
provideCodeLenses: (model: ITextModel, token: CancellationToken): modes.ICodeLensSymbol[] | Promise<modes.ICodeLensSymbol[]> => {
return this._proxy.$provideCodeLenses(handle, model.uri, token).then(dto => {
if (dto) {
dto.forEach(obj => {
this._heapService.trackObject(obj);
this._heapService.trackObject(obj.command);
});
provideCodeLenses: (model: ITextModel, token: CancellationToken): Promise<modes.CodeLensList | undefined> => {
return this._proxy.$provideCodeLenses(handle, model.uri, token).then(listDto => {
if (!listDto) {
return undefined;
}
return dto;
return {
lenses: listDto.lenses,
dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId)
};
});
},
resolveCodeLens: (_model: ITextModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> => {
return this._proxy.$resolveCodeLens(handle, codeLens, token).then(obj => {
if (obj) {
this._heapService.trackObject(obj);
this._heapService.trackObject(obj.command);
}
return obj;
});
resolveCodeLens: (_model: ITextModel, codeLens: modes.CodeLens, token: CancellationToken): Promise<modes.CodeLens | undefined> => {
return this._proxy.$resolveCodeLens(handle, codeLens, token);
}
};
if (typeof eventHandle === 'number') {
const emitter = new Emitter<modes.CodeLensProvider>();
this._registrations[eventHandle] = emitter;
this._registrations.set(eventHandle, emitter);
provider.onDidChange = emitter.event;
}
this._registrations[handle] = modes.CodeLensProviderRegistry.register(selector, provider);
this._registrations.set(handle, modes.CodeLensProviderRegistry.register(selector, provider));
}
$emitCodeLensEvent(eventHandle: number, event?: any): void {
const obj = this._registrations[eventHandle];
const obj = this._registrations.get(eventHandle);
if (obj instanceof Emitter) {
obj.fire(event);
}
}
// -- code inset
$registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void {
const provider = <codeInset.CodeInsetProvider>{
provideCodeInsets: (model: ITextModel, token: CancellationToken): CodeInsetDto[] | Thenable<CodeInsetDto[]> => {
return this._proxy.$provideCodeInsets(handle, model.uri, token).then(dto => {
if (dto) { dto.forEach(obj => this._heapService.trackObject(obj)); }
return dto;
});
},
resolveCodeInset: (model: ITextModel, codeInset: CodeInsetDto, token: CancellationToken): CodeInsetDto | Thenable<CodeInsetDto> => {
return this._proxy.$resolveCodeInset(handle, model.uri, codeInset, token).then(obj => {
this._heapService.trackObject(obj);
return obj;
});
}
};
if (typeof eventHandle === 'number') {
const emitter = new Emitter<codeInset.CodeInsetProvider>();
this._registrations[eventHandle] = emitter;
provider.onDidChange = emitter.event;
}
const langSelector = selector;
this._registrations[handle] = codeInset.CodeInsetProviderRegistry.register(langSelector, provider);
}
// --- declaration
$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.DefinitionProviderRegistry.register(selector, <modes.DefinitionProvider>{
this._registrations.set(handle, modes.DefinitionProviderRegistry.register(selector, <modes.DefinitionProvider>{
provideDefinition: (model, position, token): Promise<modes.LocationLink[]> => {
return this._proxy.$provideDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto);
}
});
}));
}
$registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.DeclarationProviderRegistry.register(selector, <modes.DeclarationProvider>{
this._registrations.set(handle, modes.DeclarationProviderRegistry.register(selector, <modes.DeclarationProvider>{
provideDeclaration: (model, position, token) => {
return this._proxy.$provideDeclaration(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto);
}
});
}));
}
$registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.ImplementationProviderRegistry.register(selector, <modes.ImplementationProvider>{
this._registrations.set(handle, modes.ImplementationProviderRegistry.register(selector, <modes.ImplementationProvider>{
provideImplementation: (model, position, token): Promise<modes.LocationLink[]> => {
return this._proxy.$provideImplementation(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto);
}
});
}));
}
$registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.TypeDefinitionProviderRegistry.register(selector, <modes.TypeDefinitionProvider>{
this._registrations.set(handle, modes.TypeDefinitionProviderRegistry.register(selector, <modes.TypeDefinitionProvider>{
provideTypeDefinition: (model, position, token): Promise<modes.LocationLink[]> => {
return this._proxy.$provideTypeDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto);
}
});
}));
}
// --- extra info
$registerHoverProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.HoverProviderRegistry.register(selector, <modes.HoverProvider>{
this._registrations.set(handle, modes.HoverProviderRegistry.register(selector, <modes.HoverProvider>{
provideHover: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.Hover | undefined> => {
return this._proxy.$provideHover(handle, model.uri, position, token);
}
});
}));
}
// --- occurrences
$registerDocumentHighlightProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.DocumentHighlightProviderRegistry.register(selector, <modes.DocumentHighlightProvider>{
this._registrations.set(handle, modes.DocumentHighlightProviderRegistry.register(selector, <modes.DocumentHighlightProvider>{
provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.DocumentHighlight[] | undefined> => {
return this._proxy.$provideDocumentHighlights(handle, model.uri, position, token);
}
});
}));
}
// --- references
$registerReferenceSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.ReferenceProviderRegistry.register(selector, <modes.ReferenceProvider>{
this._registrations.set(handle, modes.ReferenceProviderRegistry.register(selector, <modes.ReferenceProvider>{
provideReferences: (model: ITextModel, position: EditorPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<modes.Location[]> => {
return this._proxy.$provideReferences(handle, model.uri, position, context, token).then(MainThreadLanguageFeatures._reviveLocationDto);
}
});
}));
}
// --- quick fix
$registerQuickFixSupport(handle: number, selector: ISerializedDocumentFilter[], providedCodeActionKinds?: string[]): void {
this._registrations[handle] = modes.CodeActionProviderRegistry.register(selector, <modes.CodeActionProvider>{
provideCodeActions: (model: ITextModel, rangeOrSelection: EditorRange | Selection, context: modes.CodeActionContext, token: CancellationToken): Promise<modes.CodeAction[]> => {
return this._proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, context, token).then(dto => {
if (dto) {
dto.forEach(obj => { this._heapService.trackObject(obj.command); });
this._registrations.set(handle, modes.CodeActionProviderRegistry.register(selector, <modes.CodeActionProvider>{
provideCodeActions: async (model: ITextModel, rangeOrSelection: EditorRange | Selection, context: modes.CodeActionContext, token: CancellationToken): Promise<modes.CodeActionList | undefined> => {
const listDto = await this._proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, context, token);
if (!listDto) {
return undefined;
}
return <modes.CodeActionList>{
actions: MainThreadLanguageFeatures._reviveCodeActionDto(listDto.actions),
dispose: () => {
if (typeof listDto.cacheId === 'number') {
this._proxy.$releaseCodeActions(handle, listDto.cacheId);
}
}
return MainThreadLanguageFeatures._reviveCodeActionDto(dto);
});
};
},
providedCodeActionKinds
});
}));
}
// --- formatting
$registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier, displayName: string): void {
this._registrations[handle] = modes.DocumentFormattingEditProviderRegistry.register(selector, <modes.DocumentFormattingEditProvider>{
this._registrations.set(handle, modes.DocumentFormattingEditProviderRegistry.register(selector, <modes.DocumentFormattingEditProvider>{
extensionId,
displayName,
provideDocumentFormattingEdits: (model: ITextModel, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined> => {
return this._proxy.$provideDocumentFormattingEdits(handle, model.uri, options, token);
}
});
}));
}
$registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier, displayName: string): void {
this._registrations[handle] = modes.DocumentRangeFormattingEditProviderRegistry.register(selector, <modes.DocumentRangeFormattingEditProvider>{
this._registrations.set(handle, modes.DocumentRangeFormattingEditProviderRegistry.register(selector, <modes.DocumentRangeFormattingEditProvider>{
extensionId,
displayName,
provideDocumentRangeFormattingEdits: (model: ITextModel, range: EditorRange, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined> => {
return this._proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options, token);
}
});
}));
}
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void {
this._registrations[handle] = modes.OnTypeFormattingEditProviderRegistry.register(selector, <modes.OnTypeFormattingEditProvider>{
this._registrations.set(handle, modes.OnTypeFormattingEditProviderRegistry.register(selector, <modes.OnTypeFormattingEditProvider>{
extensionId,
autoFormatTriggerCharacters,
provideOnTypeFormattingEdits: (model: ITextModel, position: EditorPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined> => {
return this._proxy.$provideOnTypeFormattingEdits(handle, model.uri, position, ch, options, token);
}
});
}));
}
// --- navigate type
$registerNavigateTypeSupport(handle: number): void {
let lastResultId: number | undefined;
this._registrations[handle] = search.WorkspaceSymbolProviderRegistry.register(<search.IWorkspaceSymbolProvider>{
this._registrations.set(handle, search.WorkspaceSymbolProviderRegistry.register(<search.IWorkspaceSymbolProvider>{
provideWorkspaceSymbols: (search: string, token: CancellationToken): Promise<search.IWorkspaceSymbol[]> => {
return this._proxy.$provideWorkspaceSymbols(handle, search, token).then(result => {
if (lastResultId !== undefined) {
@@ -341,21 +308,20 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return undefined;
});
}
});
}));
}
// --- rename
$registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportResolveLocation: boolean): void {
this._registrations[handle] = modes.RenameProviderRegistry.register(selector, <modes.RenameProvider>{
this._registrations.set(handle, modes.RenameProviderRegistry.register(selector, <modes.RenameProvider>{
provideRenameEdits: (model: ITextModel, position: EditorPosition, newName: string, token: CancellationToken): Promise<modes.WorkspaceEdit> => {
return this._proxy.$provideRenameEdits(handle, model.uri, position, newName, token).then(reviveWorkspaceEditDto);
},
resolveRenameLocation: supportResolveLocation
? (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined> => this._proxy.$resolveRenameLocation(handle, model.uri, position, token)
: undefined
});
}));
}
// --- suggest
@@ -380,9 +346,10 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
};
}
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean): void {
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean, extensionId: ExtensionIdentifier): void {
const provider: modes.CompletionItemProvider = {
triggerCharacters,
_debugDisplayName: extensionId.value,
provideCompletionItems: (model: ITextModel, position: EditorPosition, context: modes.CompletionContext, token: CancellationToken): Promise<modes.CompletionList | undefined> => {
return this._proxy.$provideCompletionItems(handle, model.uri, position, context, token).then(result => {
if (!result) {
@@ -408,21 +375,30 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
});
};
}
this._registrations[handle] = modes.CompletionProviderRegistry.register(selector, provider);
this._registrations.set(handle, modes.CompletionProviderRegistry.register(selector, provider));
}
// --- parameter hints
$registerSignatureHelpProvider(handle: number, selector: ISerializedDocumentFilter[], metadata: ISerializedSignatureHelpProviderMetadata): void {
this._registrations[handle] = modes.SignatureHelpProviderRegistry.register(selector, <modes.SignatureHelpProvider>{
this._registrations.set(handle, modes.SignatureHelpProviderRegistry.register(selector, <modes.SignatureHelpProvider>{
signatureHelpTriggerCharacters: metadata.triggerCharacters,
signatureHelpRetriggerCharacters: metadata.retriggerCharacters,
provideSignatureHelp: (model: ITextModel, position: EditorPosition, token: CancellationToken, context: modes.SignatureHelpContext): Promise<modes.SignatureHelp | undefined> => {
return this._proxy.$provideSignatureHelp(handle, model.uri, position, context, token);
provideSignatureHelp: async (model: ITextModel, position: EditorPosition, token: CancellationToken, context: modes.SignatureHelpContext): Promise<modes.SignatureHelpResult | undefined> => {
const result = await this._proxy.$provideSignatureHelp(handle, model.uri, position, context, token);
if (!result) {
return undefined;
}
return {
value: result,
dispose: () => {
this._proxy.$releaseSignatureHelp(handle, result.id);
}
};
}
});
}));
}
// --- links
@@ -456,14 +432,14 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
});
};
}
this._registrations[handle] = modes.LinkProviderRegistry.register(selector, provider);
this._registrations.set(handle, modes.LinkProviderRegistry.register(selector, provider));
}
// --- colors
$registerDocumentColorProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
const proxy = this._proxy;
this._registrations[handle] = modes.ColorProviderRegistry.register(selector, <modes.DocumentColorProvider>{
this._registrations.set(handle, modes.ColorProviderRegistry.register(selector, <modes.DocumentColorProvider>{
provideDocumentColors: (model, token) => {
return proxy.$provideDocumentColors(handle, model.uri, token)
.then(documentColors => {
@@ -490,34 +466,34 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
range: colorInfo.range
}, token);
}
});
}));
}
// --- folding
$registerFoldingRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
const proxy = this._proxy;
this._registrations[handle] = modes.FoldingRangeProviderRegistry.register(selector, <modes.FoldingRangeProvider>{
this._registrations.set(handle, modes.FoldingRangeProviderRegistry.register(selector, <modes.FoldingRangeProvider>{
provideFoldingRanges: (model, context, token) => {
return proxy.$provideFoldingRanges(handle, model.uri, context, token);
}
});
}));
}
// -- smart select
$registerSelectionRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.SelectionRangeRegistry.register(selector, {
this._registrations.set(handle, modes.SelectionRangeRegistry.register(selector, {
provideSelectionRanges: (model, positions, token) => {
return this._proxy.$provideSelectionRanges(handle, model.uri, positions, token);
}
});
}));
}
// --- call hierarchy
$registerCallHierarchyProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = callh.CallHierarchyProviderRegistry.register(selector, {
this._registrations.set(handle, callh.CallHierarchyProviderRegistry.register(selector, {
provideCallHierarchyItem: (document, position, token) => {
return this._proxy.$provideCallHierarchyItem(handle, document.uri, position, token).then(MainThreadLanguageFeatures._reviveCallHierarchyItemDto);
},
@@ -535,7 +511,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return data as [callh.CallHierarchyItem, modes.Location[]][];
});
}
});
}));
}
// --- configuration
@@ -596,7 +572,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
const languageIdentifier = this._modeService.getLanguageIdentifier(languageId);
if (languageIdentifier) {
this._registrations[handle] = LanguageConfigurationRegistry.register(languageIdentifier, configuration);
this._registrations.set(handle, LanguageConfigurationRegistry.register(languageIdentifier, configuration));
}
}

View File

@@ -90,7 +90,8 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
// if promise has not been resolved yet, now is the time to ensure a return value
// otherwise if already resolved it means the user clicked one of the buttons
Event.once(messageHandle.onDidClose)(() => {
dispose(...primaryActions, ...secondaryActions);
dispose(primaryActions);
dispose(secondaryActions);
resolve(undefined);
});
});

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IProgress, IProgressService2, IProgressStep, ProgressLocation, IProgressOptions, IProgressNotificationOptions } from 'vs/platform/progress/common/progress';
import { IProgress, IProgressService, IProgressStep, ProgressLocation, IProgressOptions, IProgressNotificationOptions } from 'vs/platform/progress/common/progress';
import { MainThreadProgressShape, MainContext, IExtHostContext, ExtHostProgressShape, ExtHostContext } from '../common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { Action } from 'vs/base/common/actions';
@@ -22,13 +22,13 @@ class ManageExtensionAction extends Action {
@extHostNamedCustomer(MainContext.MainThreadProgress)
export class MainThreadProgress implements MainThreadProgressShape {
private readonly _progressService: IProgressService2;
private readonly _progressService: IProgressService;
private _progress = new Map<number, { resolve: () => void, progress: IProgress<IProgressStep> }>();
private readonly _proxy: ExtHostProgressShape;
constructor(
extHostContext: IExtHostContext,
@IProgressService2 progressService: IProgressService2,
@IProgressService progressService: IProgressService,
@ICommandService private readonly _commandService: ICommandService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostProgress);

View File

@@ -174,13 +174,13 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
params[param].forEach((item: TransferQuickPickItems) => {
handlesToItems.set(item.handle, item);
});
input[param] = params[param];
(input as any)[param] = params[param];
} else if (param === 'activeItems' || param === 'selectedItems') {
input[param] = params[param]
(input as any)[param] = params[param]
.filter((handle: number) => handlesToItems.has(handle))
.map((handle: number) => handlesToItems.get(handle));
} else if (param === 'buttons') {
input[param] = params.buttons!.map(button => {
(input as any)[param] = params.buttons!.map(button => {
if (button.handle === -1) {
return this._quickInputService.backButton;
}
@@ -195,7 +195,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
};
});
} else {
input[param] = params[param];
(input as any)[param] = params[param];
}
}
return Promise.resolve(undefined);

View File

@@ -6,7 +6,7 @@
import { URI, UriComponents } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { assign } from 'vs/base/common/objects';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation } from 'vs/workbench/contrib/scm/common/scm';
import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../common/extHost.protocol';
import { Command } from 'vs/editor/common/modes';
@@ -24,7 +24,7 @@ class MainThreadSCMResourceGroup implements ISCMResourceGroup {
get hideWhenEmpty(): boolean { return !!this.features.hideWhenEmpty; }
private _onDidChange = new Emitter<void>();
get onDidChange(): Event<void> { return this._onDidChange.event; }
readonly onDidChange: Event<void> = this._onDidChange.event;
constructor(
private readonly sourceControlHandle: number,
@@ -105,7 +105,7 @@ class MainThreadSCMProvider implements ISCMProvider {
// }
private _onDidChangeResources = new Emitter<void>();
get onDidChangeResources(): Event<void> { return this._onDidChangeResources.event; }
readonly onDidChangeResources: Event<void> = this._onDidChangeResources.event;
private features: SCMProviderFeatures = {};
@@ -120,13 +120,13 @@ class MainThreadSCMProvider implements ISCMProvider {
get count(): number | undefined { return this.features.count; }
private _onDidChangeCommitTemplate = new Emitter<string>();
get onDidChangeCommitTemplate(): Event<string> { return this._onDidChangeCommitTemplate.event; }
readonly onDidChangeCommitTemplate: Event<string> = this._onDidChangeCommitTemplate.event;
private _onDidChangeStatusBarCommands = new Emitter<Command[]>();
get onDidChangeStatusBarCommands(): Event<Command[]> { return this._onDidChangeStatusBarCommands.event; }
private _onDidChange = new Emitter<void>();
get onDidChange(): Event<void> { return this._onDidChange.event; }
readonly onDidChange: Event<void> = this._onDidChange.event;
constructor(
private readonly proxy: ExtHostSCMShape,
@@ -266,9 +266,9 @@ class MainThreadSCMProvider implements ISCMProvider {
export class MainThreadSCM implements MainThreadSCMShape {
private readonly _proxy: ExtHostSCMShape;
private _repositories: { [handle: number]: ISCMRepository; } = Object.create(null);
private _inputDisposables: { [handle: number]: IDisposable; } = Object.create(null);
private _disposables: IDisposable[] = [];
private _repositories = new Map<number, ISCMRepository>();
private _inputDisposables = new Map<number, IDisposable>();
private readonly _disposables = new DisposableStore();
constructor(
extHostContext: IExtHostContext,
@@ -281,28 +281,26 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
dispose(): void {
Object.keys(this._repositories)
.forEach(id => this._repositories[id].dispose());
this._repositories = Object.create(null);
this._repositories.forEach(r => r.dispose());
this._repositories.clear();
Object.keys(this._inputDisposables)
.forEach(id => this._inputDisposables[id].dispose());
this._inputDisposables = Object.create(null);
this._inputDisposables.forEach(d => d.dispose());
this._inputDisposables.clear();
this._disposables = dispose(this._disposables);
this._disposables.dispose();
}
$registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined): void {
const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri && URI.revive(rootUri), this.scmService);
const repository = this.scmService.registerSCMProvider(provider);
this._repositories[handle] = repository;
this._repositories.set(handle, repository);
const inputDisposable = repository.input.onDidChange(value => this._proxy.$onInputBoxValueChange(handle, value));
this._inputDisposables[handle] = inputDisposable;
this._inputDisposables.set(handle, inputDisposable);
}
$updateSourceControl(handle: number, features: SCMProviderFeatures): void {
const repository = this._repositories[handle];
const repository = this._repositories.get(handle);
if (!repository) {
return;
@@ -313,21 +311,21 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
$unregisterSourceControl(handle: number): void {
const repository = this._repositories[handle];
const repository = this._repositories.get(handle);
if (!repository) {
return;
}
this._inputDisposables[handle].dispose();
delete this._inputDisposables[handle];
this._inputDisposables.get(handle)!.dispose();
this._inputDisposables.delete(handle);
repository.dispose();
delete this._repositories[handle];
this._repositories.delete(handle);
}
$registerGroup(sourceControlHandle: number, groupHandle: number, id: string, label: string): void {
const repository = this._repositories[sourceControlHandle];
const repository = this._repositories.get(sourceControlHandle);
if (!repository) {
return;
@@ -338,7 +336,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
$updateGroup(sourceControlHandle: number, groupHandle: number, features: SCMGroupFeatures): void {
const repository = this._repositories[sourceControlHandle];
const repository = this._repositories.get(sourceControlHandle);
if (!repository) {
return;
@@ -349,7 +347,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
$updateGroupLabel(sourceControlHandle: number, groupHandle: number, label: string): void {
const repository = this._repositories[sourceControlHandle];
const repository = this._repositories.get(sourceControlHandle);
if (!repository) {
return;
@@ -360,7 +358,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
$spliceResourceStates(sourceControlHandle: number, splices: SCMRawResourceSplices[]): void {
const repository = this._repositories[sourceControlHandle];
const repository = this._repositories.get(sourceControlHandle);
if (!repository) {
return;
@@ -371,7 +369,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
$unregisterGroup(sourceControlHandle: number, handle: number): void {
const repository = this._repositories[sourceControlHandle];
const repository = this._repositories.get(sourceControlHandle);
if (!repository) {
return;
@@ -382,7 +380,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
$setInputBoxValue(sourceControlHandle: number, value: string): void {
const repository = this._repositories[sourceControlHandle];
const repository = this._repositories.get(sourceControlHandle);
if (!repository) {
return;
@@ -392,7 +390,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
$setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void {
const repository = this._repositories[sourceControlHandle];
const repository = this._repositories.get(sourceControlHandle);
if (!repository) {
return;
@@ -402,7 +400,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
$setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void {
const repository = this._repositories[sourceControlHandle];
const repository = this._repositories.get(sourceControlHandle);
if (!repository) {
return;
@@ -412,7 +410,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
$setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void {
const repository = this._repositories[sourceControlHandle];
const repository = this._repositories.get(sourceControlHandle);
if (!repository) {
return;

View File

@@ -28,7 +28,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
// {{SQL CARBON EDIT}}
@@ -299,10 +299,10 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
if (CodeActionKind.SourceFixAll.contains(b)) {
return 0;
}
return 1;
return -1;
}
if (CodeActionKind.SourceFixAll.contains(b)) {
return -1;
return 1;
}
return 0;
});
@@ -334,6 +334,8 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
await this.applyCodeActions(actionsToRun.actions);
} catch {
// Failure to apply a code action should not block other on save actions
} finally {
actionsToRun.dispose();
}
}
}
@@ -391,7 +393,7 @@ export class SaveParticipant implements ISaveParticipant {
constructor(
extHostContext: IExtHostContext,
@IInstantiationService instantiationService: IInstantiationService,
@IProgressService2 private readonly _progressService: IProgressService2,
@IProgressService private readonly _progressService: IProgressService,
@ILogService private readonly _logService: ILogService
) {
this._saveParticipants = new IdleValue(() => [

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { values } from 'vs/base/common/map';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -96,7 +96,7 @@ class SearchOperation {
class RemoteSearchProvider implements ISearchResultProvider, IDisposable {
private readonly _registrations: IDisposable[];
private readonly _registrations = new DisposableStore();
private readonly _searches = new Map<number, SearchOperation>();
constructor(
@@ -106,11 +106,11 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable {
private readonly _handle: number,
private readonly _proxy: ExtHostSearchShape
) {
this._registrations = [searchService.registerSearchResultProvider(this._scheme, type, this)];
this._registrations.add(searchService.registerSearchResultProvider(this._scheme, type, this));
}
dispose(): void {
dispose(this._registrations);
this._registrations.dispose();
}
fileSearch(query: IFileQuery, token: CancellationToken = CancellationToken.None): Promise<ISearchComplete> {

View File

@@ -3,11 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar';
import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/platform/statusbar/common/statusbar';
import { MainThreadStatusBarShape, MainContext, IExtHostContext } from '../common/extHost.protocol';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { dispose } from 'vs/base/common/lifecycle';
@extHostNamedCustomer(MainContext.MainThreadStatusBar)
@@ -25,8 +24,8 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
this.entries.clear();
}
$setEntry(id: number, extensionId: ExtensionIdentifier, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void {
const entry = { text, tooltip, command, color, extensionId };
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void {
const entry: IStatusbarEntry = { text, tooltip, command, color };
// Reset existing entry if alignment or priority changed
let existingEntry = this.entries.get(id);
@@ -38,7 +37,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
// Create new entry if not existing
if (!existingEntry) {
this.entries.set(id, { accessor: this.statusbarService.addEntry(entry, alignment, priority), alignment, priority });
this.entries.set(id, { accessor: this.statusbarService.addEntry(entry, statusId, statusName, alignment, priority), alignment, priority });
}
// Otherwise update

View File

@@ -16,7 +16,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import {
ContributedTask, KeyedTaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind,
ContributedTask, ConfiguringTask, KeyedTaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind,
PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource,
TaskSourceKind, ExtensionTaskSource, RunOptions, TaskSet, TaskDefinition
} from 'vs/workbench/contrib/tasks/common/tasks';
@@ -29,7 +29,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import {
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecutionDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecutionDTO, CustomExecution2DTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
RunOptionsDTO
} from 'vs/workbench/api/common/shared/tasks';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
@@ -131,7 +131,7 @@ namespace ProcessExecutionOptionsDTO {
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is ProcessExecutionDTO {
const candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
}
@@ -199,7 +199,7 @@ namespace ShellExecutionOptionsDTO {
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is ShellExecutionDTO {
const candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
}
@@ -231,7 +231,7 @@ namespace ShellExecutionDTO {
}
namespace CustomExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is CustomExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is CustomExecutionDTO {
const candidate = value as CustomExecutionDTO;
return candidate && candidate.customExecution === 'customExecution';
}
@@ -250,6 +250,26 @@ namespace CustomExecutionDTO {
}
}
namespace CustomExecution2DTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is CustomExecution2DTO {
const candidate = value as CustomExecution2DTO;
return candidate && candidate.customExecution === 'customExecution2';
}
export function from(value: CommandConfiguration): CustomExecution2DTO {
return {
customExecution: 'customExecution2'
};
}
export function to(value: CustomExecution2DTO): CommandConfiguration {
return {
runtime: RuntimeType.CustomExecution2,
presentation: undefined
};
}
}
namespace TaskSourceDTO {
export function from(value: TaskSource): TaskSourceDTO {
const result: TaskSourceDTO = {
@@ -304,8 +324,8 @@ namespace TaskHandleDTO {
}
namespace TaskDTO {
export function from(task: Task): TaskDTO | undefined {
if (task === undefined || task === null || (!CustomTask.is(task) && !ContributedTask.is(task))) {
export function from(task: Task | ConfiguringTask): TaskDTO | undefined {
if (task === undefined || task === null || (!CustomTask.is(task) && !ContributedTask.is(task) && !ConfiguringTask.is(task))) {
return undefined;
}
const result: TaskDTO = {
@@ -314,7 +334,7 @@ namespace TaskDTO {
definition: TaskDefinitionDTO.from(task.getDefinition()),
source: TaskSourceDTO.from(task._source),
execution: undefined,
presentationOptions: task.command ? TaskPresentationOptionsDTO.from(task.command.presentation) : undefined,
presentationOptions: !ConfiguringTask.is(task) && task.command ? TaskPresentationOptionsDTO.from(task.command.presentation) : undefined,
isBackground: task.configurationProperties.isBackground,
problemMatchers: [],
hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false,
@@ -323,7 +343,7 @@ namespace TaskDTO {
if (task.configurationProperties.group) {
result.group = task.configurationProperties.group;
}
if (task.command) {
if (!ConfiguringTask.is(task) && task.command) {
if (task.command.runtime === RuntimeType.Process) {
result.execution = ProcessExecutionDTO.from(task.command);
} else if (task.command.runtime === RuntimeType.Shell) {
@@ -353,6 +373,8 @@ namespace TaskDTO {
command = ProcessExecutionDTO.to(task.execution);
} else if (CustomExecutionDTO.is(task.execution)) {
command = CustomExecutionDTO.to(task.execution);
} else if (CustomExecution2DTO.is(task.execution)) {
command = CustomExecution2DTO.to(task.execution);
}
}
@@ -442,7 +464,7 @@ export class MainThreadTask implements MainThreadTaskShape {
});
}
public $registerTaskProvider(handle: number): Promise<void> {
public $registerTaskProvider(handle: number, type: string): Promise<void> {
const provider: ITaskProvider = {
provideTasks: (validTypes: IStringDictionary<boolean>) => {
return Promise.resolve(this._proxy.$provideTasks(handle, validTypes)).then((value) => {
@@ -460,9 +482,24 @@ export class MainThreadTask implements MainThreadTaskShape {
extension: value.extension
} as TaskSet;
});
},
resolveTask: (task: ConfiguringTask) => {
const dto = TaskDTO.from(task);
if (dto) {
dto.name = ((dto.name === undefined) ? '' : dto.name); // Using an empty name causes the name to default to the one given by the provider.
return Promise.resolve(this._proxy.$resolveTask(handle, dto)).then(resolvedTask => {
if (resolvedTask) {
return TaskDTO.to(resolvedTask, this._workspaceContextServer, true);
}
return undefined;
});
}
return Promise.resolve<ContributedTask | undefined>(undefined);
}
};
const disposable = this._taskService.registerTaskProvider(provider);
const disposable = this._taskService.registerTaskProvider(provider, type);
this._providers.set(handle, { disposable, provider });
return Promise.resolve(undefined);
}

View File

@@ -6,6 +6,7 @@
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { MainThreadTelemetryShape, MainContext, IExtHostContext } from '../common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings';
@extHostNamedCustomer(MainContext.MainThreadTelemetry)
export class MainThreadTelemetry implements MainThreadTelemetryShape {
@@ -28,4 +29,10 @@ export class MainThreadTelemetry implements MainThreadTelemetryShape {
data[MainThreadTelemetry._name] = true;
this._telemetryService.publicLog(eventName, data);
}
$publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data: StrictPropertyCheck<T, E>): void {
this.$publicLog(eventName, data as any);
}
}

View File

@@ -3,30 +3,37 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from 'vs/workbench/api/common/extHost.protocol';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IAvailableShellsRequest, IDefaultShellAndArgsRequest, ITerminalVirtualProcessRequest } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { UriComponents, URI } from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { StopWatch } from 'vs/base/common/stopwatch';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
private _proxy: ExtHostTerminalServiceShape;
private _remoteAuthority: string | null;
private _toDispose: IDisposable[] = [];
private _terminalProcesses: { [id: number]: ITerminalProcessExtHostProxy } = {};
private _terminalOnDidWriteDataListeners: { [id: number]: IDisposable } = {};
private _terminalOnDidAcceptInputListeners: { [id: number]: IDisposable } = {};
private readonly _toDispose = new DisposableStore();
private readonly _terminalProcesses = new Map<number, Promise<ITerminalProcessExtHostProxy>>();
private readonly _terminalProcessesReady = new Map<number, (proxy: ITerminalProcessExtHostProxy) => void>();
private readonly _terminalOnDidWriteDataListeners = new Map<number, IDisposable>();
private readonly _terminalOnDidAcceptInputListeners = new Map<number, IDisposable>();
constructor(
extHostContext: IExtHostContext,
@ITerminalService private readonly terminalService: ITerminalService
@ITerminalService private readonly _terminalService: ITerminalService,
@ITerminalInstanceService readonly terminalInstanceService: ITerminalInstanceService,
@IRemoteAgentService readonly _remoteAgentService: IRemoteAgentService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
this._remoteAuthority = extHostContext.remoteAuthority;
this._toDispose.push(terminalService.onInstanceCreated((instance) => {
// ITerminalService listeners
this._toDispose.add(_terminalService.onInstanceCreated((instance) => {
// Delay this message so the TerminalInstance constructor has a chance to finish and
// return the ID normally to the extension host. The ID that is passed here will be used
// to register non-extension API terminals in the extension host.
@@ -35,45 +42,58 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._onInstanceDimensionsChanged(instance);
}, EXT_HOST_CREATION_DELAY);
}));
this._toDispose.push(terminalService.onInstanceDisposed(instance => this._onTerminalDisposed(instance)));
this._toDispose.push(terminalService.onInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance)));
this._toDispose.push(terminalService.onInstanceDimensionsChanged(instance => this._onInstanceDimensionsChanged(instance)));
this._toDispose.push(terminalService.onInstanceRequestExtHostProcess(request => this._onTerminalRequestExtHostProcess(request)));
this._toDispose.push(terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : null)));
this._toDispose.push(terminalService.onInstanceTitleChanged(instance => this._onTitleChanged(instance.id, instance.title)));
this._toDispose.add(_terminalService.onInstanceDisposed(instance => this._onTerminalDisposed(instance)));
this._toDispose.add(_terminalService.onInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance)));
this._toDispose.add(_terminalService.onInstanceDimensionsChanged(instance => this._onInstanceDimensionsChanged(instance)));
this._toDispose.add(_terminalService.onInstanceMaximumDimensionsChanged(instance => this._onInstanceMaximumDimensionsChanged(instance)));
this._toDispose.add(_terminalService.onInstanceRequestExtHostProcess(request => this._onTerminalRequestExtHostProcess(request)));
this._toDispose.add(_terminalService.onInstanceRequestVirtualProcess(e => this._onTerminalRequestVirtualProcess(e)));
this._toDispose.add(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : null)));
this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => this._onTitleChanged(instance.id, instance.title)));
this._toDispose.add(_terminalService.configHelper.onWorkspacePermissionsChanged(isAllowed => this._onWorkspacePermissionsChanged(isAllowed)));
this._toDispose.add(_terminalService.onRequestAvailableShells(e => this._onRequestAvailableShells(e)));
// ITerminalInstanceService listeners
if (terminalInstanceService.onRequestDefaultShellAndArgs) {
this._toDispose.add(terminalInstanceService.onRequestDefaultShellAndArgs(e => this._onRequestDefaultShellAndArgs(e)));
}
// Set initial ext host state
this.terminalService.terminalInstances.forEach(t => {
this._terminalService.terminalInstances.forEach(t => {
this._onTerminalOpened(t);
t.processReady.then(() => this._onTerminalProcessIdReady(t));
});
const activeInstance = this.terminalService.getActiveInstance();
const activeInstance = this._terminalService.getActiveInstance();
if (activeInstance) {
this._proxy.$acceptActiveTerminalChanged(activeInstance.id);
}
this.terminalService.extHostReady(extHostContext.remoteAuthority);
this._terminalService.extHostReady(extHostContext.remoteAuthority);
}
public dispose(): void {
this._toDispose = dispose(this._toDispose);
this._toDispose.dispose();
// TODO@Daniel: Should all the previously created terminals be disposed
// when the extension host process goes down ?
}
public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string, cwd?: string | UriComponents, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }> {
public $createTerminal(launchConfig: TerminalLaunchConfig): Promise<{ id: number, name: string }> {
const shellLaunchConfig: IShellLaunchConfig = {
name,
executable: shellPath,
args: shellArgs,
cwd: typeof cwd === 'string' ? cwd : URI.revive(cwd),
waitOnExit,
name: launchConfig.name,
executable: launchConfig.shellPath,
args: launchConfig.shellArgs,
cwd: typeof launchConfig.cwd === 'string' ? launchConfig.cwd : URI.revive(launchConfig.cwd),
waitOnExit: launchConfig.waitOnExit,
ignoreConfigurationCwd: true,
env,
strictEnv
env: launchConfig.env,
strictEnv: launchConfig.strictEnv,
hideFromUser: launchConfig.hideFromUser,
isVirtualProcess: launchConfig.isVirtualProcess
};
const terminal = this.terminalService.createTerminal(shellLaunchConfig);
const terminal = this._terminalService.createTerminal(shellLaunchConfig);
this._terminalProcesses.set(terminal.id, new Promise<ITerminalProcessExtHostProxy>(r => this._terminalProcessesReady.set(terminal.id, r)));
return Promise.resolve({
id: terminal.id,
name: terminal.title
@@ -81,92 +101,94 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
public $createTerminalRenderer(name: string): Promise<number> {
const instance = this.terminalService.createTerminalRenderer(name);
const instance = this._terminalService.createTerminalRenderer(name);
return Promise.resolve(instance.id);
}
public $show(terminalId: number, preserveFocus: boolean): void {
const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
this.terminalService.setActiveInstance(terminalInstance);
this.terminalService.showPanel(!preserveFocus);
this._terminalService.setActiveInstance(terminalInstance);
this._terminalService.showPanel(!preserveFocus);
}
}
public $hide(terminalId: number): void {
const instance = this.terminalService.getActiveInstance();
const instance = this._terminalService.getActiveInstance();
if (instance && instance.id === terminalId) {
this.terminalService.hidePanel();
this._terminalService.hidePanel();
}
}
public $dispose(terminalId: number): void {
const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
terminalInstance.dispose();
}
}
public $terminalRendererWrite(terminalId: number, text: string): void {
const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
terminalInstance.write(text);
}
}
public $terminalRendererSetName(terminalId: number, name: string): void {
const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
terminalInstance.setTitle(name, false);
}
}
public $terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void {
const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
terminalInstance.setDimensions(dimensions);
}
}
public $terminalRendererRegisterOnInputListener(terminalId: number): void {
const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (!terminalInstance) {
return;
}
// Listener already registered
if (this._terminalOnDidAcceptInputListeners.hasOwnProperty(terminalId)) {
if (this._terminalOnDidAcceptInputListeners.has(terminalId)) {
return;
}
// Register
this._terminalOnDidAcceptInputListeners[terminalId] = terminalInstance.onRendererInput(data => this._onTerminalRendererInput(terminalId, data));
terminalInstance.addDisposable(this._terminalOnDidAcceptInputListeners[terminalId]);
const listener = terminalInstance.onRendererInput(data => this._onTerminalRendererInput(terminalId, data));
this._terminalOnDidAcceptInputListeners.set(terminalId, listener);
terminalInstance.addDisposable(listener);
}
public $sendText(terminalId: number, text: string, addNewLine: boolean): void {
const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
terminalInstance.sendText(text, addNewLine);
}
}
public $registerOnDataListener(terminalId: number): void {
const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (!terminalInstance) {
return;
}
// Listener already registered
if (this._terminalOnDidWriteDataListeners[terminalId]) {
if (this._terminalOnDidWriteDataListeners.has(terminalId)) {
return;
}
// Register
this._terminalOnDidWriteDataListeners[terminalId] = terminalInstance.onData(data => {
const listener = terminalInstance.onData(data => {
this._onTerminalData(terminalId, data);
});
terminalInstance.addDisposable(this._terminalOnDidWriteDataListeners[terminalId]);
this._terminalOnDidWriteDataListeners.set(terminalId, listener);
terminalInstance.addDisposable(listener);
}
private _onActiveTerminalChanged(terminalId: number | null): void {
@@ -181,6 +203,10 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._proxy.$acceptTerminalTitleChange(terminalId, name);
}
private _onWorkspacePermissionsChanged(isAllowed: boolean): void {
this._proxy.$acceptWorkspacePermissionsChanged(isAllowed);
}
private _onTerminalRendererInput(terminalId: number, data: string): void {
this._proxy.$acceptTerminalRendererInput(terminalId, data);
}
@@ -210,13 +236,24 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._proxy.$acceptTerminalDimensions(instance.id, instance.cols, instance.rows);
}
private _onInstanceMaximumDimensionsChanged(instance: ITerminalInstance): void {
this._proxy.$acceptTerminalMaximumDimensions(instance.id, instance.maxCols, instance.maxRows);
}
private _onTerminalRequestExtHostProcess(request: ITerminalProcessExtHostRequest): void {
// Only allow processes on remote ext hosts
if (!this._remoteAuthority) {
return;
}
this._terminalProcesses[request.proxy.terminalId] = request.proxy;
const proxy = request.proxy;
const ready = this._terminalProcessesReady.get(proxy.terminalId);
if (ready) {
ready(proxy);
this._terminalProcessesReady.delete(proxy.terminalId);
} else {
this._terminalProcesses.set(proxy.terminalId, Promise.resolve(proxy));
}
const shellLaunchConfigDto: ShellLaunchConfigDto = {
name: request.shellLaunchConfig.name,
executable: request.shellLaunchConfig.executable,
@@ -224,38 +261,66 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
cwd: request.shellLaunchConfig.cwd,
env: request.shellLaunchConfig.env
};
this._proxy.$createProcess(request.proxy.terminalId, shellLaunchConfigDto, request.activeWorkspaceRootUri, request.cols, request.rows, request.isWorkspaceShellAllowed);
request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data));
request.proxy.onResize(dimensions => this._proxy.$acceptProcessResize(request.proxy.terminalId, dimensions.cols, dimensions.rows));
request.proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(request.proxy.terminalId, immediate));
request.proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(request.proxy.terminalId));
request.proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(request.proxy.terminalId));
request.proxy.onRequestLatency(() => this._onRequestLatency(request.proxy.terminalId));
this._proxy.$createProcess(proxy.terminalId, shellLaunchConfigDto, request.activeWorkspaceRootUri, request.cols, request.rows, request.isWorkspaceShellAllowed);
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data));
proxy.onResize(dimensions => this._proxy.$acceptProcessResize(proxy.terminalId, dimensions.cols, dimensions.rows));
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate));
proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.terminalId));
proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.terminalId));
proxy.onRequestLatency(() => this._onRequestLatency(proxy.terminalId));
}
private _onTerminalRequestVirtualProcess(request: ITerminalVirtualProcessRequest): void {
const proxy = request.proxy;
const ready = this._terminalProcessesReady.get(proxy.terminalId);
if (!ready) {
this._terminalProcesses.set(proxy.terminalId, Promise.resolve(proxy));
} else {
ready(proxy);
this._terminalProcessesReady.delete(proxy.terminalId);
}
// Note that onReisze is not being listened to here as it needs to fire when max dimensions
// change, excluding the dimension override
const initialDimensions: ITerminalDimensionsDto | undefined = request.cols && request.rows ? {
columns: request.cols,
rows: request.rows
} : undefined;
this._proxy.$startVirtualProcess(proxy.terminalId, initialDimensions);
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data));
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate));
proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.terminalId));
proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.terminalId));
proxy.onRequestLatency(() => this._onRequestLatency(proxy.terminalId));
}
public $sendProcessTitle(terminalId: number, title: string): void {
this._terminalProcesses[terminalId].emitTitle(title);
this._getTerminalProcess(terminalId).then(e => e.emitTitle(title));
}
public $sendProcessData(terminalId: number, data: string): void {
this._terminalProcesses[terminalId].emitData(data);
this._getTerminalProcess(terminalId).then(e => e.emitData(data));
}
public $sendProcessPid(terminalId: number, pid: number): void {
this._terminalProcesses[terminalId].emitPid(pid);
public $sendProcessReady(terminalId: number, pid: number, cwd: string): void {
this._getTerminalProcess(terminalId).then(e => e.emitReady(pid, cwd));
}
public $sendProcessExit(terminalId: number, exitCode: number): void {
this._terminalProcesses[terminalId].emitExit(exitCode);
delete this._terminalProcesses[terminalId];
this._getTerminalProcess(terminalId).then(e => e.emitExit(exitCode));
this._terminalProcesses.delete(terminalId);
}
public $sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void {
this._getTerminalProcess(terminalId).then(e => e.emitOverrideDimensions(dimensions));
}
public $sendProcessInitialCwd(terminalId: number, initialCwd: string): void {
this._terminalProcesses[terminalId].emitInitialCwd(initialCwd);
this._getTerminalProcess(terminalId).then(e => e.emitInitialCwd(initialCwd));
}
public $sendProcessCwd(terminalId: number, cwd: string): void {
this._terminalProcesses[terminalId].emitCwd(cwd);
this._getTerminalProcess(terminalId).then(e => e.emitCwd(cwd));
}
private async _onRequestLatency(terminalId: number): Promise<void> {
@@ -267,6 +332,35 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
sw.stop();
sum += sw.elapsed();
}
this._terminalProcesses[terminalId].emitLatency(sum / COUNT);
this._getTerminalProcess(terminalId).then(e => e.emitLatency(sum / COUNT));
}
private _isPrimaryExtHost(): boolean {
// The "primary" ext host is the remote ext host if there is one, otherwise the local
const conn = this._remoteAgentService.getConnection();
if (conn) {
return this._remoteAuthority === conn.remoteAuthority;
}
return true;
}
private _onRequestAvailableShells(request: IAvailableShellsRequest): void {
if (this._isPrimaryExtHost()) {
this._proxy.$requestAvailableShells().then(e => request(e));
}
}
private _onRequestDefaultShellAndArgs(request: IDefaultShellAndArgsRequest): void {
if (this._isPrimaryExtHost()) {
this._proxy.$requestDefaultShellAndArgs().then(e => request(e.shell, e.args));
}
}
private _getTerminalProcess(terminalId: number): Promise<ITerminalProcessExtHostProxy> {
const terminal = this._terminalProcesses.get(terminalId);
if (!terminal) {
throw new Error(`Unknown terminal: ${terminalId}`);
}
return terminal;
}
}

View File

@@ -81,7 +81,10 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
await treeView.refresh();
}
for (const parent of parentChain) {
await treeView.expand(parent);
const parentItem = dataProvider.getItem(parent.handle);
if (parentItem) {
await treeView.expand(parentItem);
}
}
const item = dataProvider.getItem(itemIn.handle);
if (item) {
@@ -205,7 +208,7 @@ export class TreeViewDataProvider implements ITreeViewDataProvider {
if (current) {
const properties = distinct([...Object.keys(current), ...Object.keys(treeItem)]);
for (const property of properties) {
current[property] = treeItem[property];
(<any>current)[property] = (<any>treeItem)[property];
}
}
}

View File

@@ -0,0 +1,368 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as map from 'vs/base/common/map';
import { URI, UriComponents } from 'vs/base/common/uri';
import * 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 { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor';
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 { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { extHostNamedCustomer } from '../common/extHostCustomers';
import { IProductService } from 'vs/platform/product/common/product';
interface MainThreadWebviewState {
readonly viewType: string;
state: any;
}
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape {
private static readonly standardSupportedLinkSchemes = new Set([
'http',
'https',
'mailto',
'vscode',
'vscode-insider',
]);
private static revivalPool = 0;
private readonly _proxy: ExtHostWebviewsShape;
private readonly _webviews = new Map<WebviewPanelHandle, WebviewEditorInput<MainThreadWebviewState>>();
private readonly _revivers = new Map<string, IDisposable>();
private _activeWebview: WebviewPanelHandle | undefined = undefined;
constructor(
context: IExtHostContext,
@IExtensionService extensionService: IExtensionService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IEditorService private readonly _editorService: IEditorService,
@IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService,
@IOpenerService private readonly _openerService: IOpenerService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IProductService private readonly _productService: IProductService,
) {
super();
this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews);
this._register(_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this));
this._register(_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, 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.
this._register(_webviewEditorService.registerReviver({
canRevive: (webview: WebviewEditorInput<any>) => {
const viewType = webview.state && webview.state.viewType;
if (typeof viewType === 'string') {
extensionService.activateByEvent(`onWebviewPanel:${viewType}`);
}
return false;
},
reviveWebview: () => { throw new Error('not implemented'); }
}));
}
public $createWebviewPanel(
handle: WebviewPanelHandle,
viewType: string,
title: string,
showOptions: { viewColumn?: EditorViewColumn, preserveFocus?: boolean },
options: WebviewInputOptions,
extensionId: ExtensionIdentifier,
extensionLocation: UriComponents
): void {
const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null);
if (showOptions) {
mainThreadShowOptions.preserveFocus = !!showOptions.preserveFocus;
mainThreadShowOptions.group = viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn);
}
const webview = this._webviewEditorService.createWebview(handle, this.getInternalWebviewViewType(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), {
location: URI.revive(extensionLocation),
id: extensionId
}, this.createWebviewEventDelegate(handle)) as WebviewEditorInput<MainThreadWebviewState>;
webview.state = {
viewType: viewType,
state: undefined
};
this._webviews.set(handle, webview);
/* __GDPR__
"webviews:createWebviewPanel" : {
"extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value });
}
public $disposeWebview(handle: WebviewPanelHandle): void {
const webview = this.getWebview(handle);
webview.dispose();
}
public $setTitle(handle: WebviewPanelHandle, value: string): void {
const webview = this.getWebview(handle);
webview.setName(value);
}
public $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void {
const webview = this.getWebview(handle);
webview.iconPath = reviveWebviewIcon(value);
}
public $setHtml(handle: WebviewPanelHandle, value: string): void {
const webview = this.getWebview(handle);
webview.html = value;
}
public $setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void {
const webview = this.getWebview(handle);
webview.setOptions(reviveWebviewOptions(options as any /*todo@mat */));
}
public $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void {
const webview = this.getWebview(handle);
if (webview.isDisposed()) {
return;
}
const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn)) || this._editorGroupService.getGroup(webview.group || 0);
if (targetGroup) {
this._webviewEditorService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus);
}
}
public async $postMessage(handle: WebviewPanelHandle, message: any): Promise<boolean> {
const webview = this.getWebview(handle);
const editors = this._editorService.visibleControls
.filter(e => e instanceof WebviewEditor)
.map(e => e as WebviewEditor)
.filter(e => e.input!.matches(webview));
if (editors.length > 0) {
editors[0].sendMessage(message);
return true;
}
if (webview.webview) {
webview.webview.sendMessage(message);
return true;
}
return false;
}
public $registerSerializer(viewType: string): void {
if (this._revivers.has(viewType)) {
throw new Error(`Reviver for ${viewType} already registered`);
}
this._revivers.set(viewType, this._webviewEditorService.registerReviver({
canRevive: (webview) => {
return webview.state && webview.state.viewType === viewType;
},
reviveWebview: async (webview): Promise<void> => {
const viewType = webview.state.viewType;
const handle = 'revival-' + MainThreadWebviews.revivalPool++;
this._webviews.set(handle, webview);
webview._events = this.createWebviewEventDelegate(handle);
let state = undefined;
if (webview.state.state) {
try {
state = JSON.parse(webview.state.state);
} catch {
// noop
}
}
try {
await this._proxy.$deserializeWebviewPanel(handle, viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group || 0), webview.options);
} catch (error) {
onUnexpectedError(error);
webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
}
}
}));
}
public $unregisterSerializer(viewType: string): void {
const reviver = this._revivers.get(viewType);
if (!reviver) {
throw new Error(`No reviver for ${viewType} registered`);
}
reviver.dispose();
this._revivers.delete(viewType);
}
private getInternalWebviewViewType(viewType: string): string {
return `mainThreadWebview-${viewType}`;
}
private createWebviewEventDelegate(handle: WebviewPanelHandle) {
return {
onDidClickLink: (uri: URI) => this.onDidClickLink(handle, uri),
onMessage: (message: any) => this._proxy.$onMessage(handle, message),
onDispose: () => {
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
this._webviews.delete(handle);
});
},
onDidUpdateWebviewState: (newState: any) => {
const webview = this.tryGetWebview(handle);
if (!webview || webview.isDisposed()) {
return;
}
(webview as WebviewEditorInput<MainThreadWebviewState>).state.state = newState;
}
};
}
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._webviews)) {
const input = this._webviews.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)
});
return;
}
// Broadcast view state update for currently active
if (typeof this._activeWebview !== 'undefined') {
const oldActiveWebview = this._webviews.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),
});
}
}
// 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;
}
}
private onVisibleEditorsChanged(): void {
this._webviews.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;
}
}
});
}
private onDidClickLink(handle: WebviewPanelHandle, link: URI): void {
if (!link) {
return;
}
const webview = this.getWebview(handle);
if (this.isSupportedLink(webview, link)) {
this._openerService.open(link);
}
}
private isSupportedLink(webview: WebviewEditorInput, link: URI): boolean {
if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) {
return true;
}
if (this._productService.urlProtocol === link.scheme) {
return true;
}
return !!webview.options.enableCommandUris && link.scheme === 'command';
}
private getWebview(handle: WebviewPanelHandle): WebviewEditorInput {
const webview = this.tryGetWebview(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
}
return webview;
}
private tryGetWebview(handle: WebviewPanelHandle): WebviewEditorInput | undefined {
return this._webviews.get(handle);
}
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';">
</head>
<body>${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)}</body>
</html>`;
}
}
function reviveWebviewOptions(options: WebviewInputOptions): WebviewInputOptions {
return {
...options,
localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined,
};
}
function reviveWebviewIcon(
value: { light: UriComponents, dark: UriComponents } | undefined
): { light: URI, dark: URI } | undefined {
if (!value) {
return undefined;
}
return {
light: URI.revive(value.light),
dark: URI.revive(value.dark)
};
}

View File

@@ -4,19 +4,20 @@
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape, IOpenUriOptions } from '../common/extHost.protocol';
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { extractLocalHostUriMetaDataForPortMapping } from 'vs/workbench/contrib/webview/common/portMapping';
@extHostNamedCustomer(MainContext.MainThreadWindow)
export class MainThreadWindow implements MainThreadWindowShape {
private readonly proxy: ExtHostWindowShape;
private disposables: IDisposable[] = [];
private readonly disposables = new DisposableStore();
private readonly _tunnels = new Map<number, Promise<RemoteTunnel>>();
constructor(
@@ -33,7 +34,7 @@ export class MainThreadWindow implements MainThreadWindowShape {
}
dispose(): void {
this.disposables = dispose(this.disposables);
this.disposables.dispose();
for (const tunnel of this._tunnels.values()) {
tunnel.then(tunnel => tunnel.dispose());
@@ -48,13 +49,11 @@ export class MainThreadWindow implements MainThreadWindowShape {
async $openUri(uriComponent: UriComponents, options: IOpenUriOptions): Promise<boolean> {
let uri = URI.revive(uriComponent);
if (options.allowTunneling && !!this.environmentService.configuration.remoteAuthority) {
if (uri.scheme === 'http' || uri.scheme === 'https') {
const port = this.getLocalhostPort(uri);
if (typeof port === 'number') {
const tunnel = await this.getOrCreateTunnel(port);
if (tunnel) {
uri = uri.with({ authority: `localhost:${tunnel.tunnelLocalPort}` });
}
const portMappingRequest = extractLocalHostUriMetaDataForPortMapping(uri);
if (portMappingRequest) {
const tunnel = await this.getOrCreateTunnel(portMappingRequest.port);
if (tunnel) {
uri = uri.with({ authority: `127.0.0.1:${tunnel.tunnelLocalPort}` });
}
}
}
@@ -62,14 +61,6 @@ export class MainThreadWindow implements MainThreadWindowShape {
return this.windowsService.openExternal(encodeURI(uri.toString(true)));
}
private getLocalhostPort(uri: URI): number | undefined {
const match = /^localhost:(\d+)$/.exec(uri.authority);
if (match) {
return +match[1];
}
return undefined;
}
private getOrCreateTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {
const existing = this._tunnels.get(remotePort);
if (existing) {

View File

@@ -5,7 +5,7 @@
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
@@ -13,7 +13,6 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService, ServicesAccessor } 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 { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
@@ -24,11 +23,12 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData, ITextSearchComplete } from '../common/extHost.protocol';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isEqualOrParent } from 'vs/base/common/resources';
import { INotificationService } from 'vs/platform/notification/common/notification';
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
private readonly _toDispose: IDisposable[] = [];
private readonly _toDispose = new DisposableStore();
private readonly _activeCancelTokens: { [id: number]: CancellationTokenSource } = Object.create(null);
private readonly _proxy: ExtHostWorkspaceShape;
private readonly _queryBuilder = this._instantiationService.createInstance(QueryBuilder);
@@ -39,7 +39,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ITextFileService private readonly _textFileService: ITextFileService,
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService,
@IStatusbarService private readonly _statusbarService: IStatusbarService,
@INotificationService private readonly _notificationService: INotificationService,
@IWindowService private readonly _windowService: IWindowService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ILabelService private readonly _labelService: ILabelService,
@@ -52,7 +52,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
}
dispose(): void {
dispose(this._toDispose);
this._toDispose.dispose();
for (let requestId in this._activeCancelTokens) {
const tokenSource = this._activeCancelTokens[requestId];
@@ -66,7 +66,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
const workspaceFoldersToAdd = foldersToAdd.map(f => ({ uri: URI.revive(f.uri), name: f.name }));
// Indicate in status message
this._statusbarService.setStatusMessage(this.getStatusMessage(extensionName, workspaceFoldersToAdd.length, deleteCount), 10 * 1000 /* 10s */);
this._notificationService.status(this.getStatusMessage(extensionName, workspaceFoldersToAdd.length, deleteCount), { hideAfter: 10 * 1000 /* 10s */ });
return this._workspaceEditingService.updateFolders(index, deleteCount, workspaceFoldersToAdd, true);
}
@@ -179,10 +179,9 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
return search;
}
$checkExists(includes: string[], token: CancellationToken): Promise<boolean> {
$checkExists(folders: UriComponents[], includes: string[], token: CancellationToken): Promise<boolean> {
const queryBuilder = this._instantiationService.createInstance(QueryBuilder);
const folders = this._contextService.getWorkspace().folders.map(folder => folder.uri);
const query = queryBuilder.file(folders, {
const query = queryBuilder.file(folders.map(folder => URI.revive(folder)), {
_reason: 'checkExists',
includePattern: includes.join(', '),
expandPatterns: true,

View File

@@ -36,7 +36,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { createCSSRule } from 'vs/base/browser/dom';
import { createCSSRule, asDomUri } from 'vs/base/browser/dom';
export interface IUserFriendlyViewsContainerDescriptor {
id: string;
@@ -321,13 +321,13 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(
new SyncActionDescriptor(OpenCustomViewletAction, id, localize('showViewlet', "Show {0}", title)),
'View: Show {0}',
`View: Show ${title}`,
localize('view', "View")
);
// Generate CSS to show the icon in the activity bar
const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`;
createCSSRule(iconClass, `-webkit-mask: url('${icon}') no-repeat 50% 50%`);
createCSSRule(iconClass, `-webkit-mask: url('${asDomUri(icon)}') no-repeat 50% 50%; -webkit-mask-size: 24px;`);
}
return viewContainer;
@@ -383,7 +383,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
when: ContextKeyExpr.deserialize(item.when),
canToggleVisibility: true,
collapsed: this.showCollapsed(container),
treeView: this.instantiationService.createInstance(CustomTreeView, item.id, container),
treeView: this.instantiationService.createInstance(CustomTreeView, item.id, item.name, container),
order: ExtensionIdentifier.equals(extension.description.identifier, container.extensionId) ? index + 1 : undefined,
extensionId: extension.description.identifier,
originalContainerId: entry.key
@@ -456,4 +456,4 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
}
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(ViewsExtensionHandler, LifecyclePhase.Starting);
workbenchRegistry.registerWorkbenchContribution(ViewsExtensionHandler, LifecyclePhase.Starting);