mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-26 17:23:15 -05:00
Merge from vscode 2e5312cd61ff99c570299ecc122c52584265eda2
This commit is contained in:
committed by
Anthony Dresser
parent
3603f55d97
commit
7f1d8fc32f
@@ -12,31 +12,35 @@ import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContex
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { MenuRegistry, MenuId, IMenuItem } from 'vs/platform/actions/common/actions';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
|
||||
interface AuthDependent {
|
||||
providerId: string;
|
||||
label: string;
|
||||
scopes: string[];
|
||||
scopeDescriptions?: string;
|
||||
}
|
||||
|
||||
const BUILT_IN_AUTH_DEPENDENTS: AuthDependent[] = [
|
||||
{
|
||||
providerId: 'microsoft',
|
||||
label: 'Settings sync',
|
||||
scopes: ['https://management.core.windows.net/.default', 'offline_access'],
|
||||
scopeDescriptions: 'Read user email'
|
||||
}
|
||||
];
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
interface AllowedExtension {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const accountUsages = new Map<string, { [accountName: string]: string[] }>();
|
||||
|
||||
function addAccountUsage(providerId: string, accountName: string, extensionOrFeatureName: string) {
|
||||
const providerAccountUsage = accountUsages.get(providerId);
|
||||
if (!providerAccountUsage) {
|
||||
accountUsages.set(providerId, { [accountName]: [extensionOrFeatureName] });
|
||||
} else {
|
||||
if (providerAccountUsage[accountName]) {
|
||||
if (!providerAccountUsage[accountName].includes(extensionOrFeatureName)) {
|
||||
providerAccountUsage[accountName].push(extensionOrFeatureName);
|
||||
}
|
||||
} else {
|
||||
providerAccountUsage[accountName] = [extensionOrFeatureName];
|
||||
}
|
||||
|
||||
accountUsages.set(providerId, providerAccountUsage);
|
||||
}
|
||||
}
|
||||
|
||||
function readAllowedExtensions(storageService: IStorageService, providerId: string, accountName: string): AllowedExtension[] {
|
||||
let trustedExtensions: AllowedExtension[] = [];
|
||||
try {
|
||||
@@ -53,17 +57,22 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
private _sessionMenuItems = new Map<string, IDisposable[]>();
|
||||
private _accounts = new Map<string, string[]>(); // Map account name to session ids
|
||||
private _sessions = new Map<string, string>(); // Map account id to name
|
||||
private _signInMenuItem: IMenuItem | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: ExtHostAuthenticationShape,
|
||||
public readonly id: string,
|
||||
public readonly displayName: string,
|
||||
public readonly dependents: AuthDependent[]
|
||||
private readonly notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
this.registerCommandsAndContextMenuItems();
|
||||
public async initialize(): Promise<void> {
|
||||
return this.registerCommandsAndContextMenuItems();
|
||||
}
|
||||
|
||||
public hasSessions(): boolean {
|
||||
return !!this._sessions.size;
|
||||
}
|
||||
|
||||
private manageTrustedExtensions(quickInputService: IQuickInputService, storageService: IStorageService, accountName: string) {
|
||||
@@ -96,50 +105,45 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
quickPick.show();
|
||||
}
|
||||
|
||||
private showUsage(quickInputService: IQuickInputService, accountName: string) {
|
||||
const quickPick = quickInputService.createQuickPick();
|
||||
const providerUsage = accountUsages.get(this.id);
|
||||
const accountUsage = (providerUsage || {})[accountName] || [];
|
||||
|
||||
quickPick.items = accountUsage.map(extensionOrFeature => {
|
||||
return {
|
||||
label: extensionOrFeature
|
||||
};
|
||||
});
|
||||
|
||||
quickPick.onDidHide(() => {
|
||||
quickPick.dispose();
|
||||
});
|
||||
|
||||
quickPick.show();
|
||||
}
|
||||
|
||||
private async registerCommandsAndContextMenuItems(): Promise<void> {
|
||||
const sessions = await this._proxy.$getSessions(this.id);
|
||||
|
||||
if (this.dependents.length) {
|
||||
this._register(CommandsRegistry.registerCommand({
|
||||
id: `signIn${this.id}`,
|
||||
handler: (accessor, args) => {
|
||||
this.login(this.dependents.reduce((previous: string[], current) => previous.concat(current.scopes), []));
|
||||
},
|
||||
}));
|
||||
|
||||
this._signInMenuItem = {
|
||||
group: '2_providers',
|
||||
command: {
|
||||
id: `signIn${this.id}`,
|
||||
title: sessions.length
|
||||
? nls.localize('addAnotherAccount', "Sign in to another {0} account", this.displayName)
|
||||
: nls.localize('addAccount', "Sign in to {0}", this.displayName)
|
||||
},
|
||||
order: 3
|
||||
};
|
||||
|
||||
this._register(MenuRegistry.appendMenuItem(MenuId.AccountsContext, this._signInMenuItem));
|
||||
}
|
||||
|
||||
sessions.forEach(session => this.registerSession(session));
|
||||
}
|
||||
|
||||
private registerSession(session: modes.AuthenticationSession) {
|
||||
this._sessions.set(session.id, session.accountName);
|
||||
this._sessions.set(session.id, session.account.displayName);
|
||||
|
||||
const existingSessionsForAccount = this._accounts.get(session.accountName);
|
||||
const existingSessionsForAccount = this._accounts.get(session.account.displayName);
|
||||
if (existingSessionsForAccount) {
|
||||
this._accounts.set(session.accountName, existingSessionsForAccount.concat(session.id));
|
||||
this._accounts.set(session.account.displayName, existingSessionsForAccount.concat(session.id));
|
||||
return;
|
||||
} else {
|
||||
this._accounts.set(session.accountName, [session.id]);
|
||||
this._accounts.set(session.account.displayName, [session.id]);
|
||||
}
|
||||
|
||||
const menuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, {
|
||||
group: '1_accounts',
|
||||
command: {
|
||||
id: `configureSessions${session.id}`,
|
||||
title: session.accountName
|
||||
title: `${session.account.displayName} (${this.displayName})`
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
@@ -149,23 +153,28 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
handler: (accessor, args) => {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
const storageService = accessor.get(IStorageService);
|
||||
const dialogService = accessor.get(IDialogService);
|
||||
|
||||
const quickPick = quickInputService.createQuickPick();
|
||||
const showUsage = nls.localize('showUsage', "Show Extensions and Features Using This Account");
|
||||
const manage = nls.localize('manageTrustedExtensions', "Manage Trusted Extensions");
|
||||
const signOut = nls.localize('signOut', "Sign Out");
|
||||
const items = ([{ label: manage }, { label: signOut }]);
|
||||
const items = ([{ label: showUsage }, { label: manage }, { label: signOut }]);
|
||||
|
||||
quickPick.items = items;
|
||||
|
||||
quickPick.onDidAccept(e => {
|
||||
const selected = quickPick.selectedItems[0];
|
||||
if (selected.label === signOut) {
|
||||
const sessionsForAccount = this._accounts.get(session.accountName);
|
||||
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
|
||||
this.signOut(dialogService, session);
|
||||
}
|
||||
|
||||
if (selected.label === manage) {
|
||||
this.manageTrustedExtensions(quickInputService, storageService, session.accountName);
|
||||
this.manageTrustedExtensions(quickInputService, storageService, session.account.displayName);
|
||||
}
|
||||
|
||||
if (selected.label === showUsage) {
|
||||
this.showUsage(quickInputService, session.account.displayName);
|
||||
}
|
||||
|
||||
quickPick.dispose();
|
||||
@@ -179,15 +188,41 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
},
|
||||
});
|
||||
|
||||
this._sessionMenuItems.set(session.accountName, [menuItem, manageCommand]);
|
||||
this._sessionMenuItems.set(session.account.displayName, [menuItem, manageCommand]);
|
||||
}
|
||||
|
||||
async signOut(dialogService: IDialogService, session: modes.AuthenticationSession): Promise<void> {
|
||||
const providerUsage = accountUsages.get(this.id);
|
||||
const accountUsage = (providerUsage || {})[session.account.displayName] || [];
|
||||
const sessionsForAccount = this._accounts.get(session.account.displayName);
|
||||
|
||||
// Skip dialog if nothing is using the account
|
||||
if (!accountUsage.length) {
|
||||
accountUsages.set(this.id, { [session.account.displayName]: [] });
|
||||
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await dialogService.confirm({
|
||||
title: nls.localize('signOutConfirm', "Sign out of {0}", session.account.displayName),
|
||||
message: nls.localize('signOutMessage', "The account {0} is currently used by: \n\n{1}\n\n Sign out of these features?", session.account.displayName, accountUsage.join('\n'))
|
||||
});
|
||||
|
||||
if (result.confirmed) {
|
||||
accountUsages.set(this.id, { [session.account.displayName]: [] });
|
||||
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
|
||||
}
|
||||
}
|
||||
|
||||
async getSessions(): Promise<ReadonlyArray<modes.AuthenticationSession>> {
|
||||
return (await this._proxy.$getSessions(this.id)).map(session => {
|
||||
return {
|
||||
id: session.id,
|
||||
accountName: session.accountName,
|
||||
getAccessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
|
||||
account: session.account,
|
||||
getAccessToken: () => {
|
||||
addAccountUsage(this.id, session.account.displayName, nls.localize('sync', "Preferences Sync"));
|
||||
return this._proxy.$getSessionAccessToken(this.id, session.id);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -200,6 +235,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
removed.forEach(sessionId => {
|
||||
const accountName = this._sessions.get(sessionId);
|
||||
if (accountName) {
|
||||
this._sessions.delete(sessionId);
|
||||
let sessionsForAccount = this._accounts.get(accountName) || [];
|
||||
const sessionIndex = sessionsForAccount.indexOf(sessionId);
|
||||
sessionsForAccount.splice(sessionIndex);
|
||||
@@ -211,33 +247,26 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
this._sessionMenuItems.delete(accountName);
|
||||
}
|
||||
this._accounts.delete(accountName);
|
||||
|
||||
if (this._signInMenuItem) {
|
||||
this._signInMenuItem.command.title = nls.localize('addAccount', "Sign in to {0}", this.displayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addedSessions.forEach(session => this.registerSession(session));
|
||||
|
||||
if (addedSessions.length && this._signInMenuItem) {
|
||||
this._signInMenuItem.command.title = nls.localize('addAnotherAccount', "Sign in to another {0} account", this.displayName);
|
||||
}
|
||||
}
|
||||
|
||||
login(scopes: string[]): Promise<modes.AuthenticationSession> {
|
||||
return this._proxy.$login(this.id, scopes).then(session => {
|
||||
return {
|
||||
id: session.id,
|
||||
accountName: session.accountName,
|
||||
account: session.account,
|
||||
getAccessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
logout(sessionId: string): Promise<void> {
|
||||
return this._proxy.$logout(this.id, sessionId);
|
||||
async logout(sessionId: string): Promise<void> {
|
||||
await this._proxy.$logout(this.id, sessionId);
|
||||
this.notificationService.info(nls.localize('signedOut', "Successfully signed out."));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -255,16 +284,16 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
extHostContext: IExtHostContext,
|
||||
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IStorageService private readonly storageService: IStorageService
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@INotificationService private readonly notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
|
||||
}
|
||||
|
||||
async $registerAuthenticationProvider(id: string, displayName: string): Promise<void> {
|
||||
const dependentBuiltIns = BUILT_IN_AUTH_DEPENDENTS.filter(dependency => dependency.providerId === id);
|
||||
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, dependentBuiltIns);
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, this.notificationService);
|
||||
await provider.initialize();
|
||||
this.authenticationService.registerAuthenticationProvider(id, provider);
|
||||
}
|
||||
|
||||
@@ -277,6 +306,8 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
}
|
||||
|
||||
async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
addAccountUsage(providerId, accountName, extensionName);
|
||||
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
const extensionData = allowList.find(extension => extension.id === extensionId);
|
||||
if (extensionData) {
|
||||
@@ -313,4 +344,12 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
|
||||
return choice === 1;
|
||||
}
|
||||
|
||||
async $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void> {
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
if (!allowList.find(allowed => allowed.id === extensionId)) {
|
||||
allowList.push({ id: extensionId, name: extensionName });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
this._commentService.registerCommentController(providerId, provider);
|
||||
this._commentControllers.set(handle, provider);
|
||||
|
||||
const commentsPanelAlreadyConstructed = !!this._viewDescriptorService.getViewDescriptor(COMMENTS_VIEW_ID);
|
||||
const commentsPanelAlreadyConstructed = !!this._viewDescriptorService.getViewDescriptorById(COMMENTS_VIEW_ID);
|
||||
if (!commentsPanelAlreadyConstructed) {
|
||||
this.registerView(commentsPanelAlreadyConstructed);
|
||||
this.registerViewOpenedListener(commentsPanelAlreadyConstructed);
|
||||
@@ -451,7 +451,8 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
|
||||
id: COMMENTS_VIEW_ID,
|
||||
name: COMMENTS_VIEW_TITLE,
|
||||
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
|
||||
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
|
||||
storageId: COMMENTS_VIEW_TITLE,
|
||||
hideIfEmpty: true,
|
||||
order: 10,
|
||||
}, ViewContainerLocation.Panel);
|
||||
|
||||
@@ -15,6 +15,7 @@ import severity from 'vs/base/common/severity';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { DebugConfigurationProviderScope } from 'vs/workbench/api/common/extHostTypes';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadDebugService)
|
||||
export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterFactory {
|
||||
@@ -154,10 +155,11 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public $registerDebugConfigurationProvider(debugType: string, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, hasProvideDebugAdapter: boolean, handle: number): Promise<void> {
|
||||
public $registerDebugConfigurationProvider(debugType: string, providerScope: DebugConfigurationProviderScope, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, hasProvideDebugAdapter: boolean, handle: number): Promise<void> {
|
||||
|
||||
const provider = <IDebugConfigurationProvider>{
|
||||
type: debugType
|
||||
type: debugType,
|
||||
scope: providerScope
|
||||
};
|
||||
if (hasProvide) {
|
||||
provider.provideDebugConfigurations = (folder, token) => {
|
||||
@@ -271,7 +273,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
this.getDebugAdapter(handle).acceptMessage(convertToVSCPaths(message, false));
|
||||
}
|
||||
|
||||
|
||||
public $acceptDAError(handle: number, name: string, message: string, stack: string) {
|
||||
this.getDebugAdapter(handle).fireError(handle, new Error(`${name}: ${message}\n${stack}`));
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContex
|
||||
import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { DEFAULT_EDITOR_ID } from 'vs/workbench/contrib/files/common/files';
|
||||
|
||||
export class MainThreadTextEditors implements MainThreadTextEditorsShape {
|
||||
|
||||
@@ -293,6 +294,30 @@ CommandsRegistry.registerCommand('_workbench.open', function (accessor: Services
|
||||
return openerService.open(resource).then(_ => undefined);
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, ITextEditorOptions | undefined, EditorViewColumn | undefined]) => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
const [resource, id, options, position] = args;
|
||||
|
||||
const group = editorGroupService.getGroup(viewColumnToEditorGroup(editorGroupService, position)) ?? editorGroupService.activeGroup;
|
||||
const textOptions = options ? { ...options, ignoreOverrides: true } : { ignoreOverrides: true };
|
||||
|
||||
const fileEditorInput = editorService.createEditorInput({ resource, forceFile: true });
|
||||
if (id === DEFAULT_EDITOR_ID) {
|
||||
return editorService.openEditor(fileEditorInput, textOptions, position);
|
||||
}
|
||||
|
||||
const editors = editorService.getEditorOverrides(fileEditorInput, undefined, group);
|
||||
for (const [handler, data] of editors) {
|
||||
if (data.id === id) {
|
||||
return handler.open(fileEditorInput, options, group, id);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.diff', function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorViewColumn]) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
|
||||
@@ -405,8 +405,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
private static _inflateSuggestDto(defaultRange: IRange | { insert: IRange, replace: IRange }, data: ISuggestDataDto): modes.CompletionItem {
|
||||
|
||||
return {
|
||||
label: data[ISuggestDataDtoField.label2] || data[ISuggestDataDtoField.label],
|
||||
kind: data[ISuggestDataDtoField.kind],
|
||||
label: data[ISuggestDataDtoField.label2] ?? data[ISuggestDataDtoField.label],
|
||||
kind: data[ISuggestDataDtoField.kind] ?? modes.CompletionItemKind.Property,
|
||||
tags: data[ISuggestDataDtoField.kindModifier],
|
||||
detail: data[ISuggestDataDtoField.detail],
|
||||
documentation: data[ISuggestDataDtoField.documentation],
|
||||
@@ -414,7 +414,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
filterText: data[ISuggestDataDtoField.filterText],
|
||||
preselect: data[ISuggestDataDtoField.preselect],
|
||||
insertText: typeof data.h === 'undefined' ? data[ISuggestDataDtoField.label] : data.h,
|
||||
range: data[ISuggestDataDtoField.range] || defaultRange,
|
||||
range: data[ISuggestDataDtoField.range] ?? defaultRange,
|
||||
insertTextRules: data[ISuggestDataDtoField.insertTextRules],
|
||||
commitCharacters: data[ISuggestDataDtoField.commitCharacters],
|
||||
additionalTextEdits: data[ISuggestDataDtoField.additionalTextEdits],
|
||||
|
||||
@@ -33,6 +33,10 @@ export class MainThreadNotebookDocument extends Disposable {
|
||||
this._register(this._textModel.onDidModelChange(e => {
|
||||
this._proxy.$acceptModelChanged(this.uri, e);
|
||||
}));
|
||||
this._register(this._textModel.onDidSelectionChange(e => {
|
||||
const selectionsChange = e ? { selections: e } : null;
|
||||
this._proxy.$acceptEditorPropertiesChanged(uri, { selections: selectionsChange });
|
||||
}));
|
||||
}
|
||||
|
||||
applyEdit(modelVersionId: number, edits: ICellEditOperation[]): boolean {
|
||||
|
||||
@@ -96,7 +96,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
|
||||
// ---- input
|
||||
|
||||
$input(options: IInputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise<string> {
|
||||
$input(options: IInputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise<string | undefined> {
|
||||
const inputOptions: IInputOptions = Object.create(null);
|
||||
|
||||
if (options) {
|
||||
|
||||
@@ -71,8 +71,8 @@ class MainThreadSCMResource implements ISCMResource {
|
||||
public decorations: ISCMResourceDecorations
|
||||
) { }
|
||||
|
||||
open(): Promise<void> {
|
||||
return this.proxy.$executeResourceCommand(this.sourceControlHandle, this.groupHandle, this.handle);
|
||||
open(preserveFocus: boolean): Promise<void> {
|
||||
return this.proxy.$executeResourceCommand(this.sourceControlHandle, this.groupHandle, this.handle, preserveFocus);
|
||||
}
|
||||
|
||||
toJSON(): any {
|
||||
|
||||
@@ -26,7 +26,9 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
|
||||
}
|
||||
|
||||
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined): void {
|
||||
const entry: IStatusbarEntry = { text, tooltip, command, color };
|
||||
// if there are icons in the text use the tooltip for the aria label
|
||||
const ariaLabel = text.indexOf('$(') === -1 ? text : tooltip || text;
|
||||
const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel };
|
||||
|
||||
if (typeof priority === 'undefined') {
|
||||
priority = 0;
|
||||
|
||||
@@ -133,6 +133,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IBackupFileService private readonly _backupService: IBackupFileService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -151,7 +152,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
this.updateWebviewViewStates(this._editorService.activeEditor);
|
||||
}));
|
||||
|
||||
// This reviver's only job is to activate webview panel extensions
|
||||
// This reviver's only job is to activate extensions.
|
||||
// This should trigger the real reviver to be registered from the extension host side.
|
||||
this._register(_webviewWorkbenchService.registerResolver({
|
||||
canResolve: (webview: WebviewInput) => {
|
||||
@@ -305,11 +306,11 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
}
|
||||
|
||||
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void {
|
||||
this.registerEditorProvider(ModelType.Text, extensionData, viewType, options, capabilities);
|
||||
this.registerEditorProvider(ModelType.Text, extensionData, viewType, options, capabilities, true);
|
||||
}
|
||||
|
||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void {
|
||||
this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options, {});
|
||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerResource: boolean): void {
|
||||
this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options, {}, supportsMultipleEditorsPerResource);
|
||||
}
|
||||
|
||||
private registerEditorProvider(
|
||||
@@ -318,11 +319,16 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
viewType: string,
|
||||
options: modes.IWebviewPanelOptions,
|
||||
capabilities: extHostProtocol.CustomTextEditorCapabilities,
|
||||
supportsMultipleEditorsPerResource: boolean,
|
||||
): DisposableStore {
|
||||
if (this._editorProviders.has(viewType)) {
|
||||
throw new Error(`Provider for ${viewType} already registered`);
|
||||
}
|
||||
|
||||
this._customEditorService.registerCustomEditorCapabilities(viewType, {
|
||||
supportsMultipleEditorsPerResource
|
||||
});
|
||||
|
||||
const extension = reviveWebviewExtension(extensionData);
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
@@ -341,7 +347,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
|
||||
let modelRef: IReference<ICustomEditorModel>;
|
||||
try {
|
||||
modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, cancellation);
|
||||
modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, { backupId: webviewInput.backupId }, cancellation);
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType);
|
||||
@@ -360,7 +366,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
if (capabilities.supportsMove) {
|
||||
webviewInput.onMove(async (newResource: URI) => {
|
||||
const oldModel = modelRef;
|
||||
modelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType, CancellationToken.None);
|
||||
modelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType, {}, CancellationToken.None);
|
||||
this._proxy.$onMoveCustomEditor(handle, newResource, viewType);
|
||||
oldModel.dispose();
|
||||
});
|
||||
@@ -398,6 +404,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
modelType: ModelType,
|
||||
resource: URI,
|
||||
viewType: string,
|
||||
options: { backupId?: string },
|
||||
cancellation: CancellationToken,
|
||||
): Promise<IReference<ICustomEditorModel>> {
|
||||
const existingModel = this._customEditorService.models.tryRetain(resource, viewType);
|
||||
@@ -405,26 +412,33 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
return existingModel;
|
||||
}
|
||||
|
||||
const model = modelType === ModelType.Text
|
||||
? CustomTextEditorModel.create(this._instantiationService, viewType, resource)
|
||||
: MainThreadCustomEditorModel.create(this._instantiationService, this._proxy, viewType, resource, () => {
|
||||
return Array.from(this._webviewInputs)
|
||||
.filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[];
|
||||
}, cancellation);
|
||||
|
||||
return this._customEditorService.models.add(resource, viewType, model);
|
||||
switch (modelType) {
|
||||
case ModelType.Text:
|
||||
{
|
||||
const model = CustomTextEditorModel.create(this._instantiationService, viewType, resource);
|
||||
return this._customEditorService.models.add(resource, viewType, model);
|
||||
}
|
||||
case ModelType.Custom:
|
||||
{
|
||||
const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxy, viewType, resource, options, () => {
|
||||
return Array.from(this._webviewInputs)
|
||||
.filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[];
|
||||
}, cancellation, this._backupService);
|
||||
return this._customEditorService.models.add(resource, viewType, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async $onDidEdit(resourceComponents: UriComponents, viewType: string, editId: number, label: string | undefined): Promise<void> {
|
||||
const resource = URI.revive(resourceComponents);
|
||||
const model = await this._customEditorService.models.get(resource, viewType);
|
||||
if (!model || !(model instanceof MainThreadCustomEditorModel)) {
|
||||
throw new Error('Could not find model for webview editor');
|
||||
}
|
||||
|
||||
const model = await this.getCustomEditorModel(resourceComponents, viewType);
|
||||
model.pushEdit(editId, label);
|
||||
}
|
||||
|
||||
public async $onContentChange(resourceComponents: UriComponents, viewType: string): Promise<void> {
|
||||
const model = await this.getCustomEditorModel(resourceComponents, viewType);
|
||||
model.changeContent();
|
||||
}
|
||||
|
||||
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
@@ -531,6 +545,15 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
return this._webviewInputs.getInputForHandle(handle);
|
||||
}
|
||||
|
||||
private async getCustomEditorModel(resourceComponents: UriComponents, viewType: string) {
|
||||
const resource = URI.revive(resourceComponents);
|
||||
const model = await this._customEditorService.models.get(resource, viewType);
|
||||
if (!model || !(model instanceof MainThreadCustomEditorModel)) {
|
||||
throw new Error('Could not find model for webview editor');
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
private static getWebviewResolvedFailedContent(viewType: string) {
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
@@ -577,7 +600,7 @@ namespace HotExitState {
|
||||
readonly type = Type.Pending;
|
||||
|
||||
constructor(
|
||||
public readonly operation: CancelablePromise<void>,
|
||||
public readonly operation: CancelablePromise<string>,
|
||||
) { }
|
||||
}
|
||||
|
||||
@@ -588,58 +611,58 @@ namespace HotExitState {
|
||||
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
|
||||
|
||||
private _hotExitState: HotExitState.State = HotExitState.Allowed;
|
||||
private readonly _fromBackup: boolean = false;
|
||||
|
||||
private _currentEditIndex: number = -1;
|
||||
private _savePoint: number = -1;
|
||||
private readonly _edits: Array<number> = [];
|
||||
private _fromBackup: boolean = false;
|
||||
private _isDirtyFromContentChange = false;
|
||||
|
||||
private _ongoingSave?: CancelablePromise<void>;
|
||||
|
||||
public static async create(
|
||||
instantiationService: IInstantiationService,
|
||||
proxy: extHostProtocol.ExtHostWebviewsShape,
|
||||
viewType: string,
|
||||
resource: URI,
|
||||
options: { backupId?: string },
|
||||
getEditors: () => CustomEditorInput[],
|
||||
cancellation: CancellationToken,
|
||||
_backupFileService: IBackupFileService,
|
||||
) {
|
||||
const { editable } = await proxy.$createWebviewCustomEditorDocument(resource, viewType, cancellation);
|
||||
const model = instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, editable, getEditors);
|
||||
await model.init();
|
||||
return model;
|
||||
const { editable } = await proxy.$createCustomDocument(resource, viewType, options.backupId, cancellation);
|
||||
return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, !!options.backupId, editable, getEditors);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: extHostProtocol.ExtHostWebviewsShape,
|
||||
private readonly _viewType: string,
|
||||
private readonly _editorResource: URI,
|
||||
fromBackup: boolean,
|
||||
private readonly _editable: boolean,
|
||||
private readonly _getEditors: () => CustomEditorInput[],
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IUndoRedoService private readonly _undoService: IUndoRedoService,
|
||||
@IBackupFileService private readonly _backupFileService: IBackupFileService,
|
||||
) {
|
||||
super();
|
||||
|
||||
if (_editable) {
|
||||
this._register(workingCopyService.registerWorkingCopy(this));
|
||||
}
|
||||
this._fromBackup = fromBackup;
|
||||
}
|
||||
|
||||
get editorResource() {
|
||||
return this._editorResource;
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
const backup = await this._backupFileService.resolve<CustomDocumentBackupData>(this.resource);
|
||||
this._fromBackup = !!backup;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
if (this._editable) {
|
||||
this._undoService.removeElements(this._editorResource);
|
||||
}
|
||||
this._proxy.$disposeWebviewCustomEditorDocument(this._editorResource, this._viewType);
|
||||
this._proxy.$disposeCustomDocument(this._editorResource, this._viewType);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -647,11 +670,15 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
|
||||
public get resource() {
|
||||
// Make sure each custom editor has a unique resource for backup and edits
|
||||
return MainThreadCustomEditorModel.toWorkingCopyResource(this._viewType, this._editorResource);
|
||||
}
|
||||
|
||||
private static toWorkingCopyResource(viewType: string, resource: URI) {
|
||||
return URI.from({
|
||||
scheme: Schemas.vscodeCustomEditor,
|
||||
authority: this._viewType,
|
||||
path: this._editorResource.path,
|
||||
query: JSON.stringify(this._editorResource.toJSON()),
|
||||
authority: viewType,
|
||||
path: resource.path,
|
||||
query: JSON.stringify(resource.toJSON()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -664,6 +691,9 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
if (this._isDirtyFromContentChange) {
|
||||
return true;
|
||||
}
|
||||
if (this._edits.length > 0) {
|
||||
return this._savePoint !== this._currentEditIndex;
|
||||
}
|
||||
@@ -705,6 +735,12 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
});
|
||||
}
|
||||
|
||||
public changeContent() {
|
||||
this.change(() => {
|
||||
this._isDirtyFromContentChange = true;
|
||||
});
|
||||
}
|
||||
|
||||
private async undo(): Promise<void> {
|
||||
if (!this._editable) {
|
||||
return;
|
||||
@@ -719,15 +755,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
this.change(() => {
|
||||
--this._currentEditIndex;
|
||||
});
|
||||
await this._proxy.$undo(this._editorResource, this.viewType, undoneEdit, this.getEditState());
|
||||
}
|
||||
|
||||
private getEditState(): extHostProtocol.CustomDocumentEditState {
|
||||
return {
|
||||
allEdits: this._edits,
|
||||
currentIndex: this._currentEditIndex,
|
||||
saveIndex: this._savePoint,
|
||||
};
|
||||
await this._proxy.$undo(this._editorResource, this.viewType, undoneEdit, this.isDirty());
|
||||
}
|
||||
|
||||
private async redo(): Promise<void> {
|
||||
@@ -744,7 +772,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
this.change(() => {
|
||||
++this._currentEditIndex;
|
||||
});
|
||||
await this._proxy.$redo(this._editorResource, this.viewType, redoneEdit, this.getEditState());
|
||||
await this._proxy.$redo(this._editorResource, this.viewType, redoneEdit, this.isDirty());
|
||||
}
|
||||
|
||||
private spliceEdits(editToInsert?: number) {
|
||||
@@ -775,21 +803,13 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._currentEditIndex === this._savePoint) {
|
||||
if (this._currentEditIndex === this._savePoint && !this._isDirtyFromContentChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
let editsToUndo: number[] = [];
|
||||
let editsToRedo: number[] = [];
|
||||
|
||||
if (this._currentEditIndex >= this._savePoint) {
|
||||
editsToUndo = this._edits.slice(this._savePoint, this._currentEditIndex).reverse();
|
||||
} else if (this._currentEditIndex < this._savePoint) {
|
||||
editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint);
|
||||
}
|
||||
|
||||
this._proxy.$revert(this._editorResource, this.viewType, { undoneEdits: editsToUndo, redoneEdits: editsToRedo }, this.getEditState());
|
||||
this._proxy.$revert(this._editorResource, this.viewType, CancellationToken.None);
|
||||
this.change(() => {
|
||||
this._isDirtyFromContentChange = false;
|
||||
this._currentEditIndex = this._savePoint;
|
||||
this.spliceEdits();
|
||||
});
|
||||
@@ -804,11 +824,23 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
return undefined;
|
||||
}
|
||||
// TODO: handle save untitled case
|
||||
// TODO: handle cancellation
|
||||
await createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token));
|
||||
|
||||
const savePromise = createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token));
|
||||
this._ongoingSave?.cancel();
|
||||
this._ongoingSave = savePromise;
|
||||
|
||||
this.change(() => {
|
||||
this._isDirtyFromContentChange = false;
|
||||
this._savePoint = this._currentEditIndex;
|
||||
});
|
||||
|
||||
try {
|
||||
await savePromise;
|
||||
} finally {
|
||||
if (this._ongoingSave === savePromise) {
|
||||
this._ongoingSave = undefined;
|
||||
}
|
||||
}
|
||||
return this._editorResource;
|
||||
}
|
||||
|
||||
@@ -838,6 +870,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
meta: {
|
||||
viewType: this.viewType,
|
||||
editorResource: this._editorResource,
|
||||
backupId: '',
|
||||
extension: primaryEditor.extension ? {
|
||||
id: primaryEditor.extension.id.value,
|
||||
location: primaryEditor.extension.location,
|
||||
@@ -864,10 +897,11 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
this._hotExitState = pendingState;
|
||||
|
||||
try {
|
||||
await pendingState.operation;
|
||||
const backupId = await pendingState.operation;
|
||||
// Make sure state has not changed in the meantime
|
||||
if (this._hotExitState === pendingState) {
|
||||
this._hotExitState = HotExitState.Allowed;
|
||||
backupData.meta!.backupId = backupId;
|
||||
}
|
||||
} catch (e) {
|
||||
// Make sure state has not changed in the meantime
|
||||
|
||||
@@ -320,7 +320,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
name: title, extensionId,
|
||||
ctorDescriptor: new SyncDescriptor(
|
||||
ViewPaneContainer,
|
||||
[id, `${id}.state`, { mergeViewWithContainerWhenSingleView: true }]
|
||||
[id, { mergeViewWithContainerWhenSingleView: true }]
|
||||
),
|
||||
hideIfEmpty: true,
|
||||
order,
|
||||
|
||||
@@ -53,7 +53,7 @@ import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyId
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { originalFSPath, joinPath } from 'vs/base/common/resources';
|
||||
import { originalFSPath } from 'vs/base/common/resources';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
|
||||
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
|
||||
@@ -74,6 +74,7 @@ import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelServ
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
|
||||
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
|
||||
import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
|
||||
@@ -585,12 +586,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
|
||||
return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer);
|
||||
},
|
||||
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider, options?: { webviewOptions?: vscode.WebviewPanelOptions }) => {
|
||||
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options?.webviewOptions);
|
||||
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions } = {}) => {
|
||||
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
|
||||
},
|
||||
registerCustomEditorProvider2: (viewType: string, provider: vscode.CustomEditorProvider, options?: { webviewOptions?: vscode.WebviewPanelOptions }) => {
|
||||
registerCustomEditorProvider2: (viewType: string, provider: vscode.CustomEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerResource?: boolean } = {}) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options?.webviewOptions);
|
||||
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
|
||||
},
|
||||
registerDecorationProvider(provider: vscode.DecorationProvider) {
|
||||
checkProposedApiEnabled(extension);
|
||||
@@ -606,11 +607,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
return extHostQuickOpen.createInputBox(extension.identifier);
|
||||
},
|
||||
get activeColorTheme(): vscode.ColorTheme {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTheming.activeColorTheme;
|
||||
},
|
||||
onDidChangeActiveColorTheme(listener, thisArg?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTheming.onDidChangeActiveColorTheme(listener, thisArg, disposables);
|
||||
}
|
||||
};
|
||||
@@ -856,7 +855,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
extHostLogService.warn('Debug API is disabled in Azure Data Studio');
|
||||
return undefined!;
|
||||
},
|
||||
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider) {
|
||||
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider, scope?: vscode.DebugConfigurationProviderScope) {
|
||||
extHostLogService.warn('Debug API is disabled in Azure Data Studio');
|
||||
return undefined!;
|
||||
},
|
||||
@@ -924,13 +923,24 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
// namespace: notebook
|
||||
const notebook: typeof vscode.notebook = {
|
||||
registerNotebookProvider: (viewType: string, provider: vscode.NotebookProvider) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.registerNotebookProvider(extension, viewType, provider);
|
||||
},
|
||||
registerNotebookOutputRenderer: (type: string, outputFilter: vscode.NotebookOutputSelector, renderer: vscode.NotebookOutputRenderer) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.registerNotebookOutputRenderer(type, extension, outputFilter, renderer);
|
||||
},
|
||||
get activeNotebookDocument(): vscode.NotebookDocument | undefined {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.activeNotebookDocument;
|
||||
},
|
||||
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.activeNotebookEditor;
|
||||
},
|
||||
createConcatTextDocument(notebook, selector) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return new ExtHostNotebookConcatDocument(extHostNotebook, extHostDocuments, notebook, selector);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1055,13 +1065,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall,
|
||||
CallHierarchyItem: extHostTypes.CallHierarchyItem,
|
||||
DebugConsoleMode: extHostTypes.DebugConsoleMode,
|
||||
DebugConfigurationProviderScope: extHostTypes.DebugConfigurationProviderScope,
|
||||
Decoration: extHostTypes.Decoration,
|
||||
UIKind: UIKind,
|
||||
ColorThemeKind: extHostTypes.ColorThemeKind,
|
||||
TimelineItem: extHostTypes.TimelineItem,
|
||||
CellKind: extHostTypes.CellKind,
|
||||
CellOutputKind: extHostTypes.CellOutputKind,
|
||||
CustomDocument: extHostTypes.CustomDocument,
|
||||
NotebookCellRunState: extHostTypes.NotebookCellRunState
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1089,11 +1100,6 @@ class Extension<T> implements vscode.Extension<T> {
|
||||
this.extensionKind = kind;
|
||||
}
|
||||
|
||||
asExtensionUri(relativePath: string): URI {
|
||||
checkProposedApiEnabled(this.packageJSON);
|
||||
return joinPath(this.packageJSON.extensionLocation, relativePath);
|
||||
}
|
||||
|
||||
get isActive(): boolean {
|
||||
return this._extensionService.isActivated(this._identifier);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCell
|
||||
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { Dto } from 'vs/base/common/types';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { DebugConfigurationProviderScope } from 'vs/workbench/api/common/extHostTypes';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
|
||||
@@ -164,6 +165,7 @@ export interface MainThreadAuthenticationShape extends IDisposable {
|
||||
$onDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
|
||||
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$loginPrompt(providerName: string, extensionName: string): Promise<boolean>;
|
||||
$setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadConfigurationShape extends IDisposable {
|
||||
@@ -538,7 +540,7 @@ export interface MainThreadQuickOpenShape extends IDisposable {
|
||||
$show(instance: number, options: quickInput.IPickOptions<TransferQuickPickItems>, token: CancellationToken): Promise<number | number[] | undefined>;
|
||||
$setItems(instance: number, items: TransferQuickPickItems[]): Promise<void>;
|
||||
$setError(instance: number, error: Error): Promise<void>;
|
||||
$input(options: IInputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise<string>;
|
||||
$input(options: IInputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise<string | undefined>;
|
||||
$createOrUpdate(params: TransferQuickInput): Promise<void>;
|
||||
$dispose(id: number): Promise<void>;
|
||||
}
|
||||
@@ -614,10 +616,11 @@ export interface MainThreadWebviewsShape extends IDisposable {
|
||||
$unregisterSerializer(viewType: string): void;
|
||||
|
||||
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
|
||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void;
|
||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerResource: boolean): void;
|
||||
$unregisterEditorProvider(viewType: string): void;
|
||||
|
||||
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
|
||||
$onContentChange(resource: UriComponents, viewType: string): void;
|
||||
}
|
||||
|
||||
export interface WebviewPanelViewStateData {
|
||||
@@ -628,12 +631,6 @@ export interface WebviewPanelViewStateData {
|
||||
};
|
||||
}
|
||||
|
||||
export interface CustomDocumentEditState {
|
||||
readonly allEdits: readonly number[];
|
||||
readonly currentIndex: number;
|
||||
readonly saveIndex: number;
|
||||
}
|
||||
|
||||
export interface ExtHostWebviewsShape {
|
||||
$onMessage(handle: WebviewPanelHandle, message: any): void;
|
||||
$onMissingCsp(handle: WebviewPanelHandle, extensionId: string): void;
|
||||
@@ -643,18 +640,18 @@ export interface ExtHostWebviewsShape {
|
||||
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise<void>;
|
||||
$createWebviewCustomEditorDocument(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<{ editable: boolean }>;
|
||||
$disposeWebviewCustomEditorDocument(resource: UriComponents, viewType: string): Promise<void>;
|
||||
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
|
||||
$disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void>;
|
||||
|
||||
$undo(resource: UriComponents, viewType: string, editId: number, state: CustomDocumentEditState): Promise<void>;
|
||||
$redo(resource: UriComponents, viewType: string, editId: number, state: CustomDocumentEditState): Promise<void>;
|
||||
$revert(resource: UriComponents, viewType: string, changes: { undoneEdits: number[], redoneEdits: number[] }, state: CustomDocumentEditState): Promise<void>;
|
||||
$undo(resource: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void>;
|
||||
$redo(resource: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void>;
|
||||
$revert(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
|
||||
$disposeEdits(resourceComponents: UriComponents, viewType: string, editIds: number[]): void;
|
||||
|
||||
$onSave(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
|
||||
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents, cancellation: CancellationToken): Promise<void>;
|
||||
|
||||
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
|
||||
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<string>;
|
||||
|
||||
$onMoveCustomEditor(handle: WebviewPanelHandle, newResource: UriComponents, viewType: string): Promise<void>;
|
||||
}
|
||||
@@ -853,7 +850,7 @@ export interface MainThreadDebugServiceShape extends IDisposable {
|
||||
$acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
|
||||
$acceptDAError(handle: number, name: string, message: string, stack: string | undefined): void;
|
||||
$acceptDAExit(handle: number, code: number | undefined, signal: string | undefined): void;
|
||||
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, hasProvideDaMethod: boolean, handle: number): Promise<void>;
|
||||
$registerDebugConfigurationProvider(type: string, scope: DebugConfigurationProviderScope, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, hasProvideDaMethod: boolean, handle: number): Promise<void>;
|
||||
$registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise<void>;
|
||||
$unregisterDebugConfigurationProvider(handle: number): void;
|
||||
$unregisterDebugAdapterDescriptorFactory(handle: number): void;
|
||||
@@ -1114,7 +1111,7 @@ export const enum ISuggestDataDtoField {
|
||||
export interface ISuggestDataDto {
|
||||
[ISuggestDataDtoField.label]: string;
|
||||
[ISuggestDataDtoField.label2]?: string | modes.CompletionItemLabel;
|
||||
[ISuggestDataDtoField.kind]: modes.CompletionItemKind;
|
||||
[ISuggestDataDtoField.kind]?: modes.CompletionItemKind;
|
||||
[ISuggestDataDtoField.detail]?: string;
|
||||
[ISuggestDataDtoField.documentation]?: string | IMarkdownString;
|
||||
[ISuggestDataDtoField.sortText]?: string;
|
||||
@@ -1399,7 +1396,7 @@ export interface ExtHostTerminalServiceShape {
|
||||
export interface ExtHostSCMShape {
|
||||
$provideOriginalResource(sourceControlHandle: number, uri: UriComponents, token: CancellationToken): Promise<UriComponents | null>;
|
||||
$onInputBoxValueChange(sourceControlHandle: number, value: string): void;
|
||||
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): Promise<void>;
|
||||
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number, preserveFocus: boolean): Promise<void>;
|
||||
$validateInput(sourceControlHandle: number, value: string, cursorPosition: number): Promise<[string, number] | undefined>;
|
||||
$setSelectedSourceControls(selectedSourceControlHandles: number[]): Promise<void>;
|
||||
}
|
||||
@@ -1535,6 +1532,15 @@ export interface ExtHostCommentsShape {
|
||||
$toggleReaction(commentControllerHandle: number, threadHandle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void>;
|
||||
}
|
||||
|
||||
export interface INotebookSelectionChangeEvent {
|
||||
// handles
|
||||
selections: number[];
|
||||
}
|
||||
|
||||
export interface INotebookEditorPropertiesChangeData {
|
||||
selections: INotebookSelectionChangeEvent | null;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookShape {
|
||||
$resolveNotebook(viewType: string, uri: UriComponents): Promise<number | undefined>;
|
||||
$executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise<void>;
|
||||
@@ -1544,6 +1550,7 @@ export interface ExtHostNotebookShape {
|
||||
$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void;
|
||||
$onDidReceiveMessage(uri: UriComponents, message: any): void;
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void;
|
||||
$acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void;
|
||||
}
|
||||
|
||||
export interface ExtHostStorageShape {
|
||||
|
||||
@@ -143,22 +143,22 @@ const newCommands: ApiCommand[] = [
|
||||
new ApiCommand(
|
||||
'vscode.executeDefinitionProvider', '_executeDefinitionProvider', 'Execute all definition providers.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
|
||||
new ApiCommandResult<modes.Location[], types.Location[] | undefined>('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to))
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location-instances.', mapLocationOrLocationLink)
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.executeTypeDefinitionProvider', '_executeTypeDefinitionProvider', 'Execute all type definition providers.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
|
||||
new ApiCommandResult<modes.Location[], types.Location[] | undefined>('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to))
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location-instances.', mapLocationOrLocationLink)
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.executeDeclarationProvider', '_executeDeclarationProvider', 'Execute all declaration providers.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
|
||||
new ApiCommandResult<modes.Location[], types.Location[] | undefined>('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to))
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location-instances.', mapLocationOrLocationLink)
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.executeImplementationProvider', '_executeImplementationProvider', 'Execute all implementation providers.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
|
||||
new ApiCommandResult<modes.Location[], types.Location[] | undefined>('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to))
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location-instances.', mapLocationOrLocationLink)
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.executeReferenceProvider', '_executeReferenceProvider', 'Execute all reference providers.',
|
||||
@@ -507,3 +507,18 @@ function tryMapWith<T, R>(f: (x: T) => R) {
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
|
||||
function mapLocationOrLocationLink(values: (modes.Location | modes.LocationLink)[]): (types.Location | vscode.LocationLink)[] | undefined {
|
||||
if (!Array.isArray(values)) {
|
||||
return undefined;
|
||||
}
|
||||
const result: (types.Location | vscode.LocationLink)[] = [];
|
||||
for (const item of values) {
|
||||
if (modes.isLocationLink(item)) {
|
||||
result.push(typeConverters.DefinitionLink.to(item));
|
||||
} else {
|
||||
result.push(typeConverters.location.to(item));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -47,12 +47,12 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
.map(session => {
|
||||
return {
|
||||
id: session.id,
|
||||
accountName: session.accountName,
|
||||
account: session.account,
|
||||
scopes: session.scopes,
|
||||
getAccessToken: async () => {
|
||||
const isAllowed = await this._proxy.$getSessionsPrompt(
|
||||
provider.id,
|
||||
session.accountName,
|
||||
session.account.displayName,
|
||||
provider.displayName,
|
||||
extensionId,
|
||||
requestingExtension.displayName || requestingExtension.name);
|
||||
@@ -80,14 +80,15 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
}
|
||||
|
||||
const session = await provider.login(scopes);
|
||||
await this._proxy.$setTrustedExtension(provider.id, session.account.displayName, ExtensionIdentifier.toKey(requestingExtension.identifier), extensionName);
|
||||
return {
|
||||
id: session.id,
|
||||
accountName: session.accountName,
|
||||
account: session.account,
|
||||
scopes: session.scopes,
|
||||
getAccessToken: async () => {
|
||||
const isAllowed = await this._proxy.$getSessionsPrompt(
|
||||
provider.id,
|
||||
session.accountName,
|
||||
session.account.displayName,
|
||||
provider.displayName,
|
||||
ExtensionIdentifier.toKey(requestingExtension.identifier),
|
||||
requestingExtension.displayName || requestingExtension.name);
|
||||
|
||||
@@ -51,7 +51,7 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape {
|
||||
addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void>;
|
||||
removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void>;
|
||||
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise<boolean>;
|
||||
registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable;
|
||||
registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, scope: vscode.DebugConfigurationProviderScope): vscode.Disposable;
|
||||
registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable;
|
||||
registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable;
|
||||
asDebugSourceUri(source: vscode.DebugProtocolSource, session?: vscode.DebugSession): vscode.Uri;
|
||||
@@ -299,7 +299,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
||||
});
|
||||
}
|
||||
|
||||
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {
|
||||
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, scope: vscode.DebugConfigurationProviderScope): vscode.Disposable {
|
||||
|
||||
if (!provider) {
|
||||
return new Disposable(() => { });
|
||||
@@ -312,7 +312,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
||||
const handle = this._configProviderHandleCounter++;
|
||||
this._configProviders.push({ type, handle, provider });
|
||||
|
||||
this._debugServiceProxy.$registerDebugConfigurationProvider(type,
|
||||
this._debugServiceProxy.$registerDebugConfigurationProvider(type, scope,
|
||||
!!provider.provideDebugConfigurations,
|
||||
!!provider.resolveDebugConfiguration,
|
||||
!!provider.resolveDebugConfigurationWithSubstitutedVariables,
|
||||
@@ -1072,11 +1072,9 @@ class DirectDebugAdapter extends AbstractDebugAdapter {
|
||||
constructor(private implementation: vscode.DebugAdapter) {
|
||||
super();
|
||||
|
||||
if (this.implementation.onDidSendMessage) {
|
||||
implementation.onDidSendMessage((message: vscode.DebugProtocolMessage) => {
|
||||
this.acceptMessage(message as DebugProtocol.ProtocolMessage);
|
||||
});
|
||||
}
|
||||
implementation.onDidSendMessage((message: vscode.DebugProtocolMessage) => {
|
||||
this.acceptMessage(message as DebugProtocol.ProtocolMessage);
|
||||
});
|
||||
}
|
||||
|
||||
startSession(): Promise<void> {
|
||||
@@ -1084,15 +1082,11 @@ class DirectDebugAdapter extends AbstractDebugAdapter {
|
||||
}
|
||||
|
||||
sendMessage(message: DebugProtocol.ProtocolMessage): void {
|
||||
if (this.implementation.handleMessage) {
|
||||
this.implementation.handleMessage(message);
|
||||
}
|
||||
this.implementation.handleMessage(message);
|
||||
}
|
||||
|
||||
stopSession(): Promise<void> {
|
||||
if (this.implementation.dispose) {
|
||||
this.implementation.dispose();
|
||||
}
|
||||
this.implementation.dispose();
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +181,10 @@ export class ExtHostDocumentData extends MirrorTextModel {
|
||||
throw new Error('Invalid argument');
|
||||
}
|
||||
|
||||
if (this._lines.length === 0) {
|
||||
return position.with(0, 0);
|
||||
}
|
||||
|
||||
let { line, character } = position;
|
||||
let hasChanged = false;
|
||||
|
||||
|
||||
@@ -371,7 +371,6 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
|
||||
get storagePath() { return that._storagePath.workspaceValue(extensionDescription); },
|
||||
get globalStoragePath() { return that._storagePath.globalValue(extensionDescription); },
|
||||
asAbsolutePath(relativePath: string) { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
|
||||
asExtensionUri(relativePath: string) { return joinPath(extensionDescription.extensionLocation, relativePath); },
|
||||
get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); }
|
||||
});
|
||||
});
|
||||
|
||||
@@ -834,7 +834,7 @@ class SuggestAdapter {
|
||||
private readonly _extension: IExtensionDescription,
|
||||
) { }
|
||||
|
||||
provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<extHostProtocol.ISuggestResultDto | undefined> {
|
||||
async provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<extHostProtocol.ISuggestResultDto | undefined> {
|
||||
|
||||
const doc = this._documents.getDocument(resource);
|
||||
const pos = typeConvert.Position.to(position);
|
||||
@@ -845,48 +845,47 @@ class SuggestAdapter {
|
||||
const replaceRange = doc.getWordRangeAtPosition(pos) || new Range(pos, pos);
|
||||
const insertRange = replaceRange.with({ end: pos });
|
||||
|
||||
return asPromise(() => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.to(context))).then(value => {
|
||||
const itemsOrList = await asPromise(() => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.to(context)));
|
||||
|
||||
if (!value) {
|
||||
// undefined and null are valid results
|
||||
return undefined;
|
||||
if (!itemsOrList) {
|
||||
// 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 list = Array.isArray(itemsOrList) ? new CompletionList(itemsOrList) : itemsOrList;
|
||||
|
||||
// keep result for providers that support resolving
|
||||
const pid: number = SuggestAdapter.supportsResolving(this._provider) ? this._cache.add(list.items) : this._cache.add([]);
|
||||
const disposables = new DisposableStore();
|
||||
this._disposables.set(pid, disposables);
|
||||
|
||||
const completions: extHostProtocol.ISuggestDataDto[] = [];
|
||||
const result: extHostProtocol.ISuggestResultDto = {
|
||||
x: pid,
|
||||
[extHostProtocol.ISuggestResultDtoField.completions]: completions,
|
||||
[extHostProtocol.ISuggestResultDtoField.defaultRanges]: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) },
|
||||
[extHostProtocol.ISuggestResultDtoField.isIncomplete]: list.isIncomplete || undefined
|
||||
};
|
||||
|
||||
for (let i = 0; i < list.items.length; i++) {
|
||||
const item = list.items[i];
|
||||
// check for bad completion item first
|
||||
if (this._validateCompletionItem(item, pos)) {
|
||||
const dto = this._convertCompletionItem(item, [pid, i], insertRange, replaceRange);
|
||||
completions.push(dto);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
const pid: number = SuggestAdapter.supportsResolving(this._provider) ? this._cache.add(list.items) : this._cache.add([]);
|
||||
const disposables = new DisposableStore();
|
||||
this._disposables.set(pid, disposables);
|
||||
|
||||
const completions: extHostProtocol.ISuggestDataDto[] = [];
|
||||
const result: extHostProtocol.ISuggestResultDto = {
|
||||
x: pid,
|
||||
[extHostProtocol.ISuggestResultDtoField.completions]: completions,
|
||||
[extHostProtocol.ISuggestResultDtoField.defaultRanges]: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) },
|
||||
[extHostProtocol.ISuggestResultDtoField.isIncomplete]: list.isIncomplete || undefined
|
||||
};
|
||||
|
||||
for (let i = 0; i < list.items.length; i++) {
|
||||
const item = list.items[i];
|
||||
// check for bad completion item first
|
||||
if (this._validateCompletionItem(item, pos)) {
|
||||
const dto = this._convertCompletionItem(item, [pid, i], insertRange, replaceRange);
|
||||
completions.push(dto);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
resolveCompletionItem(_resource: URI, position: IPosition, id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<extHostProtocol.ISuggestDataDto | undefined> {
|
||||
async resolveCompletionItem(_resource: URI, position: IPosition, id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<extHostProtocol.ISuggestDataDto | undefined> {
|
||||
|
||||
if (typeof this._provider.resolveCompletionItem !== 'function') {
|
||||
return Promise.resolve(undefined);
|
||||
@@ -901,40 +900,39 @@ class SuggestAdapter {
|
||||
const _mustNotChange = SuggestAdapter._mustNotChangeHash(item);
|
||||
const _mayNotChange = SuggestAdapter._mayNotChangeHash(item);
|
||||
|
||||
return asPromise(() => this._provider.resolveCompletionItem!(item, token)).then(resolvedItem => {
|
||||
const resolvedItem = await asPromise(() => this._provider.resolveCompletionItem!(item, token));
|
||||
|
||||
if (!resolvedItem || !this._validateCompletionItem(resolvedItem, pos)) {
|
||||
return undefined;
|
||||
}
|
||||
if (!resolvedItem || !this._validateCompletionItem(resolvedItem, pos)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
type BlameExtension = {
|
||||
extensionId: string;
|
||||
kind: string;
|
||||
index: string;
|
||||
};
|
||||
type BlameExtension = {
|
||||
extensionId: string;
|
||||
kind: string;
|
||||
index: string;
|
||||
};
|
||||
|
||||
type BlameExtensionMeta = {
|
||||
extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
|
||||
kind: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
|
||||
index: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
|
||||
};
|
||||
type BlameExtensionMeta = {
|
||||
extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
|
||||
kind: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
|
||||
index: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
|
||||
};
|
||||
|
||||
let _mustNotChangeIndex = !this._didWarnMust && SuggestAdapter._mustNotChangeDiff(_mustNotChange, resolvedItem);
|
||||
if (typeof _mustNotChangeIndex === 'string') {
|
||||
this._logService.warn(`[${this._extension.identifier.value}] INVALID result from 'resolveCompletionItem', extension MUST NOT change any of: label, sortText, filterText, insertText, or textEdit`);
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'must', index: _mustNotChangeIndex });
|
||||
this._didWarnMust = true;
|
||||
}
|
||||
let _mustNotChangeIndex = !this._didWarnMust && SuggestAdapter._mustNotChangeDiff(_mustNotChange, resolvedItem);
|
||||
if (typeof _mustNotChangeIndex === 'string') {
|
||||
this._logService.warn(`[${this._extension.identifier.value}] INVALID result from 'resolveCompletionItem', extension MUST NOT change any of: label, sortText, filterText, insertText, or textEdit`);
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'must', index: _mustNotChangeIndex });
|
||||
this._didWarnMust = true;
|
||||
}
|
||||
|
||||
let _mayNotChangeIndex = !this._didWarnShould && SuggestAdapter._mayNotChangeDiff(_mayNotChange, resolvedItem);
|
||||
if (typeof _mayNotChangeIndex === 'string') {
|
||||
this._logService.info(`[${this._extension.identifier.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`);
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'should', index: _mayNotChangeIndex });
|
||||
this._didWarnShould = true;
|
||||
}
|
||||
let _mayNotChangeIndex = !this._didWarnShould && SuggestAdapter._mayNotChangeDiff(_mayNotChange, resolvedItem);
|
||||
if (typeof _mayNotChangeIndex === 'string') {
|
||||
this._logService.info(`[${this._extension.identifier.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`);
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'should', index: _mayNotChangeIndex });
|
||||
this._didWarnShould = true;
|
||||
}
|
||||
|
||||
return this._convertCompletionItem(resolvedItem, id);
|
||||
});
|
||||
return this._convertCompletionItem(resolvedItem, id);
|
||||
}
|
||||
|
||||
releaseCompletionItems(id: number): any {
|
||||
@@ -956,7 +954,7 @@ class SuggestAdapter {
|
||||
//
|
||||
[extHostProtocol.ISuggestDataDtoField.label]: item.label,
|
||||
[extHostProtocol.ISuggestDataDtoField.label2]: item.label2,
|
||||
[extHostProtocol.ISuggestDataDtoField.kind]: typeConvert.CompletionItemKind.from(item.kind),
|
||||
[extHostProtocol.ISuggestDataDtoField.kind]: item.kind !== undefined ? typeConvert.CompletionItemKind.from(item.kind) : undefined,
|
||||
[extHostProtocol.ISuggestDataDtoField.kindModifier]: item.tags && item.tags.map(typeConvert.CompletionItemTag.from),
|
||||
[extHostProtocol.ISuggestDataDtoField.detail]: item.detail,
|
||||
[extHostProtocol.ISuggestDataDtoField.documentation]: typeof item.documentation === 'undefined' ? undefined : typeConvert.MarkdownString.fromStrict(item.documentation),
|
||||
@@ -994,7 +992,7 @@ class SuggestAdapter {
|
||||
// "old" range
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = typeConvert.Range.from(range);
|
||||
|
||||
} else if (range && !defaultInsertRange?.isEqual(range.inserting) && !defaultReplaceRange?.isEqual(range.replacing)) {
|
||||
} else if (range && (!defaultInsertRange?.isEqual(range.inserting) || !defaultReplaceRange?.isEqual(range.replacing))) {
|
||||
// ONLY send range when it's different from the default ranges (safe bandwidth)
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = {
|
||||
insert: typeConvert.Range.from(range.inserting),
|
||||
|
||||
@@ -10,12 +10,14 @@ import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecyc
|
||||
import { ISplice } from 'vs/base/common/sequence';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { Disposable as VSCodeDisposable } from './extHostTypes';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
|
||||
import { NotImplementedProxy } from 'vs/base/common/types';
|
||||
|
||||
interface IObservable<T> {
|
||||
proxy: T;
|
||||
@@ -40,39 +42,48 @@ function getObservable<T extends Object>(obj: T): IObservable<T> {
|
||||
|
||||
export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
|
||||
private originalSource: string[];
|
||||
// private originalSource: string[];
|
||||
private _outputs: any[];
|
||||
private _onDidChangeOutputs = new Emitter<ISplice<vscode.CellOutput>[]>();
|
||||
onDidChangeOutputs: Event<ISplice<vscode.CellOutput>[]> = this._onDidChangeOutputs.event;
|
||||
private _textDocument: vscode.TextDocument | undefined;
|
||||
private _initalVersion: number = -1;
|
||||
// private _textDocument: vscode.TextDocument | undefined;
|
||||
// private _initalVersion: number = -1;
|
||||
private _outputMapping = new Set<vscode.CellOutput>();
|
||||
private _metadata: vscode.NotebookCellMetadata;
|
||||
|
||||
private _metadataChangeListener: IDisposable;
|
||||
|
||||
private _documentData: ExtHostDocumentData;
|
||||
|
||||
get document(): vscode.TextDocument {
|
||||
return this._documentData.document;
|
||||
}
|
||||
|
||||
get source() {
|
||||
if (this._textDocument && this._initalVersion !== this._textDocument?.version) {
|
||||
return this._textDocument.getText();
|
||||
} else {
|
||||
return this.originalSource.join('\n');
|
||||
}
|
||||
// todo@jrieken remove this
|
||||
return this._documentData.getText();
|
||||
}
|
||||
|
||||
constructor(
|
||||
private viewType: string,
|
||||
private documentUri: URI,
|
||||
private readonly viewType: string,
|
||||
private readonly documentUri: URI,
|
||||
readonly handle: number,
|
||||
readonly uri: URI,
|
||||
private _content: string,
|
||||
content: string,
|
||||
public readonly cellKind: CellKind,
|
||||
public language: string,
|
||||
outputs: any[],
|
||||
_metadata: vscode.NotebookCellMetadata | undefined,
|
||||
private _proxy: MainThreadNotebookShape
|
||||
private _proxy: MainThreadNotebookShape,
|
||||
) {
|
||||
super();
|
||||
this.originalSource = this._content.split(/\r|\n|\r\n/g);
|
||||
this._documentData = new ExtHostDocumentData(
|
||||
new class extends NotImplementedProxy<MainThreadDocumentsShape>('document') { },
|
||||
uri,
|
||||
content.split(/\r|\n|\r\n/g), '\n',
|
||||
language, 0, false
|
||||
);
|
||||
|
||||
this._outputs = outputs;
|
||||
|
||||
const observableMetadata = getObservable(_metadata || {});
|
||||
@@ -125,26 +136,19 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
return this._proxy.$updateNotebookCellMetadata(this.viewType, this.documentUri, this.handle, this._metadata);
|
||||
}
|
||||
|
||||
getContent(): string {
|
||||
if (this._textDocument && this._initalVersion !== this._textDocument?.version) {
|
||||
return this._textDocument.getText();
|
||||
} else {
|
||||
return this.originalSource.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
attachTextDocument(document: vscode.TextDocument) {
|
||||
this._textDocument = document;
|
||||
this._initalVersion = this._textDocument.version;
|
||||
attachTextDocument(document: ExtHostDocumentData) {
|
||||
this._documentData = document;
|
||||
// this._initalVersion = this._documentData.version;
|
||||
}
|
||||
|
||||
detachTextDocument() {
|
||||
if (this._textDocument && this._textDocument.version !== this._initalVersion) {
|
||||
this.originalSource = this._textDocument.getText().split(/\r|\n|\r\n/g);
|
||||
}
|
||||
// no-op? keep stale document until new comes along?
|
||||
|
||||
this._textDocument = undefined;
|
||||
this._initalVersion = -1;
|
||||
// if (this._textDocument && this._textDocument.version !== this._initalVersion) {
|
||||
// this.originalSource = this._textDocument.getText().split(/\r|\n|\r\n/g);
|
||||
// }
|
||||
// this._textDocument = undefined;
|
||||
// this._initalVersion = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,10 +260,10 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
let cellDtos = splice[2];
|
||||
let newCells = cellDtos.map(cell => {
|
||||
const extCell = new ExtHostCell(this.viewType, this.uri, cell.handle, URI.revive(cell.uri), cell.source.join('\n'), cell.cellKind, cell.language, cell.outputs, cell.metadata, this._proxy);
|
||||
const document = this._documentsAndEditors.getDocument(URI.revive(cell.uri));
|
||||
const documentData = this._documentsAndEditors.getDocument(URI.revive(cell.uri));
|
||||
|
||||
if (document) {
|
||||
extCell.attachTextDocument(document.document);
|
||||
if (documentData) {
|
||||
extCell.attachTextDocument(documentData);
|
||||
}
|
||||
|
||||
if (!this._cellDisposableMapping.has(extCell.handle)) {
|
||||
@@ -366,15 +370,15 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
return this.cells.find(cell => cell.handle === cellHandle);
|
||||
}
|
||||
|
||||
attachCellTextDocument(textDocument: vscode.TextDocument) {
|
||||
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.uri.toString());
|
||||
attachCellTextDocument(textDocument: ExtHostDocumentData) {
|
||||
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString());
|
||||
if (cell) {
|
||||
cell.attachTextDocument(textDocument);
|
||||
}
|
||||
}
|
||||
|
||||
detachCellTextDocument(textDocument: vscode.TextDocument) {
|
||||
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.uri.toString());
|
||||
detachCellTextDocument(textDocument: ExtHostDocumentData) {
|
||||
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString());
|
||||
if (cell) {
|
||||
cell.detachTextDocument();
|
||||
}
|
||||
@@ -455,6 +459,8 @@ export class NotebookEditorCellEdit {
|
||||
|
||||
export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor {
|
||||
private _viewColumn: vscode.ViewColumn | undefined;
|
||||
|
||||
selection?: ExtHostCell = undefined;
|
||||
onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
|
||||
|
||||
constructor(
|
||||
@@ -468,22 +474,22 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
) {
|
||||
super();
|
||||
this._register(this._documentsAndEditors.onDidAddDocuments(documents => {
|
||||
for (const { document: textDocument } of documents) {
|
||||
let data = CellUri.parse(textDocument.uri);
|
||||
for (const documentData of documents) {
|
||||
let data = CellUri.parse(documentData.document.uri);
|
||||
if (data) {
|
||||
if (this.document.uri.toString() === data.notebook.toString()) {
|
||||
document.attachCellTextDocument(textDocument);
|
||||
document.attachCellTextDocument(documentData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this._documentsAndEditors.onDidRemoveDocuments(documents => {
|
||||
for (const { document: textDocument } of documents) {
|
||||
let data = CellUri.parse(textDocument.uri);
|
||||
for (const documentData of documents) {
|
||||
let data = CellUri.parse(documentData.document.uri);
|
||||
if (data) {
|
||||
if (this.document.uri.toString() === data.notebook.toString()) {
|
||||
document.detachCellTextDocument(textDocument);
|
||||
document.detachCellTextDocument(documentData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -593,6 +599,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
private readonly _documents = new Map<string, ExtHostNotebookDocument>();
|
||||
private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter<any> }>();
|
||||
private readonly _notebookOutputRenderers = new Map<number, ExtHostNotebookOutputRenderer>();
|
||||
|
||||
private readonly _onDidChangeNotebookDocument = new Emitter<{ document: ExtHostNotebookDocument, changes: NotebookCellsSplice2[] }>();
|
||||
readonly onDidChangeNotebookDocument: Event<{ document: ExtHostNotebookDocument, changes: NotebookCellsSplice2[] }> = this._onDidChangeNotebookDocument.event;
|
||||
|
||||
private _outputDisplayOrder: INotebookDisplayOrder | undefined;
|
||||
|
||||
get outputDisplayOrder(): INotebookDisplayOrder | undefined {
|
||||
@@ -605,6 +615,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
return this._activeNotebookDocument;
|
||||
}
|
||||
|
||||
private _activeNotebookEditor: ExtHostNotebookEditor | undefined;
|
||||
|
||||
get activeNotebookEditor() {
|
||||
return this._activeNotebookEditor;
|
||||
}
|
||||
|
||||
constructor(mainContext: IMainContext, commands: ExtHostCommands, private _documentsAndEditors: ExtHostDocumentsAndEditors) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook);
|
||||
|
||||
@@ -738,6 +754,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
|
||||
async $updateActiveEditor(viewType: string, uri: UriComponents): Promise<void> {
|
||||
this._activeNotebookDocument = this._documents.get(URI.revive(uri).toString());
|
||||
this._activeNotebookEditor = this._editors.get(URI.revive(uri).toString())?.editor;
|
||||
}
|
||||
|
||||
async $destoryNotebookDocument(viewType: string, uri: UriComponents): Promise<boolean> {
|
||||
@@ -782,7 +799,30 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
|
||||
if (editor) {
|
||||
editor.editor.document.accpetModelChanged(event);
|
||||
this._onDidChangeNotebookDocument.fire({
|
||||
document: editor.editor.document,
|
||||
changes: event.changes
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void {
|
||||
let editor = this._editors.get(URI.revive(uriComponents).toString());
|
||||
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.selections) {
|
||||
const cells = editor.editor.document.cells;
|
||||
|
||||
if (data.selections.selections.length) {
|
||||
const firstCell = data.selections.selections[0];
|
||||
editor.editor.selection = cells.find(cell => cell.handle === firstCell);
|
||||
} else {
|
||||
editor.editor.selection = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
158
src/vs/workbench/api/common/extHostNotebookConcatDocument.ts
Normal file
158
src/vs/workbench/api/common/extHostNotebookConcatDocument.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import * as vscode from 'vscode';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostNotebookController, ExtHostCell } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { score } from 'vs/editor/common/modes/languageSelector';
|
||||
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
|
||||
export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextDocument {
|
||||
|
||||
private _disposables = new DisposableStore();
|
||||
private _isClosed = false;
|
||||
|
||||
private _cells!: ExtHostCell[];
|
||||
private _cellLengths!: PrefixSumComputer;
|
||||
private _cellLines!: PrefixSumComputer;
|
||||
private _versionId = 0;
|
||||
|
||||
private readonly _onDidChange = new Emitter<void>();
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
constructor(
|
||||
extHostNotebooks: ExtHostNotebookController,
|
||||
extHostDocuments: ExtHostDocuments,
|
||||
private readonly _notebook: vscode.NotebookDocument,
|
||||
private readonly _selector: vscode.DocumentSelector | undefined,
|
||||
) {
|
||||
this._init();
|
||||
|
||||
this._disposables.add(extHostDocuments.onDidChangeDocument(e => {
|
||||
let cellIdx = this._cells.findIndex(cell => isEqual(cell.uri, e.document.uri));
|
||||
if (cellIdx >= 0) {
|
||||
this._cellLengths.changeValue(cellIdx, this._cells[cellIdx].document.getText().length + 1);
|
||||
this._cellLines.changeValue(cellIdx, this._cells[cellIdx].document.lineCount);
|
||||
this._versionId += 1;
|
||||
this._onDidChange.fire(undefined);
|
||||
}
|
||||
}));
|
||||
this._disposables.add(extHostNotebooks.onDidChangeNotebookDocument(e => {
|
||||
if (e.document === this._notebook) {
|
||||
this._init();
|
||||
this._versionId += 1;
|
||||
this._onDidChange.fire(undefined);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
this._isClosed = true;
|
||||
}
|
||||
|
||||
get isClosed() {
|
||||
return this._isClosed;
|
||||
}
|
||||
|
||||
private _init() {
|
||||
this._cells = [];
|
||||
const cellLengths: number[] = [];
|
||||
const cellLineCounts: number[] = [];
|
||||
for (let cell of this._notebook.cells) {
|
||||
if (cell.cellKind === CellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) {
|
||||
this._cells.push(<ExtHostCell>cell);
|
||||
cellLengths.push(cell.document.getText().length + 1);
|
||||
cellLineCounts.push(cell.document.lineCount);
|
||||
}
|
||||
}
|
||||
this._cellLengths = new PrefixSumComputer(new Uint32Array(cellLengths));
|
||||
this._cellLines = new PrefixSumComputer(new Uint32Array(cellLineCounts));
|
||||
}
|
||||
|
||||
get version(): number {
|
||||
return this._versionId;
|
||||
}
|
||||
|
||||
getText(range?: vscode.Range): string {
|
||||
if (!range) {
|
||||
let result = '';
|
||||
for (let cell of this._cells) {
|
||||
result += cell.document.getText() + '\n';
|
||||
}
|
||||
// remove last newline again
|
||||
result = result.slice(0, -1);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (range.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// get start and end locations and create substrings
|
||||
const start = this.locationAt(range.start);
|
||||
const end = this.locationAt(range.end);
|
||||
const startCell = this._cells.find(cell => isEqual(cell.uri, start.uri));
|
||||
const endCell = this._cells.find(cell => isEqual(cell.uri, end.uri));
|
||||
|
||||
if (!startCell || !endCell) {
|
||||
return '';
|
||||
} else if (startCell === endCell) {
|
||||
return startCell.document.getText(new types.Range(start.range.start, end.range.end));
|
||||
} else {
|
||||
let a = startCell.document.getText(new types.Range(start.range.start, new types.Position(startCell.document.lineCount, 0)));
|
||||
let b = endCell.document.getText(new types.Range(new types.Position(0, 0), end.range.end));
|
||||
return a + '\n' + b;
|
||||
}
|
||||
}
|
||||
|
||||
offsetAt(position: vscode.Position): number {
|
||||
const idx = this._cellLines.getIndexOf(position.line);
|
||||
const offset1 = this._cellLengths.getAccumulatedValue(idx.index - 1);
|
||||
const offset2 = this._cells[idx.index].document.offsetAt(position.with(idx.remainder));
|
||||
return offset1 + offset2;
|
||||
}
|
||||
|
||||
positionAt(locationOrOffset: vscode.Location | number): vscode.Position {
|
||||
if (typeof locationOrOffset === 'number') {
|
||||
const idx = this._cellLengths.getIndexOf(locationOrOffset);
|
||||
const lineCount = this._cellLines.getAccumulatedValue(idx.index - 1);
|
||||
return this._cells[idx.index].document.positionAt(idx.remainder).translate(lineCount);
|
||||
}
|
||||
|
||||
const idx = this._cells.findIndex(cell => isEqual(cell.uri, locationOrOffset.uri));
|
||||
if (idx >= 0) {
|
||||
let line = this._cellLines.getAccumulatedValue(idx - 1);
|
||||
return new types.Position(line + locationOrOffset.range.start.line, locationOrOffset.range.start.character);
|
||||
}
|
||||
// do better?
|
||||
// return undefined;
|
||||
return new types.Position(0, 0);
|
||||
}
|
||||
|
||||
locationAt(positionOrRange: vscode.Range | vscode.Position): types.Location {
|
||||
if (!types.Range.isRange(positionOrRange)) {
|
||||
positionOrRange = new types.Range(<types.Position>positionOrRange, <types.Position>positionOrRange);
|
||||
}
|
||||
|
||||
const startIdx = this._cellLines.getIndexOf(positionOrRange.start.line);
|
||||
let endIdx = startIdx;
|
||||
if (!positionOrRange.isEmpty) {
|
||||
endIdx = this._cellLines.getIndexOf(positionOrRange.end.line);
|
||||
}
|
||||
|
||||
let startPos = new types.Position(startIdx.remainder, positionOrRange.start.character);
|
||||
let endPos = new types.Position(endIdx.remainder, positionOrRange.end.character);
|
||||
let range = new types.Range(startPos, endPos);
|
||||
|
||||
const startCell = this._cells[startIdx.index];
|
||||
return new types.Location(startCell.uri, <types.Range>startCell.document.validateRange(range));
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { debounce } from 'vs/base/common/decorators';
|
||||
import { 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, ICommandDto } from './extHost.protocol';
|
||||
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, MainThreadTelemetryShape } from './extHost.protocol';
|
||||
import { sortedDiff, equals } from 'vs/base/common/arrays';
|
||||
import { comparePaths } from 'vs/base/common/comparers';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -194,6 +194,11 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
|
||||
|
||||
set visible(visible: boolean) {
|
||||
visible = !!visible;
|
||||
|
||||
if (this._visible === visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._visible = visible;
|
||||
this._proxy.$setInputBoxVisibility(this._sourceControlHandle, visible);
|
||||
}
|
||||
@@ -266,14 +271,14 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
return this._resourceStatesMap.get(handle);
|
||||
}
|
||||
|
||||
$executeResourceCommand(handle: number): Promise<void> {
|
||||
$executeResourceCommand(handle: number, preserveFocus: boolean): Promise<void> {
|
||||
const command = this._resourceStatesCommandsMap.get(handle);
|
||||
|
||||
if (!command) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return asPromise(() => this._commands.executeCommand(command.command, ...(command.arguments || [])));
|
||||
return asPromise(() => this._commands.executeCommand(command.command, ...(command.arguments || []), preserveFocus));
|
||||
}
|
||||
|
||||
_takeResourceStateSnapshot(): SCMRawResourceSplice[] {
|
||||
@@ -524,6 +529,7 @@ export class ExtHostSCM implements ExtHostSCMShape {
|
||||
private static _handlePool: number = 0;
|
||||
|
||||
private _proxy: MainThreadSCMShape;
|
||||
private readonly _telemetry: MainThreadTelemetryShape;
|
||||
private _sourceControls: Map<ProviderHandle, ExtHostSourceControl> = new Map<ProviderHandle, ExtHostSourceControl>();
|
||||
private _sourceControlsByExtension: Map<string, ExtHostSourceControl[]> = new Map<string, ExtHostSourceControl[]>();
|
||||
|
||||
@@ -538,6 +544,7 @@ export class ExtHostSCM implements ExtHostSCMShape {
|
||||
@ILogService private readonly logService: ILogService
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadSCM);
|
||||
this._telemetry = mainContext.getProxy(MainContext.MainThreadTelemetry);
|
||||
|
||||
_commands.registerArgumentProcessor({
|
||||
processArgument: arg => {
|
||||
@@ -581,6 +588,12 @@ export class ExtHostSCM implements ExtHostSCMShape {
|
||||
createSourceControl(extension: IExtensionDescription, id: string, label: string, rootUri: vscode.Uri | undefined): vscode.SourceControl {
|
||||
this.logService.trace('ExtHostSCM#createSourceControl', extension.identifier.value, id, label, rootUri);
|
||||
|
||||
type TEvent = { extensionId: string; };
|
||||
type TMeta = { extensionId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; };
|
||||
this._telemetry.$publicLog2<TEvent, TMeta>('api/scm/createSourceControl', {
|
||||
extensionId: extension.identifier.value,
|
||||
});
|
||||
|
||||
const handle = ExtHostSCM._handlePool++;
|
||||
const sourceControl = new ExtHostSourceControl(extension, this._proxy, this._commands, id, label, rootUri);
|
||||
this._sourceControls.set(handle, sourceControl);
|
||||
@@ -628,7 +641,7 @@ export class ExtHostSCM implements ExtHostSCMShape {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): Promise<void> {
|
||||
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number, preserveFocus: boolean): Promise<void> {
|
||||
this.logService.trace('ExtHostSCM#$executeResourceCommand', sourceControlHandle, groupHandle, handle);
|
||||
|
||||
const sourceControl = this._sourceControls.get(sourceControlHandle);
|
||||
@@ -643,7 +656,7 @@ export class ExtHostSCM implements ExtHostSCMShape {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return group.$executeResourceCommand(handle);
|
||||
return group.$executeResourceCommand(handle, preserveFocus);
|
||||
}
|
||||
|
||||
$validateInput(sourceControlHandle: number, value: string, cursorPosition: number): Promise<[string, number] | undefined> {
|
||||
|
||||
@@ -692,7 +692,7 @@ export class EnvironmentVariableCollection implements vscode.EnvironmentVariable
|
||||
|
||||
forEach(callback: (variable: string, mutator: vscode.EnvironmentVariableMutator, collection: vscode.EnvironmentVariableCollection) => any, thisArg?: any): void {
|
||||
this._checkDisposed();
|
||||
this.map.forEach((value, key) => callback(key, value, this));
|
||||
this.map.forEach((value, key) => callback.call(thisArg, key, value, this));
|
||||
}
|
||||
|
||||
delete(variable: string): void {
|
||||
|
||||
@@ -728,6 +728,18 @@ export namespace DefinitionLink {
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
export function to(value: modes.LocationLink): vscode.LocationLink {
|
||||
return {
|
||||
targetUri: value.uri,
|
||||
targetRange: Range.to(value.range),
|
||||
targetSelectionRange: value.targetSelectionRange
|
||||
? Range.to(value.targetSelectionRange)
|
||||
: undefined,
|
||||
originSelectionRange: value.originSelectionRange
|
||||
? Range.to(value.originSelectionRange)
|
||||
: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Hover {
|
||||
@@ -808,70 +820,72 @@ export namespace CompletionItemTag {
|
||||
|
||||
export namespace CompletionItemKind {
|
||||
|
||||
export function from(kind: types.CompletionItemKind | undefined): modes.CompletionItemKind {
|
||||
switch (kind) {
|
||||
case types.CompletionItemKind.Method: return modes.CompletionItemKind.Method;
|
||||
case types.CompletionItemKind.Function: return modes.CompletionItemKind.Function;
|
||||
case types.CompletionItemKind.Constructor: return modes.CompletionItemKind.Constructor;
|
||||
case types.CompletionItemKind.Field: return modes.CompletionItemKind.Field;
|
||||
case types.CompletionItemKind.Variable: return modes.CompletionItemKind.Variable;
|
||||
case types.CompletionItemKind.Class: return modes.CompletionItemKind.Class;
|
||||
case types.CompletionItemKind.Interface: return modes.CompletionItemKind.Interface;
|
||||
case types.CompletionItemKind.Struct: return modes.CompletionItemKind.Struct;
|
||||
case types.CompletionItemKind.Module: return modes.CompletionItemKind.Module;
|
||||
case types.CompletionItemKind.Property: return modes.CompletionItemKind.Property;
|
||||
case types.CompletionItemKind.Unit: return modes.CompletionItemKind.Unit;
|
||||
case types.CompletionItemKind.Value: return modes.CompletionItemKind.Value;
|
||||
case types.CompletionItemKind.Constant: return modes.CompletionItemKind.Constant;
|
||||
case types.CompletionItemKind.Enum: return modes.CompletionItemKind.Enum;
|
||||
case types.CompletionItemKind.EnumMember: return modes.CompletionItemKind.EnumMember;
|
||||
case types.CompletionItemKind.Keyword: return modes.CompletionItemKind.Keyword;
|
||||
case types.CompletionItemKind.Snippet: return modes.CompletionItemKind.Snippet;
|
||||
case types.CompletionItemKind.Text: return modes.CompletionItemKind.Text;
|
||||
case types.CompletionItemKind.Color: return modes.CompletionItemKind.Color;
|
||||
case types.CompletionItemKind.File: return modes.CompletionItemKind.File;
|
||||
case types.CompletionItemKind.Reference: return modes.CompletionItemKind.Reference;
|
||||
case types.CompletionItemKind.Folder: return modes.CompletionItemKind.Folder;
|
||||
case types.CompletionItemKind.Event: return modes.CompletionItemKind.Event;
|
||||
case types.CompletionItemKind.Operator: return modes.CompletionItemKind.Operator;
|
||||
case types.CompletionItemKind.TypeParameter: return modes.CompletionItemKind.TypeParameter;
|
||||
case types.CompletionItemKind.Issue: return modes.CompletionItemKind.Issue;
|
||||
case types.CompletionItemKind.User: return modes.CompletionItemKind.User;
|
||||
}
|
||||
return modes.CompletionItemKind.Property;
|
||||
const _from = new Map<types.CompletionItemKind, modes.CompletionItemKind>([
|
||||
[types.CompletionItemKind.Method, modes.CompletionItemKind.Method],
|
||||
[types.CompletionItemKind.Function, modes.CompletionItemKind.Function],
|
||||
[types.CompletionItemKind.Constructor, modes.CompletionItemKind.Constructor],
|
||||
[types.CompletionItemKind.Field, modes.CompletionItemKind.Field],
|
||||
[types.CompletionItemKind.Variable, modes.CompletionItemKind.Variable],
|
||||
[types.CompletionItemKind.Class, modes.CompletionItemKind.Class],
|
||||
[types.CompletionItemKind.Interface, modes.CompletionItemKind.Interface],
|
||||
[types.CompletionItemKind.Struct, modes.CompletionItemKind.Struct],
|
||||
[types.CompletionItemKind.Module, modes.CompletionItemKind.Module],
|
||||
[types.CompletionItemKind.Property, modes.CompletionItemKind.Property],
|
||||
[types.CompletionItemKind.Unit, modes.CompletionItemKind.Unit],
|
||||
[types.CompletionItemKind.Value, modes.CompletionItemKind.Value],
|
||||
[types.CompletionItemKind.Constant, modes.CompletionItemKind.Constant],
|
||||
[types.CompletionItemKind.Enum, modes.CompletionItemKind.Enum],
|
||||
[types.CompletionItemKind.EnumMember, modes.CompletionItemKind.EnumMember],
|
||||
[types.CompletionItemKind.Keyword, modes.CompletionItemKind.Keyword],
|
||||
[types.CompletionItemKind.Snippet, modes.CompletionItemKind.Snippet],
|
||||
[types.CompletionItemKind.Text, modes.CompletionItemKind.Text],
|
||||
[types.CompletionItemKind.Color, modes.CompletionItemKind.Color],
|
||||
[types.CompletionItemKind.File, modes.CompletionItemKind.File],
|
||||
[types.CompletionItemKind.Reference, modes.CompletionItemKind.Reference],
|
||||
[types.CompletionItemKind.Folder, modes.CompletionItemKind.Folder],
|
||||
[types.CompletionItemKind.Event, modes.CompletionItemKind.Event],
|
||||
[types.CompletionItemKind.Operator, modes.CompletionItemKind.Operator],
|
||||
[types.CompletionItemKind.TypeParameter, modes.CompletionItemKind.TypeParameter],
|
||||
[types.CompletionItemKind.Issue, modes.CompletionItemKind.Issue],
|
||||
[types.CompletionItemKind.User, modes.CompletionItemKind.User],
|
||||
]);
|
||||
|
||||
export function from(kind: types.CompletionItemKind): modes.CompletionItemKind {
|
||||
return _from.get(kind) ?? modes.CompletionItemKind.Property;
|
||||
}
|
||||
|
||||
const _to = new Map<modes.CompletionItemKind, types.CompletionItemKind>([
|
||||
[modes.CompletionItemKind.Method, types.CompletionItemKind.Method],
|
||||
[modes.CompletionItemKind.Function, types.CompletionItemKind.Function],
|
||||
[modes.CompletionItemKind.Constructor, types.CompletionItemKind.Constructor],
|
||||
[modes.CompletionItemKind.Field, types.CompletionItemKind.Field],
|
||||
[modes.CompletionItemKind.Variable, types.CompletionItemKind.Variable],
|
||||
[modes.CompletionItemKind.Class, types.CompletionItemKind.Class],
|
||||
[modes.CompletionItemKind.Interface, types.CompletionItemKind.Interface],
|
||||
[modes.CompletionItemKind.Struct, types.CompletionItemKind.Struct],
|
||||
[modes.CompletionItemKind.Module, types.CompletionItemKind.Module],
|
||||
[modes.CompletionItemKind.Property, types.CompletionItemKind.Property],
|
||||
[modes.CompletionItemKind.Unit, types.CompletionItemKind.Unit],
|
||||
[modes.CompletionItemKind.Value, types.CompletionItemKind.Value],
|
||||
[modes.CompletionItemKind.Constant, types.CompletionItemKind.Constant],
|
||||
[modes.CompletionItemKind.Enum, types.CompletionItemKind.Enum],
|
||||
[modes.CompletionItemKind.EnumMember, types.CompletionItemKind.EnumMember],
|
||||
[modes.CompletionItemKind.Keyword, types.CompletionItemKind.Keyword],
|
||||
[modes.CompletionItemKind.Snippet, types.CompletionItemKind.Snippet],
|
||||
[modes.CompletionItemKind.Text, types.CompletionItemKind.Text],
|
||||
[modes.CompletionItemKind.Color, types.CompletionItemKind.Color],
|
||||
[modes.CompletionItemKind.File, types.CompletionItemKind.File],
|
||||
[modes.CompletionItemKind.Reference, types.CompletionItemKind.Reference],
|
||||
[modes.CompletionItemKind.Folder, types.CompletionItemKind.Folder],
|
||||
[modes.CompletionItemKind.Event, types.CompletionItemKind.Event],
|
||||
[modes.CompletionItemKind.Operator, types.CompletionItemKind.Operator],
|
||||
[modes.CompletionItemKind.TypeParameter, types.CompletionItemKind.TypeParameter],
|
||||
[modes.CompletionItemKind.User, types.CompletionItemKind.User],
|
||||
[modes.CompletionItemKind.Issue, types.CompletionItemKind.Issue],
|
||||
]);
|
||||
|
||||
export function to(kind: modes.CompletionItemKind): types.CompletionItemKind {
|
||||
switch (kind) {
|
||||
case modes.CompletionItemKind.Method: return types.CompletionItemKind.Method;
|
||||
case modes.CompletionItemKind.Function: return types.CompletionItemKind.Function;
|
||||
case modes.CompletionItemKind.Constructor: return types.CompletionItemKind.Constructor;
|
||||
case modes.CompletionItemKind.Field: return types.CompletionItemKind.Field;
|
||||
case modes.CompletionItemKind.Variable: return types.CompletionItemKind.Variable;
|
||||
case modes.CompletionItemKind.Class: return types.CompletionItemKind.Class;
|
||||
case modes.CompletionItemKind.Interface: return types.CompletionItemKind.Interface;
|
||||
case modes.CompletionItemKind.Struct: return types.CompletionItemKind.Struct;
|
||||
case modes.CompletionItemKind.Module: return types.CompletionItemKind.Module;
|
||||
case modes.CompletionItemKind.Property: return types.CompletionItemKind.Property;
|
||||
case modes.CompletionItemKind.Unit: return types.CompletionItemKind.Unit;
|
||||
case modes.CompletionItemKind.Value: return types.CompletionItemKind.Value;
|
||||
case modes.CompletionItemKind.Constant: return types.CompletionItemKind.Constant;
|
||||
case modes.CompletionItemKind.Enum: return types.CompletionItemKind.Enum;
|
||||
case modes.CompletionItemKind.EnumMember: return types.CompletionItemKind.EnumMember;
|
||||
case modes.CompletionItemKind.Keyword: return types.CompletionItemKind.Keyword;
|
||||
case modes.CompletionItemKind.Snippet: return types.CompletionItemKind.Snippet;
|
||||
case modes.CompletionItemKind.Text: return types.CompletionItemKind.Text;
|
||||
case modes.CompletionItemKind.Color: return types.CompletionItemKind.Color;
|
||||
case modes.CompletionItemKind.File: return types.CompletionItemKind.File;
|
||||
case modes.CompletionItemKind.Reference: return types.CompletionItemKind.Reference;
|
||||
case modes.CompletionItemKind.Folder: return types.CompletionItemKind.Folder;
|
||||
case modes.CompletionItemKind.Event: return types.CompletionItemKind.Event;
|
||||
case modes.CompletionItemKind.Operator: return types.CompletionItemKind.Operator;
|
||||
case modes.CompletionItemKind.TypeParameter: return types.CompletionItemKind.TypeParameter;
|
||||
case modes.CompletionItemKind.User: return types.CompletionItemKind.User;
|
||||
case modes.CompletionItemKind.Issue: return types.CompletionItemKind.Issue;
|
||||
}
|
||||
return types.CompletionItemKind.Property;
|
||||
return _to.get(kind) ?? types.CompletionItemKind.Property;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -939,7 +953,8 @@ export namespace SignatureInformation {
|
||||
return {
|
||||
label: info.label,
|
||||
documentation: info.documentation ? MarkdownString.fromStrict(info.documentation) : undefined,
|
||||
parameters: Array.isArray(info.parameters) ? info.parameters.map(ParameterInformation.from) : []
|
||||
parameters: Array.isArray(info.parameters) ? info.parameters.map(ParameterInformation.from) : [],
|
||||
activeParameter: info.activeParameter,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -947,7 +962,8 @@ export namespace SignatureInformation {
|
||||
return {
|
||||
label: info.label,
|
||||
documentation: htmlContent.isMarkdownString(info.documentation) ? MarkdownString.to(info.documentation) : info.documentation,
|
||||
parameters: Array.isArray(info.parameters) ? info.parameters.map(ParameterInformation.to) : []
|
||||
parameters: Array.isArray(info.parameters) ? info.parameters.map(ParameterInformation.to) : [],
|
||||
activeParameter: info.activeParameter,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,18 +6,15 @@
|
||||
import { coalesce, equals } from 'vs/base/common/arrays';
|
||||
import { escapeCodicons } from 'vs/base/common/codicons';
|
||||
import { illegalArgument } from 'vs/base/common/errors';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { isMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { isStringArray } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
|
||||
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import type * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
import { assertIsDefined, isStringArray } from 'vs/base/common/types';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
function es5ClassCompat(target: Function): any {
|
||||
///@ts-expect-error
|
||||
@@ -1294,6 +1291,7 @@ export class SignatureInformation {
|
||||
label: string;
|
||||
documentation?: string | MarkdownString;
|
||||
parameters: ParameterInformation[];
|
||||
activeParameter?: number;
|
||||
|
||||
constructor(label: string, documentation?: string | MarkdownString) {
|
||||
this.label = label;
|
||||
@@ -2654,6 +2652,22 @@ export enum DebugConsoleMode {
|
||||
MergeWithParent = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* VS Code can call the `provideDebugConfigurations` method of a `DebugConfigurationProvider` in two situations (aka 'scopes'):
|
||||
* to provide the initial debug configurations for a newly create launch.json or to provide debug configurations dynamically based on context.
|
||||
* A scope value is used when registering a `DebugConfigurationProvider` with `debug.registerDebugConfigurationProvider`.
|
||||
*/
|
||||
export enum DebugConfigurationProviderScope {
|
||||
/**
|
||||
* The 'initial' scope denotes a context where all debug configurations for a newly created launch.json are needed.
|
||||
*/
|
||||
Initial = 1,
|
||||
/**
|
||||
* The 'dynamic' scope denotes a context where all debug configurations for the current context are needed.
|
||||
*/
|
||||
Dynamic = 2
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@es5ClassCompat
|
||||
@@ -2716,6 +2730,13 @@ export enum CellOutputKind {
|
||||
Rich = 3
|
||||
}
|
||||
|
||||
export enum NotebookCellRunState {
|
||||
Running = 1,
|
||||
Idle = 2,
|
||||
Success = 3,
|
||||
Error = 4
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Timeline
|
||||
@@ -2726,94 +2747,3 @@ export class TimelineItem implements vscode.TimelineItem {
|
||||
}
|
||||
|
||||
//#endregion Timeline
|
||||
|
||||
//#region Custom Editors
|
||||
|
||||
interface EditState {
|
||||
readonly allEdits: readonly number[];
|
||||
readonly currentIndex: number;
|
||||
readonly saveIndex: number;
|
||||
}
|
||||
|
||||
export class CustomDocument<EditType = unknown> implements vscode.CustomDocument<EditType> {
|
||||
|
||||
readonly #edits = new Cache<EditType>('edits');
|
||||
|
||||
readonly #uri: vscode.Uri;
|
||||
|
||||
#editState: EditState = {
|
||||
allEdits: [],
|
||||
currentIndex: -1,
|
||||
saveIndex: -1,
|
||||
};
|
||||
#isDisposed = false;
|
||||
#version = 1;
|
||||
|
||||
constructor(uri: vscode.Uri) {
|
||||
this.#uri = uri;
|
||||
}
|
||||
|
||||
//#region Public API
|
||||
|
||||
public get uri(): vscode.Uri { return this.#uri; }
|
||||
|
||||
public get fileName(): string { return this.uri.fsPath; }
|
||||
|
||||
public get isUntitled() { return this.uri.scheme === Schemas.untitled; }
|
||||
|
||||
#onDidDispose = new Emitter<void>();
|
||||
public readonly onDidDispose = this.#onDidDispose.event;
|
||||
|
||||
public get isClosed() { return this.#isDisposed; }
|
||||
|
||||
public get version() { return this.#version; }
|
||||
|
||||
public get isDirty() {
|
||||
return this.#editState.currentIndex !== this.#editState.saveIndex;
|
||||
}
|
||||
|
||||
public get appliedEdits() {
|
||||
return this.#editState.allEdits.slice(0, this.#editState.currentIndex + 1)
|
||||
.map(id => this._getEdit(id));
|
||||
}
|
||||
|
||||
public get savedEdits() {
|
||||
return this.#editState.allEdits.slice(0, this.#editState.saveIndex + 1)
|
||||
.map(id => this._getEdit(id));
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
/** @internal */ _dispose(): void {
|
||||
this.#isDisposed = true;
|
||||
this.#onDidDispose.fire();
|
||||
this.#onDidDispose.dispose();
|
||||
}
|
||||
|
||||
/** @internal */ _updateEditState(state: EditState) {
|
||||
++this.#version;
|
||||
this.#editState = state;
|
||||
}
|
||||
|
||||
/** @internal*/ _getEdit(editId: number): EditType {
|
||||
return assertIsDefined(this.#edits.get(editId, 0));
|
||||
}
|
||||
|
||||
/** @internal*/ _disposeEdits(editIds: number[]) {
|
||||
for (const editId of editIds) {
|
||||
this.#edits.delete(editId);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal*/ _addEdit(edit: EditType): number {
|
||||
const id = this.#edits.add([edit]);
|
||||
this._updateEditState({
|
||||
allEdits: [...this.#editState.allEdits.slice(0, this.#editState.currentIndex + 1), id],
|
||||
currentIndex: this.#editState.currentIndex + 1,
|
||||
saveIndex: this.#editState.saveIndex,
|
||||
});
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
@@ -18,6 +18,7 @@ import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
|
||||
import type * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
import * as extHostProtocol from './extHost.protocol';
|
||||
import * as extHostTypes from './extHostTypes';
|
||||
|
||||
@@ -262,22 +263,78 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
}
|
||||
}
|
||||
|
||||
class WebviewDocumentStore {
|
||||
private readonly _documents = new Map<string, extHostTypes.CustomDocument>();
|
||||
class CustomDocumentStoreEntry {
|
||||
|
||||
public get(viewType: string, resource: vscode.Uri): extHostTypes.CustomDocument | undefined {
|
||||
constructor(
|
||||
public readonly document: vscode.CustomDocument,
|
||||
) { }
|
||||
|
||||
private readonly _edits = new Cache<vscode.CustomDocumentEditEvent>('custom documents');
|
||||
|
||||
private _backup?: vscode.CustomDocumentBackup;
|
||||
|
||||
|
||||
addEdit(item: vscode.CustomDocumentEditEvent): number {
|
||||
return this._edits.add([item]);
|
||||
}
|
||||
|
||||
async undo(editId: number, isDirty: boolean): Promise<void> {
|
||||
await this.getEdit(editId).undo();
|
||||
if (!isDirty) {
|
||||
this.disposeBackup();
|
||||
}
|
||||
}
|
||||
|
||||
async redo(editId: number, isDirty: boolean): Promise<void> {
|
||||
await this.getEdit(editId).redo();
|
||||
if (!isDirty) {
|
||||
this.disposeBackup();
|
||||
}
|
||||
}
|
||||
|
||||
disposeEdits(editIds: number[]): void {
|
||||
for (const id of editIds) {
|
||||
this._edits.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
updateBackup(backup: vscode.CustomDocumentBackup): void {
|
||||
this._backup?.dispose();
|
||||
this._backup = backup;
|
||||
}
|
||||
|
||||
disposeBackup(): void {
|
||||
this._backup?.dispose();
|
||||
this._backup = undefined;
|
||||
}
|
||||
|
||||
private getEdit(editId: number): vscode.CustomDocumentEditEvent {
|
||||
const edit = this._edits.get(editId, 0);
|
||||
if (!edit) {
|
||||
throw new Error('No edit found');
|
||||
}
|
||||
return edit;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomDocumentStore {
|
||||
private readonly _documents = new Map<string, CustomDocumentStoreEntry>();
|
||||
|
||||
public get(viewType: string, resource: vscode.Uri): CustomDocumentStoreEntry | undefined {
|
||||
return this._documents.get(this.key(viewType, resource));
|
||||
}
|
||||
|
||||
public add(viewType: string, document: extHostTypes.CustomDocument) {
|
||||
public add(viewType: string, document: vscode.CustomDocument): CustomDocumentStoreEntry {
|
||||
const key = this.key(viewType, document.uri);
|
||||
if (this._documents.has(key)) {
|
||||
throw new Error(`Document already exists for viewType:${viewType} resource:${document.uri}`);
|
||||
}
|
||||
this._documents.set(key, document);
|
||||
const entry = new CustomDocumentStoreEntry(document);
|
||||
this._documents.set(key, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public delete(viewType: string, document: extHostTypes.CustomDocument) {
|
||||
public delete(viewType: string, document: vscode.CustomDocument) {
|
||||
const key = this.key(viewType, document.uri);
|
||||
this._documents.delete(key);
|
||||
}
|
||||
@@ -285,6 +342,7 @@ class WebviewDocumentStore {
|
||||
private key(viewType: string, resource: vscode.Uri): string {
|
||||
return `${viewType}@@@${resource}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const enum WebviewEditorType {
|
||||
@@ -342,7 +400,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
|
||||
private readonly _editorProviders = new EditorProviderStore();
|
||||
|
||||
private readonly _documents = new WebviewDocumentStore();
|
||||
private readonly _documents = new CustomDocumentStore();
|
||||
|
||||
constructor(
|
||||
mainContext: extHostProtocol.IMainContext,
|
||||
@@ -399,24 +457,17 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
extension: IExtensionDescription,
|
||||
viewType: string,
|
||||
provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider,
|
||||
options: vscode.WebviewPanelOptions | undefined = {}
|
||||
options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerResource?: boolean },
|
||||
): vscode.Disposable {
|
||||
const disposables = new DisposableStore();
|
||||
if ('resolveCustomTextEditor' in provider) {
|
||||
disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider));
|
||||
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options, {
|
||||
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, {
|
||||
supportsMove: !!provider.moveCustomTextEditor,
|
||||
});
|
||||
} else {
|
||||
disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider));
|
||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options);
|
||||
if (provider.editingDelegate) {
|
||||
disposables.add(provider.editingDelegate.onDidEdit(e => {
|
||||
const document = e.document;
|
||||
const editId = (document as extHostTypes.CustomDocument)._addEdit(e.edit);
|
||||
this._proxy.$onDidEdit(document.uri, viewType, editId, e.label);
|
||||
}));
|
||||
}
|
||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerResource);
|
||||
}
|
||||
|
||||
return extHostTypes.Disposable.from(
|
||||
@@ -504,7 +555,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
await serializer.deserializeWebviewPanel(revivedPanel, state);
|
||||
}
|
||||
|
||||
async $createWebviewCustomEditorDocument(resource: UriComponents, viewType: string, cancellation: CancellationToken) {
|
||||
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
@@ -515,14 +566,24 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
}
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
const document = await entry.provider.openCustomDocument(revivedResource, cancellation);
|
||||
this._documents.add(viewType, document as extHostTypes.CustomDocument);
|
||||
return {
|
||||
editable: !!entry.provider.editingDelegate,
|
||||
};
|
||||
const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation);
|
||||
const documentEntry = this._documents.add(viewType, document);
|
||||
|
||||
if (this.isEditable(document)) {
|
||||
document.onDidChange(e => {
|
||||
if (isEditEvent(e)) {
|
||||
const editId = documentEntry.addEdit(e);
|
||||
this._proxy.$onDidEdit(document.uri, viewType, editId, e.label);
|
||||
} else {
|
||||
this._proxy.$onContentChange(document.uri, viewType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return { editable: this.isEditable(document) };
|
||||
}
|
||||
|
||||
async $disposeWebviewCustomEditorDocument(resource: UriComponents, viewType: string): Promise<void> {
|
||||
async $disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void> {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
@@ -533,9 +594,9 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
}
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
const document = this.getCustomDocument(viewType, revivedResource);
|
||||
const { document } = this.getCustomDocumentEntry(viewType, revivedResource);
|
||||
this._documents.delete(viewType, document);
|
||||
document._dispose();
|
||||
document.dispose();
|
||||
}
|
||||
|
||||
async $resolveWebviewEditor(
|
||||
@@ -561,7 +622,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
switch (entry.type) {
|
||||
case WebviewEditorType.Custom:
|
||||
{
|
||||
const document = this.getCustomDocument(viewType, revivedResource);
|
||||
const { document } = this.getCustomDocumentEntry(viewType, revivedResource);
|
||||
return entry.provider.resolveCustomEditor(document, revivedPanel, cancellation);
|
||||
}
|
||||
case WebviewEditorType.Text:
|
||||
@@ -577,8 +638,8 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
}
|
||||
|
||||
$disposeEdits(resourceComponents: UriComponents, viewType: string, editIds: number[]): void {
|
||||
const document = this.getCustomDocument(viewType, resourceComponents);
|
||||
document._disposeEdits(editIds);
|
||||
const document = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
document.disposeEdits(editIds);
|
||||
}
|
||||
|
||||
async $onMoveCustomEditor(handle: string, newResourceComponents: UriComponents, viewType: string): Promise<void> {
|
||||
@@ -601,69 +662,65 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
await (entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor!(document, webview, CancellationToken.None);
|
||||
}
|
||||
|
||||
async $undo(resourceComponents: UriComponents, viewType: string, editId: number, state: extHostProtocol.CustomDocumentEditState): Promise<void> {
|
||||
const delegate = this.getEditingDelegate(viewType);
|
||||
const document = this.getCustomDocument(viewType, resourceComponents);
|
||||
document._updateEditState(state);
|
||||
return delegate.undoEdits(document, [document._getEdit(editId)]);
|
||||
async $undo(resourceComponents: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
return entry.undo(editId, isDirty);
|
||||
}
|
||||
|
||||
async $redo(resourceComponents: UriComponents, viewType: string, editId: number, state: extHostProtocol.CustomDocumentEditState): Promise<void> {
|
||||
const delegate = this.getEditingDelegate(viewType);
|
||||
const document = this.getCustomDocument(viewType, resourceComponents);
|
||||
document._updateEditState(state);
|
||||
return delegate.applyEdits(document, [document._getEdit(editId)]);
|
||||
async $redo(resourceComponents: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
return entry.redo(editId, isDirty);
|
||||
}
|
||||
|
||||
async $revert(resourceComponents: UriComponents, viewType: string, changes: { undoneEdits: number[], redoneEdits: number[] }, state: extHostProtocol.CustomDocumentEditState): Promise<void> {
|
||||
const delegate = this.getEditingDelegate(viewType);
|
||||
const document = this.getCustomDocument(viewType, resourceComponents);
|
||||
const undoneEdits = changes.undoneEdits.map(id => document._getEdit(id));
|
||||
const appliedEdits = changes.redoneEdits.map(id => document._getEdit(id));
|
||||
document._updateEditState(state);
|
||||
return delegate.revert(document, { undoneEdits, appliedEdits });
|
||||
async $revert(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const document = this.getEditableCustomDocument(viewType, resourceComponents);
|
||||
await document.revert(cancellation);
|
||||
entry.disposeBackup();
|
||||
}
|
||||
|
||||
async $onSave(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
const delegate = this.getEditingDelegate(viewType);
|
||||
const document = this.getCustomDocument(viewType, resourceComponents);
|
||||
return delegate.save(document, cancellation);
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const document = this.getEditableCustomDocument(viewType, resourceComponents);
|
||||
await document.save(cancellation);
|
||||
entry.disposeBackup();
|
||||
}
|
||||
|
||||
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents, cancellation: CancellationToken): Promise<void> {
|
||||
const delegate = this.getEditingDelegate(viewType);
|
||||
const document = this.getCustomDocument(viewType, resourceComponents);
|
||||
return delegate.saveAs(document, URI.revive(targetResource), cancellation);
|
||||
const document = this.getEditableCustomDocument(viewType, resourceComponents);
|
||||
return document.saveAs(URI.revive(targetResource), cancellation);
|
||||
}
|
||||
|
||||
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
const delegate = this.getEditingDelegate(viewType);
|
||||
const document = this.getCustomDocument(viewType, resourceComponents);
|
||||
return delegate.backup(document, cancellation);
|
||||
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<string> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const document = this.getEditableCustomDocument(viewType, resourceComponents);
|
||||
const backup = await document.backup(cancellation);
|
||||
entry.updateBackup(backup);
|
||||
return backup.backupId;
|
||||
}
|
||||
|
||||
private getWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebviewEditor | undefined {
|
||||
return this._webviewPanels.get(handle);
|
||||
}
|
||||
|
||||
private getCustomDocument(viewType: string, resource: UriComponents): extHostTypes.CustomDocument {
|
||||
const document = this._documents.get(viewType, URI.revive(resource));
|
||||
if (!document) {
|
||||
throw new Error('No webview editor custom document found');
|
||||
private getCustomDocumentEntry(viewType: string, resource: UriComponents): CustomDocumentStoreEntry {
|
||||
const entry = this._documents.get(viewType, URI.revive(resource));
|
||||
if (!entry) {
|
||||
throw new Error('No custom document found');
|
||||
}
|
||||
return document;
|
||||
return entry;
|
||||
}
|
||||
|
||||
private getEditingDelegate(viewType: string): vscode.CustomEditorEditingDelegate {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
private isEditable(document: vscode.CustomDocument): document is vscode.EditableCustomDocument {
|
||||
return !!(document as vscode.EditableCustomDocument).onDidChange;
|
||||
}
|
||||
|
||||
private getEditableCustomDocument(viewType: string, resource: UriComponents): vscode.EditableCustomDocument {
|
||||
const { document } = this.getCustomDocumentEntry(viewType, resource);
|
||||
if (!this.isEditable(document)) {
|
||||
throw new Error('Custom document is not editable');
|
||||
}
|
||||
const delegate = (entry.provider as vscode.CustomEditorProvider).editingDelegate;
|
||||
if (!delegate) {
|
||||
throw new Error(`Provider for ${viewType}' does not support editing`);
|
||||
}
|
||||
return delegate;
|
||||
return document;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,3 +748,8 @@ function getDefaultLocalResourceRoots(
|
||||
extension.extensionLocation,
|
||||
];
|
||||
}
|
||||
|
||||
function isEditEvent(e: vscode.CustomDocumentContentChangeEvent | vscode.CustomDocumentEditEvent): e is vscode.CustomDocumentEditEvent {
|
||||
return typeof (e as vscode.CustomDocumentEditEvent).undo === 'function'
|
||||
&& typeof (e as vscode.CustomDocumentEditEvent).redo === 'function';
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ class ExtHostWorkspaceImpl extends Workspace {
|
||||
}
|
||||
|
||||
private readonly _workspaceFolders: vscode.WorkspaceFolder[] = [];
|
||||
private readonly _structure = TernarySearchTree.forPaths<vscode.WorkspaceFolder>();
|
||||
private readonly _structure = TernarySearchTree.forUris<vscode.WorkspaceFolder>();
|
||||
|
||||
constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], configuration: URI | null, private _isUntitled: boolean) {
|
||||
super(id, folders.map(f => new WorkspaceFolder(f)), configuration);
|
||||
@@ -124,7 +124,7 @@ class ExtHostWorkspaceImpl extends Workspace {
|
||||
// setup the workspace folder data structure
|
||||
folders.forEach(folder => {
|
||||
this._workspaceFolders.push(folder);
|
||||
this._structure.set(folder.uri.toString(), folder);
|
||||
this._structure.set(folder.uri, folder);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -141,15 +141,15 @@ class ExtHostWorkspaceImpl extends Workspace {
|
||||
}
|
||||
|
||||
getWorkspaceFolder(uri: URI, resolveParent?: boolean): vscode.WorkspaceFolder | undefined {
|
||||
if (resolveParent && this._structure.get(uri.toString())) {
|
||||
if (resolveParent && this._structure.get(uri)) {
|
||||
// `uri` is a workspace folder so we check for its parent
|
||||
uri = dirname(uri);
|
||||
}
|
||||
return this._structure.findSubstr(uri.toString());
|
||||
return this._structure.findSubstr(uri);
|
||||
}
|
||||
|
||||
resolveWorkspaceFolder(uri: URI): vscode.WorkspaceFolder | undefined {
|
||||
return this._structure.get(uri.toString());
|
||||
return this._structure.get(uri);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user