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

@@ -14,47 +14,47 @@ import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorEx
import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint';
// --- mainThread participants
import '../browser/mainThreadClipboard';
import '../browser/mainThreadCommands';
import '../browser/mainThreadConfiguration';
import '../browser/mainThreadConsole';
// {{SQL CARBON EDIT}}
// import '../browser/mainThreadDebugService';
import '../browser/mainThreadDecorations';
import '../browser/mainThreadDiagnostics';
import '../browser/mainThreadDialogs';
import '../browser/mainThreadDocumentContentProviders';
import '../browser/mainThreadDocuments';
import '../browser/mainThreadDocumentsAndEditors';
import '../browser/mainThreadEditor';
import '../browser/mainThreadEditors';
import '../browser/mainThreadErrors';
import '../browser/mainThreadExtensionService';
import '../browser/mainThreadFileSystem';
import '../browser/mainThreadFileSystemEventService';
import '../browser/mainThreadHeapService';
import '../browser/mainThreadKeytar';
import '../browser/mainThreadLanguageFeatures';
import '../browser/mainThreadLanguages';
import '../browser/mainThreadLogService';
import '../browser/mainThreadMessageService';
import '../browser/mainThreadOutputService';
import '../browser/mainThreadProgress';
import '../browser/mainThreadQuickOpen';
import '../browser/mainThreadSaveParticipant';
import '../browser/mainThreadSCM';
import '../browser/mainThreadSearch';
import '../browser/mainThreadStatusBar';
import '../browser/mainThreadStorage';
import '../browser/mainThreadTelemetry';
import '../browser/mainThreadTerminalService';
import '../browser/mainThreadTreeViews';
import '../browser/mainThreadUrls';
import '../browser/mainThreadWindow';
import '../browser/mainThreadWorkspace';
import '../browser/mainThreadComments';
import '../browser/mainThreadTask';
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 {

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

@@ -2,30 +2,31 @@
* 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 { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import * as modes from 'vs/editor/common/modes';
import { localize } from 'vs/nls';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import product from 'vs/platform/product/node/product';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewInsetHandle, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol';
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 { CodeInsetController } from 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution';
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 { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
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 {
@@ -34,55 +35,47 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
'http',
'https',
'mailto',
product.urlProtocol,
'vscode',
'vscode-insiders'
'vscode-insider',
]);
private static revivalPool = 0;
private readonly _proxy: ExtHostWebviewsShape;
private readonly _webviews = new Map<WebviewPanelHandle, WebviewEditorInput>();
private readonly _webviewsElements = new Map<WebviewInsetHandle, WebviewElement>();
private readonly _webviews = new Map<WebviewPanelHandle, WebviewEditorInput<MainThreadWebviewState>>();
private readonly _revivers = new Map<string, IDisposable>();
private _activeWebview: WebviewPanelHandle | undefined = undefined;
constructor(
context: IExtHostContext,
@ILifecycleService lifecycleService: ILifecycleService,
@IExtensionService extensionService: IExtensionService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IEditorService private readonly _editorService: IEditorService,
@IWebviewEditorService private readonly _webviewService: IWebviewEditorService,
@IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService,
@IOpenerService private readonly _openerService: IOpenerService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
@IProductService private readonly _productService: IProductService,
) {
super();
this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews);
_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._toDispose);
_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this, this._toDispose);
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._toDispose.push(_webviewService.registerReviver({
canRevive: (webview) => {
const viewType = webview.state.viewType;
if (viewType) {
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'); }
}));
lifecycleService.onBeforeShutdown(e => {
e.veto(this._onBeforeShutdown());
}, this, this._toDispose);
}
public $createWebviewPanel(
@@ -100,10 +93,10 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
mainThreadShowOptions.group = viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn);
}
const webview = this._webviewService.createWebview(this.getInternalWebviewId(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), {
const webview = this._webviewEditorService.createWebview(handle, this.getInternalWebviewViewType(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), {
location: URI.revive(extensionLocation),
id: extensionId
}, this.createWebviewEventDelegate(handle));
}, this.createWebviewEventDelegate(handle)) as WebviewEditorInput<MainThreadWebviewState>;
webview.state = {
viewType: viewType,
state: undefined
@@ -119,52 +112,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value });
}
$createWebviewCodeInset(
handle: WebviewInsetHandle,
symbolId: string,
options: modes.IWebviewOptions,
extensionId: ExtensionIdentifier,
extensionLocation: UriComponents
): void {
// todo@joh main is for the lack of a code-inset service
// which we maybe wanna have... this is how it now works
// 1) create webview element
// 2) find the code inset controller that request it
// 3) let the controller adopt the widget
// 4) continue to forward messages to the webview
const webview = this._instantiationService.createInstance(
WebviewElement,
{
extension: {
location: URI.revive(extensionLocation),
id: extensionId
},
enableFindWidget: false,
},
{
allowScripts: options.enableScripts,
}
);
let found = false;
for (const editor of this._codeEditorService.listCodeEditors()) {
const ctrl = CodeInsetController.get(editor);
if (ctrl && ctrl.acceptWebview(symbolId, webview)) {
found = true;
break;
}
}
if (!found) {
webview.dispose();
return;
}
// this will leak... the adopted webview will be disposed by the
// code inset controller. we might need a dispose-event here so that
// we can clean up things.
this._webviewsElements.set(handle, webview);
}
public $disposeWebview(handle: WebviewPanelHandle): void {
const webview = this.getWebview(handle);
webview.dispose();
@@ -180,22 +127,14 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
webview.iconPath = reviveWebviewIcon(value);
}
public $setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void {
if (typeof handle === 'number') {
this.getWebviewElement(handle).html = value;
} else {
const webview = this.getWebview(handle);
webview.html = value;
}
public $setHtml(handle: WebviewPanelHandle, value: string): void {
const webview = this.getWebview(handle);
webview.html = value;
}
public $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: modes.IWebviewOptions): void {
if (typeof handle === 'number') {
this.getWebviewElement(handle).options = reviveWebviewOptions(options as any /*todo@mat */);
} else {
const webview = this.getWebview(handle);
webview.setOptions(reviveWebviewOptions(options as any /*todo@mat */));
}
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 {
@@ -206,33 +145,28 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn)) || this._editorGroupService.getGroup(webview.group || 0);
if (targetGroup) {
this._webviewService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus);
this._webviewEditorService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus);
}
}
public async $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, message: any): Promise<boolean> {
if (typeof handle === 'number') {
this.getWebviewElement(handle).sendMessage(message);
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;
} else {
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;
}
if (webview.webview) {
webview.webview.sendMessage(message);
return true;
}
return false;
}
public $registerSerializer(viewType: string): void {
@@ -240,7 +174,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
throw new Error(`Reviver for ${viewType} already registered`);
}
this._revivers.set(viewType, this._webviewService.registerReviver({
this._revivers.set(viewType, this._webviewEditorService.registerReviver({
canRevive: (webview) => {
return webview.state && webview.state.viewType === viewType;
},
@@ -278,19 +212,10 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
this._revivers.delete(viewType);
}
private getInternalWebviewId(viewType: string): string {
private getInternalWebviewViewType(viewType: string): string {
return `mainThreadWebview-${viewType}`;
}
private _onBeforeShutdown(): boolean {
this._webviews.forEach((webview) => {
if (!webview.isDisposed() && webview.state && this._revivers.has(webview.state.viewType)) {
webview.state.state = webview.webviewState;
}
});
return false; // Don't veto shutdown
}
private createWebviewEventDelegate(handle: WebviewPanelHandle) {
return {
onDidClickLink: (uri: URI) => this.onDidClickLink(handle, uri),
@@ -299,6 +224,13 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
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;
}
};
}
@@ -384,23 +316,22 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
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._webviews.get(handle);
const webview = this.tryGetWebview(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
}
return webview;
}
private getWebviewElement(handle: number): WebviewElement {
const webview = this._webviewsElements.get(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) {
@@ -409,7 +340,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
<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-core-resource: https: 'unsafe-inline'; child-src 'none'; frame-src 'none';">
<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>`;

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);

View File

@@ -11,7 +11,7 @@ import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IWindowsService, IOpenSettings, IURIToOpen } from 'vs/platform/windows/common/windows';
import { IOpenSettings, IURIToOpen, IWindowService } from 'vs/platform/windows/common/windows';
import { IDownloadService } from 'vs/platform/download/common/download';
import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
import { IRecent } from 'vs/platform/history/common/history';
@@ -129,8 +129,8 @@ export class OpenAPICommand {
CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute));
CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, uri: URI) {
const windowsService = accessor.get(IWindowsService);
return windowsService.removeFromRecentlyOpened([uri]).then(() => undefined);
const windowService = accessor.get(IWindowService);
return windowService.removeFromRecentlyOpened([uri]);
});
export class RemoveFromRecentlyOpenedAPICommand {
@@ -160,7 +160,7 @@ interface RecentEntry {
}
CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async function (accessor: ServicesAccessor, recentEntry: RecentEntry) {
const windowsService = accessor.get(IWindowsService);
const windowService = accessor.get(IWindowService);
const workspacesService = accessor.get(IWorkspacesService);
let recent: IRecent | undefined = undefined;
const uri = recentEntry.uri;
@@ -173,7 +173,7 @@ CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async functio
} else {
recent = { fileUri: uri, label };
}
return windowsService.addRecentlyOpened([recent]);
return windowService.addRecentlyOpened([recent]);
});
export class SetEditorLayoutAPICommand {

View File

@@ -42,12 +42,12 @@ const configurationEntrySchema: IJSONSchema = {
enum: ['application', 'machine', 'window', 'resource'],
default: 'window',
enumDescriptions: [
nls.localize('scope.application.description', "Application specific configuration, which can be configured only in local user settings."),
nls.localize('scope.machine.description', "Machine specific configuration, which can be configured only in local and remote user settings."),
nls.localize('scope.window.description', "Window specific configuration, which can be configured in the user or workspace settings."),
nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the user, workspace or folder settings.")
nls.localize('scope.application.description', "Application specific configuration, which can be configured only in the user settings."),
nls.localize('scope.machine.description', "Machine specific configuration, which can be configured only in the user settings when the extension is running locally, or only in the remote settings when the extension is running remotely."),
nls.localize('scope.window.description', "Window specific configuration, which can be configured in the user, remote or workspace settings."),
nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the user, remote, workspace or folder settings.")
],
description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `window` and `resource`.")
description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `application`, `machine`, `window` and `resource`.")
},
enumDescriptions: {
type: 'array',

View File

@@ -3,50 +3,50 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IRemoteConsoleLog } from 'vs/base/common/console';
import { SerializedError } from 'vs/base/common/errors';
import { IRelativePattern } from 'vs/base/common/glob';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { URI, UriComponents } from 'vs/base/common/uri';
import { TextEditorCursorStyle, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
import { RenderLineNumbersType, TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ISingleEditOperation, EndOfLineSequence } from 'vs/editor/common/model';
import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import * as modes from 'vs/editor/common/modes';
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as files from 'vs/platform/files/common/files';
import { ResourceLabelFormatter } from 'vs/platform/label/common/label';
import { LogLevel } from 'vs/platform/log/common/log';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
import * as quickInput from 'vs/platform/quickinput/common/quickInput';
import * as search from 'vs/workbench/services/search/common/search';
import { RemoteAuthorityResolverErrorCode, ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
import * as statusbar from 'vs/platform/statusbar/common/statusbar';
import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import * as tasks from 'vs/workbench/api/common/shared/tasks';
import { ITreeItem, IRevealOptions } from 'vs/workbench/common/views';
import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views';
import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { IAdapterDescriptor, IConfig, ITerminalSettings } from 'vs/workbench/contrib/debug/common/debug';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import { ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
import { IRPCProtocol, createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import * as search from 'vs/workbench/services/search/common/search';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { ResolvedAuthority, RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { IRelativePattern } from 'vs/base/common/glob';
import { IRemoteConsoleLog } from 'vs/base/common/console';
import { VSBuffer } from 'vs/base/common/buffer';
// {{SQL CARBON EDIT}}
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
@@ -62,6 +62,8 @@ export interface IEnvironment {
extensionTestsLocationURI?: URI;
globalStorageHome: URI;
userHome: URI;
webviewResourceRoot: string;
webviewCspSource: string;
}
export interface IStaticWorkspaceData {
@@ -88,11 +90,11 @@ export interface IInitData {
logLevel: LogLevel;
logsLocation: URI;
autoStart: boolean;
remoteAuthority?: string | null;
remote: { isRemote: boolean; authority: string | undefined; };
}
export interface IConfigurationInitData extends IConfigurationData {
configurationScopes: { [key: string]: ConfigurationScope };
configurationScopes: [string, ConfigurationScope | undefined][];
}
export interface IWorkspaceConfigurationChangeEventData {
@@ -121,33 +123,18 @@ export interface MainThreadCommandsShape extends IDisposable {
$getCommands(): Promise<string[]>;
}
export interface CommentThreadTemplate {
label: string;
acceptInputCommand?: modes.Command;
additionalCommands?: modes.Command[];
deleteCommand?: modes.Command;
}
export interface CommentProviderFeatures {
startDraftLabel?: string;
deleteDraftLabel?: string;
finishDraftLabel?: string;
reactionGroup?: modes.CommentReaction[];
commentThreadTemplate?: CommentThreadTemplate;
reactionHandler?: boolean;
}
export interface MainThreadCommentsShape extends IDisposable {
$registerCommentController(handle: number, id: string, label: string): void;
$unregisterCommentController(handle: number): void;
$updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void;
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange): modes.CommentThread2 | undefined;
$updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, label: string, contextValue: string | undefined, comments: modes.Comment[], acceptInputCommand: modes.Command | undefined, additionalCommands: modes.Command[], deleteCommand: modes.Command | undefined, collapseState: modes.CommentThreadCollapsibleState): void;
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, extensionId: ExtensionIdentifier): modes.CommentThread | undefined;
$updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, label: string, contextValue: string | undefined, comments: modes.Comment[], collapseState: modes.CommentThreadCollapsibleState): void;
$deleteCommentThread(handle: number, commentThreadHandle: number): void;
$setInputValue(handle: number, input: string): void;
$registerDocumentCommentProvider(handle: number, features: CommentProviderFeatures): void;
$unregisterDocumentCommentProvider(handle: number): void;
$registerWorkspaceCommentProvider(handle: number, extensionId: ExtensionIdentifier): void;
$unregisterWorkspaceCommentProvider(handle: number): void;
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void;
}
@@ -251,7 +238,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
$trySetSelections(id: string, selections: ISelection[]): Promise<void>;
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): Promise<boolean>;
$tryApplyWorkspaceEdit(workspaceEditDto: WorkspaceEditDto): Promise<boolean>;
$tryInsertSnippet(id: string, template: string, selections: IRange[], opts: IUndoStopOptions): Promise<boolean>;
$tryInsertSnippet(id: string, template: string, selections: readonly IRange[], opts: IUndoStopOptions): Promise<boolean>;
$getDiffInformation(id: string): Promise<editorCommon.ILineChange[]>;
}
@@ -336,7 +323,6 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$unregister(handle: number): void;
$registerDocumentSymbolProvider(handle: number, selector: ISerializedDocumentFilter[], label: string): void;
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void;
$registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void;
$emitCodeLensEvent(eventHandle: number, event?: any): void;
$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
@@ -351,7 +337,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
$registerNavigateTypeSupport(handle: number): void;
$registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportsResolveInitialValues: boolean): void;
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean): void;
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean, extensionId: ExtensionIdentifier): void;
$registerSignatureHelpProvider(handle: number, selector: ISerializedDocumentFilter[], metadata: ISerializedSignatureHelpProviderMetadata): void;
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[], supportsResolve: boolean): void;
$registerDocumentColorProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
@@ -392,8 +378,20 @@ export interface MainThreadProgressShape extends IDisposable {
$progressEnd(handle: number): void;
}
export interface TerminalLaunchConfig {
name?: string;
shellPath?: string;
shellArgs?: string[] | string;
cwd?: string | UriComponents;
env?: { [key: string]: string | null };
waitOnExit?: boolean;
strictEnv?: boolean;
hideFromUser?: boolean;
isVirtualProcess?: boolean;
}
export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string, cwd?: string | UriComponents, env?: { [key: string]: string | null }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }>;
$createTerminal(config: TerminalLaunchConfig): Promise<{ id: number, name: string }>;
$createTerminalRenderer(name: string): Promise<number>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
@@ -404,8 +402,9 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
// Process
$sendProcessTitle(terminalId: number, title: string): void;
$sendProcessData(terminalId: number, data: string): void;
$sendProcessPid(terminalId: number, pid: number): void;
$sendProcessReady(terminalId: number, pid: number, cwd: string): void;
$sendProcessExit(terminalId: number, exitCode: number): void;
$sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void;
$sendProcessInitialCwd(terminalId: number, cwd: string): void;
$sendProcessCwd(terminalId: number, initialCwd: string): void;
@@ -428,6 +427,8 @@ export type TransferQuickInput = TransferQuickPick | TransferInputBox;
export interface BaseTransferQuickInput {
[key: string]: any;
id: number;
type?: 'quickPick' | 'inputBox';
@@ -500,7 +501,7 @@ export interface MainThreadQuickOpenShape extends IDisposable {
}
export interface MainThreadStatusBarShape extends IDisposable {
$setEntry(id: number, extensionId: ExtensionIdentifier | undefined, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: statusbar.StatusbarAlignment, priority: number | undefined): void;
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: statusbar.StatusbarAlignment, priority: number | undefined): void;
$dispose(id: number): void;
}
@@ -511,12 +512,25 @@ export interface MainThreadStorageShape extends IDisposable {
export interface MainThreadTelemetryShape extends IDisposable {
$publicLog(eventName: string, data?: any): void;
$publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>): void;
}
export interface MainThreadEditorInsetsShape extends IDisposable {
$createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void>;
$disposeEditorInset(handle: number): void;
$setHtml(handle: number, value: string): void;
$setOptions(handle: number, options: modes.IWebviewOptions): void;
$postMessage(handle: number, value: any): Promise<boolean>;
}
export interface ExtHostEditorInsetsShape {
$onDidDispose(handle: number): void;
$onDidReceiveMessage(handle: number, message: any): void;
}
export type WebviewPanelHandle = string;
export type WebviewInsetHandle = number;
export interface WebviewPanelShowOptions {
readonly viewColumn?: EditorViewColumn;
readonly preserveFocus?: boolean;
@@ -524,15 +538,14 @@ export interface WebviewPanelShowOptions {
export interface MainThreadWebviewsShape extends IDisposable {
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void;
$createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier | undefined, extensionLocation: UriComponents | undefined): void;
$disposeWebview(handle: WebviewPanelHandle): void;
$reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void;
$setTitle(handle: WebviewPanelHandle, value: string): void;
$setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void;
$setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: modes.IWebviewOptions): void;
$postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, value: any): Promise<boolean>;
$setHtml(handle: WebviewPanelHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void;
$postMessage(handle: WebviewPanelHandle, value: any): Promise<boolean>;
$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
@@ -567,7 +580,7 @@ export interface ITextSearchComplete {
export interface MainThreadWorkspaceShape extends IDisposable {
$startFileSearch(includePattern: string | undefined, includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false | undefined, maxResults: number | undefined, token: CancellationToken): Promise<UriComponents[] | undefined>;
$startTextSearch(query: search.IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete>;
$checkExists(includes: string[], token: CancellationToken): Promise<boolean>;
$checkExists(folders: UriComponents[], includes: string[], token: CancellationToken): Promise<boolean>;
$saveAll(includeUntitled?: boolean): Promise<boolean>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Promise<void>;
$resolveProxy(url: string): Promise<string | undefined>;
@@ -581,9 +594,21 @@ export interface IFileChangeDto {
export interface MainThreadFileSystemShape extends IDisposable {
$registerFileSystemProvider(handle: number, scheme: string, capabilities: files.FileSystemProviderCapabilities): void;
$unregisterProvider(handle: number): void;
$onFileSystemChange(handle: number, resource: IFileChangeDto[]): void;
$stat(uri: UriComponents): Promise<files.IStat>;
$readdir(resource: UriComponents): Promise<[string, files.FileType][]>;
$readFile(resource: UriComponents): Promise<VSBuffer>;
$writeFile(resource: UriComponents, content: VSBuffer): Promise<void>;
$rename(resource: UriComponents, target: UriComponents, opts: files.FileOverwriteOptions): Promise<void>;
$copy(resource: UriComponents, target: UriComponents, opts: files.FileOverwriteOptions): Promise<void>;
$mkdir(resource: UriComponents): Promise<void>;
$delete(resource: UriComponents, opts: files.FileDeleteOptions): Promise<void>;
}
export interface MainThreadLabelServiceShape extends IDisposable {
$registerResourceLabelFormatter(handle: number, formatter: ResourceLabelFormatter): void;
$unregisterResourceLabelFormatter(handle: number): void;
$onFileSystemChange(handle: number, resource: IFileChangeDto[]): void;
}
export interface MainThreadSearchShape extends IDisposable {
@@ -597,7 +622,7 @@ export interface MainThreadSearchShape extends IDisposable {
export interface MainThreadTaskShape extends IDisposable {
$createTaskId(task: tasks.TaskDTO): Promise<string>;
$registerTaskProvider(handle: number): Promise<void>;
$registerTaskProvider(handle: number, type: string): Promise<void>;
$unregisterTaskProvider(handle: number): Promise<void>;
$fetchTasks(filter?: tasks.TaskFilterDTO): Promise<tasks.TaskDTO[]>;
$executeTask(task: tasks.TaskHandleDTO | tasks.TaskDTO): Promise<tasks.TaskExecutionDTO>;
@@ -813,6 +838,10 @@ export interface ExtHostFileSystemShape {
$write(handle: number, fd: number, pos: number, data: VSBuffer): Promise<number>;
}
export interface ExtHostLabelServiceShape {
$registerResourceLabelFormatter(formatter: ResourceLabelFormatter): IDisposable;
}
export interface ExtHostSearchShape {
$provideFileSearchResults(handle: number, session: number, query: search.IRawQuery, token: CancellationToken): Promise<search.ISearchCompleteStats>;
$provideTextSearchResults(handle: number, session: number, query: search.IRawTextQuery, token: CancellationToken): Promise<search.ISearchCompleteStats>;
@@ -916,6 +945,20 @@ export interface SuggestResultDto {
c?: boolean;
}
export interface SignatureHelpDto {
id: CacheId;
signatures: modes.SignatureInformation[];
activeSignature: number;
activeParameter: number;
}
export interface SignatureHelpContextDto {
readonly triggerKind: modes.SignatureHelpTriggerKind;
readonly triggerCharacter?: string;
readonly isRetrigger: boolean;
readonly activeSignatureHelp?: SignatureHelpDto;
}
export interface LocationDto {
uri: UriComponents;
range: IRange;
@@ -988,6 +1031,11 @@ export interface CodeActionDto {
isPreferred?: boolean;
}
export interface CodeActionListDto {
cacheId: number;
actions: ReadonlyArray<CodeActionDto>;
}
export type CacheId = number;
export type ChainedCacheId = [CacheId, CacheId];
@@ -1000,16 +1048,20 @@ export interface LinkDto {
cacheId?: ChainedCacheId;
range: IRange;
url?: string | UriComponents;
tooltip?: string;
}
export interface CodeLensDto extends ObjectIdentifier {
export interface CodeLensListDto {
cacheId?: number;
lenses: CodeLensDto[];
}
export interface CodeLensDto {
cacheId?: ChainedCacheId;
range: IRange;
id?: string;
command?: CommandDto;
}
export type CodeInsetDto = ObjectIdentifier & codeInset.ICodeInsetSymbol;
export interface CallHierarchyDto {
_id: number;
kind: modes.SymbolKind;
@@ -1022,10 +1074,9 @@ export interface CallHierarchyDto {
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensDto[]>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensListDto | undefined>;
$resolveCodeLens(handle: number, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined>;
$provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeInsetDto[] | undefined>;
$resolveCodeInset(handle: number, resource: UriComponents, symbol: CodeInsetDto, token: CancellationToken): Promise<CodeInsetDto>;
$releaseCodeLenses(handle: number, id: number): void;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
@@ -1033,7 +1084,8 @@ export interface ExtHostLanguageFeaturesShape {
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.Hover | undefined>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DocumentHighlight[] | undefined>;
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<LocationDto[] | undefined>;
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionDto[] | undefined>;
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionListDto | undefined>;
$releaseCodeActions(handle: number, cacheId: number): void;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
@@ -1045,7 +1097,8 @@ export interface ExtHostLanguageFeaturesShape {
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<SuggestResultDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<SuggestDataDto | undefined>;
$releaseCompletionItems(handle: number, id: number): void;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<modes.SignatureHelp | undefined>;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<SignatureHelpDto | undefined>;
$releaseSignatureHelp(handle: number, id: number): void;
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<LinksListDto | undefined>;
$resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<LinkDto | undefined>;
$releaseDocumentLinks(handle: number, id: number): void;
@@ -1076,6 +1129,21 @@ export interface ShellLaunchConfigDto {
env?: { [key: string]: string | null };
}
export interface IShellDefinitionDto {
label: string;
path: string;
}
export interface IShellAndArgsDto {
shell: string;
args: string[] | string | undefined;
}
export interface ITerminalDimensionsDto {
columns: number;
rows: number;
}
export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number): void;
$acceptTerminalOpened(id: number, name: string): void;
@@ -1085,13 +1153,18 @@ export interface ExtHostTerminalServiceShape {
$acceptTerminalRendererInput(id: number, data: string): void;
$acceptTerminalTitleChange(id: number, name: string): void;
$acceptTerminalDimensions(id: number, cols: number, rows: number): void;
$acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void;
$createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void;
$startVirtualProcess(id: number, initialDimensions: ITerminalDimensionsDto | undefined): void;
$acceptProcessInput(id: number, data: string): void;
$acceptProcessResize(id: number, cols: number, rows: number): void;
$acceptProcessShutdown(id: number, immediate: boolean): void;
$acceptProcessRequestInitialCwd(id: number): void;
$acceptProcessRequestCwd(id: number): void;
$acceptProcessRequestLatency(id: number): number;
$acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
$requestAvailableShells(): Promise<IShellDefinitionDto[]>;
$requestDefaultShellAndArgs(): Promise<IShellAndArgsDto>;
}
export interface ExtHostSCMShape {
@@ -1104,6 +1177,7 @@ export interface ExtHostSCMShape {
export interface ExtHostTaskShape {
$provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<tasks.TaskSetDTO>;
$resolveTask(handle: number, taskDTO: tasks.TaskDTO): Thenable<tasks.TaskDTO | undefined>;
$onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number): void;
$onDidStartTaskProcess(value: tasks.TaskProcessStartedDTO): void;
$onDidEndTaskProcess(value: tasks.TaskProcessEndedDTO): void;
@@ -1209,25 +1283,12 @@ export interface ExtHostProgressShape {
}
export interface ExtHostCommentsShape {
$provideDocumentComments(handle: number, document: UriComponents): Promise<modes.CommentInfo | null>;
$createNewCommentThread(handle: number, document: UriComponents, range: IRange, text: string): Promise<modes.CommentThread | null>;
$createCommentThreadTemplate(commentControllerHandle: number, uriComponents: UriComponents, range: IRange): void;
$onCommentWidgetInputChange(commentControllerHandle: number, document: UriComponents, range: IRange, input: string | undefined): Promise<number | undefined>;
$updateCommentThreadTemplate(commentControllerHandle: number, threadHandle: number, range: IRange): Promise<void>;
$deleteCommentThread(commentControllerHandle: number, commentThreadHandle: number): void;
$provideCommentingRanges(commentControllerHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise<IRange[] | undefined>;
$checkStaticContribution(commentControllerHandle: number): Promise<boolean>;
$provideReactionGroup(commentControllerHandle: number): Promise<modes.CommentReaction[] | undefined>;
$toggleReaction(commentControllerHandle: number, threadHandle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void>;
$createNewCommentWidgetCallback(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, token: CancellationToken): Promise<void>;
$replyToCommentThread(handle: number, document: UriComponents, range: IRange, commentThread: modes.CommentThread, text: string): Promise<modes.CommentThread | null>;
$editComment(handle: number, document: UriComponents, comment: modes.Comment, text: string): Promise<void>;
$deleteComment(handle: number, document: UriComponents, comment: modes.Comment): Promise<void>;
$startDraft(handle: number, document: UriComponents): Promise<void>;
$deleteDraft(handle: number, document: UriComponents): Promise<void>;
$finishDraft(handle: number, document: UriComponents): Promise<void>;
$addReaction(handle: number, document: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void>;
$deleteReaction(handle: number, document: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void>;
$provideWorkspaceComments(handle: number): Promise<modes.CommentThread[] | null>;
}
export interface ExtHostStorageShape {
@@ -1249,6 +1310,7 @@ export const MainContext = {
MainThreadDocuments: createMainId<MainThreadDocumentsShape>('MainThreadDocuments'),
MainThreadDocumentContentProviders: createMainId<MainThreadDocumentContentProvidersShape>('MainThreadDocumentContentProviders'),
MainThreadTextEditors: createMainId<MainThreadTextEditorsShape>('MainThreadTextEditors'),
MainThreadEditorInsets: createMainId<MainThreadEditorInsetsShape>('MainThreadEditorInsets'),
MainThreadErrors: createMainId<MainThreadErrorsShape>('MainThreadErrors'),
MainThreadTreeViews: createMainId<MainThreadTreeViewsShape>('MainThreadTreeViews'),
MainThreadKeytar: createMainId<MainThreadKeytarShape>('MainThreadKeytar'),
@@ -1271,6 +1333,7 @@ export const MainContext = {
MainThreadSearch: createMainId<MainThreadSearchShape>('MainThreadSearch'),
MainThreadTask: createMainId<MainThreadTaskShape>('MainThreadTask'),
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService')
};
export const ExtHostContext = {
@@ -1287,7 +1350,6 @@ export const ExtHostContext = {
ExtHostTreeViews: createExtId<ExtHostTreeViewsShape>('ExtHostTreeViews'),
ExtHostFileSystem: createExtId<ExtHostFileSystemShape>('ExtHostFileSystem'),
ExtHostFileSystemEventService: createExtId<ExtHostFileSystemEventServiceShape>('ExtHostFileSystemEventService'),
ExtHostHeapService: createExtId<ExtHostHeapServiceShape>('ExtHostHeapMonitor'),
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),
ExtHostQuickOpen: createExtId<ExtHostQuickOpenShape>('ExtHostQuickOpen'),
ExtHostExtensionService: createExtId<ExtHostExtensionServiceShape>('ExtHostExtensionService'),
@@ -1299,9 +1361,11 @@ export const ExtHostContext = {
ExtHostWorkspace: createExtId<ExtHostWorkspaceShape>('ExtHostWorkspace'),
ExtHostWindow: createExtId<ExtHostWindowShape>('ExtHostWindow'),
ExtHostWebviews: createExtId<ExtHostWebviewsShape>('ExtHostWebviews'),
ExtHostEditorInsets: createExtId<ExtHostEditorInsetsShape>('ExtHostEditorInsets'),
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress'),
ExtHostComments: createMainId<ExtHostCommentsShape>('ExtHostComments'),
ExtHostStorage: createMainId<ExtHostStorageShape>('ExtHostStorage'),
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls'),
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
ExtHosLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService')
};

View File

@@ -18,6 +18,7 @@ import { CustomCodeAction } from 'vs/workbench/api/common/extHostLanguageFeature
import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand, OpenIssueReporter } from './apiCommands';
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { IRange } from 'vs/editor/common/core/range';
export class ExtHostApiCommands {
@@ -143,7 +144,7 @@ export class ExtHostApiCommands {
description: 'Execute CodeLens provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'itemResolveCount', description: '(optional) Number of lenses that should be resolved and returned. Will only retrun resolved lenses, will impact performance)', constraint: (value: any) => value === undefined || typeof value === 'number' }
{ name: 'itemResolveCount', description: '(optional) Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)', constraint: (value: any) => value === undefined || typeof value === 'number' }
],
returns: 'A promise that resolves to an array of CodeLens-instances.'
});
@@ -223,7 +224,7 @@ export class ExtHostApiCommands {
description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.',
args: [
{ name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || value instanceof URI },
{ name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Wheter the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' }
{ name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Whether the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' }
]
});
@@ -414,15 +415,21 @@ export class ExtHostApiCommands {
});
}
private _executeSelectionRangeProvider(resource: URI, positions: types.Position[]): Promise<vscode.SelectionRange[][]> {
private _executeSelectionRangeProvider(resource: URI, positions: types.Position[]): Promise<vscode.SelectionRange[]> {
const pos = positions.map(typeConverters.Position.from);
const args = {
resource,
position: pos[0],
positions: pos
};
return this._commands.executeCommand<modes.SelectionRange[][]>('_executeSelectionRangeProvider', args).then(result => {
return result.map(oneResult => oneResult.map(typeConverters.SelectionRange.to));
return this._commands.executeCommand<IRange[][]>('_executeSelectionRangeProvider', args).then(result => {
return result.map(ranges => {
let node: types.SelectionRange | undefined;
for (const range of ranges.reverse()) {
node = new types.SelectionRange(typeConverters.Range.to(range), node);
}
return node!;
});
});
}
@@ -473,7 +480,7 @@ export class ExtHostApiCommands {
});
}
private _executeCodeActionProvider(resource: URI, range: types.Range, kind?: string): Promise<(vscode.CodeAction | vscode.Command)[] | undefined> {
private _executeCodeActionProvider(resource: URI, range: types.Range, kind?: string): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> {
const args = {
resource,
range: typeConverters.Range.from(range),
@@ -504,7 +511,7 @@ export class ExtHostApiCommands {
private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise<vscode.CodeLens[] | undefined> {
const args = { resource, itemResolveCount };
return this._commands.executeCommand<modes.ICodeLensSymbol[]>('_executeCodeLensProvider', args)
return this._commands.executeCommand<modes.CodeLens[]>('_executeCodeLensProvider', args)
.then(tryMapWith(item => {
return new types.CodeLens(
typeConverters.Range.to(item.range),

View File

@@ -0,0 +1,143 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor';
import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors';
import * as vscode from 'vscode';
import { ExtHostEditorInsetsShape, MainThreadEditorInsetsShape } from './extHost.protocol';
import { toWebviewResource, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import { generateUuid } from 'vs/base/common/uuid';
export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
private _handlePool = 0;
private _disposables = new DisposableStore();
private _insets = new Map<number, { editor: vscode.TextEditor, inset: vscode.WebviewEditorInset, onDidReceiveMessage: Emitter<any> }>();
constructor(
private readonly _proxy: MainThreadEditorInsetsShape,
private readonly _editors: ExtHostEditors,
private readonly _initData: WebviewInitData
) {
// dispose editor inset whenever the hosting editor goes away
this._disposables.add(_editors.onDidChangeVisibleTextEditors(() => {
const visibleEditor = _editors.getVisibleTextEditors();
this._insets.forEach(value => {
if (visibleEditor.indexOf(value.editor) < 0) {
value.inset.dispose(); // will remove from `this._insets`
}
});
}));
}
dispose(): void {
this._insets.forEach(value => value.inset.dispose());
this._disposables.dispose();
}
createWebviewEditorInset(editor: vscode.TextEditor, line: number, height: number, options: vscode.WebviewOptions | undefined, extension: IExtensionDescription): vscode.WebviewEditorInset {
let apiEditor: ExtHostTextEditor | undefined;
for (const candidate of this._editors.getVisibleTextEditors()) {
if (candidate === editor) {
apiEditor = <ExtHostTextEditor>candidate;
break;
}
}
if (!apiEditor) {
throw new Error('not a visible editor');
}
const that = this;
const handle = this._handlePool++;
const onDidReceiveMessage = new Emitter<any>();
const onDidDispose = new Emitter<void>();
const webview = new class implements vscode.Webview {
private readonly _uuid = generateUuid();
private _html: string = '';
private _options: vscode.WebviewOptions;
toWebviewResource(resource: vscode.Uri): vscode.Uri {
return toWebviewResource(that._initData, this._uuid, resource);
}
get cspSource(): string {
return that._initData.webviewCspSource;
}
set options(value: vscode.WebviewOptions) {
this._options = value;
that._proxy.$setOptions(handle, value);
}
get options(): vscode.WebviewOptions {
return this._options;
}
set html(value: string) {
this._html = value;
that._proxy.$setHtml(handle, value);
}
get html(): string {
return this._html;
}
get onDidReceiveMessage(): vscode.Event<any> {
return onDidReceiveMessage.event;
}
postMessage(message: any): Thenable<boolean> {
return that._proxy.$postMessage(handle, message);
}
};
const inset = new class implements vscode.WebviewEditorInset {
readonly editor: vscode.TextEditor = editor;
readonly line: number = line;
readonly height: number = height;
readonly webview: vscode.Webview = webview;
readonly onDidDispose: vscode.Event<void> = onDidDispose.event;
dispose(): void {
if (that._insets.has(handle)) {
that._insets.delete(handle);
that._proxy.$disposeEditorInset(handle);
onDidDispose.fire();
// final cleanup
onDidDispose.dispose();
onDidReceiveMessage.dispose();
}
}
};
this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.document.uri, line + 1, height, options || {}, extension.identifier, extension.extensionLocation);
this._insets.set(handle, { editor, inset, onDidReceiveMessage });
return inset;
}
$onDidDispose(handle: number): void {
const value = this._insets.get(handle);
if (value) {
value.inset.dispose();
}
}
$onDidReceiveMessage(handle: number, message: any): void {
const value = this._insets.get(handle);
if (value) {
value.onDidReceiveMessage.fire(message);
}
}
}

View File

@@ -9,7 +9,6 @@ import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import * as extHostTypeConverter from 'vs/workbench/api/common/extHostTypeConverters';
import { cloneAndChange } from 'vs/base/common/objects';
import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ObjectIdentifier, IMainContext, CommandDto } from './extHost.protocol';
import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import * as modes from 'vs/editor/common/modes';
import * as vscode from 'vscode';
@@ -18,6 +17,7 @@ import { revive } from 'vs/base/common/marshalling';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { URI } from 'vs/base/common/uri';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
interface CommandHandler {
callback: Function;
@@ -39,12 +39,11 @@ export class ExtHostCommands implements ExtHostCommandsShape {
constructor(
mainContext: IMainContext,
heapService: ExtHostHeapService,
logService: ILogService
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadCommands);
this._logService = logService;
this._converter = new CommandsConverter(this, heapService);
this._converter = new CommandsConverter(this);
this._argumentProcessors = [
{
processArgument(a) {
@@ -200,21 +199,18 @@ export class ExtHostCommands implements ExtHostCommandsShape {
export class CommandsConverter {
private readonly _delegatingCommandId: string;
private _commands: ExtHostCommands;
private _heap: ExtHostHeapService;
private readonly _commands: ExtHostCommands;
private readonly _cache = new Map<number, vscode.Command>();
private _cachIdPool = 0;
// --- conversion between internal and api commands
constructor(commands: ExtHostCommands, heap: ExtHostHeapService) {
constructor(commands: ExtHostCommands) {
this._delegatingCommandId = `_internal_command_delegation_${Date.now()}`;
this._commands = commands;
this._heap = heap;
this._commands.registerCommand(true, this._delegatingCommandId, this._executeConvertedCommand, this);
}
toInternal(command: vscode.Command): CommandDto;
toInternal(command: undefined): undefined;
toInternal(command: vscode.Command | undefined): CommandDto | undefined;
toInternal(command: vscode.Command | undefined): CommandDto | undefined {
toInternal(command: vscode.Command | undefined, disposables: DisposableStore): CommandDto | undefined {
if (!command) {
return undefined;
@@ -224,31 +220,31 @@ export class CommandsConverter {
$ident: undefined,
id: command.command,
title: command.title,
tooltip: command.tooltip
};
if (command.command && isNonEmptyArray(command.arguments)) {
// we have a contributed command with arguments. that
// means we don't want to send the arguments around
const id = this._heap.keep(command);
const id = ++this._cachIdPool;
this._cache.set(id, command);
disposables.add(toDisposable(() => this._cache.delete(id)));
result.$ident = id;
result.id = this._delegatingCommandId;
result.arguments = [id];
}
if (command.tooltip) {
result.tooltip = command.tooltip;
}
return result;
}
fromInternal(command: modes.Command): vscode.Command {
fromInternal(command: modes.Command): vscode.Command | undefined {
const id = ObjectIdentifier.of(command);
if (typeof id === 'number') {
return this._heap.get<vscode.Command>(id);
return this._cache.get(id);
} else {
return {
@@ -260,7 +256,10 @@ export class CommandsConverter {
}
private _executeConvertedCommand<R>(...args: any[]): Promise<R> {
const actualCmd = this._heap.get<vscode.Command>(args[0]);
const actualCmd = this._cache.get(args[0]);
if (!actualCmd) {
return Promise.reject('actual command NOT FOUND');
}
return this._commands.executeCommand(actualCmd.command, ...(actualCmd.arguments || []));
}

View File

@@ -4,29 +4,25 @@
*--------------------------------------------------------------------------------------------*/
import { asPromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { debounce } from 'vs/base/common/decorators';
import { Emitter } from 'vs/base/common/event';
import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IRange } from 'vs/editor/common/core/range';
import * as modes from 'vs/editor/common/modes';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import * as extHostTypeConverter from 'vs/workbench/api/common/extHostTypeConverters';
import * as types from 'vs/workbench/api/common/extHostTypes';
import * as vscode from 'vscode';
import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShape } from './extHost.protocol';
import { CommandsConverter, ExtHostCommands } from './extHostCommands';
import { IRange } from 'vs/editor/common/core/range';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Event, Emitter } from 'vs/base/common/event';
import { debounce } from 'vs/base/common/decorators';
interface HandlerData<T> {
extensionId: ExtensionIdentifier;
provider: T;
}
import { ExtHostCommands } from './extHostCommands';
type ProviderHandle = number;
export class ExtHostComments implements ExtHostCommentsShape {
export class ExtHostComments implements ExtHostCommentsShape, IDisposable {
private static handlePool = 0;
private _proxy: MainThreadCommentsShape;
@@ -35,17 +31,15 @@ export class ExtHostComments implements ExtHostCommentsShape {
private _commentControllersByExtension: Map<string, ExtHostCommentController[]> = new Map<string, ExtHostCommentController[]>();
private _documentProviders = new Map<number, HandlerData<vscode.DocumentCommentProvider>>();
private _workspaceProviders = new Map<number, HandlerData<vscode.WorkspaceCommentProvider>>();
constructor(
mainContext: IMainContext,
private _commands: ExtHostCommands,
commands: ExtHostCommands,
private readonly _documents: ExtHostDocuments,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadComments);
_commands.registerArgumentProcessor({
commands.registerArgumentProcessor({
processArgument: arg => {
if (arg && arg.$mid === 6) {
const commentController = this._commentControllers.get(arg.handle);
@@ -142,7 +136,7 @@ export class ExtHostComments implements ExtHostCommentsShape {
createCommentController(extension: IExtensionDescription, id: string, label: string): vscode.CommentController {
const handle = ExtHostComments.handlePool++;
const commentController = new ExtHostCommentController(extension, handle, this._commands.converter, this._proxy, id, label);
const commentController = new ExtHostCommentController(extension, handle, this._proxy, id, label);
this._commentControllers.set(commentController.handle, commentController);
const commentControllers = this._commentControllersByExtension.get(ExtensionIdentifier.toKey(extension.identifier)) || [];
@@ -162,15 +156,14 @@ export class ExtHostComments implements ExtHostCommentsShape {
commentController.$createCommentThreadTemplate(uriComponents, range);
}
$onCommentWidgetInputChange(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, input: string): Promise<number | undefined> {
async $updateCommentThreadTemplate(commentControllerHandle: number, threadHandle: number, range: IRange) {
const commentController = this._commentControllers.get(commentControllerHandle);
if (!commentController) {
return Promise.resolve(undefined);
return;
}
commentController.$onCommentWidgetInputChange(uriComponents, range, input);
return Promise.resolve(commentControllerHandle);
commentController.$updateCommentThreadTemplate(threadHandle, range);
}
$deleteCommentThread(commentControllerHandle: number, commentThreadHandle: number) {
@@ -203,14 +196,14 @@ export class ExtHostComments implements ExtHostCommentsShape {
return asPromise(() => {
return commentController!.reactionProvider!.availableReactions;
}).then(reactions => reactions.map(reaction => convertToReaction2(commentController.reactionProvider, reaction)));
}).then(reactions => reactions.map(reaction => convertToReaction(commentController.reactionProvider, reaction)));
}
$toggleReaction(commentControllerHandle: number, threadHandle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void> {
const document = this._documents.getDocument(URI.revive(uri));
const commentController = this._commentControllers.get(commentControllerHandle);
if (!commentController || !commentController.reactionProvider || !commentController.reactionProvider.toggleReaction) {
if (!commentController || !((commentController.reactionProvider && commentController.reactionProvider.toggleReaction) || commentController.reactionHandler)) {
return Promise.resolve(undefined);
}
@@ -219,238 +212,24 @@ export class ExtHostComments implements ExtHostCommentsShape {
if (commentThread) {
const vscodeComment = commentThread.getComment(comment.commentId);
if (commentController !== undefined && commentController.reactionProvider && commentController.reactionProvider.toggleReaction && vscodeComment) {
return commentController.reactionProvider.toggleReaction(document, vscodeComment, convertFromReaction(reaction));
if (commentController !== undefined && vscodeComment) {
if (commentController.reactionHandler) {
return commentController.reactionHandler(vscodeComment, convertFromReaction(reaction));
}
if (commentController.reactionProvider && commentController.reactionProvider.toggleReaction) {
return commentController.reactionProvider.toggleReaction(document, vscodeComment, convertFromReaction(reaction));
}
}
}
return Promise.resolve(undefined);
});
}
$createNewCommentWidgetCallback(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, token: CancellationToken): Promise<void> {
const commentController = this._commentControllers.get(commentControllerHandle);
dispose() {
if (!commentController) {
return Promise.resolve();
}
if (!(commentController as any).emptyCommentThreadFactory) {
return Promise.resolve();
}
const document = this._documents.getDocument(URI.revive(uriComponents));
return asPromise(() => {
if ((commentController as any).emptyCommentThreadFactory) {
return (commentController as any).emptyCommentThreadFactory!.createEmptyCommentThread(document, extHostTypeConverter.Range.to(range));
}
}).then(() => Promise.resolve());
}
$checkStaticContribution(commentControllerHandle: number): Promise<boolean> {
const commentController = this._commentControllers.get(commentControllerHandle);
if (!commentController) {
return Promise.resolve(false);
}
if (!(commentController as any).emptyCommentThreadFactory) {
return Promise.resolve(true);
}
return Promise.resolve(false);
}
registerWorkspaceCommentProvider(
extensionId: ExtensionIdentifier,
provider: vscode.WorkspaceCommentProvider
): vscode.Disposable {
const handle = ExtHostComments.handlePool++;
this._workspaceProviders.set(handle, {
extensionId,
provider
});
this._proxy.$registerWorkspaceCommentProvider(handle, extensionId);
this.registerListeners(handle, extensionId, provider);
return {
dispose: () => {
this._proxy.$unregisterWorkspaceCommentProvider(handle);
this._workspaceProviders.delete(handle);
}
};
}
registerDocumentCommentProvider(
extensionId: ExtensionIdentifier,
provider: vscode.DocumentCommentProvider
): vscode.Disposable {
const handle = ExtHostComments.handlePool++;
this._documentProviders.set(handle, {
extensionId,
provider
});
this._proxy.$registerDocumentCommentProvider(handle, {
startDraftLabel: provider.startDraftLabel,
deleteDraftLabel: provider.deleteDraftLabel,
finishDraftLabel: provider.finishDraftLabel,
reactionGroup: provider.reactionGroup ? provider.reactionGroup.map(reaction => convertToReaction(provider, reaction)) : undefined
});
this.registerListeners(handle, extensionId, provider);
return {
dispose: () => {
this._proxy.$unregisterDocumentCommentProvider(handle);
this._documentProviders.delete(handle);
}
};
}
$createNewCommentThread(handle: number, uri: UriComponents, range: IRange, text: string): Promise<modes.CommentThread | null> {
const data = this._documents.getDocumentData(URI.revive(uri));
const ran = <vscode.Range>extHostTypeConverter.Range.to(range);
if (!data || !data.document) {
return Promise.resolve(null);
}
const handlerData = this.getDocumentProvider(handle);
return asPromise(() => {
return handlerData.provider.createNewCommentThread(data.document, ran, text, CancellationToken.None);
}).then(commentThread => commentThread ? convertToCommentThread(handlerData.extensionId, handlerData.provider, commentThread, this._commands.converter) : null);
}
$replyToCommentThread(handle: number, uri: UriComponents, range: IRange, thread: modes.CommentThread, text: string): Promise<modes.CommentThread | null> {
const data = this._documents.getDocumentData(URI.revive(uri));
const ran = <vscode.Range>extHostTypeConverter.Range.to(range);
if (!data || !data.document) {
return Promise.resolve(null);
}
const handlerData = this.getDocumentProvider(handle);
return asPromise(() => {
return handlerData.provider.replyToCommentThread(data.document, ran, convertFromCommentThread(thread), text, CancellationToken.None);
}).then(commentThread => commentThread ? convertToCommentThread(handlerData.extensionId, handlerData.provider, commentThread, this._commands.converter) : null);
}
$editComment(handle: number, uri: UriComponents, comment: modes.Comment, text: string): Promise<void> {
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.editComment) {
return Promise.reject(new Error('not implemented'));
}
return asPromise(() => {
return handlerData.provider.editComment!(document, convertFromComment(comment), text, CancellationToken.None);
});
}
$deleteComment(handle: number, uri: UriComponents, comment: modes.Comment): Promise<void> {
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.deleteComment) {
return Promise.reject(new Error('not implemented'));
}
return asPromise(() => {
return handlerData.provider.deleteComment!(document, convertFromComment(comment), CancellationToken.None);
});
}
$startDraft(handle: number, uri: UriComponents): Promise<void> {
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.startDraft) {
return Promise.reject(new Error('not implemented'));
}
return asPromise(() => {
return handlerData.provider.startDraft!(document, CancellationToken.None);
});
}
$deleteDraft(handle: number, uri: UriComponents): Promise<void> {
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.deleteDraft) {
return Promise.reject(new Error('not implemented'));
}
return asPromise(() => {
return handlerData.provider.deleteDraft!(document, CancellationToken.None);
});
}
$finishDraft(handle: number, uri: UriComponents): Promise<void> {
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.finishDraft) {
return Promise.reject(new Error('not implemented'));
}
return asPromise(() => {
return handlerData.provider.finishDraft!(document, CancellationToken.None);
});
}
$addReaction(handle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void> {
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.addReaction) {
return Promise.reject(new Error('not implemented'));
}
return asPromise(() => {
return handlerData.provider.addReaction!(document, convertFromComment(comment), convertFromReaction(reaction));
});
}
$deleteReaction(handle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void> {
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.deleteReaction) {
return Promise.reject(new Error('not implemented'));
}
return asPromise(() => {
return handlerData.provider.deleteReaction!(document, convertFromComment(comment), convertFromReaction(reaction));
});
}
$provideDocumentComments(handle: number, uri: UriComponents): Promise<modes.CommentInfo | null> {
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
return asPromise(() => {
return handlerData.provider.provideDocumentComments(document, CancellationToken.None);
}).then(commentInfo => commentInfo ? convertCommentInfo(handle, handlerData.extensionId, handlerData.provider, commentInfo, this._commands.converter) : null);
}
$provideWorkspaceComments(handle: number): Promise<modes.CommentThread[] | null> {
const handlerData = this._workspaceProviders.get(handle);
if (!handlerData) {
return Promise.resolve(null);
}
return asPromise(() => {
return handlerData.provider.provideWorkspaceComments(CancellationToken.None);
}).then(comments =>
comments.map(comment => convertToCommentThread(handlerData.extensionId, handlerData.provider, comment, this._commands.converter)
));
}
private registerListeners(handle: number, extensionId: ExtensionIdentifier, provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider) {
provider.onDidChangeCommentThreads(event => {
this._proxy.$onDidCommentThreadsChange(handle, {
changed: event.changed.map(thread => convertToCommentThread(extensionId, provider, thread, this._commands.converter)),
added: event.added.map(thread => convertToCommentThread(extensionId, provider, thread, this._commands.converter)),
removed: event.removed.map(thread => convertToCommentThread(extensionId, provider, thread, this._commands.converter)),
draftMode: !!(provider as vscode.DocumentCommentProvider).startDraft && !!(provider as vscode.DocumentCommentProvider).finishDraft ? (event.inDraftMode ? modes.DraftMode.InDraft : modes.DraftMode.NotInDraft) : modes.DraftMode.NotSupported
});
});
}
private getDocumentProvider(handle: number): HandlerData<vscode.DocumentCommentProvider> {
const provider = this._documentProviders.get(handle);
if (!provider) {
throw new Error('unknown provider');
}
return provider;
}
}
@@ -524,36 +303,6 @@ export class ExtHostCommentThread implements vscode.CommentThread {
this._onDidUpdateCommentThread.fire();
}
private _acceptInputCommand: vscode.Command;
get acceptInputCommand(): vscode.Command {
return this._acceptInputCommand;
}
set acceptInputCommand(acceptInputCommand: vscode.Command) {
this._acceptInputCommand = acceptInputCommand;
this._onDidUpdateCommentThread.fire();
}
private _additionalCommands: vscode.Command[] = [];
get additionalCommands(): vscode.Command[] {
return this._additionalCommands;
}
set additionalCommands(additionalCommands: vscode.Command[]) {
this._additionalCommands = additionalCommands;
this._onDidUpdateCommentThread.fire();
}
private _deleteCommand?: vscode.Command;
get deleteComand(): vscode.Command | undefined {
return this._deleteCommand;
}
set deleteCommand(deleteCommand: vscode.Command) {
this._deleteCommand = deleteCommand;
this._onDidUpdateCommentThread.fire();
}
private _collapseState?: vscode.CommentThreadCollapsibleState;
get collapsibleState(): vscode.CommentThreadCollapsibleState {
@@ -575,15 +324,19 @@ export class ExtHostCommentThread implements vscode.CommentThread {
private _commentsMap: Map<vscode.Comment, number> = new Map<vscode.Comment, number>();
private _acceptInputDisposables = new MutableDisposable<DisposableStore>();
constructor(
private _proxy: MainThreadCommentsShape,
private readonly _commandsConverter: CommandsConverter,
private _commentController: ExtHostCommentController,
private _id: string | undefined,
private _uri: vscode.Uri,
private _range: vscode.Range,
private _comments: vscode.Comment[]
private _comments: vscode.Comment[],
extensionId: ExtensionIdentifier
) {
this._acceptInputDisposables.value = new DisposableStore();
if (this._id === undefined) {
this._id = `${_commentController.id}.${this.handle}`;
}
@@ -593,7 +346,8 @@ export class ExtHostCommentThread implements vscode.CommentThread {
this.handle,
this._id,
this._uri,
extHostTypeConverter.Range.from(this._range)
extHostTypeConverter.Range.from(this._range),
extensionId
);
this._localDisposables = [];
@@ -607,15 +361,21 @@ export class ExtHostCommentThread implements vscode.CommentThread {
this.comments = _comments;
}
@debounce(100)
eventuallyUpdateCommentThread(): void {
if (this._isDiposed) {
return;
}
if (!this._acceptInputDisposables.value) {
this._acceptInputDisposables.value = new DisposableStore();
}
const commentThreadRange = extHostTypeConverter.Range.from(this._range);
const label = this.label;
const contextValue = this.contextValue;
const comments = this._comments.map(cmt => { return convertToModeComment2(this, this._commentController, cmt, this._commandsConverter, this._commentsMap); });
const acceptInputCommand = this._acceptInputCommand ? this._commandsConverter.toInternal(this._acceptInputCommand) : undefined;
const additionalCommands = this._additionalCommands ? this._additionalCommands.map(x => this._commandsConverter.toInternal(x)) : [];
const deleteCommand = this._deleteCommand ? this._commandsConverter.toInternal(this._deleteCommand) : undefined;
const comments = this._comments.map(cmt => { return convertToModeComment(this, this._commentController, cmt, this._commentsMap); });
const collapsibleState = convertToCollapsibleState(this._collapseState);
this._proxy.$updateCommentThread(
@@ -627,9 +387,6 @@ export class ExtHostCommentThread implements vscode.CommentThread {
label,
contextValue,
comments,
acceptInputCommand,
additionalCommands,
deleteCommand,
collapsibleState
);
}
@@ -657,56 +414,18 @@ export class ExtHostCommentThread implements vscode.CommentThread {
}
dispose() {
this._isDiposed = true;
this._acceptInputDisposables.dispose();
this._localDisposables.forEach(disposable => disposable.dispose());
this._proxy.$deleteCommentThread(
this._commentController.handle,
this.handle
);
this._isDiposed = true;
}
}
export class ExtHostCommentInputBox implements vscode.CommentInputBox {
get resource(): vscode.Uri {
return this._resource;
}
get range(): vscode.Range {
return this._range;
}
get value(): string {
return this._value;
}
set value(newInput: string) {
this._value = newInput;
this._onDidChangeValue.fire(this._value);
this._proxy.$setInputValue(this.commentControllerHandle, newInput);
}
private _onDidChangeValue = new Emitter<string>();
get onDidChangeValue(): Event<string> {
return this._onDidChangeValue.event;
}
constructor(
private _proxy: MainThreadCommentsShape,
public commentControllerHandle: number,
private _resource: vscode.Uri,
private _range: vscode.Range,
private _value: string
) {
}
setInput(resource: vscode.Uri, range: vscode.Range, input: string) {
this._resource = resource;
this._range = range;
this._value = input;
}
}
type ReactionHandler = (comment: vscode.Comment, reaction: vscode.CommentReaction) => Promise<void>;
class ExtHostCommentController implements vscode.CommentController {
get id(): string {
return this._id;
@@ -716,16 +435,12 @@ class ExtHostCommentController implements vscode.CommentController {
return this._label;
}
public inputBox: ExtHostCommentInputBox | undefined;
public activeCommentingRange?: vscode.Range;
public get handle(): number {
return this._handle;
}
private _threads: Map<number, ExtHostCommentThread> = new Map<number, ExtHostCommentThread>();
commentingRangeProvider?: vscode.CommentingRangeProvider & { createEmptyCommentThread: (document: vscode.TextDocument, range: types.Range) => Promise<vscode.CommentThread>; };
commentingRangeProvider?: vscode.CommentingRangeProvider;
private _commentReactionProvider?: vscode.CommentReactionProvider;
@@ -736,14 +451,25 @@ class ExtHostCommentController implements vscode.CommentController {
set reactionProvider(provider: vscode.CommentReactionProvider | undefined) {
this._commentReactionProvider = provider;
if (provider) {
this._proxy.$updateCommentControllerFeatures(this.handle, { reactionGroup: provider.availableReactions.map(reaction => convertToReaction2(provider, reaction)) });
this._proxy.$updateCommentControllerFeatures(this.handle, { reactionGroup: provider.availableReactions.map(reaction => convertToReaction(provider, reaction)) });
}
}
private _reactionHandler?: ReactionHandler;
get reactionHandler(): ReactionHandler | undefined {
return this._reactionHandler;
}
set reactionHandler(handler: ReactionHandler | undefined) {
this._reactionHandler = handler;
this._proxy.$updateCommentControllerFeatures(this.handle, { reactionHandler: !!handler });
}
constructor(
_extension: IExtensionDescription,
private _extension: IExtensionDescription,
private _handle: number,
private readonly _commandsConverter: CommandsConverter,
private _proxy: MainThreadCommentsShape,
private _id: string,
private _label: string
@@ -752,27 +478,33 @@ class ExtHostCommentController implements vscode.CommentController {
}
createCommentThread(resource: vscode.Uri, range: vscode.Range, comments: vscode.Comment[]): vscode.CommentThread;
createCommentThread(id: string, resource: vscode.Uri, range: vscode.Range, comments: vscode.Comment[]): vscode.CommentThread;
createCommentThread(arg0: vscode.Uri | string, arg1: vscode.Uri | vscode.Range, arg2: vscode.Range | vscode.Comment[], arg3?: vscode.Comment[]): vscode.CommentThread {
if (typeof arg0 === 'string') {
const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, arg0, arg1 as vscode.Uri, arg2 as vscode.Range, arg3 as vscode.Comment[]);
const commentThread = new ExtHostCommentThread(this._proxy, this, arg0, arg1 as vscode.Uri, arg2 as vscode.Range, arg3 as vscode.Comment[], this._extension.identifier);
this._threads.set(commentThread.handle, commentThread);
return commentThread;
} else {
const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, undefined, arg0 as vscode.Uri, arg1 as vscode.Range, arg2 as vscode.Comment[]);
const commentThread = new ExtHostCommentThread(this._proxy, this, undefined, arg0 as vscode.Uri, arg1 as vscode.Range, arg2 as vscode.Comment[], this._extension.identifier);
this._threads.set(commentThread.handle, commentThread);
return commentThread;
}
}
$createCommentThreadTemplate(uriComponents: UriComponents, range: IRange) {
const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, undefined, URI.revive(uriComponents), extHostTypeConverter.Range.to(range), []);
$createCommentThreadTemplate(uriComponents: UriComponents, range: IRange): ExtHostCommentThread {
const commentThread = new ExtHostCommentThread(this._proxy, this, undefined, URI.revive(uriComponents), extHostTypeConverter.Range.to(range), [], this._extension.identifier);
commentThread.collapsibleState = modes.CommentThreadCollapsibleState.Expanded;
this._threads.set(commentThread.handle, commentThread);
return commentThread;
}
$deleteCommentThread(threadHandle: number) {
$updateCommentThreadTemplate(threadHandle: number, range: IRange): void {
let thread = this._threads.get(threadHandle);
if (thread) {
thread.range = extHostTypeConverter.Range.to(range);
}
}
$deleteCommentThread(threadHandle: number): void {
let thread = this._threads.get(threadHandle);
if (thread) {
@@ -782,15 +514,7 @@ class ExtHostCommentController implements vscode.CommentController {
this._threads.delete(threadHandle);
}
$onCommentWidgetInputChange(uriComponents: UriComponents, range: IRange, input: string) {
if (!this.inputBox) {
this.inputBox = new ExtHostCommentInputBox(this._proxy, this.handle, URI.revive(uriComponents), extHostTypeConverter.Range.to(range), input);
} else {
this.inputBox.setInput(URI.revive(uriComponents), extHostTypeConverter.Range.to(range), input);
}
}
getCommentThread(handle: number) {
getCommentThread(handle: number): ExtHostCommentThread | undefined {
return this._threads.get(handle);
}
@@ -803,74 +527,7 @@ class ExtHostCommentController implements vscode.CommentController {
}
}
function convertCommentInfo(owner: number, extensionId: ExtensionIdentifier, provider: vscode.DocumentCommentProvider, vscodeCommentInfo: vscode.CommentInfo, commandsConverter: CommandsConverter): modes.CommentInfo {
return {
extensionId: extensionId.value,
threads: vscodeCommentInfo.threads.map(x => convertToCommentThread(extensionId, provider, x, commandsConverter)),
commentingRanges: vscodeCommentInfo.commentingRanges ? vscodeCommentInfo.commentingRanges.map(range => extHostTypeConverter.Range.from(range)) : [],
draftMode: provider.startDraft && provider.finishDraft ? (vscodeCommentInfo.inDraftMode ? modes.DraftMode.InDraft : modes.DraftMode.NotInDraft) : modes.DraftMode.NotSupported
};
}
function convertToCommentThread(extensionId: ExtensionIdentifier, provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider, vscodeCommentThread: vscode.CommentThread, commandsConverter: CommandsConverter): modes.CommentThread {
return {
extensionId: extensionId.value,
threadId: vscodeCommentThread.id,
resource: vscodeCommentThread.resource.toString(),
range: extHostTypeConverter.Range.from(vscodeCommentThread.range),
comments: vscodeCommentThread.comments.map(comment => convertToComment(provider, comment as vscode.Comment, commandsConverter)),
collapsibleState: vscodeCommentThread.collapsibleState
};
}
function convertFromCommentThread(commentThread: modes.CommentThread): vscode.CommentThread {
return {
id: commentThread.threadId!,
threadId: commentThread.threadId!,
uri: URI.parse(commentThread.resource!),
resource: URI.parse(commentThread.resource!),
range: extHostTypeConverter.Range.to(commentThread.range),
comments: commentThread.comments ? commentThread.comments.map(convertFromComment) : [],
collapsibleState: commentThread.collapsibleState,
dispose: () => { }
} as vscode.CommentThread;
}
function convertFromComment(comment: modes.Comment): vscode.Comment {
let userIconPath: URI | undefined;
if (comment.userIconPath) {
try {
userIconPath = URI.parse(comment.userIconPath);
} catch (e) {
// Ignore
}
}
return {
id: comment.commentId,
commentId: comment.commentId,
body: extHostTypeConverter.MarkdownString.to(comment.body),
author: {
name: comment.userName,
iconPath: userIconPath
},
userName: comment.userName,
userIconPath: userIconPath,
canEdit: comment.canEdit,
canDelete: comment.canDelete,
isDraft: comment.isDraft,
commentReactions: comment.commentReactions ? comment.commentReactions.map(reaction => {
return {
label: reaction.label,
count: reaction.count,
hasReacted: reaction.hasReacted
};
}) : undefined,
mode: comment.mode ? comment.mode : modes.CommentMode.Preview
};
}
function convertToModeComment2(thread: ExtHostCommentThread, commentController: ExtHostCommentController, vscodeComment: vscode.Comment, commandsConverter: CommandsConverter, commentsMap: Map<vscode.Comment, number>): modes.Comment {
function convertToModeComment(thread: ExtHostCommentThread, commentController: ExtHostCommentController, vscodeComment: vscode.Comment, commentsMap: Map<vscode.Comment, number>): modes.Comment {
let commentUniqueId = commentsMap.get(vscodeComment)!;
if (!commentUniqueId) {
commentUniqueId = ++thread.commentHandle;
@@ -880,54 +537,19 @@ function convertToModeComment2(thread: ExtHostCommentThread, commentController:
const iconPath = vscodeComment.author && vscodeComment.author.iconPath ? vscodeComment.author.iconPath.toString() : undefined;
return {
commentId: vscodeComment.id || vscodeComment.commentId,
commentId: vscodeComment.commentId,
mode: vscodeComment.mode,
contextValue: vscodeComment.contextValue,
uniqueIdInThread: commentUniqueId,
body: extHostTypeConverter.MarkdownString.from(vscodeComment.body),
userName: vscodeComment.author ? vscodeComment.author.name : vscodeComment.userName,
userName: vscodeComment.author.name,
userIconPath: iconPath,
isDraft: vscodeComment.isDraft,
selectCommand: vscodeComment.selectCommand ? commandsConverter.toInternal(vscodeComment.selectCommand) : undefined,
editCommand: vscodeComment.editCommand ? commandsConverter.toInternal(vscodeComment.editCommand) : undefined,
deleteCommand: vscodeComment.deleteCommand ? commandsConverter.toInternal(vscodeComment.deleteCommand) : undefined,
label: vscodeComment.label,
commentReactions: vscodeComment.commentReactions ? vscodeComment.commentReactions.map(reaction => convertToReaction2(commentController.reactionProvider, reaction)) : undefined
commentReactions: vscodeComment.reactions ? vscodeComment.reactions.map(reaction => convertToReaction(commentController.reactionProvider, reaction)) : undefined
};
}
function convertToComment(provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider, vscodeComment: vscode.Comment, commandsConverter: CommandsConverter): modes.Comment {
const canEdit = !!(provider as vscode.DocumentCommentProvider).editComment && vscodeComment.canEdit;
const canDelete = !!(provider as vscode.DocumentCommentProvider).deleteComment && vscodeComment.canDelete;
const iconPath = vscodeComment.userIconPath ? vscodeComment.userIconPath.toString() : vscodeComment.gravatar;
return {
commentId: vscodeComment.commentId,
body: extHostTypeConverter.MarkdownString.from(vscodeComment.body),
userName: vscodeComment.userName,
userIconPath: iconPath,
canEdit: canEdit,
canDelete: canDelete,
selectCommand: vscodeComment.command ? commandsConverter.toInternal(vscodeComment.command) : undefined,
isDraft: vscodeComment.isDraft,
commentReactions: vscodeComment.commentReactions ? vscodeComment.commentReactions.map(reaction => convertToReaction(provider, reaction)) : undefined
};
}
function convertToReaction(provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider, reaction: vscode.CommentReaction): modes.CommentReaction {
const providerCanDeleteReaction = !!(provider as vscode.DocumentCommentProvider).deleteReaction;
const providerCanAddReaction = !!(provider as vscode.DocumentCommentProvider).addReaction;
return {
label: reaction.label,
iconPath: reaction.iconPath ? extHostTypeConverter.pathOrURIToURI(reaction.iconPath) : undefined,
count: reaction.count,
hasReacted: reaction.hasReacted,
canEdit: (reaction.hasReacted && providerCanDeleteReaction) || (!reaction.hasReacted && providerCanAddReaction)
};
}
function convertToReaction2(provider: vscode.CommentReactionProvider | undefined, reaction: vscode.CommentReaction): modes.CommentReaction {
function convertToReaction(provider: vscode.CommentReactionProvider | undefined, reaction: vscode.CommentReaction): modes.CommentReaction {
return {
label: reaction.label,
iconPath: reaction.iconPath ? extHostTypeConverter.pathOrURIToURI(reaction.iconPath) : undefined,
@@ -939,9 +561,11 @@ function convertToReaction2(provider: vscode.CommentReactionProvider | undefined
function convertFromReaction(reaction: modes.CommentReaction): vscode.CommentReaction {
return {
label: reaction.label,
count: reaction.count,
hasReacted: reaction.hasReacted
label: reaction.label || '',
count: reaction.count || 0,
iconPath: reaction.iconPath ? URI.revive(reaction.iconPath) : '',
hasReacted: reaction.hasReacted,
authorHasReacted: reaction.hasReacted || false
};
}

View File

@@ -71,14 +71,14 @@ export class ExtHostConfigProvider {
private readonly _onDidChangeConfiguration = new Emitter<vscode.ConfigurationChangeEvent>();
private readonly _proxy: MainThreadConfigurationShape;
private readonly _extHostWorkspace: ExtHostWorkspace;
private _configurationScopes: { [key: string]: ConfigurationScope };
private _configurationScopes: Map<string, ConfigurationScope | undefined>;
private _configuration: Configuration;
constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationInitData) {
this._proxy = proxy;
this._extHostWorkspace = extHostWorkspace;
this._configuration = ExtHostConfigProvider.parse(data);
this._configurationScopes = data.configurationScopes;
this._configurationScopes = this._toMap(data.configurationScopes);
}
get onDidChangeConfiguration(): Event<vscode.ConfigurationChangeEvent> {
@@ -87,7 +87,7 @@ export class ExtHostConfigProvider {
$acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData) {
this._configuration = ExtHostConfigProvider.parse(data);
this._configurationScopes = data.configurationScopes;
this._configurationScopes = this._toMap(data.configurationScopes);
this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(eventData));
}
@@ -225,7 +225,7 @@ export class ExtHostConfigProvider {
}
private _validateConfigurationAccess(key: string, resource: URI | undefined, extensionId?: ExtensionIdentifier): void {
const scope = OVERRIDE_PROPERTY_PATTERN.test(key) ? ConfigurationScope.RESOURCE : this._configurationScopes[key];
const scope = OVERRIDE_PROPERTY_PATTERN.test(key) ? ConfigurationScope.RESOURCE : this._configurationScopes.get(key);
const extensionIdText = extensionId ? `[${extensionId.value}] ` : '';
if (ConfigurationScope.RESOURCE === scope) {
if (resource === undefined) {
@@ -255,6 +255,10 @@ export class ExtHostConfigProvider {
});
}
private _toMap(scopes: [string, ConfigurationScope | undefined][]): Map<string, ConfigurationScope | undefined> {
return scopes.reduce((result, scope) => { result.set(scope[0], scope[1]); return result; }, new Map<string, ConfigurationScope | undefined>());
}
private static parse(data: IConfigurationData): Configuration {
const defaultConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.defaults);
const userConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.user);

View File

@@ -47,9 +47,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
return this._name;
}
set(uri: vscode.Uri, diagnostics: vscode.Diagnostic[]): void;
set(entries: [vscode.Uri, vscode.Diagnostic[]][]): void;
set(first: vscode.Uri | [vscode.Uri, vscode.Diagnostic[]][], diagnostics?: vscode.Diagnostic[]) {
set(uri: vscode.Uri, diagnostics: ReadonlyArray<vscode.Diagnostic>): void;
set(entries: ReadonlyArray<[vscode.Uri, ReadonlyArray<vscode.Diagnostic>]>): void;
set(first: vscode.Uri | ReadonlyArray<[vscode.Uri, ReadonlyArray<vscode.Diagnostic>]>, diagnostics?: ReadonlyArray<vscode.Diagnostic>) {
if (!first) {
// this set-call is a clear-call
@@ -167,7 +167,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
this._proxy.$clear(this._owner);
}
forEach(callback: (uri: URI, diagnostics: vscode.Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void {
forEach(callback: (uri: URI, diagnostics: ReadonlyArray<vscode.Diagnostic>, collection: DiagnosticCollection) => any, thisArg?: any): void {
this._checkDisposed();
this._data.forEach((value, key) => {
const uri = URI.parse(key);
@@ -175,11 +175,11 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
});
}
get(uri: URI): vscode.Diagnostic[] {
get(uri: URI): ReadonlyArray<vscode.Diagnostic> {
this._checkDisposed();
const result = this._data.get(uri.toString());
if (Array.isArray(result)) {
return <vscode.Diagnostic[]>Object.freeze(result.slice(0));
return <ReadonlyArray<vscode.Diagnostic>>Object.freeze(result.slice(0));
}
return [];
}
@@ -278,10 +278,10 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
return result;
}
getDiagnostics(resource: vscode.Uri): vscode.Diagnostic[];
getDiagnostics(): [vscode.Uri, vscode.Diagnostic[]][];
getDiagnostics(resource?: vscode.Uri): vscode.Diagnostic[] | [vscode.Uri, vscode.Diagnostic[]][];
getDiagnostics(resource?: vscode.Uri): vscode.Diagnostic[] | [vscode.Uri, vscode.Diagnostic[]][] {
getDiagnostics(resource: vscode.Uri): ReadonlyArray<vscode.Diagnostic>;
getDiagnostics(): ReadonlyArray<[vscode.Uri, ReadonlyArray<vscode.Diagnostic>]>;
getDiagnostics(resource?: vscode.Uri): ReadonlyArray<vscode.Diagnostic> | ReadonlyArray<[vscode.Uri, ReadonlyArray<vscode.Diagnostic>]>;
getDiagnostics(resource?: vscode.Uri): ReadonlyArray<vscode.Diagnostic> | ReadonlyArray<[vscode.Uri, ReadonlyArray<vscode.Diagnostic>]> {
if (resource) {
return this._getDiagnostics(resource);
} else {
@@ -302,7 +302,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
}
}
private _getDiagnostics(resource: vscode.Uri): vscode.Diagnostic[] {
private _getDiagnostics(resource: vscode.Uri): ReadonlyArray<vscode.Diagnostic> {
let res: vscode.Diagnostic[] = [];
this._collections.forEach(collection => {
if (collection.has(resource)) {

View File

@@ -29,14 +29,10 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro
this._proxy = mainContext.getProxy(MainContext.MainThreadDocumentContentProviders);
}
dispose(): void {
// todo@joh
}
registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider): vscode.Disposable {
// todo@remote
// check with scheme from fs-providers!
if (scheme === Schemas.file || scheme === Schemas.untitled) {
if (Object.keys(Schemas).indexOf(scheme) >= 0) {
throw new Error(`scheme '${scheme}' already registered`);
}

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 { URI, UriComponents } from 'vs/base/common/uri';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import { ExtHostDocumentsShape, IMainContext, MainContext, MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
@@ -25,7 +25,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
readonly onDidChangeDocument: Event<vscode.TextDocumentChangeEvent> = this._onDidChangeDocument.event;
readonly onDidSaveDocument: Event<vscode.TextDocument> = this._onDidSaveDocument.event;
private _toDispose: IDisposable[];
private readonly _toDispose = new DisposableStore();
private _proxy: MainThreadDocumentsShape;
private _documentsAndEditors: ExtHostDocumentsAndEditors;
private _documentLoader = new Map<string, Promise<ExtHostDocumentData>>();
@@ -34,22 +34,20 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
this._proxy = mainContext.getProxy(MainContext.MainThreadDocuments);
this._documentsAndEditors = documentsAndEditors;
this._toDispose = [
this._documentsAndEditors.onDidRemoveDocuments(documents => {
for (const data of documents) {
this._onDidRemoveDocument.fire(data.document);
}
}),
this._documentsAndEditors.onDidAddDocuments(documents => {
for (const data of documents) {
this._onDidAddDocument.fire(data.document);
}
})
];
this._documentsAndEditors.onDidRemoveDocuments(documents => {
for (const data of documents) {
this._onDidRemoveDocument.fire(data.document);
}
}, undefined, this._toDispose);
this._documentsAndEditors.onDidAddDocuments(documents => {
for (const data of documents) {
this._onDidAddDocument.fire(data.document);
}
}, undefined, this._toDispose);
}
public dispose(): void {
dispose(this._toDispose);
this._toDispose.dispose();
}
public getAllDocumentData(): ExtHostDocumentData[] {

View File

@@ -12,8 +12,9 @@ import { ExtensionActivationError, MissingDependencyError } from 'vs/workbench/s
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
export interface IExtensionMemento {
get<T>(key: string): T | undefined;
get<T>(key: string, defaultValue: T): T;
update(key: string, value: any): Promise<boolean>;
update(key: string, value: any): Promise<void>;
}
export interface IExtensionContext {
@@ -43,14 +44,13 @@ export interface IExtensionAPI {
// _extensionAPIBrand: any;
}
/* __GDPR__FRAGMENT__
"ExtensionActivationTimes" : {
"startup": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"codeLoadingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"activateCallTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"activateResolvedTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
}
*/
export type ExtensionActivationTimesFragment = {
startup?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
codeLoadingTime?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
activateCallTime?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
activateResolvedTime?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
};
export class ExtensionActivationTimes {
public static readonly NONE = new ExtensionActivationTimes(false, -1, -1, -1);

View File

@@ -8,11 +8,10 @@ import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystem
import * as vscode from 'vscode';
import * as files from 'vs/platform/files/common/files';
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
import { FileChangeType } from 'vs/workbench/api/common/extHostTypes';
import { FileChangeType, FileSystemError } from 'vs/workbench/api/common/extHostTypes';
import * as typeConverter from 'vs/workbench/api/common/extHostTypeConverters';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
import { Schemas } from 'vs/base/common/network';
import { ResourceLabelFormatter } from 'vs/platform/label/common/label';
import { State, StateMachine, LinkComputer, Edge } from 'vs/editor/common/modes/linkComputer';
import { commonPrefixLength } from 'vs/base/common/strings';
import { CharCode } from 'vs/base/common/charCode';
@@ -104,6 +103,50 @@ class FsLinkProvider {
}
}
class ConsumerFileSystem implements vscode.FileSystem {
constructor(private _proxy: MainThreadFileSystemShape) { }
stat(uri: vscode.Uri): Promise<vscode.FileStat> {
return this._proxy.$stat(uri).catch(ConsumerFileSystem._handleError);
}
readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
return this._proxy.$readdir(uri).catch(ConsumerFileSystem._handleError);
}
createDirectory(uri: vscode.Uri): Promise<void> {
return this._proxy.$mkdir(uri).catch(ConsumerFileSystem._handleError);
}
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
return this._proxy.$readFile(uri).then(buff => buff.buffer).catch(ConsumerFileSystem._handleError);
}
writeFile(uri: vscode.Uri, content: Uint8Array): Promise<void> {
return this._proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ConsumerFileSystem._handleError);
}
delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise<void> {
return this._proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ConsumerFileSystem._handleError);
}
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
return this._proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ConsumerFileSystem._handleError);
}
copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean }): Promise<void> {
return this._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ConsumerFileSystem._handleError);
}
private static _handleError(err: any): never {
// generic error
if (!(err instanceof Error)) {
throw new FileSystemError(String(err));
}
// no provider (unknown scheme) error
if (err.name === 'ENOPRO') {
throw FileSystemError.Unavailable(err.message);
}
// file system error
throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode);
}
}
export class ExtHostFileSystem implements ExtHostFileSystemShape {
private readonly _proxy: MainThreadFileSystemShape;
@@ -113,21 +156,16 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
private readonly _watches = new Map<number, IDisposable>();
private _linkProviderRegistration: IDisposable;
// Used as a handle both for file system providers and resource label formatters (being lazy)
private _handlePool: number = 0;
readonly fileSystem: vscode.FileSystem;
constructor(mainContext: IMainContext, private _extHostLanguageFeatures: ExtHostLanguageFeatures) {
this._proxy = mainContext.getProxy(MainContext.MainThreadFileSystem);
this._usedSchemes.add(Schemas.file);
this._usedSchemes.add(Schemas.untitled);
this._usedSchemes.add(Schemas.vscode);
this._usedSchemes.add(Schemas.inMemory);
this._usedSchemes.add(Schemas.internal);
this._usedSchemes.add(Schemas.http);
this._usedSchemes.add(Schemas.https);
this._usedSchemes.add(Schemas.mailto);
this._usedSchemes.add(Schemas.data);
this._usedSchemes.add(Schemas.command);
this.fileSystem = new ConsumerFileSystem(this._proxy);
// register used schemes
Object.keys(Schemas).forEach(scheme => this._usedSchemes.add(scheme));
}
dispose(): void {
@@ -154,23 +192,23 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
this._usedSchemes.add(scheme);
this._fsProvider.set(handle, provider);
let capabilites = files.FileSystemProviderCapabilities.FileReadWrite;
let capabilities = files.FileSystemProviderCapabilities.FileReadWrite;
if (options.isCaseSensitive) {
capabilites += files.FileSystemProviderCapabilities.PathCaseSensitive;
capabilities += files.FileSystemProviderCapabilities.PathCaseSensitive;
}
if (options.isReadonly) {
capabilites += files.FileSystemProviderCapabilities.Readonly;
capabilities += files.FileSystemProviderCapabilities.Readonly;
}
if (typeof provider.copy === 'function') {
capabilites += files.FileSystemProviderCapabilities.FileFolderCopy;
capabilities += files.FileSystemProviderCapabilities.FileFolderCopy;
}
if (typeof provider.open === 'function' && typeof provider.close === 'function'
&& typeof provider.read === 'function' && typeof provider.write === 'function'
) {
capabilites += files.FileSystemProviderCapabilities.FileOpenReadWriteClose;
capabilities += files.FileSystemProviderCapabilities.FileOpenReadWriteClose;
}
this._proxy.$registerFileSystemProvider(handle, scheme, capabilites);
this._proxy.$registerFileSystemProvider(handle, scheme, capabilities);
const subscription = provider.onDidChangeFile(event => {
const mapped: IFileChangeDto[] = [];
@@ -208,46 +246,37 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
});
}
registerResourceLabelFormatter(formatter: ResourceLabelFormatter): IDisposable {
const handle = this._handlePool++;
this._proxy.$registerResourceLabelFormatter(handle, formatter);
return toDisposable(() => {
this._proxy.$unregisterResourceLabelFormatter(handle);
});
}
private static _asIStat(stat: vscode.FileStat): files.IStat {
const { type, ctime, mtime, size } = stat;
return { type, ctime, mtime, size };
}
$stat(handle: number, resource: UriComponents): Promise<files.IStat> {
return Promise.resolve(this.getProvider(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat);
return Promise.resolve(this._getFsProvider(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat);
}
$readdir(handle: number, resource: UriComponents): Promise<[string, files.FileType][]> {
return Promise.resolve(this.getProvider(handle).readDirectory(URI.revive(resource)));
return Promise.resolve(this._getFsProvider(handle).readDirectory(URI.revive(resource)));
}
$readFile(handle: number, resource: UriComponents): Promise<VSBuffer> {
return Promise.resolve(this.getProvider(handle).readFile(URI.revive(resource))).then(data => VSBuffer.wrap(data));
return Promise.resolve(this._getFsProvider(handle).readFile(URI.revive(resource))).then(data => VSBuffer.wrap(data));
}
$writeFile(handle: number, resource: UriComponents, content: VSBuffer, opts: files.FileWriteOptions): Promise<void> {
return Promise.resolve(this.getProvider(handle).writeFile(URI.revive(resource), content.buffer, opts));
return Promise.resolve(this._getFsProvider(handle).writeFile(URI.revive(resource), content.buffer, opts));
}
$delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): Promise<void> {
return Promise.resolve(this.getProvider(handle).delete(URI.revive(resource), opts));
return Promise.resolve(this._getFsProvider(handle).delete(URI.revive(resource), opts));
}
$rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): Promise<void> {
return Promise.resolve(this.getProvider(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts));
return Promise.resolve(this._getFsProvider(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts));
}
$copy(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): Promise<void> {
const provider = this.getProvider(handle);
const provider = this._getFsProvider(handle);
if (!provider.copy) {
throw new Error('FileSystemProvider does not implement "copy"');
}
@@ -255,11 +284,11 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
}
$mkdir(handle: number, resource: UriComponents): Promise<void> {
return Promise.resolve(this.getProvider(handle).createDirectory(URI.revive(resource)));
return Promise.resolve(this._getFsProvider(handle).createDirectory(URI.revive(resource)));
}
$watch(handle: number, session: number, resource: UriComponents, opts: files.IWatchOptions): void {
const subscription = this.getProvider(handle).watch(URI.revive(resource), opts);
const subscription = this._getFsProvider(handle).watch(URI.revive(resource), opts);
this._watches.set(session, subscription);
}
@@ -272,7 +301,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
}
$open(handle: number, resource: UriComponents, opts: files.FileOpenOptions): Promise<number> {
const provider = this.getProvider(handle);
const provider = this._getFsProvider(handle);
if (!provider.open) {
throw new Error('FileSystemProvider does not implement "open"');
}
@@ -280,7 +309,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
}
$close(handle: number, fd: number): Promise<void> {
const provider = this.getProvider(handle);
const provider = this._getFsProvider(handle);
if (!provider.close) {
throw new Error('FileSystemProvider does not implement "close"');
}
@@ -288,7 +317,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
}
$read(handle: number, fd: number, pos: number, length: number): Promise<VSBuffer> {
const provider = this.getProvider(handle);
const provider = this._getFsProvider(handle);
if (!provider.read) {
throw new Error('FileSystemProvider does not implement "read"');
}
@@ -299,14 +328,14 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
}
$write(handle: number, fd: number, pos: number, data: VSBuffer): Promise<number> {
const provider = this.getProvider(handle);
const provider = this._getFsProvider(handle);
if (!provider.write) {
throw new Error('FileSystemProvider does not implement "write"');
}
return Promise.resolve(provider.write(fd, pos, data.buffer, 0, data.byteLength));
}
private getProvider(handle: number): vscode.FileSystemProvider {
private _getFsProvider(handle: number): vscode.FileSystemProvider {
const provider = this._fsProvider.get(handle);
if (!provider) {
const err = new Error();

View File

@@ -1,33 +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 { ExtHostHeapServiceShape } from './extHost.protocol';
export class ExtHostHeapService implements ExtHostHeapServiceShape {
private static _idPool = 0;
private _data = new Map<number, any>();
keep(obj: any): number {
const id = ExtHostHeapService._idPool++;
this._data.set(id, obj);
return id;
}
delete(id: number): boolean {
return this._data.delete(id);
}
get<T>(id: number): T {
return this._data.get(id);
}
$onGarbageCollection(ids: number[]): void {
for (const id of ids) {
this.delete(id);
}
}
}

View File

@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ResourceLabelFormatter } from 'vs/platform/label/common/label';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { MainThreadLabelServiceShape, ExtHostLabelServiceShape, MainContext, IMainContext } from 'vs/workbench/api/common/extHost.protocol';
export class ExtHostLabelService implements ExtHostLabelServiceShape {
private readonly _proxy: MainThreadLabelServiceShape;
private _handlePool: number = 0;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadLabelService);
}
$registerResourceLabelFormatter(formatter: ResourceLabelFormatter): IDisposable {
const handle = this._handlePool++;
this._proxy.$registerResourceLabelFormatter(handle, formatter);
return toDisposable(() => {
this._proxy.$unregisterResourceLabelFormatter(handle);
});
}
}

View File

@@ -10,12 +10,11 @@ import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol } from 'vs/workbench/api/common/extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics';
import { asPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape, CodeInsetDto, SuggestDataDto, LinksListDto, ChainedCacheId } from './extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, SuggestDataDto, LinksListDto, ChainedCacheId, CodeLensListDto, CodeActionListDto, SignatureHelpDto, SignatureHelpContextDto } from './extHost.protocol';
import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
@@ -25,11 +24,10 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostWebview } from 'vs/workbench/api/common/extHostWebview';
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
import { generateUuid } from 'vs/base/common/uuid';
import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { LRUCache } from 'vs/base/common/map';
import { IURITransformer } from 'vs/base/common/uriIpc';
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
// --- adapter
@@ -104,34 +102,48 @@ class CodeLensAdapter {
private static _badCmd: vscode.Command = { command: 'missing', title: '!!MISSING: command!!' };
private readonly _cache = new Cache<vscode.CodeLens>('CodeLens');
private readonly _disposables = new Map<number, DisposableStore>();
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _commands: CommandsConverter,
private readonly _heapService: ExtHostHeapService,
private readonly _provider: vscode.CodeLensProvider
) { }
provideCodeLenses(resource: URI, token: CancellationToken): Promise<CodeLensDto[]> {
provideCodeLenses(resource: URI, token: CancellationToken): Promise<CodeLensListDto | undefined> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideCodeLenses(doc, token)).then(lenses => {
const result: CodeLensDto[] = [];
if (isNonEmptyArray(lenses)) {
for (const lens of lenses) {
const id = this._heapService.keep(lens);
result.push(ObjectIdentifier.mixin({
range: typeConvert.Range.from(lens.range),
command: this._commands.toInternal(lens.command)
}, id));
}
if (!lenses || token.isCancellationRequested) {
return undefined;
}
const cacheId = this._cache.add(lenses);
const disposables = new DisposableStore();
this._disposables.set(cacheId, disposables);
const result: CodeLensListDto = {
cacheId,
lenses: [],
};
for (let i = 0; i < lenses.length; i++) {
result.lenses.push({
cacheId: [cacheId, i],
range: typeConvert.Range.from(lenses[i].range),
command: this._commands.toInternal(lenses[i].command, disposables)
});
}
return result;
});
}
resolveCodeLens(symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined> {
const lens = this._heapService.get<vscode.CodeLens>(ObjectIdentifier.of(symbol));
const lens = symbol.cacheId && this._cache.get(...symbol.cacheId);
if (!lens) {
return Promise.resolve(undefined);
}
@@ -144,51 +156,26 @@ class CodeLensAdapter {
}
return resolve.then(newLens => {
newLens = newLens || lens;
symbol.command = this._commands.toInternal(newLens.command || CodeLensAdapter._badCmd);
return symbol;
});
}
}
class CodeInsetAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _heapService: ExtHostHeapService,
private readonly _provider: vscode.CodeInsetProvider
) { }
provideCodeInsets(resource: URI, token: CancellationToken): Promise<CodeInsetDto[] | undefined> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideCodeInsets(doc, token)).then(insets => {
if (Array.isArray(insets)) {
return insets.map(inset => {
const $ident = this._heapService.keep(inset);
const id = generateUuid();
return {
$ident,
id,
range: typeConvert.Range.from(inset.range),
height: inset.height
};
});
if (token.isCancellationRequested) {
return undefined;
}
return undefined;
const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]);
if (!disposables) {
// We've already been disposed of
return undefined;
}
newLens = newLens || lens;
symbol.command = this._commands.toInternal(newLens.command || CodeLensAdapter._badCmd, disposables);
return symbol;
});
}
resolveCodeInset(symbol: CodeInsetDto, webview: vscode.Webview, token: CancellationToken): Promise<CodeInsetDto> {
const inset = this._heapService.get<vscode.CodeInset>(ObjectIdentifier.of(symbol));
if (!inset) {
return Promise.resolve(symbol);
}
return asPromise(() => this._provider.resolveCodeInset(inset, webview, token)).then(newInset => {
newInset = newInset || inset;
return symbol;
});
releaseCodeLenses(cachedId: number): void {
dispose(this._disposables.get(cachedId));
this._disposables.delete(cachedId);
this._cache.delete(cachedId);
}
}
@@ -328,6 +315,9 @@ export interface CustomCodeAction extends CodeActionDto {
class CodeActionAdapter {
private static readonly _maxCodeActionsPerFile: number = 1000;
private readonly _cache = new Cache<vscode.CodeAction | vscode.Command>('CodeAction');
private readonly _disposables = new Map<number, DisposableStore>();
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _commands: CommandsConverter,
@@ -337,7 +327,7 @@ class CodeActionAdapter {
private readonly _extensionId: ExtensionIdentifier
) { }
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionDto[] | undefined> {
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionListDto | undefined> {
const doc = this._documents.getDocument(resource);
const ran = Selection.isISelection(rangeOrSelection)
@@ -360,34 +350,39 @@ class CodeActionAdapter {
};
return asPromise(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then(commandsOrActions => {
if (!isNonEmptyArray(commandsOrActions)) {
if (!isNonEmptyArray(commandsOrActions) || token.isCancellationRequested) {
return undefined;
}
const result: CustomCodeAction[] = [];
const cacheId = this._cache.add(commandsOrActions);
const disposables = new DisposableStore();
this._disposables.set(cacheId, disposables);
const actions: CustomCodeAction[] = [];
for (const candidate of commandsOrActions) {
if (!candidate) {
continue;
}
if (CodeActionAdapter._isCommand(candidate)) {
// old school: synthetic code action
result.push({
actions.push({
_isSynthetic: true,
title: candidate.title,
command: this._commands.toInternal(candidate),
command: this._commands.toInternal(candidate, disposables),
});
} else {
if (codeActionContext.only) {
if (!candidate.kind) {
this._logService.warn(`${this._extensionId.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`);
} else if (!codeActionContext.only.contains(candidate.kind)) {
this._logService.warn(`${this._extensionId.value} -Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`);
this._logService.warn(`${this._extensionId.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`);
}
}
// new school: convert code action
result.push({
actions.push({
title: candidate.title,
command: candidate.command && this._commands.toInternal(candidate.command),
command: candidate.command && this._commands.toInternal(candidate.command, disposables),
diagnostics: candidate.diagnostics && candidate.diagnostics.map(typeConvert.Diagnostic.from),
edit: candidate.edit && typeConvert.WorkspaceEdit.from(candidate.edit),
kind: candidate.kind && candidate.kind.value,
@@ -396,10 +391,16 @@ class CodeActionAdapter {
}
}
return result;
return <CodeActionListDto>{ cacheId, actions };
});
}
public releaseCodeActions(cachedId: number): void {
dispose(this._disposables.get(cachedId));
this._disposables.delete(cachedId);
this._cache.delete(cachedId);
}
private static _isCommand(thing: any): thing is vscode.Command {
return typeof (<vscode.Command>thing).command === 'string' && typeof (<vscode.Command>thing).title === 'string';
}
@@ -623,7 +624,8 @@ class SuggestAdapter {
private _commands: CommandsConverter;
private _provider: vscode.CompletionItemProvider;
private _cache = new Cache<vscode.CompletionItem>();
private _cache = new Cache<vscode.CompletionItem>('CompletionItem');
private _disposables = new Map<number, DisposableStore>();
constructor(documents: ExtHostDocuments, commands: CommandsConverter, provider: vscode.CompletionItemProvider) {
this._documents = documents;
@@ -643,13 +645,18 @@ class SuggestAdapter {
return undefined;
}
let list = Array.isArray(value) ? new CompletionList(value) : value;
let pid: number | undefined;
if (token.isCancellationRequested) {
// cancelled -> return without further ado, esp no caching
// of results as they will leak
return undefined;
}
const list = Array.isArray(value) ? new CompletionList(value) : value;
// keep result for providers that support resolving
if (SuggestAdapter.supportsResolving(this._provider)) {
pid = this._cache.add(list.items);
}
const pid: number = SuggestAdapter.supportsResolving(this._provider) ? this._cache.add(list.items) : this._cache.add([]);
const disposables = new DisposableStore();
this._disposables.set(pid, disposables);
// the default text edit range
const wordRangeBeforePos = (doc.getWordRangeAtPosition(pos) as Range || new Range(pos, pos))
@@ -663,7 +670,7 @@ class SuggestAdapter {
};
for (let i = 0; i < list.items.length; i++) {
const suggestion = this._convertCompletionItem(list.items[i], pos, pid && [pid, i] || undefined);
const suggestion = this._convertCompletionItem(list.items[i], pos, [pid, i]);
// check for bad completion item
// for the converter did warn
if (suggestion) {
@@ -698,15 +705,22 @@ class SuggestAdapter {
}
releaseCompletionItems(id: number): any {
dispose(this._disposables.get(id));
this._disposables.delete(id);
this._cache.delete(id);
}
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: ChainedCacheId | undefined): SuggestDataDto | undefined {
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: ChainedCacheId): SuggestDataDto | undefined {
if (typeof item.label !== 'string' || item.label.length === 0) {
console.warn('INVALID text edit -> must have at least a label');
return undefined;
}
const disposables = this._disposables.get(id[0]);
if (!disposables) {
throw Error('DisposableStore is missing...');
}
const result: SuggestDataDto = {
//
x: id,
@@ -721,7 +735,7 @@ class SuggestAdapter {
i: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0,
k: item.commitCharacters,
l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
m: this._commands.toInternal(item.command),
m: this._commands.toInternal(item.command, disposables),
};
// 'insertText'-logic
@@ -756,31 +770,32 @@ class SuggestAdapter {
class SignatureHelpAdapter {
private readonly _cache = new Cache<vscode.SignatureHelp>('SignatureHelp');
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.SignatureHelpProvider,
private readonly _heap: ExtHostHeapService,
) { }
provideSignatureHelp(resource: URI, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<modes.SignatureHelp | undefined> {
provideSignatureHelp(resource: URI, position: IPosition, context: SignatureHelpContextDto, token: CancellationToken): Promise<SignatureHelpDto | undefined> {
const doc = this._documents.getDocument(resource);
const pos = typeConvert.Position.to(position);
const vscodeContext = this.reviveContext(context);
return asPromise(() => this._provider.provideSignatureHelp(doc, pos, token, vscodeContext)).then(value => {
if (value) {
const id = this._heap.keep(value);
return ObjectIdentifier.mixin(typeConvert.SignatureHelp.from(value), id);
const id = this._cache.add([value]);
return { ...typeConvert.SignatureHelp.from(value), id };
}
return undefined;
});
}
private reviveContext(context: modes.SignatureHelpContext): vscode.SignatureHelpContext {
private reviveContext(context: SignatureHelpContextDto): vscode.SignatureHelpContext {
let activeSignatureHelp: vscode.SignatureHelp | undefined = undefined;
if (context.activeSignatureHelp) {
const revivedSignatureHelp = typeConvert.SignatureHelp.to(context.activeSignatureHelp);
const saved = this._heap.get<vscode.SignatureHelp>(ObjectIdentifier.of(context.activeSignatureHelp));
const saved = this._cache.get(context.activeSignatureHelp.id, 0);
if (saved) {
activeSignatureHelp = saved;
activeSignatureHelp.activeSignature = revivedSignatureHelp.activeSignature;
@@ -791,16 +806,26 @@ class SignatureHelpAdapter {
}
return { ...context, activeSignatureHelp };
}
releaseSignatureHelp(id: number): any {
this._cache.delete(id);
}
}
class Cache<T> {
private static readonly enableDebugLogging = false;
private _data = new Map<number, T[]>();
private readonly _data = new Map<number, readonly T[]>();
private _idPool = 1;
add(item: T[]): number {
constructor(
private readonly id: string
) { }
add(item: readonly T[]): number {
const id = this._idPool++;
this._data.set(id, item);
this.logDebugInfo();
return id;
}
@@ -810,12 +835,20 @@ class Cache<T> {
delete(id: number) {
this._data.delete(id);
this.logDebugInfo();
}
private logDebugInfo() {
if (!Cache.enableDebugLogging) {
return;
}
console.log(`${this.id} cache size — ${this._data.size}`);
}
}
class LinkProviderAdapter {
private _cache = new Cache<vscode.DocumentLink>();
private _cache = new Cache<vscode.DocumentLink>('DocumentLink');
constructor(
private readonly _documents: ExtHostDocuments,
@@ -831,6 +864,12 @@ class LinkProviderAdapter {
return undefined;
}
if (token.isCancellationRequested) {
// cancelled -> return without further ado, esp no caching
// of results as they will leak
return undefined;
}
if (typeof this._provider.resolveDocumentLink !== 'function') {
// no resolve -> no caching
return { links: links.map(typeConvert.DocumentLink.from) };
@@ -877,7 +916,7 @@ class ColorProviderAdapter {
provideColors(resource: URI, token: CancellationToken): Promise<IRawColorInfo[]> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideDocumentColors(doc, token)).then(colors => {
if (!Array.isArray(colors)) {
if (!Array.isArray<vscode.ColorInformation>(colors)) {
return [];
}
@@ -1027,7 +1066,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter
| ColorProviderAdapter | FoldingProviderAdapter | CodeInsetAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter;
| ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter;
class AdapterData {
constructor(
@@ -1036,41 +1075,32 @@ class AdapterData {
) { }
}
export interface ISchemeTransformer {
transformOutgoing(scheme: string): string;
}
export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
private static _handlePool: number = 0;
private readonly _schemeTransformer: ISchemeTransformer | null;
private readonly _uriTransformer: IURITransformer | null;
private _proxy: MainThreadLanguageFeaturesShape;
private _documents: ExtHostDocuments;
private _commands: ExtHostCommands;
private _heapService: ExtHostHeapService;
private _diagnostics: ExtHostDiagnostics;
private _adapter = new Map<number, AdapterData>();
private readonly _logService: ILogService;
private _webviewProxy: MainThreadWebviewsShape;
constructor(
mainContext: IMainContext,
schemeTransformer: ISchemeTransformer | null,
uriTransformer: IURITransformer | null,
documents: ExtHostDocuments,
commands: ExtHostCommands,
heapMonitor: ExtHostHeapService,
diagnostics: ExtHostDiagnostics,
logService: ILogService
) {
this._schemeTransformer = schemeTransformer;
this._uriTransformer = uriTransformer;
this._proxy = mainContext.getProxy(MainContext.MainThreadLanguageFeatures);
this._documents = documents;
this._commands = commands;
this._heapService = heapMonitor;
this._diagnostics = diagnostics;
this._logService = logService;
this._webviewProxy = mainContext.getProxy(MainContext.MainThreadWebviews);
}
private _transformDocumentSelector(selector: vscode.DocumentSelector): Array<ISerializedDocumentFilter> {
@@ -1099,8 +1129,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
}
private _transformScheme(scheme: string | undefined): string | undefined {
if (this._schemeTransformer && typeof scheme === 'string') {
return this._schemeTransformer.transformOutgoing(scheme);
if (this._uriTransformer && typeof scheme === 'string') {
return this._uriTransformer.transformOutgoingScheme(scheme);
}
return scheme;
}
@@ -1173,7 +1203,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
const handle = this._nextHandle();
const eventHandle = typeof provider.onDidChangeCodeLenses === 'function' ? this._nextHandle() : undefined;
this._adapter.set(handle, new AdapterData(new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider), extension));
this._adapter.set(handle, new AdapterData(new CodeLensAdapter(this._documents, this._commands.converter, provider), extension));
this._proxy.$registerCodeLensSupport(handle, this._transformDocumentSelector(selector), eventHandle);
let result = this._createDisposable(handle);
@@ -1185,43 +1215,16 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return result;
}
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.ICodeLensSymbol[]> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), []);
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensListDto | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), undefined);
}
$resolveCodeLens(handle: number, symbol: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> {
$resolveCodeLens(handle: number, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined);
}
// --- code insets
registerCodeInsetProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable {
const handle = this._nextHandle();
const eventHandle = typeof provider.onDidChangeCodeInsets === 'function' ? this._nextHandle() : undefined;
this._adapter.set(handle, new AdapterData(new CodeInsetAdapter(this._documents, this._heapService, provider), extension));
this._proxy.$registerCodeInsetSupport(handle, this._transformDocumentSelector(selector), eventHandle);
let result = this._createDisposable(handle);
if (eventHandle !== undefined && provider.onDidChangeCodeInsets) {
const subscription = provider.onDidChangeCodeInsets(_ => this._proxy.$emitCodeLensEvent(eventHandle));
result = Disposable.from(result, subscription);
}
return result;
}
$provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise<codeInset.ICodeInsetSymbol[] | undefined> {
return this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.provideCodeInsets(URI.revive(resource), token), undefined);
}
$resolveCodeInset(handle: number, _resource: UriComponents, symbol: codeInset.ICodeInsetSymbol, token: CancellationToken): Promise<codeInset.ICodeInsetSymbol> {
const webviewHandle = Math.random();
const webview = new ExtHostWebview(webviewHandle, this._webviewProxy, { enableScripts: true });
return this._withAdapter(handle, CodeInsetAdapter, async (adapter, extension) => {
await this._webviewProxy.$createWebviewCodeInset(webviewHandle, symbol.id, { enableCommandUris: true, enableScripts: true }, extension ? extension.identifier : undefined, extension ? extension.extensionLocation : undefined);
return adapter.resolveCodeInset(symbol, webview, token);
}, symbol);
$releaseCodeLenses(handle: number, cacheId: number): void {
this._withAdapter(handle, CodeLensAdapter, adapter => Promise.resolve(adapter.releaseCodeLenses(cacheId)), undefined);
}
// --- declaration
@@ -1311,10 +1314,14 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
}
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionDto[] | undefined> {
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionListDto | undefined> {
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), rangeOrSelection, context, token), undefined);
}
$releaseCodeActions(handle: number, cacheId: number): void {
this._withAdapter(handle, CodeActionAdapter, adapter => Promise.resolve(adapter.releaseCodeActions(cacheId)), undefined);
}
// --- formatting
registerDocumentFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable {
@@ -1387,7 +1394,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable {
const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider), extension);
this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider));
this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider), extension.identifier);
return this._createDisposable(handle);
}
@@ -1410,15 +1417,19 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
? { triggerCharacters: metadataOrTriggerChars, retriggerCharacters: [] }
: metadataOrTriggerChars;
const handle = this._addNewAdapter(new SignatureHelpAdapter(this._documents, provider, this._heapService), extension);
const handle = this._addNewAdapter(new SignatureHelpAdapter(this._documents, provider), extension);
this._proxy.$registerSignatureHelpProvider(handle, this._transformDocumentSelector(selector), metadata);
return this._createDisposable(handle);
}
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<modes.SignatureHelp | undefined> {
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: SignatureHelpContextDto, token: CancellationToken): Promise<SignatureHelpDto | undefined> {
return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, context, token), undefined);
}
$releaseSignatureHelp(handle: number, id: number): void {
this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.releaseSignatureHelp(id), undefined);
}
// --- links
registerDocumentLinkProvider(extension: IExtensionDescription | undefined, selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {

View File

@@ -38,7 +38,9 @@ export class ExtensionMemento implements IExtensionMemento {
return this._init;
}
get<T>(key: string, defaultValue: T): T {
get<T>(key: string): T | undefined;
get<T>(key: string, defaultValue: T): T;
get<T>(key: string, defaultValue?: T): T {
let value = this._value[key];
if (typeof value === 'undefined') {
value = defaultValue;
@@ -46,11 +48,9 @@ export class ExtensionMemento implements IExtensionMemento {
return value;
}
update(key: string, value: any): Promise<boolean> {
update(key: string, value: any): Promise<void> {
this._value[key] = value;
return this._storage
.setValue(this._shared, this._id, this._value)
.then(() => true);
return this._storage.setValue(this._shared, this._id, this._value);
}
dispose(): void {

View File

@@ -458,14 +458,14 @@ function getLightIconUri(iconPath: QuickInputButton['iconPath']) {
|| iconPath instanceof URI) {
return getIconUri(iconPath);
}
return getIconUri(iconPath['light']);
return getIconUri((iconPath as any).light);
}
return undefined;
}
function getDarkIconUri(iconPath: QuickInputButton['iconPath']) {
if (iconPath && !(iconPath instanceof ThemeIcon) && iconPath['dark']) {
return getIconUri(iconPath['dark']);
if (iconPath && !(iconPath instanceof ThemeIcon) && (iconPath as any).dark) {
return getIconUri((iconPath as any).dark);
}
return undefined;
}

View File

@@ -6,7 +6,7 @@
import { URI, UriComponents } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { debounce } from 'vs/base/common/decorators';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { dispose, IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { asPromise } from 'vs/base/common/async';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, CommandDto } from './extHost.protocol';
@@ -415,6 +415,7 @@ class ExtHostSourceControl implements vscode.SourceControl {
this._proxy.$updateSourceControl(this.handle, { commitTemplate });
}
private _acceptInputDisposables = new MutableDisposable<DisposableStore>();
private _acceptInputCommand: vscode.Command | undefined = undefined;
get acceptInputCommand(): vscode.Command | undefined {
@@ -422,12 +423,15 @@ class ExtHostSourceControl implements vscode.SourceControl {
}
set acceptInputCommand(acceptInputCommand: vscode.Command | undefined) {
this._acceptInputDisposables.value = new DisposableStore();
this._acceptInputCommand = acceptInputCommand;
const internal = this._commands.converter.toInternal(acceptInputCommand);
const internal = this._commands.converter.toInternal(acceptInputCommand, this._acceptInputDisposables.value);
this._proxy.$updateSourceControl(this.handle, { acceptInputCommand: internal });
}
private _statusBarDisposables = new MutableDisposable<DisposableStore>();
private _statusBarCommands: vscode.Command[] | undefined = undefined;
get statusBarCommands(): vscode.Command[] | undefined {
@@ -439,9 +443,11 @@ class ExtHostSourceControl implements vscode.SourceControl {
return;
}
this._statusBarDisposables.value = new DisposableStore();
this._statusBarCommands = statusBarCommands;
const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal(c)) as CommandDto[];
const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal(c, this._statusBarDisposables.value!)) as CommandDto[];
this._proxy.$updateSourceControl(this.handle, { statusBarCommands: internal });
}
@@ -519,6 +525,9 @@ class ExtHostSourceControl implements vscode.SourceControl {
}
dispose(): void {
this._acceptInputDisposables.dispose();
this._statusBarDisposables.dispose();
this._groups.forEach(group => group.dispose());
this._proxy.$unregisterSourceControl(this.handle);
}

View File

@@ -7,7 +7,7 @@ import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/
import { StatusBarAlignment as ExtHostStatusBarAlignment, Disposable, ThemeColor } from './extHostTypes';
import { StatusBarItem, StatusBarAlignment } from 'vscode';
import { MainContext, MainThreadStatusBarShape, IMainContext } from './extHost.protocol';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { localize } from 'vs/nls';
export class ExtHostStatusBarEntry implements StatusBarItem {
private static ID_GEN = 0;
@@ -18,6 +18,9 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
private _disposed: boolean;
private _visible: boolean;
private _statusId: string;
private _statusName: string;
private _text: string;
private _tooltip: string;
private _color: string | ThemeColor;
@@ -26,14 +29,13 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
private _timeoutHandle: any;
private _proxy: MainThreadStatusBarShape;
private _extensionId?: ExtensionIdentifier;
constructor(proxy: MainThreadStatusBarShape, extensionId: ExtensionIdentifier | undefined, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
constructor(proxy: MainThreadStatusBarShape, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
this._id = ExtHostStatusBarEntry.ID_GEN++;
this._proxy = proxy;
this._statusId = id;
this._statusName = name;
this._alignment = alignment;
this._priority = priority;
this._extensionId = extensionId;
}
public get id(): number {
@@ -107,7 +109,7 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
this._timeoutHandle = undefined;
// Set to status bar
this._proxy.$setEntry(this.id, this._extensionId, this.text, this.tooltip, this.command, this.color,
this._proxy.$setEntry(this.id, this._statusId, this._statusName, this.text, this.tooltip, this.command, this.color,
this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT,
this._priority);
}, 0);
@@ -125,7 +127,7 @@ class StatusBarMessage {
private _messages: { message: string }[] = [];
constructor(statusBar: ExtHostStatusBar) {
this._item = statusBar.createStatusBarEntry(undefined, ExtHostStatusBarAlignment.Left, Number.MIN_VALUE);
this._item = statusBar.createStatusBarEntry('status.extensionMessage', localize('status.extensionMessage', "Extension Status"), ExtHostStatusBarAlignment.Left, Number.MIN_VALUE);
}
dispose() {
@@ -167,8 +169,8 @@ export class ExtHostStatusBar {
this._statusMessage = new StatusBarMessage(this);
}
createStatusBarEntry(extensionId: ExtensionIdentifier | undefined, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, extensionId, alignment, priority);
createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, id, name, alignment, priority);
}
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): Disposable {

View File

@@ -613,7 +613,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
});
}
insertSnippet(snippet: SnippetString, where?: Position | Position[] | Range | Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
if (this._disposed) {
return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors'));
}

View File

@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
import { basename } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol';
import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
@@ -142,8 +142,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
type Root = null | undefined;
type TreeData<T> = { message: boolean, element: T | Root | false };
// {{SQL CARBON EDIT}}
export interface TreeNode {
export interface TreeNode extends IDisposable { // {{SQL CARBON EDIT}} export interface
item: ITreeItem;
parent: TreeNode | Root;
children?: TreeNode[];
@@ -435,6 +434,7 @@ export class ExtHostTreeView<T> extends Disposable {
if (extTreeItem) {
const newNode = this.createTreeNode(extElement, extTreeItem, existing.parent);
this.updateNodeCache(extElement, newNode, existing, existing.parent);
existing.dispose();
return newNode;
}
return null;
@@ -454,18 +454,8 @@ export class ExtHostTreeView<T> extends Disposable {
return node;
}
// {{SQL CARBON EDIT}}
protected createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode | Root): TreeNode {
return {
item: this.createTreeItem(element, extensionTreeItem, parent),
parent,
children: undefined
};
}
// {{SQL CARBON EDIT}}
protected createTreeItem(element: T, extensionTreeItem: azdata.TreeItem, parent: TreeNode | Root): ITreeItem {
protected createTreeNode(element: T, extensionTreeItem: azdata.TreeItem, parent: TreeNode | Root): TreeNode { // {{SQL CARBON EDIT}} change to protected, change to azdata.TreeItem
const disposable = new DisposableStore();
const handle = this.createHandle(element, extensionTreeItem, parent);
const icon = this.getLightIconPath(extensionTreeItem);
const item = {
@@ -475,7 +465,7 @@ export class ExtHostTreeView<T> extends Disposable {
description: extensionTreeItem.description,
resourceUri: extensionTreeItem.resourceUri,
tooltip: typeof extensionTreeItem.tooltip === 'string' ? extensionTreeItem.tooltip : undefined,
command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command) : undefined,
command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command, disposable) : undefined,
contextValue: extensionTreeItem.contextValue,
icon,
iconDark: this.getDarkIconPath(extensionTreeItem) || icon,
@@ -487,7 +477,12 @@ export class ExtHostTreeView<T> extends Disposable {
type: extensionTreeItem.type
};
return item;
return {
item,
parent,
children: undefined,
dispose(): void { disposable.dispose(); }
};
}
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode | Root, returnFirst?: boolean): TreeItemHandle {
@@ -524,14 +519,14 @@ export class ExtHostTreeView<T> extends Disposable {
|| extensionTreeItem.iconPath instanceof URI) {
return this.getIconPath(extensionTreeItem.iconPath);
}
return this.getIconPath(extensionTreeItem.iconPath['light']);
return this.getIconPath((<{ light: string | URI; dark: string | URI }>extensionTreeItem.iconPath).light);
}
return undefined;
}
private getDarkIconPath(extensionTreeItem: vscode.TreeItem): URI | undefined {
if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon) && extensionTreeItem.iconPath['dark']) {
return this.getIconPath(extensionTreeItem.iconPath['dark']);
if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon) && (<{ light: string | URI; dark: string | URI }>extensionTreeItem.iconPath).dark) {
return this.getIconPath((<{ light: string | URI; dark: string | URI }>extensionTreeItem.iconPath).dark);
}
return undefined;
}
@@ -614,6 +609,7 @@ export class ExtHostTreeView<T> extends Disposable {
}
this.nodes.delete(element);
this.elements.delete(node.item.handle);
node.dispose();
}
}
@@ -621,6 +617,7 @@ export class ExtHostTreeView<T> extends Disposable {
protected clearAll(): void {
this.roots = null;
this.elements.clear();
this.nodes.forEach(node => node.dispose());
this.nodes.clear();
}

View File

@@ -237,8 +237,7 @@ export namespace MarkdownString {
const resUris: { [href: string]: UriComponents } = Object.create(null);
res.uris = resUris;
const renderer = new marked.Renderer();
renderer.image = renderer.link = (href: string): string => {
const collectUri = (href: string): string => {
try {
let uri = URI.parse(href, true);
uri = uri.with({ query: _uriMassage(uri.query, resUris) });
@@ -248,6 +247,10 @@ export namespace MarkdownString {
}
return '';
};
const renderer = new marked.Renderer();
renderer.link = collectUri;
renderer.image = href => collectUri(htmlContent.parseHrefAndDimensions(href).href);
marked(res.value, { renderer });
return res;
@@ -808,7 +811,8 @@ export namespace DocumentLink {
export function from(link: vscode.DocumentLink): modes.ILink {
return {
range: Range.from(link.range),
url: link.target
url: link.target,
tooltip: link.tooltip
};
}
@@ -858,10 +862,7 @@ export namespace Color {
export namespace SelectionRange {
export function from(obj: vscode.SelectionRange): modes.SelectionRange {
return {
kind: '',
range: Range.from(obj.range)
};
return { range: Range.from(obj.range) };
}
export function to(obj: modes.SelectionRange): vscode.SelectionRange {

View File

@@ -1440,6 +1440,8 @@ export class DocumentLink {
target?: URI;
tooltip?: string;
constructor(range: Range, target: URI | undefined) {
if (target && !(target instanceof URI)) {
throw illegalArgument('target');
@@ -1770,6 +1772,24 @@ export class CustomExecution implements vscode.CustomExecution {
}
}
export class CustomExecution2 implements vscode.CustomExecution2 {
private _callback: () => Thenable<vscode.TerminalVirtualProcess>;
constructor(callback: () => Thenable<vscode.TerminalVirtualProcess>) {
this._callback = callback;
}
public computeId(): string {
return 'customExecution' + generateUuid();
}
public set callback(value: () => Thenable<vscode.TerminalVirtualProcess>) {
this._callback = value;
}
public get callback(): (() => Thenable<vscode.TerminalVirtualProcess>) {
return this._callback;
}
}
@es5ClassCompat
export class Task implements vscode.Task2 {
@@ -1783,7 +1803,7 @@ export class Task implements vscode.Task2 {
private _definition: vscode.TaskDefinition;
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
private _name: string;
private _execution: ProcessExecution | ShellExecution | CustomExecution | undefined;
private _execution: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined;
private _problemMatchers: string[];
private _hasDefinedMatchers: boolean;
private _isBackground: boolean;
@@ -1792,8 +1812,8 @@ export class Task implements vscode.Task2 {
private _presentationOptions: vscode.TaskPresentationOptions;
private _runOptions: vscode.RunOptions;
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, arg2: string | (vscode.TaskScope.Global | vscode.TaskScope.Workspace) | vscode.WorkspaceFolder, arg3: any, arg4?: any, arg5?: any, arg6?: any) {
this.definition = definition;
let problemMatchers: string | string[];
@@ -1905,18 +1925,18 @@ export class Task implements vscode.Task2 {
}
get execution(): ProcessExecution | ShellExecution | undefined {
return (this._execution instanceof CustomExecution) ? undefined : this._execution;
return ((this._execution instanceof CustomExecution) || (this._execution instanceof CustomExecution2)) ? undefined : this._execution;
}
set execution(value: ProcessExecution | ShellExecution | undefined) {
this.execution2 = value;
}
get execution2(): ProcessExecution | ShellExecution | CustomExecution | undefined {
get execution2(): ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined {
return this._execution;
}
set execution2(value: ProcessExecution | ShellExecution | CustomExecution | undefined) {
set execution2(value: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined) {
if (value === null) {
value = undefined;
}
@@ -2317,3 +2337,8 @@ export enum ExtensionExecutionContext {
Local = 1,
Remote = 2
}
export enum ExtensionKind {
UI = 1,
Workspace = 2
}

View File

@@ -8,37 +8,41 @@ import { URI } from 'vs/base/common/uri';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import * as vscode from 'vscode';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState, WebviewInsetHandle } from './extHost.protocol';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol';
import { Disposable } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as modes from 'vs/editor/common/modes';
import { WebviewInitData, toWebviewResource } from 'vs/workbench/api/common/shared/webview';
import { generateUuid } from 'vs/base/common/uuid';
type IconPath = URI | { light: URI, dark: URI };
export class ExtHostWebview implements vscode.Webview {
private readonly _handle: WebviewPanelHandle | WebviewInsetHandle;
private readonly _proxy: MainThreadWebviewsShape;
private _html: string;
private _options: vscode.WebviewOptions;
private _isDisposed: boolean = false;
public readonly _onMessageEmitter = new Emitter<any>();
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
constructor(
handle: WebviewPanelHandle | WebviewInsetHandle,
proxy: MainThreadWebviewsShape,
options: vscode.WebviewOptions
) {
this._handle = handle;
this._proxy = proxy;
this._options = options;
}
private readonly _handle: WebviewPanelHandle,
private readonly _proxy: MainThreadWebviewsShape,
private _options: vscode.WebviewOptions,
private readonly _initData: WebviewInitData
) { }
public dispose() {
this._onMessageEmitter.dispose();
}
public toWebviewResource(resource: vscode.Uri): vscode.Uri {
return toWebviewResource(this._initData, this._handle, resource);
}
public get cspSource(): string {
return this._initData.webviewCspSource.replace('{{uuid}}', this._handle);
}
public get html(): string {
this.assertNotDisposed();
return this._html;
@@ -228,10 +232,9 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
}
export class ExtHostWebviews implements ExtHostWebviewsShape {
private static webviewHandlePool = 1;
private static newHandle(): WebviewPanelHandle {
return ExtHostWebviews.webviewHandlePool++ + '';
return generateUuid();
}
private readonly _proxy: MainThreadWebviewsShape;
@@ -239,7 +242,8 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
private readonly _serializers = new Map<string, vscode.WebviewPanelSerializer>();
constructor(
mainContext: IMainContext
mainContext: IMainContext,
private readonly initData: WebviewInitData
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews);
}
@@ -260,7 +264,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
const handle = ExtHostWebviews.newHandle();
this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, convertWebviewOptions(options), extension.identifier, extension.extensionLocation);
const webview = new ExtHostWebview(handle, this._proxy, options);
const webview = new ExtHostWebview(handle, this._proxy, options, this.initData);
const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview);
this._webviewPanels.set(handle, panel);
return panel;
@@ -333,7 +337,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
return Promise.reject(new Error(`No serializer found for '${viewType}'`));
}
const webview = new ExtHostWebview(webviewHandle, this._proxy, options);
const webview = new ExtHostWebview(webviewHandle, this._proxy, options, this.initData);
const revivedPanel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview);
this._webviewPanels.set(webviewHandle, revivedPanel);
return Promise.resolve(serializer.deserializeWebviewPanel(revivedPanel, state));

View File

@@ -9,7 +9,6 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { Counter } from 'vs/base/common/numbers';
import { isLinux } from 'vs/base/common/platform';
import { basenameOrAuthority, dirname, isEqual, relativePath, basename } from 'vs/base/common/resources';
import { compare } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
@@ -34,7 +33,7 @@ export interface IExtHostWorkspaceProvider {
}
function isFolderEqual(folderA: URI, folderB: URI): boolean {
return isEqual(folderA, folderB, !isLinux);
return isEqual(folderA, folderB);
}
function compareWorkspaceFolderByUri(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {

View File

@@ -21,7 +21,6 @@ namespace schema {
export interface IUserFriendlyMenuItem {
command: string;
alt?: string;
precondition?: string;
when?: string;
group?: string;
}
@@ -84,10 +83,6 @@ namespace schema {
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'alt'));
return false;
}
if (item.precondition && typeof item.precondition !== 'string') {
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'precondition'));
return false;
}
if (item.when && typeof item.when !== 'string') {
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when'));
return false;
@@ -112,10 +107,6 @@ namespace schema {
description: localize('vscode.extension.contributes.menuItem.alt', 'Identifier of an alternative command to execute. The command must be declared in the \'commands\'-section'),
type: 'string'
},
precondition: {
description: localize('vscode.extension.contributes.menuItem.precondition', 'Condition which must be true to enable this item'),
type: 'string'
},
when: {
description: localize('vscode.extension.contributes.menuItem.when', 'Condition which must be true to show this item'),
type: 'string'
@@ -206,8 +197,8 @@ namespace schema {
type: 'array',
items: menuItem
},
'comments/commentThread/actions': {
description: localize('commentThread.actions', "The contributed comment thread actions"),
'comments/commentThread/context': {
description: localize('commentThread.actions', "The contributed comment thread context menu, rendered as buttons below the comment editor"),
type: 'array',
items: menuItem
},
@@ -216,8 +207,8 @@ namespace schema {
type: 'array',
items: menuItem
},
'comments/comment/actions': {
description: localize('comment.actions', "The contributed comment actions"),
'comments/comment/context': {
description: localize('comment.actions', "The contributed comment context menu, rendered as buttons below the comment editor"),
type: 'array',
items: menuItem
},
@@ -229,6 +220,7 @@ namespace schema {
export interface IUserFriendlyCommand {
command: string;
title: string | ILocalizedString;
enablement?: string;
category?: string | ILocalizedString;
icon?: IUserFriendlyIcon;
}
@@ -247,6 +239,10 @@ namespace schema {
if (!isValidLocalizedString(command.title, collector, 'title')) {
return false;
}
if (command.enablement && typeof command.enablement !== 'string') {
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'precondition'));
return false;
}
if (command.category && !isValidLocalizedString(command.category, collector, 'category')) {
return false;
}
@@ -300,6 +296,10 @@ namespace schema {
description: localize('vscode.extension.contributes.commandType.category', '(Optional) Category string by the command is grouped in the UI'),
type: 'string'
},
enablement: {
description: localize('vscode.extension.contributes.commandType.precondition', '(Optional) Condition which must be true to enable the command'),
type: 'string'
},
icon: {
description: localize('vscode.extension.contributes.commandType.icon', '(Optional) Icon which is used to represent the command in the UI. Either a file path or a themable configuration'),
anyOf: [{
@@ -336,10 +336,12 @@ namespace schema {
let _commandRegistrations: IDisposable[] = [];
ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.IUserFriendlyCommand[]>({
export const commandsExtensionPoint = ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.IUserFriendlyCommand[]>({
extensionPoint: 'commands',
jsonSchema: schema.commandsContribution
}).setHandler(extensions => {
});
commandsExtensionPoint.setHandler(extensions => {
function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser<any>, disposables: IDisposable[]) {
@@ -347,7 +349,7 @@ ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.I
return;
}
const { icon, category, title, command } = userFriendlyCommand;
const { icon, enablement, category, title, command } = userFriendlyCommand;
let absoluteIcon: { dark: URI; light?: URI; } | undefined;
if (icon) {
@@ -364,7 +366,13 @@ ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.I
if (MenuRegistry.getCommand(command)) {
extension.collector.info(localize('dup', "Command `{0}` appears multiple times in the `commands` section.", userFriendlyCommand.command));
}
const registration = MenuRegistry.addCommand({ id: command, title, category, iconLocation: absoluteIcon });
const registration = MenuRegistry.addCommand({
id: command,
title,
category,
precondition: ContextKeyExpr.deserialize(enablement),
iconLocation: absoluteIcon
});
disposables.push(registration);
}
@@ -439,14 +447,6 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyM
}
}
if (item.precondition) {
command.precondition = ContextKeyExpr.deserialize(item.precondition);
}
if (alt && item.precondition) {
alt.precondition = command.precondition;
}
const registration = MenuRegistry.appendMenuItem(menu, {
command,
alt,

View File

@@ -70,6 +70,10 @@ export interface CustomExecutionDTO {
customExecution: 'customExecution';
}
export interface CustomExecution2DTO {
customExecution: 'customExecution2';
}
export interface TaskSourceDTO {
label: string;
extensionId?: string;
@@ -84,7 +88,7 @@ export interface TaskHandleDTO {
export interface TaskDTO {
_id: string;
name?: string;
execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecutionDTO | undefined;
execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined;
definition: TaskDefinitionDTO;
isBackground?: boolean;
source: TaskSourceDTO;

View File

@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import * as vscode from 'vscode';
export interface WebviewInitData {
readonly webviewResourceRoot: string;
readonly webviewCspSource: string;
}
export function toWebviewResource(
initData: WebviewInitData,
uuid: string,
resource: vscode.Uri
): vscode.Uri {
const uri = initData.webviewResourceRoot
.replace('{{resource}}', resource.toString().replace(/^\S+?:/, ''))
.replace('{{uuid}}', uuid);
return URI.parse(uri);
}

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
@@ -35,8 +36,7 @@ import { ExtensionActivatedByAPI } from 'vs/workbench/api/common/extHostExtensio
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem';
import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService';
import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService';
import { ExtHostLanguageFeatures, ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages';
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
import { ExtHostMessageService } from 'vs/workbench/api/common/extHostMessageService';
@@ -68,6 +68,12 @@ import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
import { withNullAsUndefined } from 'vs/base/common/types';
import { values } from 'vs/base/common/collections';
import { Schemas } from 'vs/base/common/network';
import { IURITransformer } from 'vs/base/common/uriIpc';
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -92,46 +98,50 @@ export function createApiFactory(
extensionService: ExtHostExtensionService,
extHostLogService: ExtHostLogService,
extHostStorage: ExtHostStorage,
schemeTransformer: ISchemeTransformer | null,
outputChannelName: string
uriTransformer: IURITransformer | null
): IExtensionApiFactory {
// bootstrap services
const services = new ServiceCollection(...getSingletonServiceDescriptors());
const instaService = new InstantiationService(services);
// Addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService);
const extHostHeapService = rpcProtocol.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService());
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, new ExtHostDecorations(rpcProtocol));
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol));
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment));
const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol));
const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(rpcProtocol));
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors)));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, extHostLogService));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostLogService));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace);
rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration);
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment));
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, schemeTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics, extHostLogService));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService));
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors));
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostWorkspace, extHostDocumentsAndEditors, extHostLogService));
// {{SQL CARBON EDIT}}
// const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands));
// const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, instaService.createInstance(ExtHostDebugService, rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands)); {{SQL CARBON EDIT}} remove debug service
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer, extHostLogService));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, uriTransformer, extHostLogService));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService));
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(LogOutputChannelFactory, initData.logsLocation, rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage);
if (initData.remoteAuthority) {
const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol));
if (initData.remote.isRemote && initData.remote.authority) {
extHostTask.registerTaskSystem(Schemas.vscodeRemote, {
scheme: Schemas.vscodeRemote,
authority: initData.remoteAuthority,
authority: initData.remote.authority,
platform: process.platform
});
@@ -143,7 +153,8 @@ export function createApiFactory(
// Check that no named customers are missing
// {{SQL CARBON EDIT}} filter out the services we don't expose
const expected: ProxyIdentifier<any>[] = values(ExtHostContext).filter(v => v !== ExtHostContext.ExtHostDebugService);
const filtered: ProxyIdentifier<any>[] = [ExtHostContext.ExtHostDebugService, ExtHostContext.ExtHostTask];
const expected: ProxyIdentifier<any>[] = values(ExtHostContext).filter(v => !filtered.includes(v));
rpcProtocol.assertRegistered(expected);
// Other instances
@@ -154,6 +165,7 @@ export function createApiFactory(
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments);
// Register an output channel for exthost log
const outputChannelName = initData.remote.isRemote ? nls.localize('remote extension host Log', "Remote Extension Host") : nls.localize('extension host Log', "Extension Host");
extHostOutputService.createOutputChannelFromLogFile(outputChannelName, extHostLogService.logFile);
// Register API-ish commands
@@ -163,7 +175,7 @@ export function createApiFactory(
// Check document selectors for being overly generic. Technically this isn't a problem but
// in practice many extensions say they support `fooLang` but need fs-access to do so. Those
// extension should specify then the `file`-scheme, e.g `{ scheme: 'fooLang', language: 'fooLang' }`
// extension should specify then the `file`-scheme, e.g. `{ scheme: 'fooLang', language: 'fooLang' }`
// We only inform once, it is not a warning because we just want to raise awareness and because
// we cannot say if the extension is doing it right or wrong...
const checkSelector = (function () {
@@ -239,7 +251,7 @@ export function createApiFactory(
};
// namespace: env
const env: typeof vscode.env = Object.freeze({
const env: typeof vscode.env = {
get machineId() { return initData.telemetryInfo.machineId; },
get sessionId() { return initData.telemetryInfo.sessionId; },
get language() { return initData.environment.appLanguage; },
@@ -257,22 +269,44 @@ export function createApiFactory(
get clipboard(): vscode.Clipboard {
return extHostClipboard;
},
get shell() {
return extHostTerminalService.getDefaultShell(configProvider);
},
openExternal(uri: URI) {
return extHostWindow.openUri(uri, { allowTunneling: !!initData.remoteAuthority });
return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.isRemote });
},
get remoteName() {
if (!initData.remote.authority) {
return undefined;
}
const pos = initData.remote.authority.indexOf('+');
if (pos < 0) {
// funky? bad authority?
return initData.remote.authority;
}
return initData.remote.authority.substr(0, pos);
}
});
};
if (!initData.environment.extensionTestsLocationURI) {
// allow to patch env-function when running tests
Object.freeze(env);
}
const extensionKind = initData.remote.isRemote
? extHostTypes.ExtensionKind.Workspace
: extHostTypes.ExtensionKind.UI;
// namespace: extensions
const extensions: typeof vscode.extensions = {
getExtension(extensionId: string): Extension<any> | undefined {
const desc = extensionRegistry.getExtensionDescription(extensionId);
if (desc) {
return new Extension(extensionService, desc);
return new Extension(extensionService, desc, extensionKind);
}
return undefined;
},
get all(): Extension<any>[] {
return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc));
return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc, extensionKind));
},
get onDidChange() {
return extensionRegistry.onDidChange;
@@ -305,10 +339,6 @@ export function createApiFactory(
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
},
registerCodeInsetProvider(selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerCodeInsetProvider(extension, checkSelector(selector), provider);
},
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider);
},
@@ -469,8 +499,24 @@ export function createApiFactory(
showSaveDialog(options) {
return extHostDialogs.showSaveDialog(options);
},
createStatusBarItem(position?: vscode.StatusBarAlignment, priority?: number): vscode.StatusBarItem {
return extHostStatusBar.createStatusBarEntry(extension.identifier, <number>position, priority);
createStatusBarItem(alignmentOrOptions?: vscode.StatusBarAlignment | vscode.window.StatusBarItemOptions, priority?: number): vscode.StatusBarItem {
let id: string;
let name: string;
let alignment: number | undefined;
if (alignmentOrOptions && typeof alignmentOrOptions !== 'number') {
id = alignmentOrOptions.id;
name = alignmentOrOptions.name;
alignment = alignmentOrOptions.alignment;
priority = alignmentOrOptions.priority;
} else {
id = extension.identifier.value;
name = nls.localize('extensionLabel', "{0} (Extension)", extension.displayName || extension.name);
alignment = alignmentOrOptions as number; // {{SQL CARBON EDIT}} strict-null-check
priority = priority;
}
return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority);
},
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): vscode.Disposable {
return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable);
@@ -488,9 +534,18 @@ export function createApiFactory(
createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options);
},
createTerminal(nameOrOptions?: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options: vscode.WebviewOptions): vscode.WebviewEditorInset {
checkProposedApiEnabled(extension);
return extHostEditorInsets.createWebviewEditorInset(editor, line, height, options, extension);
},
createTerminal(nameOrOptions?: vscode.TerminalOptions | vscode.TerminalVirtualProcessOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
if (typeof nameOrOptions === 'object') {
return extHostTerminalService.createTerminalFromOptions(<vscode.TerminalOptions>nameOrOptions);
if ('virtualProcess' in nameOrOptions) {
return extHostTerminalService.createVirtualProcessTerminal(nameOrOptions);
} else {
nameOrOptions.hideFromUser = nameOrOptions.hideFromUser || (nameOrOptions.runInBackground && extension.enableProposedApi);
return extHostTerminalService.createTerminalFromOptions(nameOrOptions);
}
}
return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs);
},
@@ -634,28 +689,25 @@ export function createApiFactory(
return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider);
},
registerTaskProvider: (type: string, provider: vscode.TaskProvider) => {
return extHostTask.registerTaskProvider(extension, provider);
return extHostTask.registerTaskProvider(extension, type, provider);
},
registerFileSystemProvider(scheme, provider, options) {
return extHostFileSystem.registerFileSystemProvider(scheme, provider, options);
},
get fs() {
return extHostFileSystem.fileSystem;
},
registerFileSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.FileSearchProvider) => {
return extHostSearch.registerFileSearchProvider(scheme, provider);
}),
registerTextSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.TextSearchProvider) => {
return extHostSearch.registerTextSearchProvider(scheme, provider);
}),
registerDocumentCommentProvider: proposedApiFunction(extension, (provider: vscode.DocumentCommentProvider) => {
return extHostComment.registerDocumentCommentProvider(extension.identifier, provider);
}),
registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => {
return extHostComment.registerWorkspaceCommentProvider(extension.identifier, provider);
}),
registerRemoteAuthorityResolver: proposedApiFunction(extension, (authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver) => {
return extensionService.registerRemoteAuthorityResolver(authorityPrefix, resolver);
}),
registerResourceLabelFormatter: proposedApiFunction(extension, (formatter: vscode.ResourceLabelFormatter) => {
return extHostFileSystem.registerResourceLabelFormatter(formatter);
return extHostLabelService.$registerResourceLabelFormatter(formatter);
}),
onDidRenameFile: proposedApiFunction(extension, (listener: (e: vscode.FileRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => {
return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables);
@@ -675,7 +727,7 @@ export function createApiFactory(
}
};
const comment: typeof vscode.comment = {
const comment: typeof vscode.comments = {
createCommentController(id: string, label: string) {
return extHostComment.createCommentController(extension, id, label);
}
@@ -732,7 +784,7 @@ export function createApiFactory(
const tasks: typeof vscode.tasks = {
registerTaskProvider: (type: string, provider: vscode.TaskProvider) => {
return extHostTask.registerTaskProvider(extension, provider);
return extHostTask.registerTaskProvider(extension, type, provider);
},
fetchTasks: (filter?: vscode.TaskFilter): Thenable<vscode.Task[]> => {
return extHostTask.fetchTasks(filter);
@@ -805,7 +857,9 @@ export function createApiFactory(
EndOfLine: extHostTypes.EndOfLine,
EventEmitter: Emitter,
ExtensionExecutionContext: extHostTypes.ExtensionExecutionContext,
ExtensionKind: extHostTypes.ExtensionKind,
CustomExecution: extHostTypes.CustomExecution,
CustomExecution2: extHostTypes.CustomExecution2,
FileChangeType: extHostTypes.FileChangeType,
FileSystemError: extHostTypes.FileSystemError,
FileType: files.FileType,
@@ -872,16 +926,18 @@ class Extension<T> implements vscode.Extension<T> {
private _extensionService: ExtHostExtensionService;
private _identifier: ExtensionIdentifier;
public id: string;
public extensionPath: string;
public packageJSON: IExtensionDescription;
readonly id: string;
readonly extensionPath: string;
readonly packageJSON: IExtensionDescription;
readonly extensionKind: vscode.ExtensionKind;
constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription) {
constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) {
this._extensionService = extensionService;
this._identifier = description.identifier;
this.id = description.identifier.value;
this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
this.packageJSON = description;
this.extensionKind = kind;
}
get isActive(): boolean {

View File

@@ -81,7 +81,7 @@ export class CLIServer {
break;
default:
res.writeHead(404);
res.write(`Unkown message type: ${data.type}`, err => {
res.write(`Unknown message type: ${data.type}`, err => {
if (err) {
console.error(err);
}

View File

@@ -21,7 +21,7 @@ import { IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorksp
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug';
import { getTerminalLauncher, hasChildProcesses, prepareCommand } from 'vs/workbench/contrib/debug/node/terminals';
import { hasChildProcesses, prepareCommand, runInExternalTerminal } from 'vs/workbench/contrib/debug/node/terminals';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
import { ExtHostConfiguration, ExtHostConfigProvider } from '../common/extHostConfiguration';
@@ -357,10 +357,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
} else if (args.kind === 'external') {
const terminalLauncher = getTerminalLauncher();
if (terminalLauncher) {
return terminalLauncher.runInTerminal(args, config);
}
runInExternalTerminal(args, config);
}
return Promise.resolve(undefined);
}

View File

@@ -9,7 +9,7 @@ import * as path from 'vs/base/common/path';
import { createApiFactory, initializeExtensionApi, ISqlExtensionApiFactory } from 'sql/workbench/api/node/sqlExtHost.api.impl';
import { originalFSPath } from 'vs/base/common/resources';
import { Barrier } from 'vs/base/common/async';
import { dispose, toDisposable } from 'vs/base/common/lifecycle';
import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
@@ -18,7 +18,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor';
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IResolveAuthorityResult } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
@@ -29,25 +29,40 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
import { Schemas } from 'vs/base/common/network';
import { withNullAsUndefined } from 'vs/base/common/types';
import { VSBuffer } from 'vs/base/common/buffer';
import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures';
import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
import { RemoteAuthorityResolverError, ExtensionExecutionContext } from 'vs/workbench/api/common/extHostTypes';
import { IURITransformer } from 'vs/base/common/uriIpc';
interface ITestRunner {
/** Old test runner API, as exported from `vscode/lib/testrunner` */
run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
}
interface INewTestRunner {
/** New test runner API, as explained in the extension test doc */
run(): Promise<void>;
}
export interface IHostUtils {
exit(code?: number): void;
exists(path: string): Promise<boolean>;
realpath(path: string): Promise<string>;
}
type TelemetryActivationEventFragment = {
id: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
name: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
extensionVersion: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
publisherDisplayName: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
activationEvents: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
isBuiltin: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
reason: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000;
@@ -78,6 +93,8 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private _started: boolean;
private readonly _disposables: DisposableStore;
constructor(
hostUtils: IHostUtils,
initData: IInitData,
@@ -86,8 +103,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
extHostConfiguration: ExtHostConfiguration,
environment: IEnvironment,
extHostLogService: ExtHostLogService,
schemeTransformer: ISchemeTransformer | null,
outputChannelName: string
uriTransformer: IURITransformer | null
) {
this._hostUtils = hostUtils;
this._initData = initData;
@@ -96,6 +112,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
this._extHostConfiguration = extHostConfiguration;
this._environment = environment;
this._extHostLogService = extHostLogService;
this._disposables = new DisposableStore();
this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace);
this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry);
@@ -137,8 +154,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
this,
this._extHostLogService,
this._storage,
schemeTransformer,
outputChannelName
uriTransformer
);
this._resolvers = Object.create(null);
@@ -160,7 +176,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
// NodeModuleRequireInterceptor.INSTANCE.register(new VSCodeNodeModuleFactory(this._extensionApiFactory, extensionPaths, this._registry, configProvider));
await initializeExtensionApi(this, this._extensionApiFactory, this._registry, configProvider);
NodeModuleRequireInterceptor.INSTANCE.register(new KeytarNodeModuleFactory(this._extHostContext.getProxy(MainContext.MainThreadKeytar), this._environment));
if (this._initData.remoteAuthority) {
if (this._initData.remote.isRemote) {
NodeModuleRequireInterceptor.INSTANCE.register(new OpenNodeModuleFactory(
this._extHostContext.getProxy(MainContext.MainThreadWindow),
this._extHostContext.getProxy(MainContext.MainThreadTelemetry),
@@ -314,23 +330,32 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._mainThreadTelemetryProxy.$publicLog('extensionActivationTimes', {
type ExtensionActivationTimesClassification = {
outcome: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
} & TelemetryActivationEventFragment & ExtensionActivationTimesFragment;
type ExtensionActivationTimesEvent = {
outcome: string
} & ActivationTimesEvent & TelemetryActivationEvent;
type ActivationTimesEvent = {
startup?: boolean;
codeLoadingTime?: number;
activateCallTime?: number;
activateResolvedTime?: number;
};
this._mainThreadTelemetryProxy.$publicLog2<ExtensionActivationTimesEvent, ExtensionActivationTimesClassification>('extensionActivationTimes', {
...event,
...(activationTimes || {}),
outcome,
outcome
});
}
private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
const event = getTelemetryActivationEvent(extensionDescription, reason);
/* __GDPR__
"activatePlugin" : {
"${include}": [
"${TelemetryActivationEvent}"
]
}
*/
this._mainThreadTelemetryProxy.$publicLog('activatePlugin', event);
type ActivatePluginClassification = {} & TelemetryActivationEventFragment;
this._mainThreadTelemetryProxy.$publicLog2<TelemetryActivationEvent, ActivatePluginClassification>('activatePlugin', event);
if (!extensionDescription.main) {
// Treat the extension as being empty => NOT AN ERROR CASE
return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
@@ -347,7 +372,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
});
}
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<IExtensionContext> {
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<vscode.ExtensionContext> {
const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage);
const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage);
@@ -368,7 +393,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
globalStoragePath: this._storagePath.globalValue(extensionDescription),
asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier),
executionContext: this._initData.remoteAuthority ? ExtensionExecutionContext.Remote : ExtensionExecutionContext.Local
executionContext: this._initData.remote.isRemote ? ExtensionExecutionContext.Remote : ExtensionExecutionContext.Local,
});
});
}
@@ -428,27 +453,33 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
console.error(err);
});
return this._handleWorkspaceContainsEagerExtensions(this._extHostWorkspace.workspace);
this._disposables.add(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added)));
const folders = this._extHostWorkspace.workspace ? this._extHostWorkspace.workspace.folders : [];
return this._handleWorkspaceContainsEagerExtensions(folders);
}
private _handleWorkspaceContainsEagerExtensions(workspace: IWorkspace | undefined): Promise<void> {
if (!workspace || workspace.folders.length === 0) {
private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray<vscode.WorkspaceFolder>): Promise<void> {
if (folders.length === 0) {
return Promise.resolve(undefined);
}
return Promise.all(
this._registry.getAllExtensionDescriptions().map((desc) => {
return this._handleWorkspaceContainsEagerExtension(workspace, desc);
return this._handleWorkspaceContainsEagerExtension(folders, desc);
})
).then(() => { });
}
private _handleWorkspaceContainsEagerExtension(workspace: IWorkspace, desc: IExtensionDescription): Promise<void> {
private _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray<vscode.WorkspaceFolder>, desc: IExtensionDescription): Promise<void> {
const activationEvents = desc.activationEvents;
if (!activationEvents) {
return Promise.resolve(undefined);
}
if (this.isActivated(desc.identifier)) {
return Promise.resolve(undefined);
}
const fileNames: string[] = [];
const globPatterns: string[] = [];
@@ -467,16 +498,16 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return Promise.resolve(undefined);
}
const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.identifier, fileName))).then(() => { });
const globPatternPromise = this._activateIfGlobPatterns(desc.identifier, globPatterns);
const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(folders, desc.identifier, fileName))).then(() => { });
const globPatternPromise = this._activateIfGlobPatterns(folders, desc.identifier, globPatterns);
return Promise.all([fileNamePromise, globPatternPromise]).then(() => { });
}
private async _activateIfFileName(workspace: IWorkspace, extensionId: ExtensionIdentifier, fileName: string): Promise<void> {
private async _activateIfFileName(folders: ReadonlyArray<vscode.WorkspaceFolder>, extensionId: ExtensionIdentifier, fileName: string): Promise<void> {
// find exact path
for (const { uri } of workspace.folders) {
for (const { uri } of folders) {
if (await this._hostUtils.exists(path.join(URI.revive(uri).fsPath, fileName))) {
// the file was found
return (
@@ -489,7 +520,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return undefined;
}
private async _activateIfGlobPatterns(extensionId: ExtensionIdentifier, globPatterns: string[]): Promise<void> {
private async _activateIfGlobPatterns(folders: ReadonlyArray<vscode.WorkspaceFolder>, extensionId: ExtensionIdentifier, globPatterns: string[]): Promise<void> {
this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`);
if (globPatterns.length === 0) {
@@ -497,7 +528,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
const tokenSource = new CancellationTokenSource();
const searchP = this._mainThreadWorkspaceProxy.$checkExists(globPatterns, tokenSource.token);
const searchP = this._mainThreadWorkspaceProxy.$checkExists(folders.map(folder => folder.uri), globPatterns, tokenSource.token);
const timer = setTimeout(async () => {
tokenSource.cancel();
@@ -545,7 +576,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
const extensionTestsPath = originalFSPath(extensionTestsLocationURI);
// Require the test runner via node require from the provided path
let testRunner: ITestRunner | undefined;
let testRunner: ITestRunner | INewTestRunner | undefined;
let requireError: Error | undefined;
try {
testRunner = <any>require.__$__nodeRequire(extensionTestsPath);
@@ -553,10 +584,10 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
requireError = error;
}
// Execute the runner if it follows our spec
// Execute the runner if it follows the old `run` spec
if (testRunner && typeof testRunner.run === 'function') {
return new Promise<void>((c, e) => {
testRunner!.run(extensionTestsPath, (error, failures) => {
const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
if (error) {
e(error.toString());
} else {
@@ -565,7 +596,22 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
// after tests have run, we shutdown the host
this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
});
};
const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);
// Using the new API `run(): Promise<void>`
if (runResult && runResult.then) {
runResult
.then(() => {
c();
this._gracefulExit(0);
})
.catch((err: Error) => {
e(err.toString());
this._gracefulExit(1);
});
}
});
}
@@ -582,7 +628,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
// messages to the main process, we delay the exit() by some time
setTimeout(() => {
// If extension tests are running, give the exit code to the renderer
if (this._initData.remoteAuthority && !!this._initData.environment.extensionTestsLocationURI) {
if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) {
this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
return;
}
@@ -735,22 +781,20 @@ function loadCommonJSModule<T>(logService: ILogService, modulePath: string, acti
return Promise.resolve(r);
}
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any {
type TelemetryActivationEvent = {
id: string;
name: string;
extensionVersion: string;
publisherDisplayName: string;
activationEvents: string | null;
isBuiltin: boolean;
reason: string;
};
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TelemetryActivationEvent {
const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent :
reason instanceof ExtensionActivatedByAPI ? 'api' :
'';
/* __GDPR__FRAGMENT__
"TelemetryActivationEvent" : {
"id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"extensionVersion": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
const event = {
id: extensionDescription.identifier.value,
name: extensionDescription.name,

View File

@@ -51,7 +51,7 @@ export const LogOutputChannelFactory = new class implements IOutputChannelFactor
try {
const outputDirPath = join(logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
const outputDir = await dirExists(outputDirPath).then(exists => exists ? exists : mkdirp(outputDirPath).then(() => true)).then(() => outputDirPath);
const fileName = `${this._namePool++}-${name}`;
const fileName = `${this._namePool++}-${name.replace(/[\\/:\*\?"<>\|]/g, '')}`;
const file = URI.file(join(outputDir, `${fileName}.log`));
const appender = new OutputAppender(fileName, file.fsPath);
return new ExtHostOutputChannelBackedByFile(name, appender, proxy);

View File

@@ -16,10 +16,7 @@ import { OutputChannel } from 'vs/workbench/services/search/node/ripgrepSearchUt
import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager';
import * as vscode from 'vscode';
import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from '../common/extHost.protocol';
export interface ISchemeTransformer {
transformOutgoing(scheme: string): string;
}
import { IURITransformer } from 'vs/base/common/uriIpc';
export class ExtHostSearch implements ExtHostSearchShape {
@@ -35,14 +32,14 @@ export class ExtHostSearch implements ExtHostSearchShape {
private _fileSearchManager: FileSearchManager;
constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer | null, private _logService: ILogService, private _pfs = pfs) {
constructor(mainContext: IMainContext, private _uriTransformer: IURITransformer | null, private _logService: ILogService, private _pfs = pfs) {
this._proxy = mainContext.getProxy(MainContext.MainThreadSearch);
this._fileSearchManager = new FileSearchManager();
}
private _transformScheme(scheme: string): string {
if (this._schemeTransformer) {
return this._schemeTransformer.transformOutgoing(scheme);
if (this._uriTransformer) {
return this._uriTransformer.transformOutgoingScheme(scheme);
}
return scheme;
}

View File

@@ -22,6 +22,7 @@ import {
ProcessExecutionOptionsDTO, ProcessExecutionDTO,
ShellExecutionOptionsDTO, ShellExecutionDTO,
CustomExecutionDTO,
CustomExecution2DTO,
TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
} from '../common/shared/tasks';
// {{SQL CARBON EDIT}}
@@ -31,7 +32,7 @@ import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfigurati
import { ExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/node/extHostTerminalService';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
namespace TaskDefinitionDTO {
@@ -80,7 +81,7 @@ namespace ProcessExecutionOptionsDTO {
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is ProcessExecutionDTO {
if (value) {
const candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
@@ -125,7 +126,7 @@ namespace ShellExecutionOptionsDTO {
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is ShellExecutionDTO {
if (value) {
const candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
@@ -163,7 +164,7 @@ namespace ShellExecutionDTO {
}
namespace CustomExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is CustomExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecutionDTO {
if (value) {
let candidate = value as CustomExecutionDTO;
return candidate && candidate.customExecution === 'customExecution';
@@ -179,6 +180,23 @@ namespace CustomExecutionDTO {
}
}
namespace CustomExecution2DTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecution2DTO {
if (value) {
let candidate = value as CustomExecution2DTO;
return candidate && candidate.customExecution === 'customExecution2';
} else {
return false;
}
}
export function from(value: vscode.CustomExecution2): CustomExecution2DTO {
return {
customExecution: 'customExecution2'
};
}
}
namespace TaskHandleDTO {
export function from(value: types.Task): TaskHandleDTO {
let folder: UriComponents | undefined;
@@ -212,14 +230,17 @@ namespace TaskDTO {
if (value === undefined || value === null) {
return undefined;
}
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined;
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined;
if (value.execution instanceof types.ProcessExecution) {
execution = ProcessExecutionDTO.from(value.execution);
} else if (value.execution instanceof types.ShellExecution) {
execution = ShellExecutionDTO.from(value.execution);
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution) {
execution = CustomExecutionDTO.from(<types.CustomExecution>(<vscode.Task2>value).execution2);
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution2) {
execution = CustomExecution2DTO.from(<types.CustomExecution2>(<vscode.Task2>value).execution2);
}
const definition: TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition);
let scope: number | UriComponents;
if (value.scope) {
@@ -348,6 +369,7 @@ namespace TaskExecutionDTO {
}
interface HandlerData {
type: string;
provider: vscode.TaskProvider;
extension: IExtensionDescription;
}
@@ -356,7 +378,7 @@ class CustomExecutionData implements IDisposable {
private static waitForDimensionsTimeoutInMs: number = 5000;
private _cancellationSource?: CancellationTokenSource;
private readonly _onTaskExecutionComplete: Emitter<CustomExecutionData> = new Emitter<CustomExecutionData>();
private readonly _disposables: IDisposable[] = [];
private readonly _disposables = new DisposableStore();
private terminal?: vscode.Terminal;
private terminalId?: number;
public result: number | undefined;
@@ -368,7 +390,7 @@ class CustomExecutionData implements IDisposable {
public dispose(): void {
this._cancellationSource = undefined;
dispose(this._disposables);
this._disposables.dispose();
}
public get onTaskExecutionComplete(): Event<CustomExecutionData> {
@@ -405,7 +427,7 @@ class CustomExecutionData implements IDisposable {
const callbackTerminals: vscode.Terminal[] = this.terminalService.terminals.filter((terminal) => terminal._id === terminalId);
if (!callbackTerminals || callbackTerminals.length === 0) {
this._disposables.push(this.terminalService.onDidOpenTerminal(this.onDidOpenTerminal.bind(this)));
this._disposables.add(this.terminalService.onDidOpenTerminal(this.onDidOpenTerminal.bind(this)));
return;
}
@@ -441,9 +463,9 @@ class CustomExecutionData implements IDisposable {
}
this._cancellationSource = new CancellationTokenSource();
this._disposables.push(this._cancellationSource);
this._disposables.add(this._cancellationSource);
this._disposables.push(this.terminalService.onDidCloseTerminal(this.onDidCloseTerminal.bind(this)));
this._disposables.add(this.terminalService.onDidCloseTerminal(this.onDidCloseTerminal.bind(this)));
// Regardless of how the task completes, we are done with this custom execution task.
this.customExecution.callback(terminalRenderer, this._cancellationSource.token).then(
@@ -468,6 +490,8 @@ export class ExtHostTask implements ExtHostTaskShape {
private _taskExecutions: Map<string, TaskExecutionImpl>;
private _providedCustomExecutions: Map<string, CustomExecutionData>;
private _activeCustomExecutions: Map<string, CustomExecutionData>;
private _providedCustomExecutions2: Map<string, vscode.CustomExecution2>;
private _activeCustomExecutions2: Map<string, vscode.CustomExecution2>;
private readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
private readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
@@ -491,15 +515,17 @@ export class ExtHostTask implements ExtHostTaskShape {
this._taskExecutions = new Map<string, TaskExecutionImpl>();
this._providedCustomExecutions = new Map<string, CustomExecutionData>();
this._activeCustomExecutions = new Map<string, CustomExecutionData>();
this._providedCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
this._activeCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
}
public registerTaskProvider(extension: IExtensionDescription, provider: vscode.TaskProvider): vscode.Disposable {
public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable {
if (!provider) {
return new types.Disposable(() => { });
}
const handle = this.nextHandle();
this._handlers.set(handle, { provider, extension });
this._proxy.$registerTaskProvider(handle);
this._handlers.set(handle, { type, provider, extension });
this._proxy.$registerTaskProvider(handle, type);
return new types.Disposable(() => {
this._handlers.delete(handle);
this._proxy.$unregisterTaskProvider(handle);
@@ -555,6 +581,19 @@ export class ExtHostTask implements ExtHostTaskShape {
}
public async $onDidStartTask(execution: TaskExecutionDTO, terminalId: number): Promise<void> {
const execution2: vscode.CustomExecution2 | undefined = this._providedCustomExecutions2.get(execution.id);
if (execution2) {
if (this._activeCustomExecutions2.get(execution.id) !== undefined) {
throw new Error('We should not be trying to start the same custom task executions twice.');
}
// Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task.
this._activeCustomExecutions2.set(execution.id, execution2);
this._terminalService.performTerminalIdAction(terminalId, async terminal => {
this._terminalService.attachVirtualProcessToTerminal(terminalId, await execution2.callback());
});
}
// Once a terminal is spun up for the custom execution task this event will be fired.
// At that point, we need to actually start the callback, but
// only if it hasn't already begun.
@@ -630,6 +669,7 @@ export class ExtHostTask implements ExtHostTaskShape {
// since we obviously cannot send callback functions through the proxy.
// So, clear out any existing ones.
this._providedCustomExecutions.clear();
this._providedCustomExecutions2.clear();
// Set up a list of task ID promises that we can wait on
// before returning the provided tasks. The ensures that
@@ -653,15 +693,13 @@ export class ExtHostTask implements ExtHostTaskShape {
taskDTOs.push(taskDTO);
if (CustomExecutionDTO.is(taskDTO.execution)) {
taskIdPromises.push(new Promise((resolve) => {
// The ID is calculated on the main thread task side, so, let's call into it here.
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
// is invoked, we have to be able to map it back to our data.
this._proxy.$createTaskId(taskDTO).then((taskId) => {
this._providedCustomExecutions.set(taskId, new CustomExecutionData(<vscode.CustomExecution>(<vscode.Task2>task).execution2, this._terminalService));
resolve();
});
}));
// The ID is calculated on the main thread task side, so, let's call into it here.
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
// is invoked, we have to be able to map it back to our data.
taskIdPromises.push(this.addCustomExecution(taskDTO, <vscode.Task2>task));
} else if (CustomExecution2DTO.is(taskDTO.execution)) {
taskIdPromises.push(this.addCustomExecution2(taskDTO, <vscode.Task2>task));
}
}
}
@@ -682,6 +720,43 @@ export class ExtHostTask implements ExtHostTaskShape {
}
// {{SQL CARBON EDIT}} disable debug related method
public async $resolveTask(handle: number, taskDTO: TaskDTO): Promise<TaskDTO | undefined> {
/*const handler = this._handlers.get(handle);
if (!handler) {
return Promise.reject(new Error('no handler found'));
}
if (taskDTO.definition.type !== handler.type) {
throw new Error(`Unexpected: Task of type [${taskDTO.definition.type}] cannot be resolved by provider of type [${handler.type}].`);
}
const task = await TaskDTO.to(taskDTO, this._workspaceProvider);
if (!task) {
throw new Error('Unexpected: Task cannot be resolved.');
}
const resolvedTask = await handler.provider.resolveTask(task, CancellationToken.None);
if (!resolvedTask) {
return;
}
const resolvedTaskDTO: TaskDTO | undefined = TaskDTO.from(resolvedTask, handler.extension);
if (!resolvedTaskDTO) {
throw new Error('Unexpected: Task cannot be resolved.');
}
if (CustomExecutionDTO.is(resolvedTaskDTO.execution)) {
await this.addCustomExecution(taskDTO, <vscode.Task2>task);
}
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
await this.addCustomExecution2(taskDTO, <vscode.Task2>task);
}
return resolvedTaskDTO;*/
return undefined;
}
public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> {
/*const configProvider = await this._configurationService.getConfigProvider();
const uri: URI = URI.revive(uriComponents);
@@ -714,7 +789,7 @@ export class ExtHostTask implements ExtHostTaskShape {
paths[i] = resolver.resolve(ws, paths[i]);
}
}
result.process = win32.findExecutable(
result.process = await win32.findExecutable(
resolver.resolve(ws, toResolve.process.name),
toResolve.process.cwd !== undefined ? resolver.resolve(ws, toResolve.process.cwd) : undefined,
paths
@@ -728,6 +803,16 @@ export class ExtHostTask implements ExtHostTaskShape {
return this._handleCounter++;
}
private async addCustomExecution(taskDTO: TaskDTO, task: vscode.Task2): Promise<void> {
const taskId = await this._proxy.$createTaskId(taskDTO);
this._providedCustomExecutions.set(taskId, new CustomExecutionData(<vscode.CustomExecution>(<vscode.Task2>task).execution2, this._terminalService));
}
private async addCustomExecution2(taskDTO: TaskDTO, task: vscode.Task2): Promise<void> {
const taskId = await this._proxy.$createTaskId(taskDTO);
this._providedCustomExecutions2.set(taskId, <vscode.CustomExecution2>(<vscode.Task2>task).execution2);
}
private async getTaskExecution(execution: TaskExecutionDTO | string, task?: vscode.Task): Promise<TaskExecutionImpl> {
if (typeof execution === 'string') {
const taskExecution = this._taskExecutions.get(execution);
@@ -757,5 +842,9 @@ export class ExtHostTask implements ExtHostTaskShape {
this._proxy.$customExecutionComplete(execution.id, extensionCallback.result);
extensionCallback.dispose();
}
const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id);
if (extensionCallback2) {
this._activeCustomExecutions2.delete(execution.id);
}
}
}

View File

@@ -10,10 +10,10 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { ILogService } from 'vs/platform/log/common/log';
import { EXT_HOST_CREATION_DELAY, IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal';
import { EXT_HOST_CREATION_DELAY, IShellLaunchConfig, ITerminalEnvironment, ITerminalChildProcess, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal';
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
import { timeout } from 'vs/base/common/async';
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
@@ -21,7 +21,9 @@ import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
// {{SQL CARBON EDIT}}
// import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal';
import { getSystemShell, detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal';
import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment';
import { IDisposable } from 'vs/base/common/lifecycle';
const RENDERER_NO_PROCESS_ID = -1;
@@ -115,9 +117,17 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
cwd?: string | URI,
env?: { [key: string]: string | null },
waitOnExit?: boolean,
strictEnv?: boolean
strictEnv?: boolean,
hideFromUser?: boolean
): void {
this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv).then(terminal => {
this._proxy.$createTerminal({ name: this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser }).then(terminal => {
this._name = terminal.name;
this._runQueuedRequests(terminal.id);
});
}
public createVirtualProcess(): Promise<void> {
return this._proxy.$createTerminal({ name: this._name, isVirtualProcess: true }).then(terminal => {
this._name = terminal.name;
this._runQueuedRequests(terminal.id);
});
@@ -176,7 +186,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
this._pidPromiseComplete(processId);
this._pidPromiseComplete = null;
} else {
// Recreate the promise if this is the nth processId set (eg. reused task terminals)
// Recreate the promise if this is the nth processId set (e.g. reused task terminals)
this._pidPromise.then(pid => {
if (pid !== processId) {
this._pidPromise = Promise.resolve(processId);
@@ -274,10 +284,13 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _proxy: MainThreadTerminalServiceShape;
private _activeTerminal: ExtHostTerminal | undefined;
private _terminals: ExtHostTerminal[] = [];
private _terminalProcesses: { [id: number]: TerminalProcess } = {};
private _terminalProcesses: { [id: number]: ITerminalChildProcess } = {};
private _terminalRenderers: ExtHostTerminalRenderer[] = [];
private _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal> } = {};
// TODO: Pull this from main side
private _isWorkspaceShellAllowed: boolean = false;
public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; }
public get terminals(): ExtHostTerminal[] { return this._terminals; }
@@ -309,11 +322,28 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, options.name);
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv);
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv, options.hideFromUser);
this._terminals.push(terminal);
return terminal;
}
public createVirtualProcessTerminal(options: vscode.TerminalVirtualProcessOptions): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, options.name);
const p = new ExtHostVirtualProcess(options.virtualProcess);
terminal.createVirtualProcess().then(() => this._setupExtHostProcessListeners(terminal._id, p));
this._terminals.push(terminal);
return terminal;
}
public attachVirtualProcessToTerminal(id: number, virtualProcess: vscode.TerminalVirtualProcess) {
const terminal = this._getTerminalById(id);
if (!terminal) {
throw new Error(`Cannot resolve terminal with id ${id} for virtual process`);
}
const p = new ExtHostVirtualProcess(virtualProcess);
this._setupExtHostProcessListeners(id, p);
}
public createTerminalRenderer(name: string): vscode.TerminalRenderer {
const terminal = new ExtHostTerminal(this._proxy, name);
terminal._setProcessId(undefined);
@@ -325,6 +355,32 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
return renderer;
}
public getDefaultShell(configProvider: ExtHostConfigProvider): string {
const fetchSetting = (key: string) => {
const setting = configProvider
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
.inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
return this._apiInspectConfigToPlain<string | string[]>(setting);
};
return terminalEnvironment.getDefaultShell(
fetchSetting,
this._isWorkspaceShellAllowed,
getSystemShell(platform.platform),
process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'),
process.env.windir
);
}
private _getDefaultShellArgs(configProvider: ExtHostConfigProvider): string[] | string | undefined {
const fetchSetting = (key: string) => {
const setting = configProvider
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
.inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
return this._apiInspectConfigToPlain<string | string[]>(setting);
};
return terminalEnvironment.getDefaultShellArgs(fetchSetting, this._isWorkspaceShellAllowed);
}
public async resolveTerminalRenderer(id: number): Promise<vscode.TerminalRenderer> {
// Check to see if the extension host already knows about this terminal.
for (const terminalRenderer of this._terminalRenderers) {
@@ -352,7 +408,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
return;
}
this._performTerminalIdAction(id, terminal => {
this.performTerminalIdAction(id, terminal => {
if (terminal) {
this._activeTerminal = terminal;
if (original !== this._activeTerminal) {
@@ -380,14 +436,26 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
});
}
}
// When a terminal's dimensions change, a renderer's _maximum_ dimensions change
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._setMaximumDimensions(cols, rows);
}
});
}
public $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void {
if (this._terminalProcesses[id]) {
// Virtual processes only - when virtual process resize fires it means that the
// terminal's maximum dimensions changed
this._terminalProcesses[id].resize(cols, rows);
} else {
// Terminal renderer
this._getTerminalByIdEventually(id).then(() => {
// When a terminal's dimensions change, a renderer's _maximum_ dimensions change
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._setMaximumDimensions(cols, rows);
}
});
}
}
public $acceptTerminalRendererInput(id: number, data: string): void {
const renderer = this._getTerminalRendererById(id);
if (renderer) {
@@ -425,10 +493,10 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
public $acceptTerminalProcessId(id: number, processId: number): void {
this._performTerminalIdAction(id, terminal => terminal._setProcessId(processId));
this.performTerminalIdAction(id, terminal => terminal._setProcessId(processId));
}
private _performTerminalIdAction(id: number, callback: (terminal: ExtHostTerminal) => void): void {
public performTerminalIdAction(id: number, callback: (terminal: ExtHostTerminal) => void): void {
let terminal = this._getTerminalById(id);
if (terminal) {
callback(terminal);
@@ -439,7 +507,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
if (terminal) {
callback(terminal);
}
}, EXT_HOST_CREATION_DELAY);
}, EXT_HOST_CREATION_DELAY * 2);
}
}
@@ -453,6 +521,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
};
}
private async _getNonInheritedEnv(): Promise<platform.IProcessEnvironment> {
const env = await getMainProcessParentEnv();
env.VSCODE_IPC_HOOK_CLI = process.env['VSCODE_IPC_HOOK_CLI']!;
return env;
}
public async $createProcess(id: number, shellLaunchConfigDto: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void> {
const shellLaunchConfig: IShellLaunchConfig = {
name: shellLaunchConfigDto.name,
@@ -466,13 +540,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
const configProvider = await this._extHostConfiguration.getConfigProvider();
if (!shellLaunchConfig.executable) {
const fetchSetting = (key: string) => {
const setting = configProvider
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
.inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
return this._apiInspectConfigToPlain<string | string[]>(setting);
};
terminalEnvironment.mergeDefaultShellPathAndArgs(shellLaunchConfig, fetchSetting, isWorkspaceShellAllowed || false, getDefaultShell(platform.platform));
shellLaunchConfig.executable = this.getDefaultShell(configProvider);
shellLaunchConfig.args = this._getDefaultShellArgs(configProvider);
}
// Get the initial cwd
@@ -495,6 +564,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
// {{SQL CARBON EDIT}}
// const variableResolver = workspaceFolders ? new ExtHostVariableResolverService(workspaceFolders, this._extHostDocumentsAndEditors, configProvider) : undefined;
const variableResolver = undefined;
const baseEnv = terminalConfig.get<boolean>('inheritEnv', true) ? process.env as platform.IProcessEnvironment : await this._getNonInheritedEnv();
const env = terminalEnvironment.createTerminalEnvironment(
shellLaunchConfig,
lastActiveWorkspace,
@@ -502,16 +572,30 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
variableResolver,
isWorkspaceShellAllowed,
pkg.version,
terminalConfig.get<boolean>('setLocaleVariables', false)
terminalConfig.get<boolean>('setLocaleVariables', false),
baseEnv
);
// Fork the process and listen for messages
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env);
const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty') as boolean, this._logService);
p.onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid));
// TODO: Support conpty on remote, it doesn't seem to work for some reason?
// TODO: When conpty is enabled, only enable it when accessibilityMode is off
const enableConpty = false; //terminalConfig.get('windowsEnableConpty') as boolean;
this._setupExtHostProcessListeners(id, new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, enableConpty, this._logService));
}
public $startVirtualProcess(id: number, initialDimensions: ITerminalDimensionsDto | undefined): void {
(this._terminalProcesses[id] as ExtHostVirtualProcess).startSendingEvents(initialDimensions);
}
private _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): void {
p.onProcessReady((e: { pid: number, cwd: string }) => this._proxy.$sendProcessReady(id, e.pid, e.cwd));
p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title));
p.onProcessData(data => this._proxy.$sendProcessData(id, data));
p.onProcessExit(exitCode => this._onProcessExit(id, exitCode));
if (p.onProcessOverrideDimensions) {
p.onProcessOverrideDimensions(e => this._proxy.$sendOverrideDimensions(id, e));
}
this._terminalProcesses[id] = p;
}
@@ -546,10 +630,19 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
return id;
}
private _onProcessExit(id: number, exitCode: number): void {
// Remove listeners
this._terminalProcesses[id].dispose();
public $requestAvailableShells(): Promise<IShellDefinitionDto[]> {
return detectAvailableShells();
}
public async $requestDefaultShellAndArgs(): Promise<IShellAndArgsDto> {
const configProvider = await this._extHostConfiguration.getConfigProvider();
return Promise.resolve({
shell: this.getDefaultShell(configProvider),
args: this._getDefaultShellArgs(configProvider)
});
}
private _onProcessExit(id: number, exitCode: number): void {
// Remove process reference
delete this._terminalProcesses[id];
@@ -611,6 +704,10 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
});
return index;
}
public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void {
this._isWorkspaceShellAllowed = isAllowed;
}
}
class ApiRequest {
@@ -626,3 +723,90 @@ class ApiRequest {
this._callback.apply(proxy, [id].concat(this._args));
}
}
class ExtHostVirtualProcess implements ITerminalChildProcess {
private _queuedEvents: (IQueuedEvent<string> | IQueuedEvent<number> | IQueuedEvent<{ pid: number, cwd: string }> | IQueuedEvent<ITerminalDimensions | undefined>)[] = [];
private _queueDisposables: IDisposable[] | undefined;
private readonly _onProcessData = new Emitter<string>();
public readonly onProcessData: Event<string> = this._onProcessData.event;
private readonly _onProcessExit = new Emitter<number>();
public readonly onProcessExit: Event<number> = this._onProcessExit.event;
private readonly _onProcessReady = new Emitter<{ pid: number, cwd: string }>();
public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
private readonly _onProcessTitleChanged = new Emitter<string>();
public readonly onProcessTitleChanged: Event<string> = this._onProcessTitleChanged.event;
private readonly _onProcessOverrideDimensions = new Emitter<ITerminalDimensions | undefined>();
public get onProcessOverrideDimensions(): Event<ITerminalDimensions | undefined> { return this._onProcessOverrideDimensions.event; }
constructor(
private readonly _virtualProcess: vscode.TerminalVirtualProcess
) {
this._queueDisposables = [];
this._queueDisposables.push(this._virtualProcess.onDidWrite(e => this._queuedEvents.push({ emitter: this._onProcessData, data: e })));
if (this._virtualProcess.onDidExit) {
this._queueDisposables.push(this._virtualProcess.onDidExit(e => this._queuedEvents.push({ emitter: this._onProcessExit, data: e })));
}
if (this._virtualProcess.onDidOverrideDimensions) {
this._queueDisposables.push(this._virtualProcess.onDidOverrideDimensions(e => this._queuedEvents.push({ emitter: this._onProcessOverrideDimensions, data: e ? { cols: e.columns, rows: e.rows } : undefined })));
}
}
shutdown(): void {
if (this._virtualProcess.shutdown) {
this._virtualProcess.shutdown();
}
}
input(data: string): void {
if (this._virtualProcess.handleInput) {
this._virtualProcess.handleInput(data);
}
}
resize(cols: number, rows: number): void {
if (this._virtualProcess.setDimensions) {
this._virtualProcess.setDimensions({ columns: cols, rows });
}
}
getInitialCwd(): Promise<string> {
return Promise.resolve('');
}
getCwd(): Promise<string> {
return Promise.resolve('');
}
getLatency(): Promise<number> {
return Promise.resolve(0);
}
startSendingEvents(initialDimensions: ITerminalDimensionsDto | undefined): void {
// Flush all buffered events
this._queuedEvents.forEach(e => (<any>e.emitter.fire)(e.data));
this._queuedEvents = [];
this._queueDisposables = undefined;
// Attach the real listeners
this._virtualProcess.onDidWrite(e => this._onProcessData.fire(e));
if (this._virtualProcess.onDidExit) {
this._virtualProcess.onDidExit(e => {
// Ensure only positive exit codes are returned
this._onProcessExit.fire(e >= 0 ? e : 1);
});
}
if (this._virtualProcess.onDidOverrideDimensions) {
this._virtualProcess.onDidOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e ? { cols: e.columns, rows: e.rows } : undefined)); // {{SQL CARBON EDIT}} strict-null-check
}
if (this._virtualProcess.start) {
this._virtualProcess.start(initialDimensions);
}
}
}
interface IQueuedEvent<T> {
emitter: Emitter<T>;
data: T;
}