mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-30 13:31:07 +01:00
Merge branch 'main' into aeschli/hcLightThemeKind
This commit is contained in:
@@ -223,9 +223,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
if (typeof filter.exclusive === 'boolean') {
|
||||
checkProposedApiEnabled(extension, 'documentFiltersExclusive');
|
||||
}
|
||||
if (typeof filter.notebookType === 'string') {
|
||||
checkProposedApiEnabled(extension, 'notebookDocumentSelector');
|
||||
}
|
||||
}
|
||||
return selector;
|
||||
};
|
||||
@@ -498,6 +495,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension, 'inlineCompletions');
|
||||
return extHostLanguageFeatures.registerInlineCompletionsProvider(extension, checkSelector(selector), provider);
|
||||
},
|
||||
registerInlineCompletionItemProviderNew(selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProviderNew): vscode.Disposable {
|
||||
checkProposedApiEnabled(extension, 'inlineCompletionsNew');
|
||||
if (provider.handleDidShowCompletionItem && !isProposedApiEnabled(extension, 'inlineCompletionsAdditions')) {
|
||||
throw new Error(`When the method "handleDidShowCompletionItem" is implemented on a provider, the usage of the proposed api 'inlineCompletionsAdditions' must be declared!`);
|
||||
}
|
||||
return extHostLanguageFeatures.registerInlineCompletionsProviderNew(extension, checkSelector(selector), provider);
|
||||
},
|
||||
registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerDocumentLinkProvider(extension, checkSelector(selector), provider);
|
||||
},
|
||||
@@ -652,9 +656,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
return extHostProgress.withProgress(extension, options, task);
|
||||
},
|
||||
createOutputChannel(name: string, languageId?: string): vscode.OutputChannel {
|
||||
if (languageId) {
|
||||
checkProposedApiEnabled(extension, 'outputChannelLanguage');
|
||||
}
|
||||
return extHostOutputService.createOutputChannel(name, languageId, extension);
|
||||
},
|
||||
createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn; preserveFocus?: boolean }, options?: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
|
||||
@@ -882,6 +883,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
}
|
||||
return extHostNotebook.getNotebookDocument(uri).apiNotebook;
|
||||
},
|
||||
onDidSaveNotebookDocument(listener, thisArg, disposables) {
|
||||
checkProposedApiEnabled(extension, 'notebookDocumentEvents');
|
||||
return extHostNotebookDocuments.onDidSaveNotebookDocument(listener, thisArg, disposables);
|
||||
},
|
||||
onDidChangeNotebookDocument(listener, thisArg, disposables) {
|
||||
checkProposedApiEnabled(extension, 'notebookDocumentEvents');
|
||||
return extHostNotebookDocuments.onDidChangeNotebookDocument(listener, thisArg, disposables);
|
||||
},
|
||||
get onDidOpenNotebookDocument(): Event<vscode.NotebookDocument> {
|
||||
return extHostNotebook.onDidOpenNotebookDocument;
|
||||
},
|
||||
@@ -1203,6 +1212,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
InlineValueVariableLookup: extHostTypes.InlineValueVariableLookup,
|
||||
InlineValueEvaluatableExpression: extHostTypes.InlineValueEvaluatableExpression,
|
||||
InlineCompletionTriggerKind: extHostTypes.InlineCompletionTriggerKind,
|
||||
InlineCompletionTriggerKindNew: extHostTypes.InlineCompletionTriggerKindNew,
|
||||
EventEmitter: Emitter,
|
||||
ExtensionKind: extHostTypes.ExtensionKind,
|
||||
ExtensionMode: extHostTypes.ExtensionMode,
|
||||
@@ -1216,7 +1226,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
FoldingRangeKind: extHostTypes.FoldingRangeKind,
|
||||
FunctionBreakpoint: extHostTypes.FunctionBreakpoint,
|
||||
InlineCompletionItem: extHostTypes.InlineSuggestion,
|
||||
InlineCompletionItemNew: extHostTypes.InlineSuggestionNew,
|
||||
InlineCompletionList: extHostTypes.InlineSuggestions,
|
||||
InlineCompletionListNew: extHostTypes.InlineSuggestionsNew,
|
||||
Hover: extHostTypes.Hover,
|
||||
IndentAction: languageConfiguration.IndentAction,
|
||||
Location: extHostTypes.Location,
|
||||
|
||||
@@ -628,8 +628,7 @@ export interface IEditorTabGroupDto {
|
||||
export enum TabKind {
|
||||
Singular = 0,
|
||||
Diff = 1,
|
||||
SidebySide = 2,
|
||||
Other = 3
|
||||
SidebySide = 2
|
||||
}
|
||||
|
||||
export interface IEditorTabDto {
|
||||
@@ -1339,7 +1338,10 @@ export interface ExtHostSearchShape {
|
||||
|
||||
export interface ExtHostExtensionServiceShape {
|
||||
$resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
|
||||
$getCanonicalURI(remoteAuthority: string, uri: UriComponents): Promise<UriComponents>;
|
||||
/**
|
||||
* Returns `null` if no resolver for `remoteAuthority` is found.
|
||||
*/
|
||||
$getCanonicalURI(remoteAuthority: string, uri: UriComponents): Promise<UriComponents | null>;
|
||||
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
|
||||
$extensionTestsExecute(): Promise<number>;
|
||||
$extensionTestsExit(code: number): Promise<void>;
|
||||
@@ -2005,7 +2007,7 @@ export type NotebookCellsChangedEventDto = {
|
||||
};
|
||||
|
||||
export interface ExtHostNotebookDocumentsShape {
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: SerializableObjectWithBuffers<NotebookCellsChangedEventDto>, isDirty: boolean): void;
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: SerializableObjectWithBuffers<NotebookCellsChangedEventDto>, isDirty: boolean, newMetadata?: notebookCommon.NotebookDocumentMetadata): void;
|
||||
$acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void;
|
||||
$acceptModelSaved(uriComponents: UriComponents): void;
|
||||
$acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void;
|
||||
|
||||
@@ -50,7 +50,7 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo
|
||||
return arg;
|
||||
}
|
||||
|
||||
return commentController;
|
||||
return commentController.value;
|
||||
} else if (arg && arg.$mid === MarshalledId.CommentThread) {
|
||||
const commentController = this._commentControllers.get(arg.commentControlHandle);
|
||||
|
||||
@@ -64,7 +64,7 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo
|
||||
return arg;
|
||||
}
|
||||
|
||||
return commentThread;
|
||||
return commentThread.value;
|
||||
} else if (arg && arg.$mid === MarshalledId.CommentThreadReply) {
|
||||
const commentController = this._commentControllers.get(arg.thread.commentControlHandle);
|
||||
|
||||
@@ -79,7 +79,7 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo
|
||||
}
|
||||
|
||||
return {
|
||||
thread: commentThread,
|
||||
thread: commentThread.value,
|
||||
text: arg.text
|
||||
};
|
||||
} else if (arg && arg.$mid === MarshalledId.CommentNode) {
|
||||
|
||||
@@ -953,7 +953,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
|
||||
if (activeEditor) {
|
||||
return activeEditor.document.uri;
|
||||
}
|
||||
const activeTab = editorTabs.tabGroups.all.find(group => group.isActive)?.activeTab;
|
||||
const activeTab = editorTabs.tabGroups.groups.find(group => group.isActive)?.activeTab;
|
||||
if (activeTab !== undefined) {
|
||||
// Resolve a resource from the tab
|
||||
const asSideBySideResource = activeTab.resource as { primary?: URI; secondary?: URI } | undefined;
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface IEditorTabGroup {
|
||||
}
|
||||
|
||||
export interface IEditorTabGroups {
|
||||
all: IEditorTabGroup[];
|
||||
groups: IEditorTabGroup[];
|
||||
activeTabGroup: IEditorTabGroup | undefined;
|
||||
readonly onDidChangeTabGroup: Event<void>;
|
||||
readonly onDidChangeActiveTabGroup: Event<IEditorTabGroup | undefined>;
|
||||
@@ -55,7 +55,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
|
||||
private readonly _onDidChangeActiveTabGroup = new Emitter<IEditorTabGroup | undefined>();
|
||||
|
||||
private _tabGroups: IEditorTabGroups = {
|
||||
all: [],
|
||||
groups: [],
|
||||
activeTabGroup: undefined,
|
||||
onDidChangeTabGroup: this._onDidChangeTabGroup.event,
|
||||
onDidChangeActiveTabGroup: this._onDidChangeActiveTabGroup.event
|
||||
@@ -71,7 +71,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
|
||||
|
||||
$acceptEditorTabModel(tabGroups: IEditorTabGroupDto[]): void {
|
||||
// Clears the tab groups array
|
||||
this._tabGroups.all.length = 0;
|
||||
this._tabGroups.groups.length = 0;
|
||||
let activeGroupFound = false;
|
||||
for (const group of tabGroups) {
|
||||
let activeTab: IEditorTab | undefined;
|
||||
@@ -82,7 +82,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
|
||||
}
|
||||
return extHostTab;
|
||||
});
|
||||
this._tabGroups.all.push(Object.freeze({
|
||||
this._tabGroups.groups.push(Object.freeze({
|
||||
isActive: group.isActive,
|
||||
viewColumn: typeConverters.ViewColumn.to(group.viewColumn),
|
||||
activeTab,
|
||||
@@ -93,7 +93,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
|
||||
if (group.isActive) {
|
||||
activeGroupFound = true;
|
||||
const oldActiveTabGroup = this._tabGroups.activeTabGroup;
|
||||
this._tabGroups.activeTabGroup = this._tabGroups.all[this._tabGroups.all.length - 1];
|
||||
this._tabGroups.activeTabGroup = this._tabGroups.groups[this._tabGroups.groups.length - 1];
|
||||
// Diff the old and current active group to decide if we should fire a change event
|
||||
if (this.groupDiff(oldActiveTabGroup, this._tabGroups.activeTabGroup)) {
|
||||
this._onDidChangeActiveTabGroup.fire(this._tabGroups.activeTabGroup);
|
||||
|
||||
@@ -10,8 +10,7 @@ import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/c
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionActivationReason, MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
|
||||
/**
|
||||
* Represents the source code (module) of an extension.
|
||||
@@ -166,14 +165,11 @@ type ActivationIdAndReason = { id: ExtensionIdentifier; reason: ExtensionActivat
|
||||
|
||||
export class ExtensionsActivator implements IDisposable {
|
||||
|
||||
private _isDisposed: boolean;
|
||||
|
||||
private readonly _registry: ExtensionDescriptionRegistry;
|
||||
private readonly _resolvedExtensionsSet: Set<string>;
|
||||
private readonly _hostExtensionsMap: Map<string, ExtensionIdentifier>;
|
||||
private readonly _externalExtensionsMap: Map<string, ExtensionIdentifier>;
|
||||
private readonly _host: IExtensionsActivatorHost;
|
||||
private readonly _activatingExtensions: Map<string, Promise<void>>;
|
||||
private readonly _activatedExtensions: Map<string, ActivatedExtension>;
|
||||
private readonly _operations: Map<string, ActivationOperation>;
|
||||
/**
|
||||
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
|
||||
*/
|
||||
@@ -182,130 +178,115 @@ export class ExtensionsActivator implements IDisposable {
|
||||
constructor(
|
||||
registry: ExtensionDescriptionRegistry,
|
||||
resolvedExtensions: ExtensionIdentifier[],
|
||||
hostExtensions: ExtensionIdentifier[],
|
||||
externalExtensions: ExtensionIdentifier[],
|
||||
host: IExtensionsActivatorHost,
|
||||
@ILogService private readonly _logService: ILogService
|
||||
) {
|
||||
this._isDisposed = false;
|
||||
this._registry = registry;
|
||||
this._resolvedExtensionsSet = new Set<string>();
|
||||
resolvedExtensions.forEach((extensionId) => this._resolvedExtensionsSet.add(ExtensionIdentifier.toKey(extensionId)));
|
||||
this._hostExtensionsMap = new Map<string, ExtensionIdentifier>();
|
||||
hostExtensions.forEach((extensionId) => this._hostExtensionsMap.set(ExtensionIdentifier.toKey(extensionId), extensionId));
|
||||
this._externalExtensionsMap = new Map<string, ExtensionIdentifier>();
|
||||
externalExtensions.forEach((extensionId) => this._externalExtensionsMap.set(ExtensionIdentifier.toKey(extensionId), extensionId));
|
||||
this._host = host;
|
||||
this._activatingExtensions = new Map<string, Promise<void>>();
|
||||
this._activatedExtensions = new Map<string, ActivatedExtension>();
|
||||
this._operations = new Map<string, ActivationOperation>();
|
||||
this._alreadyActivatedEvents = Object.create(null);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._isDisposed = true;
|
||||
for (const [_, op] of this._operations) {
|
||||
op.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public isActivated(extensionId: ExtensionIdentifier): boolean {
|
||||
const extensionKey = ExtensionIdentifier.toKey(extensionId);
|
||||
|
||||
return this._activatedExtensions.has(extensionKey);
|
||||
const op = this._operations.get(ExtensionIdentifier.toKey(extensionId));
|
||||
return Boolean(op && op.value);
|
||||
}
|
||||
|
||||
public getActivatedExtension(extensionId: ExtensionIdentifier): ActivatedExtension {
|
||||
const extensionKey = ExtensionIdentifier.toKey(extensionId);
|
||||
|
||||
const activatedExtension = this._activatedExtensions.get(extensionKey);
|
||||
if (!activatedExtension) {
|
||||
throw new Error('Extension `' + extensionId.value + '` is not known or not activated');
|
||||
const op = this._operations.get(ExtensionIdentifier.toKey(extensionId));
|
||||
if (!op || !op.value) {
|
||||
throw new Error(`Extension '${extensionId.value}' is not known or not activated`);
|
||||
}
|
||||
return activatedExtension;
|
||||
return op.value;
|
||||
}
|
||||
|
||||
public activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
|
||||
public async activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
|
||||
if (this._alreadyActivatedEvents[activationEvent]) {
|
||||
return NO_OP_VOID_PROMISE;
|
||||
return;
|
||||
}
|
||||
|
||||
const activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent);
|
||||
return this._activateExtensions(activateExtensions.map(e => ({
|
||||
await this._activateExtensions(activateExtensions.map(e => ({
|
||||
id: e.identifier,
|
||||
reason: { startup, extensionId: e.identifier, activationEvent }
|
||||
}))).then(() => {
|
||||
this._alreadyActivatedEvents[activationEvent] = true;
|
||||
});
|
||||
})));
|
||||
|
||||
this._alreadyActivatedEvents[activationEvent] = true;
|
||||
}
|
||||
|
||||
public activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
|
||||
const desc = this._registry.getExtensionDescription(extensionId);
|
||||
if (!desc) {
|
||||
throw new Error('Extension `' + extensionId + '` is not known');
|
||||
throw new Error(`Extension '${extensionId}' is not known`);
|
||||
}
|
||||
return this._activateExtensions([{ id: desc.identifier, reason }]);
|
||||
}
|
||||
|
||||
return this._activateExtensions([{
|
||||
id: desc.identifier,
|
||||
reason
|
||||
}]);
|
||||
private async _activateExtensions(extensions: ActivationIdAndReason[]): Promise<void> {
|
||||
const operations = extensions
|
||||
.filter((p) => !this.isActivated(p.id))
|
||||
.map(ext => this._handleActivationRequest(ext));
|
||||
await Promise.all(operations.map(op => op.wait()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle semantics related to dependencies for `currentExtension`.
|
||||
* semantics: `redExtensions` must wait for `greenExtensions`.
|
||||
* We don't need to worry about dependency loops because they are handled by the registry.
|
||||
*/
|
||||
private _handleActivateRequest(currentActivation: ActivationIdAndReason, greenExtensions: { [id: string]: ActivationIdAndReason }, redExtensions: ActivationIdAndReason[]): void {
|
||||
if (this._hostExtensionsMap.has(ExtensionIdentifier.toKey(currentActivation.id))) {
|
||||
greenExtensions[ExtensionIdentifier.toKey(currentActivation.id)] = currentActivation;
|
||||
return;
|
||||
private _handleActivationRequest(currentActivation: ActivationIdAndReason): ActivationOperation {
|
||||
if (this._operations.has(ExtensionIdentifier.toKey(currentActivation.id))) {
|
||||
return this._operations.get(ExtensionIdentifier.toKey(currentActivation.id))!;
|
||||
}
|
||||
|
||||
if (this._externalExtensionsMap.has(ExtensionIdentifier.toKey(currentActivation.id))) {
|
||||
return this._createAndSaveOperation(currentActivation, null, [], null);
|
||||
}
|
||||
|
||||
const currentExtension = this._registry.getExtensionDescription(currentActivation.id);
|
||||
if (!currentExtension) {
|
||||
// Error condition 0: unknown extension
|
||||
const error = new Error(`Cannot activate unknown extension '${currentActivation.id.value}'`);
|
||||
const result = this._createAndSaveOperation(currentActivation, null, [], new FailedExtension(error));
|
||||
this._host.onExtensionActivationError(
|
||||
currentActivation.id,
|
||||
error,
|
||||
new MissingExtensionDependency(currentActivation.id.value)
|
||||
);
|
||||
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentActivation.id), new FailedExtension(error));
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
|
||||
const deps: ActivationOperation[] = [];
|
||||
const depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies);
|
||||
let currentExtensionGetsGreenLight = true;
|
||||
|
||||
for (let j = 0, lenJ = depIds.length; j < lenJ; j++) {
|
||||
const depId = depIds[j];
|
||||
for (const depId of depIds) {
|
||||
|
||||
if (this._resolvedExtensionsSet.has(ExtensionIdentifier.toKey(depId))) {
|
||||
// This dependency is already resolved
|
||||
continue;
|
||||
}
|
||||
|
||||
const dep = this._activatedExtensions.get(ExtensionIdentifier.toKey(depId));
|
||||
if (dep && !dep.activationFailed) {
|
||||
// the dependency is already activated OK
|
||||
const dep = this._operations.get(ExtensionIdentifier.toKey(depId));
|
||||
if (dep) {
|
||||
deps.push(dep);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dep && dep.activationFailed) {
|
||||
// Error condition 2: a dependency has already failed activation
|
||||
const currentExtensionFriendlyName = currentExtension.displayName || currentExtension.identifier.value;
|
||||
const depDesc = this._registry.getExtensionDescription(depId);
|
||||
const depFriendlyName = (depDesc ? depDesc.displayName || depId : depId);
|
||||
const error = new Error(`Cannot activate the '${currentExtensionFriendlyName}' extension because its dependency '${depFriendlyName}' failed to activate`);
|
||||
(<any>error).detail = dep.activationFailedError;
|
||||
this._host.onExtensionActivationError(
|
||||
currentExtension.identifier,
|
||||
error,
|
||||
null
|
||||
);
|
||||
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._hostExtensionsMap.has(ExtensionIdentifier.toKey(depId))) {
|
||||
if (this._externalExtensionsMap.has(ExtensionIdentifier.toKey(depId))) {
|
||||
// must first wait for the dependency to activate
|
||||
currentExtensionGetsGreenLight = false;
|
||||
greenExtensions[ExtensionIdentifier.toKey(depId)] = {
|
||||
id: this._hostExtensionsMap.get(ExtensionIdentifier.toKey(depId))!,
|
||||
deps.push(this._handleActivationRequest({
|
||||
id: this._externalExtensionsMap.get(ExtensionIdentifier.toKey(depId))!,
|
||||
reason: currentActivation.reason
|
||||
};
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -317,118 +298,140 @@ export class ExtensionsActivator implements IDisposable {
|
||||
}
|
||||
|
||||
// must first wait for the dependency to activate
|
||||
currentExtensionGetsGreenLight = false;
|
||||
greenExtensions[ExtensionIdentifier.toKey(depId)] = {
|
||||
deps.push(this._handleActivationRequest({
|
||||
id: depDesc.identifier,
|
||||
reason: currentActivation.reason
|
||||
};
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Error condition 1: unknown dependency
|
||||
const currentExtensionFriendlyName = currentExtension.displayName || currentExtension.identifier.value;
|
||||
const error = new Error(`Cannot activate the '${currentExtensionFriendlyName}' extension because it depends on unknown extension '${depId}'`);
|
||||
const result = this._createAndSaveOperation(currentActivation, currentExtension.displayName, [], new FailedExtension(error));
|
||||
this._host.onExtensionActivationError(
|
||||
currentExtension.identifier,
|
||||
error,
|
||||
new MissingExtensionDependency(depId)
|
||||
);
|
||||
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
|
||||
return result;
|
||||
}
|
||||
|
||||
return this._createAndSaveOperation(currentActivation, currentExtension.displayName, deps, null);
|
||||
}
|
||||
|
||||
private _createAndSaveOperation(activation: ActivationIdAndReason, displayName: string | null | undefined, deps: ActivationOperation[], value: ActivatedExtension | null): ActivationOperation {
|
||||
const operation = new ActivationOperation(activation.id, displayName, activation.reason, deps, value, this._host, this._logService);
|
||||
this._operations.set(ExtensionIdentifier.toKey(activation.id), operation);
|
||||
return operation;
|
||||
}
|
||||
}
|
||||
|
||||
class ActivationOperation {
|
||||
|
||||
private readonly _barrier = new Barrier();
|
||||
private _isDisposed = false;
|
||||
|
||||
public get value(): ActivatedExtension | null {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
public get friendlyName(): string {
|
||||
return this._displayName || this._id.value;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _id: ExtensionIdentifier,
|
||||
private readonly _displayName: string | null | undefined,
|
||||
private readonly _reason: ExtensionActivationReason,
|
||||
private readonly _deps: ActivationOperation[],
|
||||
private _value: ActivatedExtension | null,
|
||||
private readonly _host: IExtensionsActivatorHost,
|
||||
@ILogService private readonly _logService: ILogService
|
||||
) {
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._isDisposed = true;
|
||||
}
|
||||
|
||||
public wait() {
|
||||
return this._barrier.wait();
|
||||
}
|
||||
|
||||
private async _initialize(): Promise<void> {
|
||||
await this._waitForDepsThenActivate();
|
||||
this._barrier.open();
|
||||
}
|
||||
|
||||
private async _waitForDepsThenActivate(): Promise<void> {
|
||||
if (this._value) {
|
||||
// this operation is already finished
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentExtensionGetsGreenLight) {
|
||||
greenExtensions[ExtensionIdentifier.toKey(currentExtension.identifier)] = currentActivation;
|
||||
} else {
|
||||
redExtensions.push(currentActivation);
|
||||
}
|
||||
}
|
||||
while (this._deps.length > 0) {
|
||||
// remove completed deps
|
||||
for (let i = 0; i < this._deps.length; i++) {
|
||||
const dep = this._deps[i];
|
||||
|
||||
private _activateExtensions(extensions: ActivationIdAndReason[]): Promise<void> {
|
||||
if (extensions.length === 0) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
if (dep.value && !dep.value.activationFailed) {
|
||||
// the dependency is already activated OK
|
||||
this._deps.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
extensions = extensions.filter((p) => !this._activatedExtensions.has(ExtensionIdentifier.toKey(p.id)));
|
||||
if (extensions.length === 0) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
if (dep.value && dep.value.activationFailed) {
|
||||
// Error condition 2: a dependency has already failed activation
|
||||
const error = new Error(`Cannot activate the '${this.friendlyName}' extension because its dependency '${dep.friendlyName}' failed to activate`);
|
||||
(<any>error).detail = dep.value.activationFailedError;
|
||||
this._value = new FailedExtension(error);
|
||||
this._host.onExtensionActivationError(this._id, error, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const greenMap: { [id: string]: ActivationIdAndReason } = Object.create(null),
|
||||
red: ActivationIdAndReason[] = [];
|
||||
|
||||
for (let i = 0, len = extensions.length; i < len; i++) {
|
||||
this._handleActivateRequest(extensions[i], greenMap, red);
|
||||
}
|
||||
|
||||
// Make sure no red is also green
|
||||
for (let i = 0, len = red.length; i < len; i++) {
|
||||
const redExtensionKey = ExtensionIdentifier.toKey(red[i].id);
|
||||
if (greenMap[redExtensionKey]) {
|
||||
delete greenMap[redExtensionKey];
|
||||
if (this._deps.length > 0) {
|
||||
// wait for one dependency
|
||||
await Promise.race(this._deps.map(dep => dep.wait()));
|
||||
}
|
||||
}
|
||||
|
||||
const green = Object.keys(greenMap).map(id => greenMap[id]);
|
||||
|
||||
if (red.length === 0) {
|
||||
// Finally reached only leafs!
|
||||
return Promise.all(green.map((p) => this._activateExtension(p.id, p.reason))).then(_ => undefined);
|
||||
}
|
||||
|
||||
return this._activateExtensions(green).then(_ => {
|
||||
return this._activateExtensions(red);
|
||||
});
|
||||
await this._activate();
|
||||
}
|
||||
|
||||
private _activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
|
||||
const extensionKey = ExtensionIdentifier.toKey(extensionId);
|
||||
|
||||
if (this._activatedExtensions.has(extensionKey)) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const currentlyActivatingExtension = this._activatingExtensions.get(extensionKey);
|
||||
if (currentlyActivatingExtension) {
|
||||
return currentlyActivatingExtension;
|
||||
}
|
||||
|
||||
const newlyActivatingExtension = this._host.actualActivateExtension(extensionId, reason).then(undefined, (err) => {
|
||||
private async _activate(): Promise<void> {
|
||||
try {
|
||||
this._value = await this._host.actualActivateExtension(this._id, this._reason);
|
||||
} catch (err) {
|
||||
|
||||
const error = new Error();
|
||||
if (err && err.name) {
|
||||
error.name = err.name;
|
||||
}
|
||||
if (err && err.message) {
|
||||
error.message = `Activating extension '${extensionId.value}' failed: ${err.message}.`;
|
||||
error.message = `Activating extension '${this._id.value}' failed: ${err.message}.`;
|
||||
} else {
|
||||
error.message = `Activating extension '${extensionId.value}' failed: ${err}.`;
|
||||
error.message = `Activating extension '${this._id.value}' failed: ${err}.`;
|
||||
}
|
||||
if (err && err.stack) {
|
||||
error.stack = err.stack;
|
||||
}
|
||||
|
||||
// Treat the extension as being empty
|
||||
this._value = new FailedExtension(error);
|
||||
|
||||
if (this._isDisposed && errors.isCancellationError(err)) {
|
||||
// It is expected for ongoing activations to fail if the extension host is going down
|
||||
// So simply ignore and don't log canceled errors in this case
|
||||
return new FailedExtension(err);
|
||||
return;
|
||||
}
|
||||
|
||||
this._host.onExtensionActivationError(
|
||||
extensionId,
|
||||
error,
|
||||
null
|
||||
);
|
||||
this._logService.error(`Activating extension ${extensionId.value} failed due to an error:`);
|
||||
this._host.onExtensionActivationError(this._id, error, null);
|
||||
this._logService.error(`Activating extension ${this._id.value} failed due to an error:`);
|
||||
this._logService.error(err);
|
||||
// Treat the extension as being empty
|
||||
return new FailedExtension(err);
|
||||
}).then((x: ActivatedExtension) => {
|
||||
this._activatedExtensions.set(extensionKey, x);
|
||||
this._activatingExtensions.delete(extensionKey);
|
||||
});
|
||||
|
||||
this._activatingExtensions.set(extensionKey, newlyActivatingExtension);
|
||||
return newlyActivatingExtension;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,12 +753,13 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
}
|
||||
}
|
||||
|
||||
public async $getCanonicalURI(remoteAuthority: string, uriComponents: UriComponents): Promise<UriComponents> {
|
||||
public async $getCanonicalURI(remoteAuthority: string, uriComponents: UriComponents): Promise<UriComponents | null> {
|
||||
this._logService.info(`$getCanonicalURI invoked for authority (${getRemoteAuthorityPrefix(remoteAuthority)})`);
|
||||
|
||||
const { authorityPrefix, resolver } = await this._activateAndGetResolver(remoteAuthority);
|
||||
const { resolver } = await this._activateAndGetResolver(remoteAuthority);
|
||||
if (!resolver) {
|
||||
throw new Error(`Cannot get canonical URI because no remote extension is installed to resolve ${authorityPrefix}`);
|
||||
// Return `null` if no resolver for `remoteAuthority` is found.
|
||||
return null;
|
||||
}
|
||||
|
||||
const uri = URI.revive(uriComponents);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location, InlineCompletionTriggerKindNew } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import * as languages from 'vs/editor/common/languages';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
@@ -34,6 +34,7 @@ import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { isCancellationError } from 'vs/base/common/errors';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { raceCancellationError } from 'vs/base/common/async';
|
||||
import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
// --- adapter
|
||||
|
||||
@@ -1112,6 +1113,101 @@ class InlineCompletionAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
class InlineCompletionAdapterNew {
|
||||
private readonly _cache = new Cache<vscode.InlineCompletionItemNew>('InlineCompletionItemNew');
|
||||
private readonly _disposables = new Map<number, DisposableStore>();
|
||||
|
||||
private readonly isAdditionProposedApiEnabled = isProposedApiEnabled(this.extension, 'inlineCompletionsAdditions');
|
||||
|
||||
constructor(
|
||||
private readonly extension: IExtensionDescription,
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _provider: vscode.InlineCompletionItemProviderNew,
|
||||
private readonly _commands: CommandsConverter,
|
||||
) { }
|
||||
|
||||
private readonly languageTriggerKindToVSCodeTriggerKind: Record<languages.InlineCompletionTriggerKind, vscode.InlineCompletionTriggerKindNew> = {
|
||||
[languages.InlineCompletionTriggerKind.Automatic]: InlineCompletionTriggerKindNew.Automatic,
|
||||
[languages.InlineCompletionTriggerKind.Explicit]: InlineCompletionTriggerKindNew.Invoke,
|
||||
};
|
||||
|
||||
public async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
|
||||
const doc = this._documents.getDocument(resource);
|
||||
const pos = typeConvert.Position.to(position);
|
||||
|
||||
const result = await this._provider.provideInlineCompletionItems(doc, pos, {
|
||||
selectedCompletionInfo:
|
||||
context.selectedSuggestionInfo
|
||||
? {
|
||||
range: typeConvert.Range.to(context.selectedSuggestionInfo.range),
|
||||
text: context.selectedSuggestionInfo.text
|
||||
}
|
||||
: undefined,
|
||||
triggerKind: this.languageTriggerKindToVSCodeTriggerKind[context.triggerKind]
|
||||
}, token);
|
||||
|
||||
if (!result) {
|
||||
// undefined and null are valid results
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
// cancelled -> return without further ado, esp no caching
|
||||
// of results as they will leak
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const normalizedResult = isArray(result) ? result : result.items;
|
||||
|
||||
const pid = this._cache.add(normalizedResult);
|
||||
let disposableStore: DisposableStore | undefined = undefined;
|
||||
|
||||
return {
|
||||
pid,
|
||||
items: normalizedResult.map<extHostProtocol.IdentifiableInlineCompletion>((item, idx) => {
|
||||
let command: languages.Command | undefined = undefined;
|
||||
if (item.command) {
|
||||
if (!disposableStore) {
|
||||
disposableStore = new DisposableStore();
|
||||
this._disposables.set(pid, disposableStore);
|
||||
}
|
||||
command = this._commands.toInternal(item.command, disposableStore);
|
||||
}
|
||||
|
||||
const insertText = item.insertText;
|
||||
if (insertText === undefined) {
|
||||
throw new Error('text or insertText must be defined');
|
||||
}
|
||||
return ({
|
||||
text: typeof insertText === 'string' ? insertText : { snippet: insertText.value },
|
||||
range: item.range ? typeConvert.Range.from(item.range) : undefined,
|
||||
command,
|
||||
idx: idx,
|
||||
completeBracketPairs: this.isAdditionProposedApiEnabled ? item.completeBracketPairs : false
|
||||
});
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
public disposeCompletions(pid: number) {
|
||||
this._cache.delete(pid);
|
||||
const d = this._disposables.get(pid);
|
||||
if (d) {
|
||||
d.clear();
|
||||
}
|
||||
this._disposables.delete(pid);
|
||||
}
|
||||
|
||||
public handleDidShowCompletionItem(pid: number, idx: number): void {
|
||||
const completionItem = this._cache.get(pid, idx);
|
||||
if (completionItem) {
|
||||
if (this._provider.handleDidShowCompletionItem && isProposedApiEnabled(this.extension, 'inlineCompletionsAdditions')) {
|
||||
this._provider.handleDidShowCompletionItem(completionItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class InlineCompletionController<T extends vscode.InlineCompletionItem> implements vscode.InlineCompletionController<T> {
|
||||
private static readonly map = new WeakMap<vscode.InlineCompletionItemProvider<any>, InlineCompletionController<any>>();
|
||||
|
||||
@@ -1616,7 +1712,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov
|
||||
| SelectionRangeAdapter | CallHierarchyAdapter | TypeHierarchyAdapter
|
||||
| DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter
|
||||
| EvaluatableExpressionAdapter | InlineValuesAdapter
|
||||
| LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionAdapter;
|
||||
| LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionAdapter | InlineCompletionAdapterNew;
|
||||
|
||||
class AdapterData {
|
||||
constructor(
|
||||
@@ -2050,6 +2146,12 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
||||
registerInlineCompletionsProviderNew(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProviderNew): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new InlineCompletionAdapterNew(extension, this._documents, provider, this._commands.converter), extension);
|
||||
this._proxy.$registerInlineCompletionsSupport(handle, this._transformDocumentSelector(selector));
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
||||
$provideInlineCompletions(handle: number, resource: UriComponents, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
|
||||
return this._withAdapter(handle, InlineCompletionAdapter, adapter => adapter.provideInlineCompletions(URI.revive(resource), position, context, token), undefined, token);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,25 @@ import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { NotebookRange } from 'vs/workbench/api/common/extHostTypes';
|
||||
import * as notebookCommon from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
class RawContentChangeEvent {
|
||||
|
||||
constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: vscode.NotebookCell[], readonly items: ExtHostCell[]) { }
|
||||
|
||||
constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: vscode.NotebookCell[], readonly items: ExtHostCell[]) {
|
||||
|
||||
}
|
||||
|
||||
asApiEvent(): vscode.NotebookDocumentContentChange {
|
||||
return {
|
||||
range: new NotebookRange(this.start, this.start + this.deletedCount),
|
||||
addedCells: this.items.map(cell => cell.apiCell),
|
||||
removedCells: this.deletedItems,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
static asApiEvents(events: RawContentChangeEvent[]): readonly vscode.NotebookCellsChangeData[] {
|
||||
return events.map(event => {
|
||||
@@ -157,7 +170,7 @@ export class ExtHostNotebookDocument {
|
||||
) {
|
||||
this._notebookType = data.viewType;
|
||||
this._metadata = Object.freeze(data.metadata ?? Object.create(null));
|
||||
this._spliceNotebookCells([[0, 0, data.cells]], true /* init -> no event*/);
|
||||
this._spliceNotebookCells([[0, 0, data.cells]], true /* init -> no event*/, undefined);
|
||||
this._versionId = data.versionId;
|
||||
}
|
||||
|
||||
@@ -213,29 +226,75 @@ export class ExtHostNotebookDocument {
|
||||
this._isDirty = isDirty;
|
||||
}
|
||||
|
||||
acceptModelChanged(event: extHostProtocol.NotebookCellsChangedEventDto, isDirty: boolean): void {
|
||||
acceptModelChanged(event: extHostProtocol.NotebookCellsChangedEventDto, isDirty: boolean, newMetadata: notebookCommon.NotebookDocumentMetadata | undefined): vscode.NotebookDocumentChangeEvent {
|
||||
this._versionId = event.versionId;
|
||||
this._isDirty = isDirty;
|
||||
this.acceptDocumentPropertiesChanged({ metadata: newMetadata });
|
||||
|
||||
const result = {
|
||||
notebook: this.apiNotebook,
|
||||
metadata: newMetadata,
|
||||
cellChanges: <vscode.NotebookDocumentContentCellChange[]>[],
|
||||
contentChanges: <vscode.NotebookDocumentContentChange[]>[],
|
||||
};
|
||||
|
||||
type RelaxedCellChange = Partial<vscode.NotebookDocumentContentCellChange> & { cell: vscode.NotebookCell };
|
||||
const relaxedCellChanges: RelaxedCellChange[] = [];
|
||||
|
||||
// -- apply change and populate content changes
|
||||
|
||||
for (const rawEvent of event.rawEvents) {
|
||||
if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ModelChange) {
|
||||
this._spliceNotebookCells(rawEvent.changes, false);
|
||||
this._spliceNotebookCells(rawEvent.changes, false, result.contentChanges);
|
||||
|
||||
} else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.Move) {
|
||||
this._moveCell(rawEvent.index, rawEvent.newIdx);
|
||||
this._moveCell(rawEvent.index, rawEvent.newIdx, result.contentChanges);
|
||||
|
||||
} else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.Output) {
|
||||
this._setCellOutputs(rawEvent.index, rawEvent.outputs);
|
||||
relaxedCellChanges.push({ cell: this._cells[rawEvent.index].apiCell, outputs: this._cells[rawEvent.index].apiCell.outputs });
|
||||
|
||||
} else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.OutputItem) {
|
||||
this._setCellOutputItems(rawEvent.index, rawEvent.outputId, rawEvent.append, rawEvent.outputItems);
|
||||
relaxedCellChanges.push({ cell: this._cells[rawEvent.index].apiCell, outputs: this._cells[rawEvent.index].apiCell.outputs });
|
||||
|
||||
} else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeLanguage) {
|
||||
this._changeCellLanguage(rawEvent.index, rawEvent.language);
|
||||
} else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellMime) {
|
||||
this._changeCellMime(rawEvent.index, rawEvent.mime);
|
||||
} else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellMetadata) {
|
||||
this._changeCellMetadata(rawEvent.index, rawEvent.metadata);
|
||||
relaxedCellChanges.push({ cell: this._cells[rawEvent.index].apiCell, metadata: this._cells[rawEvent.index].apiCell.metadata });
|
||||
|
||||
} else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellInternalMetadata) {
|
||||
this._changeCellInternalMetadata(rawEvent.index, rawEvent.internalMetadata);
|
||||
relaxedCellChanges.push({ cell: this._cells[rawEvent.index].apiCell, executionSummary: this._cells[rawEvent.index].apiCell.executionSummary });
|
||||
}
|
||||
}
|
||||
|
||||
// -- compact cellChanges
|
||||
|
||||
const map = new Map<vscode.NotebookCell, number>();
|
||||
for (let i = 0; i < relaxedCellChanges.length; i++) {
|
||||
const relaxedCellChange = relaxedCellChanges[i];
|
||||
const existing = map.get(relaxedCellChange.cell);
|
||||
if (existing === undefined) {
|
||||
const newLen = result.cellChanges.push({
|
||||
executionSummary: undefined,
|
||||
metadata: undefined,
|
||||
outputs: undefined,
|
||||
...relaxedCellChange,
|
||||
});
|
||||
map.set(relaxedCellChange.cell, newLen - 1);
|
||||
} else {
|
||||
result.cellChanges[existing] = {
|
||||
...result.cellChanges[existing],
|
||||
...relaxedCellChange
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _validateIndex(index: number): number {
|
||||
@@ -277,7 +336,7 @@ export class ExtHostNotebookDocument {
|
||||
return this._proxy.$trySaveNotebook(this.uri);
|
||||
}
|
||||
|
||||
private _spliceNotebookCells(splices: notebookCommon.NotebookCellTextModelSplice<extHostProtocol.NotebookCellDto>[], initialization: boolean): void {
|
||||
private _spliceNotebookCells(splices: notebookCommon.NotebookCellTextModelSplice<extHostProtocol.NotebookCellDto>[], initialization: boolean, bucket: vscode.NotebookDocumentContentChange[] | undefined): void {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
@@ -303,7 +362,6 @@ export class ExtHostNotebookDocument {
|
||||
removedCellDocuments.push(cell.uri);
|
||||
changeEvent.deletedItems.push(cell.apiCell);
|
||||
}
|
||||
|
||||
contentChangeEvents.push(changeEvent);
|
||||
});
|
||||
|
||||
@@ -312,6 +370,12 @@ export class ExtHostNotebookDocument {
|
||||
removedDocuments: removedCellDocuments
|
||||
});
|
||||
|
||||
if (bucket) {
|
||||
for (let changeEvent of contentChangeEvents) {
|
||||
bucket.push(changeEvent.asApiEvent());
|
||||
}
|
||||
}
|
||||
|
||||
if (!initialization) {
|
||||
this._emitter.emitModelChange(deepFreeze({
|
||||
document: this.apiNotebook,
|
||||
@@ -320,13 +384,16 @@ export class ExtHostNotebookDocument {
|
||||
}
|
||||
}
|
||||
|
||||
private _moveCell(index: number, newIdx: number): void {
|
||||
private _moveCell(index: number, newIdx: number, bucket: vscode.NotebookDocumentContentChange[]): void {
|
||||
const cells = this._cells.splice(index, 1);
|
||||
this._cells.splice(newIdx, 0, ...cells);
|
||||
const changes = [
|
||||
new RawContentChangeEvent(index, 1, cells.map(c => c.apiCell), []),
|
||||
new RawContentChangeEvent(newIdx, 0, [], cells)
|
||||
];
|
||||
for (const change of changes) {
|
||||
bucket.push(change.asApiEvent());
|
||||
}
|
||||
this._emitter.emitModelChange(deepFreeze({
|
||||
document: this.apiNotebook,
|
||||
changes: RawContentChangeEvent.asApiEvents(changes)
|
||||
|
||||
@@ -8,6 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
@@ -16,17 +17,21 @@ export class ExtHostNotebookDocuments implements extHostProtocol.ExtHostNotebook
|
||||
private readonly _onDidChangeNotebookDocumentMetadata = new Emitter<vscode.NotebookDocumentMetadataChangeEvent>();
|
||||
readonly onDidChangeNotebookDocumentMetadata = this._onDidChangeNotebookDocumentMetadata.event;
|
||||
|
||||
private _onDidSaveNotebookDocument = new Emitter<vscode.NotebookDocument>();
|
||||
private readonly _onDidSaveNotebookDocument = new Emitter<vscode.NotebookDocument>();
|
||||
readonly onDidSaveNotebookDocument = this._onDidSaveNotebookDocument.event;
|
||||
|
||||
private readonly _onDidChangeNotebookDocument = new Emitter<vscode.NotebookDocumentChangeEvent>();
|
||||
readonly onDidChangeNotebookDocument = this._onDidChangeNotebookDocument.event;
|
||||
|
||||
constructor(
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
private readonly _notebooksAndEditors: ExtHostNotebookController,
|
||||
) { }
|
||||
|
||||
$acceptModelChanged(uri: UriComponents, event: SerializableObjectWithBuffers<extHostProtocol.NotebookCellsChangedEventDto>, isDirty: boolean): void {
|
||||
$acceptModelChanged(uri: UriComponents, event: SerializableObjectWithBuffers<extHostProtocol.NotebookCellsChangedEventDto>, isDirty: boolean, newMetadata?: NotebookDocumentMetadata): void {
|
||||
const document = this._notebooksAndEditors.getNotebookDocument(URI.revive(uri));
|
||||
document.acceptModelChanged(event.value, isDirty);
|
||||
const e = document.acceptModelChanged(event.value, isDirty, newMetadata);
|
||||
this._onDidChangeNotebookDocument.fire(e);
|
||||
}
|
||||
|
||||
$acceptDirtyStateChanged(uri: UriComponents, isDirty: boolean): void {
|
||||
|
||||
@@ -1612,6 +1612,28 @@ export class InlineSuggestions implements vscode.InlineCompletionList {
|
||||
}
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class InlineSuggestionNew implements vscode.InlineCompletionItemNew {
|
||||
insertText: string;
|
||||
range?: Range;
|
||||
command?: vscode.Command;
|
||||
|
||||
constructor(insertText: string, range?: Range, command?: vscode.Command) {
|
||||
this.insertText = insertText;
|
||||
this.range = range;
|
||||
this.command = command;
|
||||
}
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class InlineSuggestionsNew implements vscode.InlineCompletionListNew {
|
||||
items: vscode.InlineCompletionItemNew[];
|
||||
|
||||
constructor(items: vscode.InlineCompletionItemNew[]) {
|
||||
this.items = items;
|
||||
}
|
||||
}
|
||||
|
||||
export enum ViewColumn {
|
||||
Active = -1,
|
||||
Beside = -2,
|
||||
@@ -2587,6 +2609,11 @@ export enum InlineCompletionTriggerKind {
|
||||
Explicit = 1,
|
||||
}
|
||||
|
||||
export enum InlineCompletionTriggerKindNew {
|
||||
Invoke = 0,
|
||||
Automatic = 1,
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class InlineValueText implements vscode.InlineValueText {
|
||||
readonly range: Range;
|
||||
|
||||
Reference in New Issue
Block a user