mirror of
https://github.com/coder/code-server.git
synced 2026-05-06 12:31:58 +02:00
chore(vscode): update to 1.54.2
This commit is contained in:
@@ -11,6 +11,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle
|
||||
// --- other interested parties
|
||||
import { JSONValidationExtensionPoint } from 'vs/workbench/api/common/jsonValidationExtensionPoint';
|
||||
import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorExtensionPoint';
|
||||
import { IconExtensionPoint, IconFontExtensionPoint } from 'vs/workbench/services/themes/common/iconExtensionPoint';
|
||||
import { TokenClassificationExtensionPoints } from 'vs/workbench/services/themes/common/tokenClassificationExtensionPoint';
|
||||
import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint';
|
||||
|
||||
@@ -79,6 +80,8 @@ export class ExtensionPoints implements IWorkbenchContribution {
|
||||
// Classes that handle extension points...
|
||||
this.instantiationService.createInstance(JSONValidationExtensionPoint);
|
||||
this.instantiationService.createInstance(ColorExtensionPoint);
|
||||
this.instantiationService.createInstance(IconExtensionPoint);
|
||||
this.instantiationService.createInstance(IconFontExtensionPoint);
|
||||
this.instantiationService.createInstance(TokenClassificationExtensionPoints);
|
||||
this.instantiationService.createInstance(LanguageConfigurationFileHandler);
|
||||
}
|
||||
|
||||
@@ -7,72 +7,17 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import * as nls from 'vs/nls';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { IAuthenticationService, AllowedExtension, readAllowedExtensions, getAuthenticationProviderActivationEvent } from 'vs/workbench/services/authentication/browser/authenticationService';
|
||||
import { IAuthenticationService, AllowedExtension, readAllowedExtensions, getAuthenticationProviderActivationEvent, addAccountUsage, readAccountUsages, removeAccountUsage } from 'vs/workbench/services/authentication/browser/authenticationService';
|
||||
import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { fromNow } from 'vs/base/common/date';
|
||||
import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
|
||||
const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser', 'ms-vscode.github-browser', 'github.codespaces'];
|
||||
|
||||
interface IAccountUsage {
|
||||
extensionId: string;
|
||||
extensionName: string;
|
||||
lastUsed: number;
|
||||
}
|
||||
|
||||
function readAccountUsages(storageService: IStorageService, providerId: string, accountName: string,): IAccountUsage[] {
|
||||
const accountKey = `${providerId}-${accountName}-usages`;
|
||||
const storedUsages = storageService.get(accountKey, StorageScope.GLOBAL);
|
||||
let usages: IAccountUsage[] = [];
|
||||
if (storedUsages) {
|
||||
try {
|
||||
usages = JSON.parse(storedUsages);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
return usages;
|
||||
}
|
||||
|
||||
function removeAccountUsage(storageService: IStorageService, providerId: string, accountName: string): void {
|
||||
const accountKey = `${providerId}-${accountName}-usages`;
|
||||
storageService.remove(accountKey, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
function addAccountUsage(storageService: IStorageService, providerId: string, accountName: string, extensionId: string, extensionName: string) {
|
||||
const accountKey = `${providerId}-${accountName}-usages`;
|
||||
const usages = readAccountUsages(storageService, providerId, accountName);
|
||||
|
||||
const existingUsageIndex = usages.findIndex(usage => usage.extensionId === extensionId);
|
||||
if (existingUsageIndex > -1) {
|
||||
usages.splice(existingUsageIndex, 1, {
|
||||
extensionId,
|
||||
extensionName,
|
||||
lastUsed: Date.now()
|
||||
});
|
||||
} else {
|
||||
usages.push({
|
||||
extensionId,
|
||||
extensionName,
|
||||
lastUsed: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
storageService.store(accountKey, JSON.stringify(usages), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
}
|
||||
|
||||
export class MainThreadAuthenticationProvider extends Disposable {
|
||||
private _accounts = new Map<string, string[]>(); // Map account name to session ids
|
||||
private _sessions = new Map<string, string>(); // Map account id to name
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: ExtHostAuthenticationShape,
|
||||
public readonly id: string,
|
||||
@@ -85,15 +30,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public async initialize(): Promise<void> {
|
||||
return this.registerCommandsAndContextMenuItems();
|
||||
}
|
||||
|
||||
public hasSessions(): boolean {
|
||||
return !!this._sessions.size;
|
||||
}
|
||||
|
||||
public manageTrustedExtensions(accountName: string) {
|
||||
const allowedExtensions = readAllowedExtensions(this.storageService, this.id, accountName);
|
||||
|
||||
@@ -117,7 +53,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
});
|
||||
|
||||
quickPick.items = items;
|
||||
quickPick.selectedItems = items;
|
||||
quickPick.selectedItems = items.filter(item => item.extension.allowed === undefined || item.extension.allowed);
|
||||
quickPick.title = nls.localize('manageTrustedExtensions', "Manage Trusted Extensions");
|
||||
quickPick.placeholder = nls.localize('manageExensions', "Choose which extensions can access this account");
|
||||
|
||||
@@ -135,77 +71,40 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
quickPick.show();
|
||||
}
|
||||
|
||||
private async registerCommandsAndContextMenuItems(): Promise<void> {
|
||||
try {
|
||||
const sessions = await this._proxy.$getSessions(this.id);
|
||||
sessions.forEach(session => this.registerSession(session));
|
||||
} catch (_) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
private registerSession(session: modes.AuthenticationSession) {
|
||||
this._sessions.set(session.id, session.account.label);
|
||||
|
||||
const existingSessionsForAccount = this._accounts.get(session.account.label);
|
||||
if (existingSessionsForAccount) {
|
||||
this._accounts.set(session.account.label, existingSessionsForAccount.concat(session.id));
|
||||
return;
|
||||
} else {
|
||||
this._accounts.set(session.account.label, [session.id]);
|
||||
}
|
||||
}
|
||||
|
||||
async signOut(accountName: string): Promise<void> {
|
||||
async removeAccountSessions(accountName: string, sessions: modes.AuthenticationSession[]): Promise<void> {
|
||||
const accountUsages = readAccountUsages(this.storageService, this.id, accountName);
|
||||
const sessionsForAccount = this._accounts.get(accountName);
|
||||
|
||||
const result = await this.dialogService.confirm({
|
||||
title: nls.localize('signOutConfirm', "Sign out of {0}", accountName),
|
||||
message: accountUsages.length
|
||||
? nls.localize('signOutMessagve', "The account {0} has been used by: \n\n{1}\n\n Sign out of these features?", accountName, accountUsages.map(usage => usage.extensionName).join('\n'))
|
||||
: nls.localize('signOutMessageSimple', "Sign out of {0}?", accountName)
|
||||
});
|
||||
const result = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
accountUsages.length
|
||||
? nls.localize('signOutMessagve', "The account '{0}' has been used by: \n\n{1}\n\n Sign out from these extensions?", accountName, accountUsages.map(usage => usage.extensionName).join('\n'))
|
||||
: nls.localize('signOutMessageSimple', "Sign out of '{0}'?", accountName),
|
||||
[
|
||||
nls.localize('signOut', "Sign out"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
],
|
||||
{
|
||||
cancelId: 1
|
||||
});
|
||||
|
||||
if (result.confirmed) {
|
||||
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
|
||||
if (result.choice === 0) {
|
||||
const removeSessionPromises = sessions.map(session => this.removeSession(session.id));
|
||||
await Promise.all(removeSessionPromises);
|
||||
removeAccountUsage(this.storageService, this.id, accountName);
|
||||
this.storageService.remove(`${this.id}-${accountName}`, StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
async getSessions(): Promise<ReadonlyArray<modes.AuthenticationSession>> {
|
||||
return this._proxy.$getSessions(this.id);
|
||||
async getSessions(scopes?: string[]) {
|
||||
return this._proxy.$getSessions(this.id, scopes);
|
||||
}
|
||||
|
||||
async updateSessionItems(event: modes.AuthenticationSessionsChangeEvent): Promise<void> {
|
||||
const { added, removed } = event;
|
||||
const session = await this._proxy.$getSessions(this.id);
|
||||
const addedSessions = session.filter(session => added.some(id => id === session.id));
|
||||
|
||||
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);
|
||||
|
||||
if (!sessionsForAccount.length) {
|
||||
this._accounts.delete(accountName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addedSessions.forEach(session => this.registerSession(session));
|
||||
createSession(scopes: string[]): Promise<modes.AuthenticationSession> {
|
||||
return this._proxy.$createSession(this.id, scopes);
|
||||
}
|
||||
|
||||
login(scopes: string[]): Promise<modes.AuthenticationSession> {
|
||||
return this._proxy.$login(this.id, scopes);
|
||||
}
|
||||
|
||||
async logout(sessionId: string): Promise<void> {
|
||||
await this._proxy.$logout(this.id, sessionId);
|
||||
async removeSession(sessionId: string): Promise<void> {
|
||||
await this._proxy.$removeSession(this.id, sessionId);
|
||||
this.notificationService.info(nls.localize('signedOut', "Successfully signed out."));
|
||||
}
|
||||
}
|
||||
@@ -220,7 +119,6 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService
|
||||
) {
|
||||
@@ -228,7 +126,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
|
||||
|
||||
this._register(this.authenticationService.onDidChangeSessions(e => {
|
||||
this._proxy.$onDidChangeAuthenticationSessions(e.providerId, e.label, e.event);
|
||||
this._proxy.$onDidChangeAuthenticationSessions(e.providerId, e.label);
|
||||
}));
|
||||
|
||||
this._register(this.authenticationService.onDidRegisterAuthenticationProvider(info => {
|
||||
@@ -246,13 +144,8 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
}));
|
||||
}
|
||||
|
||||
$getProviderIds(): Promise<string[]> {
|
||||
return Promise.resolve(this.authenticationService.getProviderIds());
|
||||
}
|
||||
|
||||
async $registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): Promise<void> {
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageService, this.quickInputService, this.dialogService);
|
||||
await provider.initialize();
|
||||
this.authenticationService.registerAuthenticationProvider(id, provider);
|
||||
}
|
||||
|
||||
@@ -268,172 +161,10 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
this.authenticationService.sessionsUpdate(id, event);
|
||||
}
|
||||
|
||||
$getSessions(id: string): Promise<ReadonlyArray<modes.AuthenticationSession>> {
|
||||
return this.authenticationService.getSessions(id);
|
||||
$removeSession(providerId: string, sessionId: string): Promise<void> {
|
||||
return this.authenticationService.removeSession(providerId, sessionId);
|
||||
}
|
||||
|
||||
$login(providerId: string, scopes: string[]): Promise<modes.AuthenticationSession> {
|
||||
return this.authenticationService.login(providerId, scopes);
|
||||
}
|
||||
|
||||
$logout(providerId: string, sessionId: string): Promise<void> {
|
||||
return this.authenticationService.logout(providerId, sessionId);
|
||||
}
|
||||
|
||||
async $requestNewSession(providerId: string, scopes: string[], extensionId: string, extensionName: string): Promise<void> {
|
||||
return this.authenticationService.requestNewSession(providerId, scopes, extensionId, extensionName);
|
||||
}
|
||||
|
||||
async $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone: boolean, clearSessionPreference: boolean }): Promise<modes.AuthenticationSession | undefined> {
|
||||
const orderedScopes = scopes.sort().join(' ');
|
||||
const sessions = (await this.$getSessions(providerId)).filter(session => session.scopes.slice().sort().join(' ') === orderedScopes);
|
||||
const label = this.authenticationService.getLabel(providerId);
|
||||
|
||||
if (sessions.length) {
|
||||
if (!this.authenticationService.supportsMultipleAccounts(providerId)) {
|
||||
const session = sessions[0];
|
||||
const allowed = await this.$getSessionsPrompt(providerId, session.account.label, label, extensionId, extensionName);
|
||||
if (allowed) {
|
||||
return session;
|
||||
} else {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
}
|
||||
|
||||
// On renderer side, confirm consent, ask user to choose between accounts if multiple sessions are valid
|
||||
const selected = await this.$selectSession(providerId, label, extensionId, extensionName, sessions, scopes, !!options.clearSessionPreference);
|
||||
return sessions.find(session => session.id === selected.id);
|
||||
} else {
|
||||
if (options.createIfNone) {
|
||||
const isAllowed = await this.$loginPrompt(label, extensionName);
|
||||
if (!isAllowed) {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
|
||||
const session = await this.authenticationService.login(providerId, scopes);
|
||||
await this.$setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id);
|
||||
return session;
|
||||
} else {
|
||||
await this.$requestNewSession(providerId, scopes, extensionId, extensionName);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async $selectSession(providerId: string, providerName: string, extensionId: string, extensionName: string, potentialSessions: modes.AuthenticationSession[], scopes: string[], clearSessionPreference: boolean): Promise<modes.AuthenticationSession> {
|
||||
if (!potentialSessions.length) {
|
||||
throw new Error('No potential sessions found');
|
||||
}
|
||||
|
||||
if (clearSessionPreference) {
|
||||
this.storageService.remove(`${extensionName}-${providerId}`, StorageScope.GLOBAL);
|
||||
} else {
|
||||
const existingSessionPreference = this.storageService.get(`${extensionName}-${providerId}`, StorageScope.GLOBAL);
|
||||
if (existingSessionPreference) {
|
||||
const matchingSession = potentialSessions.find(session => session.id === existingSessionPreference);
|
||||
if (matchingSession) {
|
||||
const allowed = await this.$getSessionsPrompt(providerId, matchingSession.account.label, providerName, extensionId, extensionName);
|
||||
if (allowed) {
|
||||
return matchingSession;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const quickPick = this.quickInputService.createQuickPick<{ label: string, session?: modes.AuthenticationSession }>();
|
||||
quickPick.ignoreFocusOut = true;
|
||||
const items: { label: string, session?: modes.AuthenticationSession }[] = potentialSessions.map(session => {
|
||||
return {
|
||||
label: session.account.label,
|
||||
session
|
||||
};
|
||||
});
|
||||
|
||||
items.push({
|
||||
label: nls.localize('useOtherAccount', "Sign in to another account")
|
||||
});
|
||||
|
||||
quickPick.items = items;
|
||||
quickPick.title = nls.localize(
|
||||
{
|
||||
key: 'selectAccount',
|
||||
comment: ['The placeholder {0} is the name of an extension. {1} is the name of the type of account, such as Microsoft or GitHub.']
|
||||
},
|
||||
"The extension '{0}' wants to access a {1} account",
|
||||
extensionName,
|
||||
providerName);
|
||||
quickPick.placeholder = nls.localize('getSessionPlateholder', "Select an account for '{0}' to use or Esc to cancel", extensionName);
|
||||
|
||||
quickPick.onDidAccept(async _ => {
|
||||
const selected = quickPick.selectedItems[0];
|
||||
|
||||
const session = selected.session ?? await this.authenticationService.login(providerId, scopes);
|
||||
|
||||
const accountName = session.account.label;
|
||||
|
||||
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, StorageTarget.USER);
|
||||
}
|
||||
|
||||
this.storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
|
||||
quickPick.dispose();
|
||||
resolve(session);
|
||||
});
|
||||
|
||||
quickPick.onDidHide(_ => {
|
||||
if (!quickPick.selectedItems[0]) {
|
||||
reject('User did not consent to account access');
|
||||
}
|
||||
|
||||
quickPick.dispose();
|
||||
});
|
||||
|
||||
quickPick.show();
|
||||
});
|
||||
}
|
||||
|
||||
async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
const extensionData = allowList.find(extension => extension.id === extensionId);
|
||||
if (extensionData) {
|
||||
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
|
||||
return true;
|
||||
}
|
||||
|
||||
const remoteConnection = this.remoteAgentService.getConnection();
|
||||
const isVSO = remoteConnection !== null
|
||||
? remoteConnection.remoteAuthority.startsWith('vsonline') || remoteConnection.remoteAuthority.startsWith('codespaces')
|
||||
: isWeb;
|
||||
|
||||
if (isVSO && VSO_ALLOWED_EXTENSIONS.includes(extensionId)) {
|
||||
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
|
||||
return true;
|
||||
}
|
||||
|
||||
const { choice } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmAuthenticationAccess', "The extension '{0}' wants to access the {1} account '{2}'.", extensionName, providerName, accountName),
|
||||
[nls.localize('allow', "Allow"), nls.localize('cancel', "Cancel")],
|
||||
{
|
||||
cancelId: 1
|
||||
}
|
||||
);
|
||||
|
||||
const allow = choice === 0;
|
||||
if (allow) {
|
||||
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
|
||||
allowList.push({ id: extensionId, name: extensionName });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER);
|
||||
}
|
||||
|
||||
return allow;
|
||||
}
|
||||
|
||||
async $loginPrompt(providerName: string, extensionName: string): Promise<boolean> {
|
||||
private async loginPrompt(providerName: string, extensionName: string): Promise<boolean> {
|
||||
const { choice } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName),
|
||||
@@ -446,14 +177,94 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
return choice === 0;
|
||||
}
|
||||
|
||||
async $setTrustedExtensionAndAccountPreference(providerId: string, accountName: string, extensionId: string, extensionName: string, sessionId: 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, StorageTarget.USER);
|
||||
private async setTrustedExtensionAndAccountPreference(providerId: string, accountName: string, extensionId: string, extensionName: string, sessionId: string): Promise<void> {
|
||||
this.authenticationService.updatedAllowedExtension(providerId, accountName, extensionId, extensionName, true);
|
||||
this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
|
||||
}
|
||||
|
||||
private async selectSession(providerId: string, extensionId: string, extensionName: string, scopes: string[], potentialSessions: readonly modes.AuthenticationSession[], clearSessionPreference: boolean, silent: boolean): Promise<modes.AuthenticationSession | undefined> {
|
||||
if (!potentialSessions.length) {
|
||||
throw new Error('No potential sessions found');
|
||||
}
|
||||
|
||||
this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
|
||||
if (clearSessionPreference) {
|
||||
this.storageService.remove(`${extensionName}-${providerId}`, StorageScope.GLOBAL);
|
||||
} else {
|
||||
const existingSessionPreference = this.storageService.get(`${extensionName}-${providerId}`, StorageScope.GLOBAL);
|
||||
if (existingSessionPreference) {
|
||||
const matchingSession = potentialSessions.find(session => session.id === existingSessionPreference);
|
||||
if (matchingSession) {
|
||||
const allowed = this.authenticationService.isAccessAllowed(providerId, matchingSession.account.label, extensionId);
|
||||
if (!allowed) {
|
||||
if (!silent) {
|
||||
const didAcceptPrompt = await this.authenticationService.showGetSessionPrompt(providerId, matchingSession.account.label, extensionId, extensionName);
|
||||
if (!didAcceptPrompt) {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
} else {
|
||||
this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, scopes, potentialSessions);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return matchingSession;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (silent) {
|
||||
this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, scopes, potentialSessions);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.authenticationService.selectSession(providerId, extensionId, extensionName, scopes, potentialSessions);
|
||||
}
|
||||
|
||||
async $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone: boolean, clearSessionPreference: boolean }): Promise<modes.AuthenticationSession | undefined> {
|
||||
const sessions = await this.authenticationService.getSessions(providerId, scopes, true);
|
||||
|
||||
const silent = !options.createIfNone;
|
||||
let session: modes.AuthenticationSession | undefined;
|
||||
if (sessions.length) {
|
||||
if (!this.authenticationService.supportsMultipleAccounts(providerId)) {
|
||||
session = sessions[0];
|
||||
const allowed = this.authenticationService.isAccessAllowed(providerId, session.account.label, extensionId);
|
||||
if (!allowed) {
|
||||
if (!silent) {
|
||||
const didAcceptPrompt = await this.authenticationService.showGetSessionPrompt(providerId, session.account.label, extensionId, extensionName);
|
||||
if (!didAcceptPrompt) {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
} else if (allowed !== false) {
|
||||
this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, scopes, [session]);
|
||||
return undefined;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return this.selectSession(providerId, extensionId, extensionName, scopes, sessions, !!options.clearSessionPreference, silent);
|
||||
}
|
||||
} else {
|
||||
if (!silent) {
|
||||
const providerName = await this.authenticationService.getLabel(providerId);
|
||||
const isAllowed = await this.loginPrompt(providerName, extensionName);
|
||||
if (!isAllowed) {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
|
||||
session = await this.authenticationService.createSession(providerId, scopes, true);
|
||||
await this.setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id);
|
||||
} else {
|
||||
await this.authenticationService.requestNewSession(providerId, scopes, extensionId, extensionName);
|
||||
}
|
||||
}
|
||||
|
||||
if (session) {
|
||||
addAccountUsage(this.storageService, providerId, session.account.label, extensionId, extensionName);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,10 +26,9 @@ import { IExtensionManifest } from 'vs/workbench/workbench.web.api';
|
||||
|
||||
// this class contains the commands that the CLI server is reying on
|
||||
|
||||
CommandsRegistry.registerCommand('_remoteCLI.openExternal', function (accessor: ServicesAccessor, uri: UriComponents, options: { allowTunneling?: boolean }) {
|
||||
// TODO: discuss martin, ben where to put this
|
||||
CommandsRegistry.registerCommand('_remoteCLI.openExternal', function (accessor: ServicesAccessor, uri: UriComponents | string) {
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
openerService.open(URI.revive(uri), { openExternal: true, allowTunneling: options?.allowTunneling === true });
|
||||
openerService.open(isString(uri) ? uri : URI.revive(uri), { openExternal: true, allowTunneling: true });
|
||||
});
|
||||
|
||||
interface ManageExtensionsArgs {
|
||||
@@ -100,7 +99,7 @@ class RemoteExtensionCLIManagementService extends ExtensionManagementCLIService
|
||||
|
||||
protected validateExtensionKind(manifest: IExtensionManifest, output: CLIOutput): boolean {
|
||||
if (!canExecuteOnWorkspace(manifest, this.productService, this.configurationService)) {
|
||||
output.log(localize('cannot be installed', "Cannot install '{0}' because this extension has defined that it cannot run on the remote server.", getExtensionId(manifest.publisher, manifest.name)));
|
||||
output.log(localize('cannot be installed', "Cannot install the '{0}' extension because it is declared to not run in this setup.", getExtensionId(manifest.publisher, manifest.name)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -473,7 +473,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
if (!commentsViewAlreadyRegistered) {
|
||||
const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
|
||||
id: COMMENTS_VIEW_ID,
|
||||
name: COMMENTS_VIEW_TITLE,
|
||||
title: COMMENTS_VIEW_TITLE,
|
||||
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
|
||||
storageId: COMMENTS_VIEW_TITLE,
|
||||
hideIfEmpty: true,
|
||||
|
||||
@@ -75,8 +75,8 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
return Promise.resolve(this._proxy.$substituteVariables(folder ? folder.uri : undefined, config));
|
||||
}
|
||||
|
||||
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined> {
|
||||
return this._proxy.$runInTerminal(args);
|
||||
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {
|
||||
return this._proxy.$runInTerminal(args, sessionId);
|
||||
}
|
||||
|
||||
// RPC methods (MainThreadDebugServiceShape)
|
||||
|
||||
@@ -13,7 +13,6 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { IFileService, FileOperation } from 'vs/platform/files/common/files';
|
||||
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors';
|
||||
import { ExtHostContext, ExtHostDocumentsShape, IExtHostContext, MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ITextEditorModel } from 'vs/workbench/common/editor';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { toLocalResource, extUri, IExtUri } from 'vs/base/common/resources';
|
||||
@@ -47,8 +46,8 @@ export class BoundModelReferenceCollection {
|
||||
}
|
||||
}
|
||||
|
||||
add(uri: URI, ref: IReference<ITextEditorModel>): void {
|
||||
const length = ref.object.textEditorModel.getValueLength();
|
||||
add(uri: URI, ref: IReference<any>, length: number = 0): void {
|
||||
// const length = ref.object.textEditorModel.getValueLength();
|
||||
let handle: any;
|
||||
let entry: { uri: URI, length: number, dispose(): void };
|
||||
const dispose = () => {
|
||||
@@ -267,7 +266,7 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen
|
||||
|
||||
private _handleAsResourceInput(uri: URI): Promise<URI> {
|
||||
return this._textModelResolverService.createModelReference(uri).then(ref => {
|
||||
this._modelReferenceCollection.add(uri, ref);
|
||||
this._modelReferenceCollection.add(uri, ref, ref.object.textEditorModel.getValueLength());
|
||||
return ref.object.textEditorModel.uri;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,7 +79,6 @@ export class MainThreadTextEditorProperties {
|
||||
return {
|
||||
insertSpaces: modelOptions.insertSpaces,
|
||||
tabSize: modelOptions.tabSize,
|
||||
indentSize: modelOptions.indentSize,
|
||||
cursorStyle: cursorStyle,
|
||||
lineNumbers: lineNumbers
|
||||
};
|
||||
@@ -146,7 +145,6 @@ export class MainThreadTextEditorProperties {
|
||||
}
|
||||
return (
|
||||
a.tabSize === b.tabSize
|
||||
&& a.indentSize === b.indentSize
|
||||
&& a.insertSpaces === b.insertSpaces
|
||||
&& a.cursorStyle === b.cursorStyle
|
||||
&& a.lineNumbers === b.lineNumbers
|
||||
@@ -377,13 +375,6 @@ export class MainThreadTextEditor {
|
||||
if (typeof newConfiguration.tabSize !== 'undefined') {
|
||||
newOpts.tabSize = newConfiguration.tabSize;
|
||||
}
|
||||
if (typeof newConfiguration.indentSize !== 'undefined') {
|
||||
if (newConfiguration.indentSize === 'tabSize') {
|
||||
newOpts.indentSize = newOpts.tabSize || creationOpts.tabSize;
|
||||
} else {
|
||||
newOpts.indentSize = newConfiguration.indentSize;
|
||||
}
|
||||
}
|
||||
this._model.updateOptions(newOpts);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import { IDecorationOptions, IDecorationRenderOptions, ILineChange } from 'vs/editor/common/editorCommon';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/model';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ITextEditorOptions, IResourceEditorInput, EditorActivation } from 'vs/platform/editor/common/editor';
|
||||
import { ITextEditorOptions, IResourceEditorInput, EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors';
|
||||
import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor';
|
||||
@@ -142,7 +142,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
|
||||
// preserve pre 1.38 behaviour to not make group active when preserveFocus: true
|
||||
// but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633
|
||||
activation: options.preserveFocus ? EditorActivation.RESTORE : undefined,
|
||||
override: false
|
||||
override: EditorOverride.DISABLED
|
||||
};
|
||||
|
||||
const input: IResourceEditorInput = {
|
||||
|
||||
@@ -129,13 +129,13 @@ export class MainThreadFileSystemEventService {
|
||||
}
|
||||
} else {
|
||||
if (operation === FileOperation.CREATE) {
|
||||
message = localize('ask.N.create', "{0} extensions want to make refactoring changes with this file creation", data.extensionNames.length);
|
||||
message = localize({ key: 'ask.N.create', comment: ['{0} is a number, e.g "3 extensions want..."'] }, "{0} extensions want to make refactoring changes with this file creation", data.extensionNames.length);
|
||||
} else if (operation === FileOperation.COPY) {
|
||||
message = localize('ask.N.copy', "{0} extensions want to make refactoring changes with this file copy", data.extensionNames.length);
|
||||
message = localize({ key: 'ask.N.copy', comment: ['{0} is a number, e.g "3 extensions want..."'] }, "{0} extensions want to make refactoring changes with this file copy", data.extensionNames.length);
|
||||
} else if (operation === FileOperation.MOVE) {
|
||||
message = localize('ask.N.move', "{0} extensions want to make refactoring changes with this file move", data.extensionNames.length);
|
||||
message = localize({ key: 'ask.N.move', comment: ['{0} is a number, e.g "3 extensions want..."'] }, "{0} extensions want to make refactoring changes with this file move", data.extensionNames.length);
|
||||
} else /* if (operation === FileOperation.DELETE) */ {
|
||||
message = localize('ask.N.delete', "{0} extensions want to make refactoring changes with this file deletion", data.extensionNames.length);
|
||||
message = localize({ key: 'ask.N.delete', comment: ['{0} is a number, e.g "3 extensions want..."'] }, "{0} extensions want to make refactoring changes with this file deletion", data.extensionNames.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -250,6 +250,31 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
}));
|
||||
}
|
||||
|
||||
// --- inline values
|
||||
|
||||
$registerInlineValuesProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void {
|
||||
const provider = <modes.InlineValuesProvider>{
|
||||
provideInlineValues: (model: ITextModel, viewPort: EditorRange, context: modes.InlineValueContext, token: CancellationToken): Promise<modes.InlineValue[] | undefined> => {
|
||||
return this._proxy.$provideInlineValues(handle, model.uri, viewPort, context, token);
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof eventHandle === 'number') {
|
||||
const emitter = new Emitter<void>();
|
||||
this._registrations.set(eventHandle, emitter);
|
||||
provider.onDidChangeInlineValues = emitter.event;
|
||||
}
|
||||
|
||||
this._registrations.set(handle, modes.InlineValuesProviderRegistry.register(selector, provider));
|
||||
}
|
||||
|
||||
$emitInlineValuesEvent(eventHandle: number, event?: any): void {
|
||||
const obj = this._registrations.get(eventHandle);
|
||||
if (obj instanceof Emitter) {
|
||||
obj.fire(event);
|
||||
}
|
||||
}
|
||||
|
||||
// --- occurrences
|
||||
|
||||
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void {
|
||||
@@ -727,7 +752,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
const languageIdentifier = this._modeService.getLanguageIdentifier(languageId);
|
||||
if (languageIdentifier) {
|
||||
this._registrations.set(handle, LanguageConfigurationRegistry.register(languageIdentifier, configuration));
|
||||
this._registrations.set(handle, LanguageConfigurationRegistry.register(languageIdentifier, configuration, 100));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { StandardTokenType } from 'vs/editor/common/modes';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadLanguages)
|
||||
export class MainThreadLanguages implements MainThreadLanguagesShape {
|
||||
@@ -18,7 +19,8 @@ export class MainThreadLanguages implements MainThreadLanguagesShape {
|
||||
constructor(
|
||||
_extHostContext: IExtHostContext,
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@IModelService private readonly _modelService: IModelService
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@ITextModelService private _resolverService: ITextModelService,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -30,18 +32,20 @@ export class MainThreadLanguages implements MainThreadLanguagesShape {
|
||||
return Promise.resolve(this._modeService.getRegisteredModes());
|
||||
}
|
||||
|
||||
$changeLanguage(resource: UriComponents, languageId: string): Promise<void> {
|
||||
const uri = URI.revive(resource);
|
||||
const model = this._modelService.getModel(uri);
|
||||
if (!model) {
|
||||
return Promise.reject(new Error('Invalid uri'));
|
||||
}
|
||||
async $changeLanguage(resource: UriComponents, languageId: string): Promise<void> {
|
||||
|
||||
const languageIdentifier = this._modeService.getLanguageIdentifier(languageId);
|
||||
if (!languageIdentifier || languageIdentifier.language !== languageId) {
|
||||
return Promise.reject(new Error(`Unknown language id: ${languageId}`));
|
||||
}
|
||||
this._modelService.setMode(model, this._modeService.create(languageId));
|
||||
return Promise.resolve(undefined);
|
||||
|
||||
const uri = URI.revive(resource);
|
||||
const ref = await this._resolverService.createModelReference(uri);
|
||||
try {
|
||||
this._modelService.setMode(ref.object.textEditorModel, this._modeService.create(languageId));
|
||||
} finally {
|
||||
ref.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
async $tokensAtPosition(resource: UriComponents, position: IPosition): Promise<undefined | { type: StandardTokenType, range: IRange }> {
|
||||
|
||||
@@ -8,14 +8,14 @@ import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtHostContext, ExtHostContext, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { UriComponents, URI } from 'vs/base/common/uri';
|
||||
import { FileLogService } from 'vs/platform/log/common/fileLogService';
|
||||
import { FileLogger } from 'vs/platform/log/common/fileLog';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadLog)
|
||||
export class MainThreadLogService implements MainThreadLogShape {
|
||||
|
||||
private readonly _loggers = new Map<string, FileLogService>();
|
||||
private readonly _loggers = new Map<string, FileLogger>();
|
||||
private readonly _logListener: IDisposable;
|
||||
|
||||
constructor(
|
||||
@@ -40,7 +40,7 @@ export class MainThreadLogService implements MainThreadLogShape {
|
||||
const uri = URI.revive(file);
|
||||
let logger = this._loggers.get(uri.toString());
|
||||
if (!logger) {
|
||||
logger = this._instaService.createInstance(FileLogService, basename(file.path), URI.revive(file), this._logService.getLevel());
|
||||
logger = this._instaService.createInstance(FileLogger, basename(file.path), URI.revive(file), this._logService.getLevel());
|
||||
this._loggers.set(uri.toString(), logger);
|
||||
}
|
||||
logger.log(level, message);
|
||||
|
||||
@@ -3,60 +3,46 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { diffMaps, diffSets } from 'vs/base/common/collections';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle';
|
||||
import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IExtUri } from 'vs/base/common/resources';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { EditorActivation, ITextEditorOptions, EditorOverride } from 'vs/platform/editor/common/editor';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThreadDocuments';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { viewColumnToEditorGroup } from 'vs/workbench/common/editor';
|
||||
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
|
||||
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookEditorModel, INotebookExclusiveDocumentFilter, NotebookCellOutputsSplice, NotebookCellsChangeType, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookCellsChangeType, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
|
||||
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith';
|
||||
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol';
|
||||
|
||||
class DocumentAndEditorState {
|
||||
static compute(before: DocumentAndEditorState | undefined, after: DocumentAndEditorState): INotebookDocumentsAndEditorsDelta {
|
||||
if (!before) {
|
||||
const apiEditors = [];
|
||||
for (let id in after.textEditors) {
|
||||
const editor = after.textEditors.get(id)!;
|
||||
apiEditors.push({ id, documentUri: editor.uri!, selections: editor!.getSelectionHandles(), visibleRanges: editor.visibleRanges });
|
||||
}
|
||||
|
||||
return {
|
||||
addedDocuments: [],
|
||||
addedEditors: apiEditors,
|
||||
addedDocuments: [...after.documents].map(DocumentAndEditorState._asModelAddData),
|
||||
addedEditors: [...after.textEditors.values()].map(DocumentAndEditorState._asEditorAddData),
|
||||
visibleEditors: [...after.visibleEditors].map(editor => editor[0])
|
||||
};
|
||||
}
|
||||
const documentDelta = diffSets(before.documents, after.documents);
|
||||
const editorDelta = diffMaps(before.textEditors, after.textEditors);
|
||||
const addedAPIEditors = editorDelta.added.map(add => ({
|
||||
id: add.getId(),
|
||||
documentUri: add.uri!,
|
||||
selections: add.getSelectionHandles(),
|
||||
visibleRanges: add.visibleRanges
|
||||
}));
|
||||
const addedAPIEditors = editorDelta.added.map(DocumentAndEditorState._asEditorAddData);
|
||||
|
||||
const removedAPIEditors = editorDelta.removed.map(removed => removed.getId());
|
||||
|
||||
@@ -66,29 +52,7 @@ class DocumentAndEditorState {
|
||||
const visibleEditorDelta = diffMaps(before.visibleEditors, after.visibleEditors);
|
||||
|
||||
return {
|
||||
addedDocuments: documentDelta.added.map((e: NotebookTextModel): INotebookModelAddedData => {
|
||||
return {
|
||||
viewType: e.viewType,
|
||||
uri: e.uri,
|
||||
metadata: e.metadata,
|
||||
versionId: e.versionId,
|
||||
cells: e.cells.map(cell => ({
|
||||
handle: cell.handle,
|
||||
uri: cell.uri,
|
||||
source: cell.textBuffer.getLinesContent(),
|
||||
eol: cell.textBuffer.getEOL(),
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: cell.outputs,
|
||||
metadata: cell.metadata
|
||||
})),
|
||||
contentOptions: e.transientOptions,
|
||||
// attachedEditor: editorId ? {
|
||||
// id: editorId,
|
||||
// selections: document.textModel.selections
|
||||
// } : undefined
|
||||
};
|
||||
}),
|
||||
addedDocuments: documentDelta.added.map(DocumentAndEditorState._asModelAddData),
|
||||
removedDocuments: documentDelta.removed.map(e => e.uri),
|
||||
addedEditors: addedAPIEditors,
|
||||
removedEditors: removedAPIEditors,
|
||||
@@ -107,42 +71,90 @@ class DocumentAndEditorState {
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
private static _asModelAddData(e: NotebookTextModel): INotebookModelAddedData {
|
||||
return {
|
||||
viewType: e.viewType,
|
||||
uri: e.uri,
|
||||
metadata: e.metadata,
|
||||
versionId: e.versionId,
|
||||
cells: e.cells.map(cell => ({
|
||||
handle: cell.handle,
|
||||
uri: cell.uri,
|
||||
source: cell.textBuffer.getLinesContent(),
|
||||
eol: cell.textBuffer.getEOL(),
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: cell.outputs,
|
||||
metadata: cell.metadata
|
||||
})),
|
||||
contentOptions: e.transientOptions,
|
||||
};
|
||||
}
|
||||
|
||||
private static _asEditorAddData(add: IEditor): INotebookEditorAddData {
|
||||
return {
|
||||
id: add.getId(),
|
||||
documentUri: add.uri!,
|
||||
selections: add.getSelections(),
|
||||
visibleRanges: add.visibleRanges
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadNotebook)
|
||||
export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape {
|
||||
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
private readonly _notebookProviders = new Map<string, { controller: IMainNotebookController, disposable: IDisposable }>();
|
||||
private readonly _notebookKernelProviders = new Map<number, { extension: NotebookExtensionDescription, emitter: Emitter<URI | undefined>, provider: IDisposable }>();
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
private _toDisposeOnEditorRemove = new Map<string, IDisposable>();
|
||||
private _currentState?: DocumentAndEditorState;
|
||||
private _editorEventListenersMapping: Map<string, DisposableStore> = new Map();
|
||||
private _documentEventListenersMapping: ResourceMap<DisposableStore> = new ResourceMap();
|
||||
private readonly _toDisposeOnEditorRemove = new Map<string, IDisposable>();
|
||||
private readonly _editorEventListenersMapping: Map<string, DisposableStore> = new Map();
|
||||
private readonly _documentEventListenersMapping: ResourceMap<DisposableStore> = new ResourceMap();
|
||||
private readonly _cellStatusBarEntries: Map<number, IDisposable> = new Map();
|
||||
private readonly _modelReferenceCollection: BoundModelReferenceCollection;
|
||||
|
||||
private _currentState?: DocumentAndEditorState;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@INotebookService private _notebookService: INotebookService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@INotebookCellStatusBarService private readonly cellStatusBarService: INotebookCellStatusBarService,
|
||||
@IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService,
|
||||
@INotebookService private _notebookService: INotebookService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@INotebookCellStatusBarService private readonly _cellStatusBarService: INotebookCellStatusBarService,
|
||||
@INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService,
|
||||
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
this._modelReferenceCollection = new BoundModelReferenceCollection(this._uriIdentityService.extUri);
|
||||
this._register(this._modelReferenceCollection);
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
this._modelReferenceCollection.dispose();
|
||||
|
||||
// remove all notebook providers
|
||||
for (let item of this._notebookProviders.values()) {
|
||||
item.disposable.dispose();
|
||||
}
|
||||
|
||||
// remove all kernel providers
|
||||
for (let item of this._notebookKernelProviders.values()) {
|
||||
item.emitter.dispose();
|
||||
item.provider.dispose();
|
||||
}
|
||||
dispose(this._editorEventListenersMapping.values());
|
||||
dispose(this._documentEventListenersMapping.values());
|
||||
dispose(this._toDisposeOnEditorRemove.values());
|
||||
dispose(this._cellStatusBarEntries.values());
|
||||
}
|
||||
|
||||
async $tryApplyEdits(_viewType: string, resource: UriComponents, modelVersionId: number, cellEdits: ICellEditOperation[]): Promise<boolean> {
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
if (!textModel) {
|
||||
@@ -188,6 +200,22 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
|
||||
registerListeners() {
|
||||
|
||||
// forward changes to dirty state
|
||||
// todo@rebornix todo@mjbvz this seem way too complicated... is there an easy way to
|
||||
// the actual resource from a working copy?
|
||||
this._register(this._workingCopyService.onDidChangeDirty(e => {
|
||||
if (e.resource.scheme !== Schemas.vscodeNotebook) {
|
||||
return;
|
||||
}
|
||||
for (const notebook of this._notebookService.getNotebookTextModels()) {
|
||||
if (isEqual(notebook.uri.with({ scheme: Schemas.vscodeNotebook }), e.resource)) {
|
||||
this._proxy.$acceptDirtyStateChanged(notebook.uri, e.isDirty());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this._notebookService.listNotebookEditors().forEach((e) => {
|
||||
this._addNotebookEditor(e);
|
||||
});
|
||||
@@ -216,8 +244,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}));
|
||||
|
||||
disposableStore.add(editor.onDidChangeSelection(() => {
|
||||
const selectionHandles = editor.getSelectionHandles();
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: null, selections: { selections: selectionHandles } });
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: null, selections: { selections: editor.getSelections() } });
|
||||
}));
|
||||
|
||||
this._editorEventListenersMapping.set(editor.getId(), disposableStore);
|
||||
@@ -326,27 +353,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
this._proxy.$acceptModelSaved(e);
|
||||
}));
|
||||
|
||||
const updateOrder = () => {
|
||||
let userOrder = this.configurationService.getValue<string[]>(DisplayOrderKey);
|
||||
this._proxy.$acceptDisplayOrder({
|
||||
defaultOrder: this.accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER,
|
||||
userOrder: userOrder
|
||||
});
|
||||
};
|
||||
|
||||
updateOrder();
|
||||
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectedKeys.indexOf(DisplayOrderKey) >= 0) {
|
||||
updateOrder();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => {
|
||||
updateOrder();
|
||||
}));
|
||||
|
||||
const activeEditorPane = this.editorService.activeEditorPane as any | undefined;
|
||||
const activeEditorPane = this._editorService.activeEditorPane as any | undefined;
|
||||
const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined;
|
||||
this._updateState(notebookEditor);
|
||||
}
|
||||
@@ -359,7 +366,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}),
|
||||
));
|
||||
|
||||
const activeEditorPane = this.editorService.activeEditorPane as any | undefined;
|
||||
const activeEditorPane = this._editorService.activeEditorPane as any | undefined;
|
||||
const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined;
|
||||
this._updateState(notebookEditor);
|
||||
}
|
||||
@@ -379,7 +386,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
private async _updateState(focusedNotebookEditor?: IEditor) {
|
||||
let activeEditor: string | null = null;
|
||||
|
||||
const activeEditorPane = this.editorService.activeEditorPane as any | undefined;
|
||||
const activeEditorPane = this._editorService.activeEditorPane as any | undefined;
|
||||
if (activeEditorPane?.isNotebookEditor) {
|
||||
const notebookEditor = (activeEditorPane.getControl() as INotebookEditor);
|
||||
activeEditor = notebookEditor && notebookEditor.hasModel() ? notebookEditor!.getId() : null;
|
||||
@@ -396,7 +403,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
});
|
||||
|
||||
const visibleEditorsMap = new Map<string, IEditor>();
|
||||
this.editorService.visibleEditorPanes.forEach(editor => {
|
||||
this._editorService.visibleEditorPanes.forEach(editor => {
|
||||
if ((editor as any).isNotebookEditor) {
|
||||
const nbEditorWidget = (editor as any).getControl() as INotebookEditor;
|
||||
if (nbEditorWidget && editors.has(nbEditorWidget.getId())) {
|
||||
@@ -425,11 +432,11 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
|
||||
// if (!isEmptyChange) {
|
||||
this._currentState = newState;
|
||||
await this._emitDelta(delta);
|
||||
this._emitDelta(delta);
|
||||
// }
|
||||
}
|
||||
|
||||
async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, options: {
|
||||
async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, options: {
|
||||
transientOutputs: boolean;
|
||||
transientMetadata: TransientMetadata;
|
||||
viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; };
|
||||
@@ -437,7 +444,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
let contentOptions = { transientOutputs: options.transientOutputs, transientMetadata: options.transientMetadata };
|
||||
|
||||
const controller: IMainNotebookController = {
|
||||
supportBackup,
|
||||
get options() {
|
||||
return contentOptions;
|
||||
},
|
||||
@@ -446,24 +452,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
contentOptions.transientOutputs = newOptions.transientOutputs;
|
||||
},
|
||||
viewOptions: options.viewOptions,
|
||||
reloadNotebook: async (mainthreadTextModel: NotebookTextModel) => {
|
||||
const data = await this._proxy.$resolveNotebookData(viewType, mainthreadTextModel.uri);
|
||||
mainthreadTextModel.updateLanguages(data.languages);
|
||||
mainthreadTextModel.metadata = data.metadata;
|
||||
mainthreadTextModel.transientOptions = contentOptions;
|
||||
|
||||
const edits: ICellEditOperation[] = [
|
||||
{ editType: CellEditType.Replace, index: 0, count: mainthreadTextModel.cells.length, cells: data.cells }
|
||||
];
|
||||
await new Promise(resolve => {
|
||||
DOM.scheduleAtNextAnimationFrame(() => {
|
||||
const ret = mainthreadTextModel!.applyEdits(mainthreadTextModel!.versionId, edits, true, undefined, () => undefined, undefined);
|
||||
resolve(ret);
|
||||
});
|
||||
});
|
||||
},
|
||||
resolveNotebookDocument: async (viewType: string, uri: URI, backupId?: string) => {
|
||||
const data = await this._proxy.$resolveNotebookData(viewType, uri, backupId);
|
||||
openNotebook: async (viewType: string, uri: URI, backupId?: string) => {
|
||||
const data = await this._proxy.$openNotebook(viewType, uri, backupId);
|
||||
return {
|
||||
data,
|
||||
transientOptions: contentOptions
|
||||
@@ -482,13 +472,12 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
return this._proxy.$saveNotebookAs(viewType, uri, target, token);
|
||||
},
|
||||
backup: async (uri: URI, token: CancellationToken) => {
|
||||
return this._proxy.$backup(viewType, uri, token);
|
||||
return this._proxy.$backupNotebook(viewType, uri, token);
|
||||
}
|
||||
};
|
||||
|
||||
const disposable = this._notebookService.registerNotebookController(viewType, extension, controller);
|
||||
this._notebookProviders.set(viewType, { controller, disposable });
|
||||
return;
|
||||
}
|
||||
|
||||
async $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise<void> {
|
||||
@@ -515,38 +504,46 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void> {
|
||||
const emitter = new Emitter<URI | undefined>();
|
||||
const that = this;
|
||||
|
||||
const provider = this._notebookService.registerNotebookKernelProvider({
|
||||
providerExtensionId: extension.id.value,
|
||||
providerDescription: extension.description,
|
||||
onDidChangeKernels: emitter.event,
|
||||
selector: documentFilter,
|
||||
provideKernels: async (uri: URI, token: CancellationToken) => {
|
||||
const kernels = await that._proxy.$provideNotebookKernels(handle, uri, token);
|
||||
return kernels.map(kernel => {
|
||||
return {
|
||||
...kernel,
|
||||
providerHandle: handle
|
||||
};
|
||||
});
|
||||
},
|
||||
resolveKernel: (editorId: string, uri: URI, kernelId: string, token: CancellationToken) => {
|
||||
return that._proxy.$resolveNotebookKernel(handle, editorId, uri, kernelId, token);
|
||||
},
|
||||
executeNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => {
|
||||
this.logService.debug('MainthreadNotebooks.registerNotebookKernelProvider#executeNotebook', uri.path, kernelId, cellHandle);
|
||||
return that._proxy.$executeNotebookKernelFromProvider(handle, uri, kernelId, cellHandle);
|
||||
},
|
||||
cancelNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => {
|
||||
this.logService.debug('MainthreadNotebooks.registerNotebookKernelProvider#cancelNotebook', uri.path, kernelId, cellHandle);
|
||||
return that._proxy.$cancelNotebookKernelFromProvider(handle, uri, kernelId, cellHandle);
|
||||
},
|
||||
provideKernels: async (uri: URI, token: CancellationToken): Promise<INotebookKernel[]> => {
|
||||
const result: INotebookKernel[] = [];
|
||||
const kernelsDto = await that._proxy.$provideNotebookKernels(handle, uri, token);
|
||||
for (const dto of kernelsDto) {
|
||||
result.push({
|
||||
id: dto.id,
|
||||
friendlyId: dto.friendlyId,
|
||||
label: dto.label,
|
||||
extension: dto.extension,
|
||||
extensionLocation: URI.revive(dto.extensionLocation),
|
||||
providerHandle: dto.providerHandle,
|
||||
description: dto.description,
|
||||
detail: dto.detail,
|
||||
isPreferred: dto.isPreferred,
|
||||
preloads: dto.preloads?.map(u => URI.revive(u)),
|
||||
supportedLanguages: dto.supportedLanguages,
|
||||
resolve: (uri: URI, editorId: string, token: CancellationToken): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.resolveNotebookKernel', uri.path, dto.friendlyId);
|
||||
return this._proxy.$resolveNotebookKernel(handle, editorId, uri, dto.friendlyId, token);
|
||||
},
|
||||
executeNotebookCell: (uri: URI, cellHandle: number | undefined): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.executeNotebookCell', uri.path, dto.friendlyId, cellHandle);
|
||||
return this._proxy.$executeNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellHandle);
|
||||
},
|
||||
cancelNotebookCell: (uri: URI, cellHandle: number | undefined): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.cancelNotebookCell', uri.path, dto.friendlyId, cellHandle);
|
||||
return this._proxy.$cancelNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellHandle);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
this._notebookKernelProviders.set(handle, {
|
||||
extension,
|
||||
emitter,
|
||||
provider
|
||||
});
|
||||
|
||||
this._notebookKernelProviders.set(handle, { extension, emitter, provider });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -566,35 +563,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
entry?.emitter.fire(uriComponents ? URI.revive(uriComponents) : undefined);
|
||||
}
|
||||
|
||||
async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise<void> {
|
||||
this.logService.debug('MainThreadNotebooks#updateNotebookLanguages', resource.path, languages);
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
textModel?.updateLanguages(languages);
|
||||
}
|
||||
|
||||
async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise<void> {
|
||||
this.logService.debug('MainThreadNotebooks#spliceNotebookCellOutputs', resource.path, cellHandle);
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
|
||||
if (!textModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cell = textModel.cells.find(cell => cell.handle === cellHandle);
|
||||
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
textModel.applyEdits(textModel.versionId, [
|
||||
{
|
||||
editType: CellEditType.OutputsSplice,
|
||||
index: textModel.cells.indexOf(cell),
|
||||
splices
|
||||
}
|
||||
], true, undefined, () => undefined, undefined);
|
||||
}
|
||||
|
||||
async $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean> {
|
||||
const editor = this._notebookService.getNotebookEditor(editorId) as INotebookEditor | undefined;
|
||||
if (editor?.isNotebookEditor) {
|
||||
@@ -605,32 +573,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
return false;
|
||||
}
|
||||
|
||||
$onUndoableContentChange(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void {
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
|
||||
if (textModel) {
|
||||
textModel.handleUnknownUndoableEdit(label, () => {
|
||||
const isDirty = this._workingCopyService.isDirty(textModel.uri.with({ scheme: Schemas.vscodeNotebook }));
|
||||
return this._proxy.$undoNotebook(textModel.viewType, textModel.uri, editId, isDirty);
|
||||
}, () => {
|
||||
const isDirty = this._workingCopyService.isDirty(textModel.uri.with({ scheme: Schemas.vscodeNotebook }));
|
||||
return this._proxy.$redoNotebook(textModel.viewType, textModel.uri, editId, isDirty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$onContentChange(resource: UriComponents, viewType: string): void {
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
|
||||
if (textModel) {
|
||||
textModel.applyEdits(textModel.versionId, [
|
||||
{
|
||||
editType: CellEditType.Unknown
|
||||
}
|
||||
], true, undefined, () => undefined, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
async $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType) {
|
||||
const editor = this._notebookService.listNotebookEditors().find(editor => editor.getId() === id);
|
||||
if (editor && editor.isNotebookEditor) {
|
||||
@@ -687,9 +629,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
|
||||
if (statusBarEntry.visible) {
|
||||
this._cellStatusBarEntries.set(
|
||||
id,
|
||||
this.cellStatusBarService.addEntry(statusBarEntry));
|
||||
this._cellStatusBarEntries.set(id, this._cellStatusBarService.addEntry(statusBarEntry));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,6 +642,15 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
return uri;
|
||||
}
|
||||
|
||||
async $trySaveDocument(uriComponents: UriComponents) {
|
||||
const uri = URI.revive(uriComponents);
|
||||
|
||||
const ref = await this._notebookModelResolverService.resolve(uri);
|
||||
const saveResult = await ref.object.save();
|
||||
ref.dispose();
|
||||
return saveResult;
|
||||
}
|
||||
|
||||
async $tryShowNotebookDocument(resource: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string> {
|
||||
const editorOptions: ITextEditorOptions = {
|
||||
preserveFocus: options.preserveFocus,
|
||||
@@ -710,36 +659,36 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
// preserve pre 1.38 behaviour to not make group active when preserveFocus: true
|
||||
// but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633
|
||||
activation: options.preserveFocus ? EditorActivation.RESTORE : undefined,
|
||||
override: false,
|
||||
override: EditorOverride.DISABLED,
|
||||
};
|
||||
|
||||
const columnArg = viewColumnToEditorGroup(this._editorGroupService, options.position);
|
||||
const columnArg = viewColumnToEditorGroup(this._editorGroupsService, options.position);
|
||||
|
||||
let group: IEditorGroup | undefined = undefined;
|
||||
|
||||
if (columnArg === SIDE_GROUP) {
|
||||
const direction = preferredSideBySideGroupDirection(this.configurationService);
|
||||
const direction = preferredSideBySideGroupDirection(this._configurationService);
|
||||
|
||||
let neighbourGroup = this.editorGroupsService.findGroup({ direction });
|
||||
let neighbourGroup = this._editorGroupsService.findGroup({ direction });
|
||||
if (!neighbourGroup) {
|
||||
neighbourGroup = this.editorGroupsService.addGroup(this.editorGroupsService.activeGroup, direction);
|
||||
neighbourGroup = this._editorGroupsService.addGroup(this._editorGroupsService.activeGroup, direction);
|
||||
}
|
||||
group = neighbourGroup;
|
||||
} else {
|
||||
group = this.editorGroupsService.getGroup(viewColumnToEditorGroup(this.editorGroupsService, columnArg)) ?? this.editorGroupsService.activeGroup;
|
||||
group = this._editorGroupsService.getGroup(viewColumnToEditorGroup(this._editorGroupsService, columnArg)) ?? this._editorGroupsService.activeGroup;
|
||||
}
|
||||
|
||||
const input = this.editorService.createEditorInput({ resource: URI.revive(resource), options: editorOptions });
|
||||
const input = this._editorService.createEditorInput({ resource: URI.revive(resource), options: editorOptions });
|
||||
|
||||
// TODO: handle options.selection
|
||||
const editorPane = await this._instantiationService.invokeFunction(openEditorWith, input, viewType, options, group);
|
||||
const editorPane = await this._editorService.openEditor(input, { ...options, override: viewType }, group);
|
||||
const notebookEditor = (editorPane as unknown as { isNotebookEditor?: boolean })?.isNotebookEditor ? (editorPane!.getControl() as INotebookEditor) : undefined;
|
||||
|
||||
if (notebookEditor) {
|
||||
if (notebookEditor.viewModel && options.selection && notebookEditor.viewModel.viewCells[options.selection.start]) {
|
||||
const focusedCell = notebookEditor.viewModel.viewCells[options.selection.start];
|
||||
notebookEditor.revealInCenterIfOutsideViewport(focusedCell);
|
||||
notebookEditor.selectElement(focusedCell);
|
||||
notebookEditor.focusElement(focusedCell);
|
||||
}
|
||||
return notebookEditor.getId();
|
||||
} else {
|
||||
@@ -747,45 +696,3 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class BoundModelReferenceCollection {
|
||||
|
||||
private _data = new Array<{ uri: URI, dispose(): void }>();
|
||||
|
||||
constructor(
|
||||
private readonly _extUri: IExtUri,
|
||||
private readonly _maxAge: number = 1000 * 60 * 3,
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._data = dispose(this._data);
|
||||
}
|
||||
|
||||
remove(uri: URI): void {
|
||||
for (const entry of [...this._data] /* copy array because dispose will modify it */) {
|
||||
if (this._extUri.isEqualOrParent(entry.uri, uri)) {
|
||||
entry.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(uri: URI, ref: IReference<INotebookEditorModel>): void {
|
||||
let handle: any;
|
||||
let entry: { uri: URI, dispose(): void };
|
||||
const dispose = () => {
|
||||
const idx = this._data.indexOf(entry);
|
||||
if (idx >= 0) {
|
||||
ref.dispose();
|
||||
clearTimeout(handle);
|
||||
this._data.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
handle = setTimeout(dispose, this._maxAge);
|
||||
entry = { uri, dispose };
|
||||
|
||||
this._data.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IShellLaunchConfig, ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, ITerminalDimensions, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest, ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest, ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, IShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -12,11 +12,12 @@ import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalExternalLinkProvider, ITerminalLink } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
||||
import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
|
||||
import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { IShellLaunchConfig, ITerminalDimensions } from 'vs/platform/terminal/common/terminal';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
|
||||
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
|
||||
@@ -130,6 +131,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
strictEnv: launchConfig.strictEnv,
|
||||
hideFromUser: launchConfig.hideFromUser,
|
||||
isExtensionTerminal: launchConfig.isExtensionTerminal,
|
||||
isExtensionOwnedTerminal: launchConfig.isExtensionOwnedTerminal,
|
||||
extHostTerminalId: extHostTerminalId,
|
||||
isFeatureTerminal: launchConfig.isFeatureTerminal
|
||||
};
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isDefined } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { getTestSubscriptionKey, RunTestsRequest, RunTestsResult, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { getTestSubscriptionKey, ISerializedTestResults, ITestState, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { HydratedTestResult, ITestResultService, LiveTestResult } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol';
|
||||
|
||||
@@ -18,12 +20,7 @@ const reviveDiff = (diff: TestsDiff) => {
|
||||
const item = entry[1];
|
||||
if (item.item.location) {
|
||||
item.item.location.uri = URI.revive(item.item.location.uri);
|
||||
}
|
||||
|
||||
for (const message of item.item.state.messages) {
|
||||
if (message.location) {
|
||||
message.location.uri = URI.revive(message.location.uri);
|
||||
}
|
||||
item.item.location.range = Range.lift(item.item.location.range);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,49 +30,92 @@ const reviveDiff = (diff: TestsDiff) => {
|
||||
export class MainThreadTesting extends Disposable implements MainThreadTestingShape {
|
||||
private readonly proxy: ExtHostTestingShape;
|
||||
private readonly testSubscriptions = new Map<string, IDisposable>();
|
||||
private readonly testProviderRegistrations = new Map<string, IDisposable>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ITestService private readonly testService: ITestService,
|
||||
@ITestResultService resultService: ITestResultService,
|
||||
@ITestResultService private readonly resultService: ITestResultService,
|
||||
) {
|
||||
super();
|
||||
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostTesting);
|
||||
this._register(this.testService.onShouldSubscribe(args => this.proxy.$subscribeToTests(args.resource, args.uri)));
|
||||
this._register(this.testService.onShouldUnsubscribe(args => this.proxy.$unsubscribeFromTests(args.resource, args.uri)));
|
||||
|
||||
const testCompleteListener = this._register(new MutableDisposable());
|
||||
this._register(resultService.onNewTestResult(results => {
|
||||
testCompleteListener.value = results.onComplete(() => this.proxy.$publishTestResults({ tests: results.tests }));
|
||||
|
||||
const prevResults = resultService.results.map(r => r.toJSON()).filter(isDefined);
|
||||
if (prevResults.length) {
|
||||
this.proxy.$publishTestResults(prevResults);
|
||||
}
|
||||
|
||||
this._register(resultService.onResultsChanged(evt => {
|
||||
const results = 'completed' in evt ? evt.completed : ('inserted' in evt ? evt.inserted : undefined);
|
||||
const serialized = results?.toJSON();
|
||||
if (serialized) {
|
||||
this.proxy.$publishTestResults([serialized]);
|
||||
}
|
||||
}));
|
||||
|
||||
testService.updateRootProviderCount(1);
|
||||
|
||||
const lastCompleted = resultService.results.find(r => !r.isComplete);
|
||||
if (lastCompleted) {
|
||||
this.proxy.$publishTestResults({ tests: lastCompleted.tests });
|
||||
}
|
||||
|
||||
for (const { resource, uri } of this.testService.subscriptions) {
|
||||
this.proxy.$subscribeToTests(resource, uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void {
|
||||
this.resultService.push(new HydratedTestResult(results, persist));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$retireTest(extId: string): void {
|
||||
for (const result of this.resultService.results) {
|
||||
if (result instanceof LiveTestResult) {
|
||||
result.retire(extId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void {
|
||||
const r = this.resultService.getResult(runId);
|
||||
if (r && r instanceof LiveTestResult) {
|
||||
for (const message of state.messages) {
|
||||
if (message.location) {
|
||||
message.location.uri = URI.revive(message.location.uri);
|
||||
message.location.range = Range.lift(message.location.range);
|
||||
}
|
||||
}
|
||||
|
||||
r.updateState(testId, state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $registerTestProvider(id: string) {
|
||||
this.testService.registerTestController(id, {
|
||||
const disposable = this.testService.registerTestController(id, {
|
||||
runTests: (req, token) => this.proxy.$runTestsForProvider(req, token),
|
||||
lookupTest: test => this.proxy.$lookupTest(test),
|
||||
});
|
||||
|
||||
this.testProviderRegistrations.set(id, disposable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $unregisterTestProvider(id: string) {
|
||||
this.testService.unregisterTestController(id);
|
||||
this.testProviderRegistrations.get(id)?.dispose();
|
||||
this.testProviderRegistrations.delete(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,11 +145,13 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
this.testService.publishDiff(resource, URI.revive(uri), diff);
|
||||
}
|
||||
|
||||
public $runTests(req: RunTestsRequest, token: CancellationToken): Promise<RunTestsResult> {
|
||||
return this.testService.runTests(req, token);
|
||||
public async $runTests(req: RunTestsRequest, token: CancellationToken): Promise<string> {
|
||||
const result = await this.testService.runTests(req, token);
|
||||
return result.id;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
super.dispose();
|
||||
this.testService.updateRootProviderCount(-1);
|
||||
for (const subscription of this.testSubscriptions.values()) {
|
||||
subscription.dispose();
|
||||
|
||||
@@ -31,7 +31,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
|
||||
}
|
||||
|
||||
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void {
|
||||
async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): Promise<void> {
|
||||
this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options);
|
||||
|
||||
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape, CandidatePortSource } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { CandidatePort, IRemoteExplorerService, makeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { CandidatePort, IRemoteExplorerService, makeAddress, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, isPortPrivileged } from 'vs/platform/remote/common/tunnel';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { PORT_AUTO_FORWARD_SETTING } from 'vs/workbench/contrib/remote/browser/tunnelView';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTunnelService)
|
||||
export class MainThreadTunnelService extends Disposable implements MainThreadTunnelServiceShape {
|
||||
@@ -27,7 +27,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
@ITunnelService private readonly tunnelService: ITunnelService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ILogService private readonly logService: ILogService
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTunnelService);
|
||||
@@ -35,7 +36,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
this._register(tunnelService.onTunnelClosed(() => this._proxy.$onDidTunnelsChange()));
|
||||
}
|
||||
|
||||
async $setCandidateFinder(): Promise<void> {
|
||||
async $setRemoteTunnelService(processId: number): Promise<void> {
|
||||
this.remoteExplorerService.namedProcesses.set(processId, 'Code Extension Host');
|
||||
if (this.remoteExplorerService.portsFeaturesEnabled) {
|
||||
this._proxy.$registerCandidateFinder(this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING));
|
||||
} else {
|
||||
@@ -131,6 +133,32 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
});
|
||||
}
|
||||
|
||||
async $setCandidatePortSource(source: CandidatePortSource): Promise<void> {
|
||||
// Must wait for the remote environment before trying to set settings there.
|
||||
this.remoteAgentService.getEnvironment().then(() => {
|
||||
switch (source) {
|
||||
case CandidatePortSource.None: {
|
||||
const autoDetectionEnablement = this.configurationService.inspect(PORT_AUTO_FORWARD_SETTING);
|
||||
if (autoDetectionEnablement.userRemote === undefined) {
|
||||
// Only update the remote setting if the user hasn't already set it.
|
||||
this.configurationService.updateValue(PORT_AUTO_FORWARD_SETTING, false, ConfigurationTarget.USER_REMOTE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CandidatePortSource.Output: {
|
||||
const candidatePortSourceSetting = this.configurationService.inspect(PORT_AUTO_SOURCE_SETTING);
|
||||
if (candidatePortSourceSetting.userRemote === undefined) {
|
||||
// Only update the remote setting if the user hasn't already set it.
|
||||
this.configurationService.updateValue(PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, ConfigurationTarget.USER_REMOTE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: // Do nothing, the defaults for these settings should be used.
|
||||
}
|
||||
}).catch(() => {
|
||||
// The remote failed to get setup. Errors from that area will already be surfaced to the user.
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpe
|
||||
return;
|
||||
}
|
||||
|
||||
await this.extensionService.activateByEvent(`onUriOpen:${targetUri.scheme}`);
|
||||
await this.extensionService.activateByEvent(`onOpenExternalUri:${targetUri.scheme}`);
|
||||
|
||||
for (const [id, openerMetadata] of this._registeredOpeners) {
|
||||
if (openerMetadata.schemes.has(targetUri.scheme)) {
|
||||
@@ -87,7 +87,10 @@ export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpe
|
||||
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('openerFailedMessage', 'Could not open uri with \'{0}\': {1}', id, e.toString()),
|
||||
message: localize({
|
||||
key: 'openerFailedMessage',
|
||||
comment: ['{0} is the id of the opener. {1} is the url being opened.'],
|
||||
}, 'Could not open uri with \'{0}\': {1}', id, e.toString()),
|
||||
actions: {
|
||||
primary: [
|
||||
openDefaultAction
|
||||
@@ -117,7 +120,7 @@ export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpe
|
||||
extensionId,
|
||||
});
|
||||
|
||||
this._contributedExternalUriOpenersStore.add(id, extensionId.value);
|
||||
this._contributedExternalUriOpenersStore.didRegisterOpener(id, extensionId.value);
|
||||
}
|
||||
|
||||
async $unregisterUriOpener(id: string): Promise<void> {
|
||||
|
||||
@@ -16,6 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { WorkspaceTrustStateChangeEvent, IWorkspaceTrustService, WorkspaceTrustState } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { IWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
@@ -45,19 +46,21 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@IFileService fileService: IFileService
|
||||
@IFileService fileService: IFileService,
|
||||
@IWorkspaceTrustService private readonly _workspaceTrustService: IWorkspaceTrustService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace);
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
// The workspace file is provided be a unknown file system provider. It might come
|
||||
// from the extension host. So initialize now knowing that `rootPath` is undefined.
|
||||
if (workspace.configuration && !isNative && !fileService.canHandleResource(workspace.configuration)) {
|
||||
this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace));
|
||||
this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace), this.getWorkspaceTrustState());
|
||||
} else {
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace)));
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace), this.getWorkspaceTrustState()));
|
||||
}
|
||||
this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
this._contextService.onDidChangeWorkbenchState(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
this._workspaceTrustService.onDidChangeTrustState(this._onDidChangeWorkspaceTrustState, this, this._toDispose);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -202,4 +205,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
$resolveProxy(url: string): Promise<string | undefined> {
|
||||
return this._requestService.resolveProxy(url);
|
||||
}
|
||||
|
||||
// --- trust ---
|
||||
|
||||
$requireWorkspaceTrust(modal?: boolean): Promise<WorkspaceTrustState> {
|
||||
return this._workspaceTrustService.requireWorkspaceTrust({ modal: modal ?? true });
|
||||
}
|
||||
|
||||
private getWorkspaceTrustState(): WorkspaceTrustState {
|
||||
return this._workspaceTrustService.getWorkspaceTrustState();
|
||||
}
|
||||
|
||||
private _onDidChangeWorkspaceTrustState(state: WorkspaceTrustStateChangeEvent): void {
|
||||
this._proxy.$onDidChangeWorkspaceTrustState(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
@@ -18,8 +17,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { CustomTreeView, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView';
|
||||
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { Extensions as ViewletExtensions, ShowViewletAction2, ViewletRegistry } from 'vs/workbench/browser/viewlet';
|
||||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { Extensions as ViewletExtensions, ViewletRegistry } from 'vs/workbench/browser/viewlet';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { Extensions as ViewContainerExtensions, ITreeViewDescriptor, IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug';
|
||||
@@ -158,6 +156,7 @@ const viewDescriptor: IJSONSchema = {
|
||||
|
||||
const remoteViewDescriptor: IJSONSchema = {
|
||||
type: 'object',
|
||||
required: ['id', 'name'],
|
||||
properties: {
|
||||
id: {
|
||||
description: localize('vscode.extension.contributes.view.id', 'Identifier of the view. This should be unique across all views. It is recommended to include your extension id as part of the view id. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.'),
|
||||
@@ -374,7 +373,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
|
||||
viewContainer = this.viewContainersRegistry.registerViewContainer({
|
||||
id,
|
||||
name: title, extensionId,
|
||||
title, extensionId,
|
||||
ctorDescriptor: new SyncDescriptor(
|
||||
ViewPaneContainer,
|
||||
[id, { mergeViewWithContainerWhenSingleView: true }]
|
||||
@@ -384,16 +383,6 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
icon,
|
||||
}, location);
|
||||
|
||||
// Register Action to Open Viewlet
|
||||
registerAction2(class OpenCustomViewletAction extends ShowViewletAction2 {
|
||||
constructor() {
|
||||
super({ id, f1: true, title: localize('showViewlet', "Show {0}", title), category: CATEGORIES.View.value });
|
||||
}
|
||||
|
||||
protected viewletId() {
|
||||
return id;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return viewContainer;
|
||||
@@ -474,7 +463,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
name: item.name,
|
||||
when: ContextKeyExpr.deserialize(item.when),
|
||||
containerIcon: icon || viewContainer?.icon,
|
||||
containerTitle: item.contextualTitle || viewContainer?.name,
|
||||
containerTitle: item.contextualTitle || viewContainer?.title,
|
||||
canToggleVisibility: true,
|
||||
canMoveView: viewContainer?.id !== REMOTE,
|
||||
treeView: type === ViewType.Tree ? this.instantiationService.createInstance(CustomTreeView, item.id, item.name) : undefined,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { score } from 'vs/editor/common/modes/languageSelector';
|
||||
import * as files from 'vs/platform/files/common/files';
|
||||
import { ExtHostContext, MainContext, ExtHostLogServiceShape, UIKind } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostContext, MainContext, ExtHostLogServiceShape, UIKind, CandidatePortSource } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands';
|
||||
import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard';
|
||||
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
@@ -35,7 +35,7 @@ import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages';
|
||||
import { ExtHostMessageService } from 'vs/workbench/api/common/extHostMessageService';
|
||||
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
|
||||
import { ExtHostProgress } from 'vs/workbench/api/common/extHostProgress';
|
||||
import { ExtHostQuickOpen } from 'vs/workbench/api/common/extHostQuickOpen';
|
||||
import { createExtHostQuickOpen } from 'vs/workbench/api/common/extHostQuickOpen';
|
||||
import { ExtHostSCM } from 'vs/workbench/api/common/extHostSCM';
|
||||
import { ExtHostStatusBar } from 'vs/workbench/api/common/extHostStatusBar';
|
||||
import { IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
@@ -48,7 +48,7 @@ import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls';
|
||||
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { IExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { throwProposedApiError, checkProposedApiEnabled, checkRequiresWorkspaceTrust } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -147,7 +147,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService, extHostApiDeprecation));
|
||||
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
|
||||
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors));
|
||||
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
|
||||
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, createExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
|
||||
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
|
||||
const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments));
|
||||
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
|
||||
@@ -198,10 +198,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
} else if (typeof selector === 'string') {
|
||||
informOnce(selector);
|
||||
} else {
|
||||
if (typeof selector.scheme === 'undefined') {
|
||||
const filter = selector as vscode.DocumentFilter; // TODO: microsoft/TypeScript#42768
|
||||
if (typeof filter.scheme === 'undefined') {
|
||||
informOnce(selector);
|
||||
}
|
||||
if (!extension.enableProposedApi && typeof selector.exclusive === 'boolean') {
|
||||
if (!extension.enableProposedApi && typeof filter.exclusive === 'boolean') {
|
||||
throwProposedApiError(extension);
|
||||
}
|
||||
}
|
||||
@@ -217,7 +218,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
return extHostAuthentication.onDidChangeSessions;
|
||||
},
|
||||
registerAuthenticationProvider(id: string, label: string, provider: vscode.AuthenticationProvider, options?: vscode.AuthenticationProviderOptions): vscode.Disposable {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostAuthentication.registerAuthenticationProvider(id, label, provider, options);
|
||||
},
|
||||
get onDidChangeAuthenticationProviders(): Event<vscode.AuthenticationProvidersChangeEvent> {
|
||||
@@ -230,7 +230,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
},
|
||||
logout(providerId: string, sessionId: string): Thenable<void> {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostAuthentication.logout(providerId, sessionId);
|
||||
return extHostAuthentication.removeSession(providerId, sessionId);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -262,7 +262,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
registerDiffInformationCommand: (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise<any> => {
|
||||
const activeTextEditor = extHostEditors.getActiveTextEditor();
|
||||
const activeTextEditor = extHostDocumentsAndEditors.activeEditor(true);
|
||||
if (!activeTextEditor) {
|
||||
extHostLogService.warn('Cannot execute ' + id + ' because there is no active text editor.');
|
||||
return undefined;
|
||||
@@ -288,9 +288,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
get appName() { return initData.environment.appName; },
|
||||
get appRoot() { return initData.environment.appRoot?.fsPath ?? ''; },
|
||||
get uriScheme() { return initData.environment.appUriScheme; },
|
||||
get clipboard(): vscode.Clipboard {
|
||||
return extHostClipboard;
|
||||
},
|
||||
get clipboard(): vscode.Clipboard { return extHostClipboard.value; },
|
||||
get shell() {
|
||||
return extHostTerminalService.getDefaultShell(false, configProvider);
|
||||
},
|
||||
@@ -340,13 +338,17 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.runTests(provider);
|
||||
},
|
||||
publishTestResult(results, persist = true) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.publishExtensionProvidedResults(results, persist);
|
||||
},
|
||||
get onDidChangeTestResults() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.onLastResultsChanged;
|
||||
return extHostTesting.onResultsChanged;
|
||||
},
|
||||
get testResults() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.lastResults;
|
||||
return extHostTesting.results;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -411,6 +413,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
registerEvaluatableExpressionProvider(selector: vscode.DocumentSelector, provider: vscode.EvaluatableExpressionProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerEvaluatableExpressionProvider(extension, checkSelector(selector), provider, extension.identifier);
|
||||
},
|
||||
registerInlineValuesProvider(selector: vscode.DocumentSelector, provider: vscode.InlineValuesProvider): vscode.Disposable {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostLanguageFeatures.registerInlineValuesProvider(extension, checkSelector(selector), provider, extension.identifier);
|
||||
},
|
||||
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider);
|
||||
},
|
||||
@@ -570,7 +576,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
showSaveDialog(options) {
|
||||
return extHostDialogs.showSaveDialog(options);
|
||||
},
|
||||
createStatusBarItem(alignmentOrOptions?: vscode.StatusBarAlignment | vscode.window.StatusBarItemOptions, priority?: number): vscode.StatusBarItem {
|
||||
createStatusBarItem(alignmentOrOptions?: vscode.StatusBarAlignment | vscode.StatusBarItemOptions, priority?: number): vscode.StatusBarItem {
|
||||
let id: string;
|
||||
let name: string;
|
||||
let alignment: number | undefined;
|
||||
@@ -831,7 +837,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
return extHostFileSystem.registerFileSystemProvider(extension.identifier, scheme, provider, options);
|
||||
},
|
||||
get fs() {
|
||||
return extHostConsumerFileSystem;
|
||||
return extHostConsumerFileSystem.value;
|
||||
},
|
||||
registerFileSearchProvider: (scheme: string, provider: vscode.FileSearchProvider) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
@@ -883,11 +889,23 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
onDidChangeTunnels: (listener, thisArg?, disposables?) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables);
|
||||
|
||||
},
|
||||
registerTimelineProvider: (scheme: string | string[], provider: vscode.TimelineProvider) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTimeline.registerTimelineProvider(scheme, provider, extension.identifier, extHostCommands.converter);
|
||||
},
|
||||
get trustState() {
|
||||
checkProposedApiEnabled(extension);
|
||||
checkRequiresWorkspaceTrust(extension);
|
||||
return extHostWorkspace.trustState;
|
||||
},
|
||||
requireWorkspaceTrust: (modal?: boolean) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
checkRequiresWorkspaceTrust(extension);
|
||||
return extHostWorkspace.requireWorkspaceTrust(modal);
|
||||
},
|
||||
onDidChangeWorkspaceTrustState: (listener, thisArgs?, disposables?) => {
|
||||
return extHostWorkspace.onDidChangeWorkspaceTrustState(listener, thisArgs, disposables);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1119,6 +1137,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall,
|
||||
CancellationError: errors.CancellationError,
|
||||
CancellationTokenSource: CancellationTokenSource,
|
||||
CandidatePortSource: CandidatePortSource,
|
||||
CodeAction: extHostTypes.CodeAction,
|
||||
CodeActionKind: extHostTypes.CodeActionKind,
|
||||
CodeActionTrigger: extHostTypes.CodeActionTrigger,
|
||||
@@ -1155,6 +1174,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
EndOfLine: extHostTypes.EndOfLine,
|
||||
EnvironmentVariableMutatorType: extHostTypes.EnvironmentVariableMutatorType,
|
||||
EvaluatableExpression: extHostTypes.EvaluatableExpression,
|
||||
InlineValueText: extHostTypes.InlineValueText,
|
||||
InlineValueVariableLookup: extHostTypes.InlineValueVariableLookup,
|
||||
InlineValueEvaluatableExpression: extHostTypes.InlineValueEvaluatableExpression,
|
||||
EventEmitter: Emitter,
|
||||
ExtensionKind: extHostTypes.ExtensionKind,
|
||||
ExtensionMode: extHostTypes.ExtensionMode,
|
||||
@@ -1168,7 +1190,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
FunctionBreakpoint: extHostTypes.FunctionBreakpoint,
|
||||
Hover: extHostTypes.Hover,
|
||||
IndentAction: languageConfiguration.IndentAction,
|
||||
InlineHint: extHostTypes.InlineHint,
|
||||
Location: extHostTypes.Location,
|
||||
MarkdownString: extHostTypes.MarkdownString,
|
||||
OverviewRulerLane: OverviewRulerLane,
|
||||
@@ -1218,6 +1239,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
ViewColumn: extHostTypes.ViewColumn,
|
||||
WorkspaceEdit: extHostTypes.WorkspaceEdit,
|
||||
// proposed api types
|
||||
get InlineHint() {
|
||||
return extHostTypes.InlineHint;
|
||||
},
|
||||
get InlineHintKind() {
|
||||
return extHostTypes.InlineHintKind;
|
||||
},
|
||||
get RemoteAuthorityResolverError() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.RemoteAuthorityResolverError;
|
||||
@@ -1238,18 +1265,23 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TimelineItem;
|
||||
},
|
||||
get CellKind() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.CellKind;
|
||||
get NotebookCellRange() {
|
||||
return extHostTypes.NotebookCellRange;
|
||||
},
|
||||
get CellOutputKind() {
|
||||
get NotebookCellKind() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.CellOutputKind;
|
||||
return extHostTypes.NotebookCellKind;
|
||||
},
|
||||
get NotebookCellRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellRunState;
|
||||
},
|
||||
get NotebookDocumentMetadata() {
|
||||
return extHostTypes.NotebookDocumentMetadata;
|
||||
},
|
||||
get NotebookCellMetadata() {
|
||||
return extHostTypes.NotebookCellMetadata;
|
||||
},
|
||||
get NotebookRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookRunState;
|
||||
@@ -1282,19 +1314,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TestMessageSeverity;
|
||||
},
|
||||
get TestState() {
|
||||
get WorkspaceTrustState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TestState;
|
||||
},
|
||||
return extHostTypes.WorkspaceTrustState;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
class Extension<T> implements vscode.Extension<T> {
|
||||
|
||||
private _extensionService: IExtHostExtensionService;
|
||||
private _originExtensionId: ExtensionIdentifier;
|
||||
private _identifier: ExtensionIdentifier;
|
||||
#extensionService: IExtHostExtensionService;
|
||||
#originExtensionId: ExtensionIdentifier;
|
||||
#identifier: ExtensionIdentifier;
|
||||
|
||||
readonly id: string;
|
||||
readonly extensionUri: URI;
|
||||
@@ -1303,9 +1335,9 @@ class Extension<T> implements vscode.Extension<T> {
|
||||
readonly extensionKind: vscode.ExtensionKind;
|
||||
|
||||
constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) {
|
||||
this._extensionService = extensionService;
|
||||
this._originExtensionId = originExtensionId;
|
||||
this._identifier = description.identifier;
|
||||
this.#extensionService = extensionService;
|
||||
this.#originExtensionId = originExtensionId;
|
||||
this.#identifier = description.identifier;
|
||||
this.id = description.identifier.value;
|
||||
this.extensionUri = description.extensionLocation;
|
||||
this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
|
||||
@@ -1314,17 +1346,17 @@ class Extension<T> implements vscode.Extension<T> {
|
||||
}
|
||||
|
||||
get isActive(): boolean {
|
||||
return this._extensionService.isActivated(this._identifier);
|
||||
return this.#extensionService.isActivated(this.#identifier);
|
||||
}
|
||||
|
||||
get exports(): T {
|
||||
if (this.packageJSON.api === 'none') {
|
||||
return undefined!; // Strict nulloverride - Public api
|
||||
}
|
||||
return <T>this._extensionService.getExtensionExports(this._identifier);
|
||||
return <T>this.#extensionService.getExtensionExports(this.#identifier);
|
||||
}
|
||||
|
||||
activate(): Thenable<T> {
|
||||
return this._extensionService.activateByIdWithErrors(this._identifier, { startup: false, extensionId: this._originExtensionId, activationEvent: 'api' }).then(() => this.exports);
|
||||
return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import * as tasks from 'vs/workbench/api/common/shared/tasks';
|
||||
import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views';
|
||||
import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { ITerminalDimensions, IShellLaunchConfig, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ActivationKind, ExtensionActivationError, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import * as search from 'vs/workbench/services/search/common/search';
|
||||
@@ -51,15 +50,17 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
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 { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { DebugConfigurationProviderTriggerKind, WorkspaceTrustState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync';
|
||||
import { InternalTestItem, InternalTestResults, RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { InternalTestItem, ITestState, RunTestForProviderRequest, RunTestsRequest, TestIdWithProvider, TestsDiff, ISerializedTestResults } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
|
||||
|
||||
export interface IEnvironment {
|
||||
isExtensionDevelopmentDebug: boolean;
|
||||
@@ -165,18 +166,9 @@ export interface MainThreadAuthenticationShape extends IDisposable {
|
||||
$registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): void;
|
||||
$unregisterAuthenticationProvider(id: string): void;
|
||||
$ensureProvider(id: string): Promise<void>;
|
||||
$getProviderIds(): Promise<string[]>;
|
||||
$sendDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
|
||||
$getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone?: boolean, clearSessionPreference?: boolean }): Promise<modes.AuthenticationSession | undefined>;
|
||||
$selectSession(providerId: string, providerName: string, extensionId: string, extensionName: string, potentialSessions: modes.AuthenticationSession[], scopes: string[], clearSessionPreference: boolean): Promise<modes.AuthenticationSession>;
|
||||
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$loginPrompt(providerName: string, extensionName: string): Promise<boolean>;
|
||||
$setTrustedExtensionAndAccountPreference(providerId: string, accountName: string, extensionId: string, extensionName: string, sessionId: string): Promise<void>;
|
||||
$requestNewSession(providerId: string, scopes: string[], extensionId: string, extensionName: string): Promise<void>;
|
||||
|
||||
$getSessions(providerId: string): Promise<ReadonlyArray<modes.AuthenticationSession>>;
|
||||
$login(providerId: string, scopes: string[]): Promise<modes.AuthenticationSession>;
|
||||
$logout(providerId: string, sessionId: string): Promise<void>;
|
||||
$removeSession(providerId: string, sessionId: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadSecretStateShape extends IDisposable {
|
||||
@@ -237,7 +229,6 @@ export interface MainThreadDocumentsShape extends IDisposable {
|
||||
|
||||
export interface ITextEditorConfigurationUpdate {
|
||||
tabSize?: number | 'auto';
|
||||
indentSize?: number | 'tabSize';
|
||||
insertSpaces?: boolean | 'auto';
|
||||
cursorStyle?: TextEditorCursorStyle;
|
||||
lineNumbers?: RenderLineNumbersType;
|
||||
@@ -245,7 +236,6 @@ export interface ITextEditorConfigurationUpdate {
|
||||
|
||||
export interface IResolvedTextEditorConfiguration {
|
||||
tabSize: number;
|
||||
indentSize: number;
|
||||
insertSpaces: boolean;
|
||||
cursorStyle: TextEditorCursorStyle;
|
||||
lineNumbers: RenderLineNumbersType;
|
||||
@@ -295,7 +285,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadTreeViewsShape extends IDisposable {
|
||||
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): void;
|
||||
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): Promise<void>;
|
||||
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem; }): Promise<void>;
|
||||
$reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise<void>;
|
||||
$setMessage(treeViewId: string, message: string): void;
|
||||
@@ -388,6 +378,8 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
|
||||
$registerTypeDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerInlineValuesProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void;
|
||||
$emitInlineValuesEvent(eventHandle: number, event?: any): void;
|
||||
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
@@ -467,6 +459,7 @@ export interface TerminalLaunchConfig {
|
||||
hideFromUser?: boolean;
|
||||
isExtensionTerminal?: boolean;
|
||||
isFeatureTerminal?: boolean;
|
||||
isExtensionOwnedTerminal?: boolean;
|
||||
}
|
||||
|
||||
export interface MainThreadTerminalServiceShape extends IDisposable {
|
||||
@@ -755,7 +748,7 @@ export interface ICellDto {
|
||||
source: string[];
|
||||
language: string;
|
||||
cellKind: CellKind;
|
||||
outputs: IProcessedOutput[];
|
||||
outputs: IOutputDto[];
|
||||
metadata?: NotebookCellMetadata;
|
||||
}
|
||||
|
||||
@@ -768,7 +761,7 @@ export type NotebookCellsSplice = [
|
||||
export type NotebookCellOutputsSplice = [
|
||||
number /* start */,
|
||||
number /* delete count */,
|
||||
IProcessedOutput[]
|
||||
IOutputDto[]
|
||||
];
|
||||
|
||||
export enum NotebookEditorRevealType {
|
||||
@@ -788,7 +781,7 @@ export interface INotebookDocumentShowOptions {
|
||||
export type INotebookCellStatusBarEntryDto = Dto<INotebookCellStatusBarEntry>;
|
||||
|
||||
export interface MainThreadNotebookShape extends IDisposable {
|
||||
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, options: {
|
||||
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, options: {
|
||||
transientOutputs: boolean;
|
||||
transientMetadata: TransientMetadata;
|
||||
viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; };
|
||||
@@ -798,9 +791,8 @@ export interface MainThreadNotebookShape extends IDisposable {
|
||||
$registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void>;
|
||||
$unregisterNotebookKernelProvider(handle: number): Promise<void>;
|
||||
$onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void;
|
||||
$trySaveDocument(uri: UriComponents): Promise<boolean>;
|
||||
$tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise<boolean>;
|
||||
$updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise<void>;
|
||||
$spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise<void>;
|
||||
$postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean>;
|
||||
$setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise<void>;
|
||||
$tryOpenDocument(uriComponents: UriComponents, viewType?: string): Promise<URI>;
|
||||
@@ -809,8 +801,6 @@ export interface MainThreadNotebookShape extends IDisposable {
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void;
|
||||
$removeNotebookEditorDecorationType(key: string): void;
|
||||
$trySetDecorations(id: string, range: ICellRange, decorationKey: string): void;
|
||||
$onUndoableContentChange(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
|
||||
$onContentChange(resource: UriComponents, viewType: string): void;
|
||||
}
|
||||
|
||||
export interface MainThreadUrlsShape extends IDisposable {
|
||||
@@ -844,6 +834,7 @@ export interface MainThreadWorkspaceShape extends IDisposable {
|
||||
$saveAll(includeUntitled?: boolean): Promise<boolean>;
|
||||
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string; }[]): Promise<void>;
|
||||
$resolveProxy(url: string): Promise<string | undefined>;
|
||||
$requireWorkspaceTrust(modal?: boolean): Promise<WorkspaceTrustState>
|
||||
}
|
||||
|
||||
export interface IFileChangeDto {
|
||||
@@ -1003,14 +994,21 @@ export interface MainThreadWindowShape extends IDisposable {
|
||||
$asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise<UriComponents>;
|
||||
}
|
||||
|
||||
export enum CandidatePortSource {
|
||||
None = 0,
|
||||
Process = 1,
|
||||
Output = 2
|
||||
}
|
||||
|
||||
export interface MainThreadTunnelServiceShape extends IDisposable {
|
||||
$openTunnel(tunnelOptions: TunnelOptions, source: string | undefined): Promise<TunnelDto | undefined>;
|
||||
$closeTunnel(remote: { host: string, port: number }): Promise<void>;
|
||||
$getTunnels(): Promise<TunnelDescription[]>;
|
||||
$setTunnelProvider(features: TunnelProviderFeatures): Promise<void>;
|
||||
$setCandidateFinder(): Promise<void>;
|
||||
$setRemoteTunnelService(processId: number): Promise<void>;
|
||||
$setCandidateFilter(): Promise<void>;
|
||||
$onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<void>;
|
||||
$setCandidatePortSource(source: CandidatePortSource): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadTimelineShape extends IDisposable {
|
||||
@@ -1106,9 +1104,10 @@ export interface ExtHostTreeViewsShape {
|
||||
}
|
||||
|
||||
export interface ExtHostWorkspaceShape {
|
||||
$initializeWorkspace(workspace: IWorkspaceData | null): void;
|
||||
$initializeWorkspace(workspace: IWorkspaceData | null, trustState: WorkspaceTrustState): void;
|
||||
$acceptWorkspaceData(workspace: IWorkspaceData | null): void;
|
||||
$handleTextSearchResult(result: search.IRawFileMatch2, requestId: number): void;
|
||||
$onDidChangeWorkspaceTrustState(state: WorkspaceTrustStateChangeEvent): void;
|
||||
}
|
||||
|
||||
export interface ExtHostFileSystemInfoShape {
|
||||
@@ -1137,11 +1136,10 @@ export interface ExtHostLabelServiceShape {
|
||||
}
|
||||
|
||||
export interface ExtHostAuthenticationShape {
|
||||
$getSessions(id: string): Promise<ReadonlyArray<modes.AuthenticationSession>>;
|
||||
$getSessionAccessToken(id: string, sessionId: string): Promise<string>;
|
||||
$login(id: string, scopes: string[]): Promise<modes.AuthenticationSession>;
|
||||
$logout(id: string, sessionId: string): Promise<void>;
|
||||
$onDidChangeAuthenticationSessions(id: string, label: string, event: modes.AuthenticationSessionsChangeEvent): Promise<void>;
|
||||
$getSessions(id: string, scopes?: string[]): Promise<ReadonlyArray<modes.AuthenticationSession>>;
|
||||
$createSession(id: string, scopes: string[]): Promise<modes.AuthenticationSession>;
|
||||
$removeSession(id: string, sessionId: string): Promise<void>;
|
||||
$onDidChangeAuthenticationSessions(id: string, label: string): Promise<void>;
|
||||
$onDidChangeAuthenticationProviders(added: modes.AuthenticationProviderInformation[], removed: modes.AuthenticationProviderInformation[]): Promise<void>;
|
||||
$setProviders(providers: modes.AuthenticationProviderInformation[]): Promise<void>;
|
||||
}
|
||||
@@ -1313,9 +1311,10 @@ export interface ISignatureHelpContextDto {
|
||||
export interface IInlineHintDto {
|
||||
text: string;
|
||||
range: IRange;
|
||||
hoverMessage?: string;
|
||||
kind: modes.InlineHintKind;
|
||||
whitespaceBefore?: boolean;
|
||||
whitespaceAfter?: boolean;
|
||||
hoverMessage?: string;
|
||||
}
|
||||
|
||||
export interface IInlineHintsDto {
|
||||
@@ -1478,6 +1477,10 @@ export interface ILinkedEditingRangesDto {
|
||||
wordPattern?: IRegExpDto;
|
||||
}
|
||||
|
||||
export interface IInlineValueContextDto {
|
||||
stoppedLocation: IRange;
|
||||
}
|
||||
|
||||
export interface ExtHostLanguageFeaturesShape {
|
||||
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
|
||||
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<ICodeLensListDto | undefined>;
|
||||
@@ -1489,6 +1492,7 @@ export interface ExtHostLanguageFeaturesShape {
|
||||
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IDefinitionLinkDto[]>;
|
||||
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.Hover | undefined>;
|
||||
$provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.EvaluatableExpression | undefined>;
|
||||
$provideInlineValues(handle: number, resource: UriComponents, range: IRange, context: modes.InlineValueContext, token: CancellationToken): Promise<modes.InlineValue[] | undefined>;
|
||||
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DocumentHighlight[] | undefined>;
|
||||
$provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<ILinkedEditingRangesDto | undefined>;
|
||||
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<ILocationDto[] | undefined>;
|
||||
@@ -1681,7 +1685,7 @@ export type IDebugSessionDto = IDebugSessionFullDto | DebugSessionUUID;
|
||||
|
||||
export interface ExtHostDebugServiceShape {
|
||||
$substituteVariables(folder: UriComponents | undefined, config: IConfig): Promise<IConfig>;
|
||||
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined>;
|
||||
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined>;
|
||||
$startDASession(handle: number, session: IDebugSessionDto): Promise<void>;
|
||||
$stopDASession(handle: number): Promise<void>;
|
||||
$sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
|
||||
@@ -1739,17 +1743,11 @@ export interface ExtHostCommentsShape {
|
||||
}
|
||||
|
||||
export interface INotebookSelectionChangeEvent {
|
||||
// handles
|
||||
selections: number[];
|
||||
}
|
||||
|
||||
export interface INotebookCellVisibleRange {
|
||||
start: number;
|
||||
end: number;
|
||||
selections: ICellRange[];
|
||||
}
|
||||
|
||||
export interface INotebookVisibleRangesEvent {
|
||||
ranges: INotebookCellVisibleRange[];
|
||||
ranges: ICellRange[];
|
||||
}
|
||||
|
||||
export interface INotebookEditorPropertiesChangeData {
|
||||
@@ -1767,14 +1765,13 @@ export interface INotebookModelAddedData {
|
||||
cells: IMainCellDto[],
|
||||
viewType: string;
|
||||
metadata?: NotebookDocumentMetadata;
|
||||
attachedEditor?: { id: string; selections: number[]; visibleRanges: ICellRange[] }
|
||||
contentOptions: { transientOutputs: boolean; transientMetadata: TransientMetadata; }
|
||||
}
|
||||
|
||||
export interface INotebookEditorAddData {
|
||||
id: string;
|
||||
documentUri: UriComponents;
|
||||
selections: number[];
|
||||
selections: ICellRange[];
|
||||
visibleRanges: ICellRange[];
|
||||
}
|
||||
|
||||
@@ -1787,27 +1784,38 @@ export interface INotebookDocumentsAndEditorsDelta {
|
||||
visibleEditors?: string[];
|
||||
}
|
||||
|
||||
export interface INotebookKernelInfoDto2 {
|
||||
id?: string;
|
||||
friendlyId: string;
|
||||
label: string;
|
||||
extension: ExtensionIdentifier;
|
||||
extensionLocation: UriComponents;
|
||||
providerHandle?: number;
|
||||
description?: string;
|
||||
detail?: string;
|
||||
isPreferred?: boolean;
|
||||
preloads?: UriComponents[];
|
||||
supportedLanguages?: string[]
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookShape {
|
||||
$resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto>;
|
||||
$resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise<void>;
|
||||
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined }): void;
|
||||
$provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise<INotebookKernelInfoDto2[]>;
|
||||
$resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise<void>;
|
||||
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
|
||||
$cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
|
||||
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
|
||||
$openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto>;
|
||||
$saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean>;
|
||||
$saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean>;
|
||||
$backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string | undefined>;
|
||||
$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void;
|
||||
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined }): void;
|
||||
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
|
||||
$backupNotebook(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string>;
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void;
|
||||
$acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void;
|
||||
$acceptModelSaved(uriComponents: UriComponents): void;
|
||||
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void;
|
||||
$acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void;
|
||||
$acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void;
|
||||
$undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void>;
|
||||
$redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void>;
|
||||
|
||||
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void;
|
||||
}
|
||||
|
||||
export interface ExtHostStorageShape {
|
||||
@@ -1839,12 +1847,12 @@ export const enum ExtHostTestingResource {
|
||||
}
|
||||
|
||||
export interface ExtHostTestingShape {
|
||||
$runTestsForProvider(req: RunTestForProviderRequest, token: CancellationToken): Promise<RunTestsResult>;
|
||||
$runTestsForProvider(req: RunTestForProviderRequest, token: CancellationToken): Promise<void>;
|
||||
$subscribeToTests(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
$unsubscribeFromTests(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
$lookupTest(test: TestIdWithProvider): Promise<InternalTestItem | undefined>;
|
||||
$acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
|
||||
$publishTestResults(results: InternalTestResults): void;
|
||||
$publishTestResults(results: ISerializedTestResults[]): void;
|
||||
}
|
||||
|
||||
export interface MainThreadTestingShape {
|
||||
@@ -1853,7 +1861,10 @@ export interface MainThreadTestingShape {
|
||||
$subscribeToDiffs(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
$unsubscribeFromDiffs(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
$publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
|
||||
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<RunTestsResult>;
|
||||
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void;
|
||||
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>;
|
||||
$publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void;
|
||||
$retireTest(extId: string): void;
|
||||
}
|
||||
|
||||
// --- proxy identifiers
|
||||
|
||||
@@ -117,7 +117,7 @@ const newCommands: ApiCommand[] = [
|
||||
// -- selection range
|
||||
new ApiCommand(
|
||||
'vscode.executeSelectionRangeProvider', '_executeSelectionRangeProvider', 'Execute selection range provider.',
|
||||
[ApiCommandArgument.Uri, new ApiCommandArgument<types.Position[], IPosition[]>('position', 'A positions in a text document', v => Array.isArray(v) && v.every(v => types.Position.isPosition(v)), v => v.map(typeConverters.Position.from))],
|
||||
[ApiCommandArgument.Uri, new ApiCommandArgument<types.Position[], IPosition[]>('position', 'A position in a text document', v => Array.isArray(v) && v.every(v => types.Position.isPosition(v)), v => v.map(typeConverters.Position.from))],
|
||||
new ApiCommandResult<IRange[][], types.SelectionRange[]>('A promise that resolves to an array of ranges.', result => {
|
||||
return result.map(ranges => {
|
||||
let node: types.SelectionRange | undefined;
|
||||
@@ -359,9 +359,17 @@ const newCommands: ApiCommand[] = [
|
||||
};
|
||||
}))
|
||||
),
|
||||
// --- debug support
|
||||
new ApiCommand(
|
||||
'vscode.executeInlineValueProvider', '_executeInlineValueProvider', 'Execute inline value provider',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Range],
|
||||
new ApiCommandResult<modes.InlineValue[], vscode.InlineValue[]>('A promise that resolves to an array of InlineValue objects', result => {
|
||||
return result.map(typeConverters.InlineValue.to);
|
||||
})
|
||||
),
|
||||
// --- open'ish commands
|
||||
new ApiCommand(
|
||||
'vscode.open', '_workbench.open', 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.',
|
||||
'vscode.open', '_workbench.open', 'Opens the provided resource in the editor. Can be a text or binary file, or an http(s) URL. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.',
|
||||
[
|
||||
ApiCommandArgument.Uri,
|
||||
new ApiCommandArgument<vscode.ViewColumn | typeConverters.TextEditorOpenOptions | undefined, [number?, ITextEditorOptions?] | undefined>('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions',
|
||||
@@ -388,7 +396,7 @@ const newCommands: ApiCommand[] = [
|
||||
'vscode.diff', '_workbench.diff', 'Opens the provided resources in the diff editor to compare their contents.',
|
||||
[
|
||||
ApiCommandArgument.Uri.with('left', 'Left-hand side resource of the diff editor'),
|
||||
ApiCommandArgument.Uri.with('right', 'Rigth-hand side resource of the diff editor'),
|
||||
ApiCommandArgument.Uri.with('right', 'Right-hand side resource of the diff editor'),
|
||||
ApiCommandArgument.String.with('title', 'Human readable title for the diff editor').optional(),
|
||||
new ApiCommandArgument<typeConverters.TextEditorOpenOptions | undefined, [number?, ITextEditorOptions?] | undefined>('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions',
|
||||
v => v === undefined || typeof v === 'object',
|
||||
|
||||
@@ -83,54 +83,17 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
|
||||
private async _getSession(requestingExtension: IExtensionDescription, extensionId: string, providerId: string, scopes: string[], options: vscode.AuthenticationGetSessionOptions = {}): Promise<vscode.AuthenticationSession | undefined> {
|
||||
await this._proxy.$ensureProvider(providerId);
|
||||
const providerData = this._authenticationProviders.get(providerId);
|
||||
const extensionName = requestingExtension.displayName || requestingExtension.name;
|
||||
|
||||
if (!providerData) {
|
||||
return this._proxy.$getSession(providerId, scopes, extensionId, extensionName, options);
|
||||
}
|
||||
|
||||
const orderedScopes = scopes.sort().join(' ');
|
||||
const sessions = (await providerData.provider.getSessions()).filter(session => session.scopes.slice().sort().join(' ') === orderedScopes);
|
||||
|
||||
let session: vscode.AuthenticationSession | undefined = undefined;
|
||||
if (sessions.length) {
|
||||
if (!providerData.options.supportsMultipleAccounts) {
|
||||
session = sessions[0];
|
||||
const allowed = await this._proxy.$getSessionsPrompt(providerId, session.account.label, providerData.label, extensionId, extensionName);
|
||||
if (!allowed) {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
} else {
|
||||
// On renderer side, confirm consent, ask user to choose between accounts if multiple sessions are valid
|
||||
const selected = await this._proxy.$selectSession(providerId, providerData.label, extensionId, extensionName, sessions, scopes, !!options.clearSessionPreference);
|
||||
session = sessions.find(session => session.id === selected.id);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (options.createIfNone) {
|
||||
const isAllowed = await this._proxy.$loginPrompt(providerData.label, extensionName);
|
||||
if (!isAllowed) {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
|
||||
session = await providerData.provider.login(scopes);
|
||||
await this._proxy.$setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id);
|
||||
} else {
|
||||
await this._proxy.$requestNewSession(providerId, scopes, extensionId, extensionName);
|
||||
}
|
||||
}
|
||||
|
||||
return session;
|
||||
return this._proxy.$getSession(providerId, scopes, extensionId, extensionName, options);
|
||||
}
|
||||
|
||||
async logout(providerId: string, sessionId: string): Promise<void> {
|
||||
async removeSession(providerId: string, sessionId: string): Promise<void> {
|
||||
const providerData = this._authenticationProviders.get(providerId);
|
||||
if (!providerData) {
|
||||
return this._proxy.$logout(providerId, sessionId);
|
||||
return this._proxy.$removeSession(providerId, sessionId);
|
||||
}
|
||||
|
||||
return providerData.provider.logout(sessionId);
|
||||
return providerData.provider.removeSession(sessionId);
|
||||
}
|
||||
|
||||
registerAuthenticationProvider(id: string, label: string, provider: vscode.AuthenticationProvider, options?: vscode.AuthenticationProviderOptions): vscode.Disposable {
|
||||
@@ -148,7 +111,11 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
}
|
||||
|
||||
const listener = provider.onDidChangeSessions(e => {
|
||||
this._proxy.$sendDidChangeSessions(id, e);
|
||||
this._proxy.$sendDidChangeSessions(id, {
|
||||
added: e.added ?? [],
|
||||
changed: e.changed ?? [],
|
||||
removed: e.removed ?? []
|
||||
});
|
||||
});
|
||||
|
||||
this._proxy.$registerAuthenticationProvider(id, label, options?.supportsMultipleAccounts ?? false);
|
||||
@@ -166,50 +133,35 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
});
|
||||
}
|
||||
|
||||
$login(providerId: string, scopes: string[]): Promise<modes.AuthenticationSession> {
|
||||
$createSession(providerId: string, scopes: string[]): Promise<modes.AuthenticationSession> {
|
||||
const providerData = this._authenticationProviders.get(providerId);
|
||||
if (providerData) {
|
||||
return Promise.resolve(providerData.provider.login(scopes));
|
||||
return Promise.resolve(providerData.provider.createSession(scopes));
|
||||
}
|
||||
|
||||
throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
|
||||
}
|
||||
|
||||
$logout(providerId: string, sessionId: string): Promise<void> {
|
||||
$removeSession(providerId: string, sessionId: string): Promise<void> {
|
||||
const providerData = this._authenticationProviders.get(providerId);
|
||||
if (providerData) {
|
||||
return Promise.resolve(providerData.provider.logout(sessionId));
|
||||
return Promise.resolve(providerData.provider.removeSession(sessionId));
|
||||
}
|
||||
|
||||
throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
|
||||
}
|
||||
|
||||
$getSessions(providerId: string): Promise<ReadonlyArray<modes.AuthenticationSession>> {
|
||||
$getSessions(providerId: string, scopes?: string[]): Promise<ReadonlyArray<modes.AuthenticationSession>> {
|
||||
const providerData = this._authenticationProviders.get(providerId);
|
||||
if (providerData) {
|
||||
return Promise.resolve(providerData.provider.getSessions());
|
||||
return Promise.resolve(providerData.provider.getSessions(scopes));
|
||||
}
|
||||
|
||||
throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
|
||||
}
|
||||
|
||||
async $getSessionAccessToken(providerId: string, sessionId: string): Promise<string> {
|
||||
const providerData = this._authenticationProviders.get(providerId);
|
||||
if (providerData) {
|
||||
const sessions = await providerData.provider.getSessions();
|
||||
const session = sessions.find(session => session.id === sessionId);
|
||||
if (session) {
|
||||
return session.accessToken;
|
||||
}
|
||||
|
||||
throw new Error(`Unable to find session with id: ${sessionId}`);
|
||||
}
|
||||
|
||||
throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
|
||||
}
|
||||
|
||||
$onDidChangeAuthenticationSessions(id: string, label: string, event: modes.AuthenticationSessionsChangeEvent) {
|
||||
this._onDidChangeSessions.fire({ provider: { id, label }, ...event });
|
||||
$onDidChangeAuthenticationSessions(id: string, label: string) {
|
||||
this._onDidChangeSessions.fire({ provider: { id, label } });
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,22 +3,22 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IMainContext, MainContext, MainThreadClipboardShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
export class ExtHostClipboard implements vscode.Clipboard {
|
||||
export class ExtHostClipboard {
|
||||
|
||||
private readonly _proxy: MainThreadClipboardShape;
|
||||
readonly value: vscode.Clipboard;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadClipboard);
|
||||
}
|
||||
|
||||
readText(): Promise<string> {
|
||||
return this._proxy.$readText();
|
||||
}
|
||||
|
||||
writeText(value: string): Promise<void> {
|
||||
return this._proxy.$writeText(value);
|
||||
const proxy = mainContext.getProxy(MainContext.MainThreadClipboard);
|
||||
this.value = Object.freeze({
|
||||
readText() {
|
||||
return proxy.$readText();
|
||||
},
|
||||
writeText(value: string) {
|
||||
return proxy.$writeText(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
|
||||
createWebviewEditorInset(editor: vscode.TextEditor, line: number, height: number, options: vscode.WebviewOptions | undefined, extension: IExtensionDescription): vscode.WebviewEditorInset {
|
||||
|
||||
let apiEditor: ExtHostTextEditor | undefined;
|
||||
for (const candidate of this._editors.getVisibleTextEditors()) {
|
||||
if (candidate === editor) {
|
||||
for (const candidate of this._editors.getVisibleTextEditors(true)) {
|
||||
if (candidate.value === editor) {
|
||||
apiEditor = <ExtHostTextEditor>candidate;
|
||||
break;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
|
||||
}
|
||||
};
|
||||
|
||||
this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.document.uri, line + 1, height, options || {}, extension.identifier, extension.extensionLocation);
|
||||
this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.value.document.uri, line + 1, height, options || {}, extension.identifier, extension.extensionLocation);
|
||||
this._insets.set(handle, { editor, inset, onDidReceiveMessage });
|
||||
|
||||
return inset;
|
||||
|
||||
@@ -213,6 +213,14 @@ export class ExtHostCommands implements ExtHostCommandsShape {
|
||||
try {
|
||||
return await callback.apply(thisArg, args);
|
||||
} catch (err) {
|
||||
// The indirection-command from the converter can fail when invoking the actual
|
||||
// command and in that case it is better to blame the correct command
|
||||
if (id === this.converter.delegatingCommandId) {
|
||||
const actual = this.converter.getActualCommand(...args);
|
||||
if (actual) {
|
||||
id = actual.command;
|
||||
}
|
||||
}
|
||||
this._logService.error(err, id);
|
||||
throw new Error(`Running the contributed command: '${id}' failed.`);
|
||||
}
|
||||
@@ -257,7 +265,7 @@ export const IExtHostCommands = createDecorator<IExtHostCommands>('IExtHostComma
|
||||
|
||||
export class CommandsConverter {
|
||||
|
||||
private readonly _delegatingCommandId: string;
|
||||
readonly delegatingCommandId: string = `_vscode_delegate_cmd_${Date.now().toString(36)}`;
|
||||
private readonly _cache = new Map<number, vscode.Command>();
|
||||
private _cachIdPool = 0;
|
||||
|
||||
@@ -267,8 +275,7 @@ export class CommandsConverter {
|
||||
private readonly _lookupApiCommand: (id: string) => ApiCommand | undefined,
|
||||
private readonly _logService: ILogService
|
||||
) {
|
||||
this._delegatingCommandId = `_vscode_delegate_cmd_${Date.now().toString(36)}`;
|
||||
this._commands.registerCommand(true, this._delegatingCommandId, this._executeConvertedCommand, this);
|
||||
this._commands.registerCommand(true, this.delegatingCommandId, this._executeConvertedCommand, this);
|
||||
}
|
||||
|
||||
toInternal(command: vscode.Command, disposables: DisposableStore): ICommandDto;
|
||||
@@ -311,7 +318,7 @@ export class CommandsConverter {
|
||||
}));
|
||||
result.$ident = id;
|
||||
|
||||
result.id = this._delegatingCommandId;
|
||||
result.id = this.delegatingCommandId;
|
||||
result.arguments = [id];
|
||||
|
||||
this._logService.trace('CommandsConverter#CREATE', command.command, id);
|
||||
@@ -335,8 +342,13 @@ export class CommandsConverter {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getActualCommand(...args: any[]): vscode.Command | undefined {
|
||||
return this._cache.get(args[0]);
|
||||
}
|
||||
|
||||
private _executeConvertedCommand<R>(...args: any[]): Promise<R> {
|
||||
const actualCmd = this._cache.get(args[0]);
|
||||
const actualCmd = this.getActualCommand(...args);
|
||||
this._logService.trace('CommandsConverter#EXECUTE', args[0], actualCmd ? actualCmd.command : 'MISSING');
|
||||
|
||||
if (!actualCmd) {
|
||||
|
||||
@@ -89,7 +89,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
get onDidReceiveDebugSessionCustomEvent(): Event<vscode.DebugSessionCustomEvent> { return this._onDidReceiveDebugSessionCustomEvent.event; }
|
||||
|
||||
private _activeDebugConsole: ExtHostDebugConsole;
|
||||
get activeDebugConsole(): ExtHostDebugConsole { return this._activeDebugConsole; }
|
||||
get activeDebugConsole(): vscode.DebugConsole { return this._activeDebugConsole.value; }
|
||||
|
||||
private _breakpoints: Map<string, vscode.Breakpoint>;
|
||||
private _breakpointEventsActive: boolean;
|
||||
@@ -361,7 +361,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
|
||||
// RPC methods (ExtHostDebugServiceShape)
|
||||
|
||||
public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined> {
|
||||
public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -911,20 +911,20 @@ export class ExtHostDebugSession implements vscode.DebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostDebugConsole implements vscode.DebugConsole {
|
||||
export class ExtHostDebugConsole {
|
||||
|
||||
private _debugServiceProxy: MainThreadDebugServiceShape;
|
||||
readonly value: vscode.DebugConsole;
|
||||
|
||||
constructor(proxy: MainThreadDebugServiceShape) {
|
||||
this._debugServiceProxy = proxy;
|
||||
}
|
||||
|
||||
append(value: string): void {
|
||||
this._debugServiceProxy.$appendDebugConsole(value);
|
||||
}
|
||||
|
||||
appendLine(value: string): void {
|
||||
this.append(value + '\n');
|
||||
this.value = Object.freeze({
|
||||
append(value: string): void {
|
||||
proxy.$appendDebugConsole(value);
|
||||
},
|
||||
appendLine(value: string): void {
|
||||
this.append(value + '\n');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,6 +945,9 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
|
||||
getConfigurationValue: (folderUri: URI | undefined, section: string): string | undefined => {
|
||||
return configurationService.getConfiguration(undefined, folderUri).get<string>(section);
|
||||
},
|
||||
getAppRoot: (): string | undefined => {
|
||||
return env ? env['VSCODE_CWD'] : undefined;
|
||||
},
|
||||
getExecPath: (): string | undefined => {
|
||||
return env ? env['VSCODE_EXEC_PATH'] : undefined;
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
|
||||
class Reference<T> {
|
||||
private _count = 0;
|
||||
@@ -49,13 +50,13 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
|
||||
|
||||
private readonly _onDidAddDocuments = new Emitter<ExtHostDocumentData[]>();
|
||||
private readonly _onDidRemoveDocuments = new Emitter<ExtHostDocumentData[]>();
|
||||
private readonly _onDidChangeVisibleTextEditors = new Emitter<ExtHostTextEditor[]>();
|
||||
private readonly _onDidChangeActiveTextEditor = new Emitter<ExtHostTextEditor | undefined>();
|
||||
private readonly _onDidChangeVisibleTextEditors = new Emitter<vscode.TextEditor[]>();
|
||||
private readonly _onDidChangeActiveTextEditor = new Emitter<vscode.TextEditor | undefined>();
|
||||
|
||||
readonly onDidAddDocuments: Event<ExtHostDocumentData[]> = this._onDidAddDocuments.event;
|
||||
readonly onDidRemoveDocuments: Event<ExtHostDocumentData[]> = this._onDidRemoveDocuments.event;
|
||||
readonly onDidChangeVisibleTextEditors: Event<ExtHostTextEditor[]> = this._onDidChangeVisibleTextEditors.event;
|
||||
readonly onDidChangeActiveTextEditor: Event<ExtHostTextEditor | undefined> = this._onDidChangeActiveTextEditor.event;
|
||||
readonly onDidChangeVisibleTextEditors: Event<vscode.TextEditor[]> = this._onDidChangeVisibleTextEditors.event;
|
||||
readonly onDidChangeActiveTextEditor: Event<vscode.TextEditor | undefined> = this._onDidChangeActiveTextEditor.event;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService private readonly _extHostRpc: IExtHostRpcService,
|
||||
@@ -135,7 +136,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
|
||||
data.id,
|
||||
this._extHostRpc.getProxy(MainContext.MainThreadTextEditors),
|
||||
this._logService,
|
||||
documentData,
|
||||
new Lazy(() => documentData.document),
|
||||
data.selections.map(typeConverters.Selection.to),
|
||||
data.options,
|
||||
data.visibleRanges.map(range => typeConverters.Range.to(range)),
|
||||
@@ -162,7 +163,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
|
||||
}
|
||||
|
||||
if (delta.removedEditors || delta.addedEditors) {
|
||||
this._onDidChangeVisibleTextEditors.fire(this.allEditors());
|
||||
this._onDidChangeVisibleTextEditors.fire(this.allEditors().map(editor => editor.value));
|
||||
}
|
||||
if (delta.newActiveEditor !== undefined) {
|
||||
this._onDidChangeActiveTextEditor.fire(this.activeEditor());
|
||||
@@ -181,11 +182,17 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
|
||||
return this._editors.get(id);
|
||||
}
|
||||
|
||||
activeEditor(): ExtHostTextEditor | undefined {
|
||||
activeEditor(): vscode.TextEditor | undefined;
|
||||
activeEditor(internal: true): ExtHostTextEditor | undefined;
|
||||
activeEditor(internal?: true): vscode.TextEditor | ExtHostTextEditor | undefined {
|
||||
if (!this._activeEditorId) {
|
||||
return undefined;
|
||||
}
|
||||
const editor = this._editors.get(this._activeEditorId);
|
||||
if (internal) {
|
||||
return editor;
|
||||
} else {
|
||||
return this._editors.get(this._activeEditorId);
|
||||
return editor?.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -388,6 +388,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
const extensionMode = extensionDescription.isUnderDevelopment
|
||||
? (this._initData.environment.extensionTestsLocationURI ? ExtensionMode.Test : ExtensionMode.Development)
|
||||
: ExtensionMode.Production;
|
||||
const installAge = Date.now() - new Date(this._initData.telemetryInfo.firstSessionDate).getTime();
|
||||
|
||||
this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
|
||||
|
||||
@@ -412,10 +413,22 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
get storageUri() { return that._storagePath.workspaceValue(extensionDescription); },
|
||||
get globalStorageUri() { return that._storagePath.globalValue(extensionDescription); },
|
||||
get extensionMode() { return extensionMode; },
|
||||
get extensionId() {
|
||||
checkProposedApiEnabled(extensionDescription);
|
||||
return extensionDescription.identifier.value;
|
||||
},
|
||||
get extensionVersion() {
|
||||
checkProposedApiEnabled(extensionDescription);
|
||||
return extensionDescription.version;
|
||||
},
|
||||
get extensionRuntime() {
|
||||
checkProposedApiEnabled(extensionDescription);
|
||||
return that.extensionRuntime;
|
||||
},
|
||||
get isNewInstall() {
|
||||
checkProposedApiEnabled(extensionDescription);
|
||||
return isNaN(installAge) ? false : installAge < 1000 * 60 * 60 * 24; // installAge is less than a day;
|
||||
},
|
||||
get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); }
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { MainThreadFileSystemShape, MainContext } from './extHost.protocol';
|
||||
import { MainContext } from './extHost.protocol';
|
||||
import * as vscode from 'vscode';
|
||||
import * as files from 'vs/platform/files/common/files';
|
||||
import { FileSystemError } from 'vs/workbench/api/common/extHostTypes';
|
||||
@@ -12,49 +12,51 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
|
||||
export class ExtHostConsumerFileSystem implements vscode.FileSystem {
|
||||
export class ExtHostConsumerFileSystem {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _proxy: MainThreadFileSystemShape;
|
||||
readonly value: vscode.FileSystem;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostFileSystemInfo private readonly _fileSystemInfo: IExtHostFileSystemInfo,
|
||||
@IExtHostFileSystemInfo fileSystemInfo: IExtHostFileSystemInfo,
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem);
|
||||
}
|
||||
const proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem);
|
||||
|
||||
stat(uri: vscode.Uri): Promise<vscode.FileStat> {
|
||||
return this._proxy.$stat(uri).catch(ExtHostConsumerFileSystem._handleError);
|
||||
}
|
||||
readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
|
||||
return this._proxy.$readdir(uri).catch(ExtHostConsumerFileSystem._handleError);
|
||||
}
|
||||
createDirectory(uri: vscode.Uri): Promise<void> {
|
||||
return this._proxy.$mkdir(uri).catch(ExtHostConsumerFileSystem._handleError);
|
||||
}
|
||||
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
|
||||
return this._proxy.$readFile(uri).then(buff => buff.buffer).catch(ExtHostConsumerFileSystem._handleError);
|
||||
}
|
||||
writeFile(uri: vscode.Uri, content: Uint8Array): Promise<void> {
|
||||
return this._proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ExtHostConsumerFileSystem._handleError);
|
||||
}
|
||||
delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise<void> {
|
||||
return this._proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
|
||||
}
|
||||
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
|
||||
return this._proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
|
||||
}
|
||||
copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
|
||||
return this._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
|
||||
}
|
||||
isWritableFileSystem(scheme: string): boolean | undefined {
|
||||
const capabilities = this._fileSystemInfo.getCapabilities(scheme);
|
||||
if (typeof capabilities === 'number') {
|
||||
return !(capabilities & files.FileSystemProviderCapabilities.Readonly);
|
||||
}
|
||||
return undefined;
|
||||
this.value = Object.freeze({
|
||||
stat(uri: vscode.Uri): Promise<vscode.FileStat> {
|
||||
return proxy.$stat(uri).catch(ExtHostConsumerFileSystem._handleError);
|
||||
},
|
||||
readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
|
||||
return proxy.$readdir(uri).catch(ExtHostConsumerFileSystem._handleError);
|
||||
},
|
||||
createDirectory(uri: vscode.Uri): Promise<void> {
|
||||
return proxy.$mkdir(uri).catch(ExtHostConsumerFileSystem._handleError);
|
||||
},
|
||||
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
|
||||
return proxy.$readFile(uri).then(buff => buff.buffer).catch(ExtHostConsumerFileSystem._handleError);
|
||||
},
|
||||
writeFile(uri: vscode.Uri, content: Uint8Array): Promise<void> {
|
||||
return proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ExtHostConsumerFileSystem._handleError);
|
||||
},
|
||||
delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise<void> {
|
||||
return proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
|
||||
},
|
||||
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
|
||||
return proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
|
||||
},
|
||||
copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
|
||||
return proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
|
||||
},
|
||||
isWritableFileSystem(scheme: string): boolean | undefined {
|
||||
const capabilities = fileSystemInfo.getCapabilities(scheme);
|
||||
if (typeof capabilities === 'number') {
|
||||
return !(capabilities & files.FileSystemProviderCapabilities.Readonly);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static _handleError(err: any): never {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AsyncEmitter, Emitter, Event, IWaitUntil } from 'vs/base/common/event';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { AsyncEmitter, IWaitUntil } from 'vs/base/common/async';
|
||||
import { IRelativePattern, parse } from 'vs/base/common/glob';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
|
||||
@@ -291,6 +291,24 @@ class EvaluatableExpressionAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
class InlineValuesAdapter {
|
||||
|
||||
constructor(
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _provider: vscode.InlineValuesProvider,
|
||||
) { }
|
||||
|
||||
public provideInlineValues(resource: URI, viewPort: IRange, context: extHostProtocol.IInlineValueContextDto, token: CancellationToken): Promise<modes.InlineValue[] | undefined> {
|
||||
const doc = this._documents.getDocument(resource);
|
||||
return asPromise(() => this._provider.provideInlineValues(doc, typeConvert.Range.to(viewPort), typeConvert.InlineValueContext.to(context), token)).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(iv => typeConvert.InlineValue.from(iv));
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class DocumentHighlightAdapter {
|
||||
|
||||
constructor(
|
||||
@@ -1334,7 +1352,8 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov
|
||||
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
|
||||
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter
|
||||
| TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter
|
||||
| SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter
|
||||
| SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter
|
||||
| EvaluatableExpressionAdapter | InlineValuesAdapter
|
||||
| LinkedEditingRangeAdapter | InlineHintsAdapter;
|
||||
|
||||
class AdapterData {
|
||||
@@ -1568,6 +1587,27 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
||||
return this._withAdapter(handle, EvaluatableExpressionAdapter, adapter => adapter.provideEvaluatableExpression(URI.revive(resource), position, token), undefined);
|
||||
}
|
||||
|
||||
// --- debug inline values
|
||||
|
||||
registerInlineValuesProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineValuesProvider, extensionId?: ExtensionIdentifier): vscode.Disposable {
|
||||
|
||||
const eventHandle = typeof provider.onDidChangeInlineValues === 'function' ? this._nextHandle() : undefined;
|
||||
const handle = this._addNewAdapter(new InlineValuesAdapter(this._documents, provider), extension);
|
||||
|
||||
this._proxy.$registerInlineValuesProvider(handle, this._transformDocumentSelector(selector), eventHandle);
|
||||
let result = this._createDisposable(handle);
|
||||
|
||||
if (eventHandle !== undefined) {
|
||||
const subscription = provider.onDidChangeInlineValues!(_ => this._proxy.$emitInlineValuesEvent(eventHandle));
|
||||
result = Disposable.from(result, subscription);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
$provideInlineValues(handle: number, resource: UriComponents, range: IRange, context: extHostProtocol.IInlineValueContextDto, token: CancellationToken): Promise<modes.InlineValue[] | undefined> {
|
||||
return this._withAdapter(handle, InlineValuesAdapter, adapter => adapter.provideInlineValues(URI.revive(resource), range, context, token), undefined);
|
||||
}
|
||||
|
||||
// --- occurrences
|
||||
|
||||
registerDocumentHighlightProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, MainContext, MainThreadBulkEditsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, INotebookKernelInfoDto2, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
@@ -17,7 +17,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
|
||||
import { addIdToOutput, CellStatusbarAlignment, CellUri, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookExclusiveDocumentFilter, INotebookKernelInfoDto2, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellStatusbarAlignment, CellUri, ICellRange, INotebookCellStatusBarEntry, INotebookExclusiveDocumentFilter, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument';
|
||||
@@ -25,6 +25,7 @@ import { ExtHostNotebookEditor } from './extHostNotebookEditor';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
|
||||
class ExtHostWebviewCommWrapper extends Disposable {
|
||||
private readonly _onDidReceiveDocumentMessage = new Emitter<any>();
|
||||
@@ -71,15 +72,9 @@ class ExtHostWebviewCommWrapper extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface ExtHostNotebookOutputRenderingHandler {
|
||||
outputDisplayOrder: INotebookDisplayOrder | undefined;
|
||||
}
|
||||
|
||||
export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
private _kernelToFriendlyId = new Map<vscode.NotebookKernel, string>();
|
||||
private _friendlyIdToKernel = new Map<string, vscode.NotebookKernel>();
|
||||
private _kernelToFriendlyId = new ResourceMap<Map<vscode.NotebookKernel, string>>();
|
||||
private _friendlyIdToKernel = new ResourceMap<Map<string, vscode.NotebookKernel>>();
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
private readonly _handle: number,
|
||||
@@ -103,16 +98,16 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
let kernel_unique_pool = 0;
|
||||
const kernelFriendlyIdCache = new Set<string>();
|
||||
|
||||
const kernelToFriendlyId = this._kernelToFriendlyId.get(document.uri);
|
||||
|
||||
const transformedData: INotebookKernelInfoDto2[] = data.map(kernel => {
|
||||
let friendlyId = this._kernelToFriendlyId.get(kernel);
|
||||
let friendlyId = kernelToFriendlyId?.get(kernel);
|
||||
if (friendlyId === undefined) {
|
||||
if (kernel.id && kernelFriendlyIdCache.has(kernel.id)) {
|
||||
friendlyId = `${this._extension.identifier.value}_${kernel.id}_${kernel_unique_pool++}`;
|
||||
} else {
|
||||
friendlyId = `${this._extension.identifier.value}_${kernel.id || UUID.generateUuid()}`;
|
||||
}
|
||||
|
||||
this._kernelToFriendlyId.set(kernel, friendlyId);
|
||||
}
|
||||
|
||||
newMap.set(kernel, friendlyId);
|
||||
@@ -123,29 +118,31 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
label: kernel.label,
|
||||
extension: this._extension.identifier,
|
||||
extensionLocation: this._extension.extensionLocation,
|
||||
providerHandle: this._handle,
|
||||
description: kernel.description,
|
||||
detail: kernel.detail,
|
||||
isPreferred: kernel.isPreferred,
|
||||
preloads: kernel.preloads
|
||||
preloads: kernel.preloads,
|
||||
supportedLanguages: kernel.supportedLanguages
|
||||
};
|
||||
});
|
||||
|
||||
this._kernelToFriendlyId = newMap;
|
||||
|
||||
this._friendlyIdToKernel.clear();
|
||||
this._kernelToFriendlyId.forEach((value, key) => {
|
||||
this._friendlyIdToKernel.set(value, key);
|
||||
this._kernelToFriendlyId.set(document.uri, newMap);
|
||||
const friendlyIdToKernel = new Map<string, vscode.NotebookKernel>();
|
||||
newMap.forEach((value, key) => {
|
||||
friendlyIdToKernel.set(value, key);
|
||||
});
|
||||
|
||||
this._friendlyIdToKernel.set(document.uri, friendlyIdToKernel);
|
||||
return transformedData;
|
||||
}
|
||||
|
||||
getKernelByFriendlyId(kernelId: string) {
|
||||
return this._friendlyIdToKernel.get(kernelId);
|
||||
getKernelByFriendlyId(uri: URI, kernelId: string) {
|
||||
return this._friendlyIdToKernel.get(uri)?.get(kernelId);
|
||||
}
|
||||
|
||||
async resolveNotebook(kernelId: string, document: ExtHostNotebookDocument, webview: vscode.NotebookCommunication, token: CancellationToken) {
|
||||
const kernel = this._friendlyIdToKernel.get(kernelId);
|
||||
const kernel = this._friendlyIdToKernel.get(document.uri)?.get(kernelId);
|
||||
|
||||
if (kernel && this._provider.resolveKernel) {
|
||||
return this._provider.resolveKernel(kernel, document.notebookDocument, webview, token);
|
||||
@@ -153,7 +150,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
}
|
||||
|
||||
async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
|
||||
const kernel = this._friendlyIdToKernel.get(kernelId);
|
||||
const kernel = this._friendlyIdToKernel.get(document.uri)?.get(kernelId);
|
||||
|
||||
if (!kernel) {
|
||||
return;
|
||||
@@ -167,7 +164,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
}
|
||||
|
||||
async cancelNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
|
||||
const kernel = this._friendlyIdToKernel.get(kernelId);
|
||||
const kernel = this._friendlyIdToKernel.get(document.uri)?.get(kernelId);
|
||||
|
||||
if (!kernel) {
|
||||
return;
|
||||
@@ -191,30 +188,35 @@ async function withToken(cb: (token: CancellationToken) => any) {
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookEditorDecorationType implements vscode.NotebookEditorDecorationType {
|
||||
export class NotebookEditorDecorationType {
|
||||
|
||||
private static readonly _Keys = new IdGenerator('NotebookEditorDecorationType');
|
||||
|
||||
private _proxy: MainThreadNotebookShape;
|
||||
public key: string;
|
||||
readonly value: vscode.NotebookEditorDecorationType;
|
||||
|
||||
constructor(proxy: MainThreadNotebookShape, options: vscode.NotebookDecorationRenderOptions) {
|
||||
this.key = NotebookEditorDecorationType._Keys.nextId();
|
||||
this._proxy = proxy;
|
||||
this._proxy.$registerNotebookEditorDecorationType(this.key, typeConverters.NotebookDecorationRenderOptions.from(options));
|
||||
}
|
||||
const key = NotebookEditorDecorationType._Keys.nextId();
|
||||
proxy.$registerNotebookEditorDecorationType(key, typeConverters.NotebookDecorationRenderOptions.from(options));
|
||||
|
||||
public dispose(): void {
|
||||
this._proxy.$removeNotebookEditorDecorationType(this.key);
|
||||
this.value = {
|
||||
key,
|
||||
dispose() {
|
||||
proxy.$removeNotebookEditorDecorationType(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostNotebookOutputRenderingHandler {
|
||||
type NotebookContentProviderData = {
|
||||
readonly provider: vscode.NotebookContentProvider;
|
||||
readonly extension: IExtensionDescription;
|
||||
};
|
||||
|
||||
export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
private static _notebookKernelProviderHandlePool: number = 0;
|
||||
|
||||
private readonly _proxy: MainThreadNotebookShape;
|
||||
private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape;
|
||||
private readonly _notebookContentProviders = new Map<string, { readonly provider: vscode.NotebookContentProvider, readonly extension: IExtensionDescription; }>();
|
||||
private readonly _notebookContentProviders = new Map<string, NotebookContentProviderData>();
|
||||
private readonly _notebookKernelProviders = new Map<number, ExtHostNotebookKernelProviderAdapter>();
|
||||
private readonly _documents = new ResourceMap<ExtHostNotebookDocument>();
|
||||
private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor; }>();
|
||||
@@ -237,16 +239,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
private readonly _onDidChangeActiveNotebookEditor = new Emitter<vscode.NotebookEditor | undefined>();
|
||||
readonly onDidChangeActiveNotebookEditor = this._onDidChangeActiveNotebookEditor.event;
|
||||
|
||||
private _outputDisplayOrder: INotebookDisplayOrder | undefined;
|
||||
|
||||
get outputDisplayOrder(): INotebookDisplayOrder | undefined {
|
||||
return this._outputDisplayOrder;
|
||||
}
|
||||
|
||||
private _activeNotebookEditor: ExtHostNotebookEditor | undefined;
|
||||
|
||||
get activeNotebookEditor() {
|
||||
return this._activeNotebookEditor;
|
||||
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
|
||||
return this._activeNotebookEditor?.editor;
|
||||
}
|
||||
private _visibleNotebookEditors: ExtHostNotebookEditor[] = [];
|
||||
get visibleNotebookEditors(): vscode.NotebookEditor[] {
|
||||
return this._visibleNotebookEditors.map(editor => editor.editor);
|
||||
}
|
||||
|
||||
private _onDidOpenNotebookDocument = new Emitter<vscode.NotebookDocument>();
|
||||
@@ -254,8 +253,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
private _onDidCloseNotebookDocument = new Emitter<vscode.NotebookDocument>();
|
||||
onDidCloseNotebookDocument: Event<vscode.NotebookDocument> = this._onDidCloseNotebookDocument.event;
|
||||
private _onDidSaveNotebookDocument = new Emitter<vscode.NotebookDocument>();
|
||||
onDidSaveNotebookDocument: Event<vscode.NotebookDocument> = this._onDidCloseNotebookDocument.event;
|
||||
visibleNotebookEditors: ExtHostNotebookEditor[] = [];
|
||||
onDidSaveNotebookDocument: Event<vscode.NotebookDocument> = this._onDidSaveNotebookDocument.event;
|
||||
private _onDidChangeActiveNotebookKernel = new Emitter<{ document: vscode.NotebookDocument, kernel: vscode.NotebookKernel | undefined; }>();
|
||||
onDidChangeActiveNotebookKernel = this._onDidChangeActiveNotebookKernel.event;
|
||||
private _onDidChangeVisibleNotebookEditors = new Emitter<vscode.NotebookEditor[]>();
|
||||
@@ -270,7 +268,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
private readonly _extensionStoragePaths: IExtensionStoragePaths,
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook);
|
||||
this._mainThreadBulkEdits = mainContext.getProxy(MainContext.MainThreadBulkEdits);
|
||||
this._commandsConverter = commands.converter;
|
||||
|
||||
commands.registerArgumentProcessor({
|
||||
@@ -299,6 +296,22 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
return this._documents.get(uri);
|
||||
}
|
||||
|
||||
private _getNotebookDocument(uri: URI): ExtHostNotebookDocument {
|
||||
const result = this._documents.get(uri);
|
||||
if (!result) {
|
||||
throw new Error(`NO notebook document for '${uri}'`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _getProviderData(viewType: string): NotebookContentProviderData {
|
||||
const result = this._notebookContentProviders.get(viewType);
|
||||
if (!result) {
|
||||
throw new Error(`NO provider for '${viewType}'`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
registerNotebookContentProvider(
|
||||
extension: IExtensionDescription,
|
||||
viewType: string,
|
||||
@@ -319,32 +332,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
|
||||
this._notebookContentProviders.set(viewType, { extension, provider });
|
||||
const listeners: vscode.Disposable[] = [];
|
||||
|
||||
listeners.push(provider.onDidChangeNotebook
|
||||
? provider.onDidChangeNotebook(e => {
|
||||
const document = this._documents.get(URI.revive(e.document.uri));
|
||||
|
||||
if (!document) {
|
||||
throw new Error(`Notebook document ${e.document.uri.toString()} not found`);
|
||||
}
|
||||
|
||||
if (isEditEvent(e)) {
|
||||
const editId = document.addEdit(e);
|
||||
this._proxy.$onUndoableContentChange(e.document.uri, viewType, editId, e.label);
|
||||
} else {
|
||||
this._proxy.$onContentChange(e.document.uri, viewType);
|
||||
}
|
||||
})
|
||||
: Disposable.None);
|
||||
|
||||
listeners.push(provider.onDidChangeNotebookContentOptions
|
||||
? provider.onDidChangeNotebookContentOptions(() => {
|
||||
let listener: IDisposable | undefined;
|
||||
if (provider.onDidChangeNotebookContentOptions) {
|
||||
listener = provider.onDidChangeNotebookContentOptions(() => {
|
||||
this._proxy.$updateNotebookProviderOptions(viewType, provider.options);
|
||||
})
|
||||
: Disposable.None);
|
||||
|
||||
const supportBackup = !!provider.backupNotebook;
|
||||
});
|
||||
}
|
||||
|
||||
const viewOptionsFilenamePattern = options?.viewOptions?.filenamePattern
|
||||
.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.from(pattern))
|
||||
@@ -354,14 +349,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
console.warn(`Notebook content provider view options file name pattern is invalid ${options?.viewOptions?.filenamePattern}`);
|
||||
}
|
||||
|
||||
this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, supportBackup, {
|
||||
this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, {
|
||||
transientOutputs: options?.transientOutputs || false,
|
||||
transientMetadata: options?.transientMetadata || {},
|
||||
viewOptions: options?.viewOptions && viewOptionsFilenamePattern ? { displayName: options.viewOptions.displayName, filenamePattern: viewOptionsFilenamePattern, exclusive: options.viewOptions.exclusive || false } : undefined
|
||||
});
|
||||
|
||||
return new extHostTypes.Disposable(() => {
|
||||
listeners.forEach(d => d.dispose());
|
||||
listener?.dispose();
|
||||
this._notebookContentProviders.delete(viewType);
|
||||
this._proxy.$unregisterNotebookProvider(viewType);
|
||||
});
|
||||
@@ -384,15 +379,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
|
||||
createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType {
|
||||
return new NotebookEditorDecorationType(this._proxy, options);
|
||||
return new NotebookEditorDecorationType(this._proxy, options).value;
|
||||
}
|
||||
|
||||
async openNotebookDocument(uriComponents: UriComponents, viewType?: string): Promise<vscode.NotebookDocument> {
|
||||
const cached = this._documents.get(URI.revive(uriComponents));
|
||||
if (cached) {
|
||||
return Promise.resolve(cached.notebookDocument);
|
||||
return cached.notebookDocument;
|
||||
}
|
||||
|
||||
await this._proxy.$tryOpenDocument(uriComponents, viewType);
|
||||
const document = this._documents.get(URI.revive(uriComponents));
|
||||
return assertIsDefined(document?.notebookDocument);
|
||||
@@ -420,7 +414,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
resolvedOptions = {
|
||||
position: typeConverters.ViewColumn.from(options.viewColumn),
|
||||
preserveFocus: options.preserveFocus,
|
||||
selection: options.selection,
|
||||
selection: options.selection && typeConverters.NotebookCellRange.from(options.selection),
|
||||
pinned: typeof options.preview === 'boolean' ? !options.preview : undefined
|
||||
};
|
||||
} else {
|
||||
@@ -433,7 +427,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
const editor = editorId && this._editors.get(editorId)?.editor;
|
||||
|
||||
if (editor) {
|
||||
return editor;
|
||||
return editor.editor;
|
||||
}
|
||||
|
||||
if (editorId) {
|
||||
@@ -459,26 +453,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
});
|
||||
}
|
||||
|
||||
async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto> {
|
||||
const provider = this._notebookContentProviders.get(viewType);
|
||||
if (!provider) {
|
||||
throw new Error(`NO provider for '${viewType}'`);
|
||||
}
|
||||
|
||||
const data = await provider.provider.openNotebook(URI.revive(uri), { backupId });
|
||||
return {
|
||||
metadata: {
|
||||
...notebookDocumentMetadataDefaults,
|
||||
...data.metadata
|
||||
},
|
||||
languages: data.languages,
|
||||
cells: data.cells.map(cell => ({
|
||||
...cell,
|
||||
outputs: cell.outputs.map(o => addIdToOutput(o))
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
async $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise<void> {
|
||||
const provider = this._notebookContentProviders.get(viewType);
|
||||
const revivedUri = URI.revive(uri);
|
||||
@@ -516,75 +490,53 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
});
|
||||
}
|
||||
|
||||
// --- open, save, saveAs, backup
|
||||
|
||||
async $openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto> {
|
||||
const { provider } = this._getProviderData(viewType);
|
||||
const data = await provider.openNotebook(URI.revive(uri), { backupId });
|
||||
return {
|
||||
metadata: {
|
||||
...notebookDocumentMetadataDefaults,
|
||||
...data.metadata
|
||||
},
|
||||
cells: data.cells.map(typeConverters.NotebookCellData.from),
|
||||
};
|
||||
}
|
||||
|
||||
async $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean> {
|
||||
const document = this._documents.get(URI.revive(uri));
|
||||
if (!document) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._notebookContentProviders.has(viewType)) {
|
||||
await this._notebookContentProviders.get(viewType)!.provider.saveNotebook(document.notebookDocument, token);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
const document = this._getNotebookDocument(URI.revive(uri));
|
||||
const { provider } = this._getProviderData(viewType);
|
||||
await provider.saveNotebook(document.notebookDocument, token);
|
||||
return true;
|
||||
}
|
||||
|
||||
async $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean> {
|
||||
const document = this._documents.get(URI.revive(uri));
|
||||
if (!document) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._notebookContentProviders.has(viewType)) {
|
||||
await this._notebookContentProviders.get(viewType)!.provider.saveNotebookAs(URI.revive(target), document.notebookDocument, token);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
const document = this._getNotebookDocument(URI.revive(uri));
|
||||
const { provider } = this._getProviderData(viewType);
|
||||
await provider.saveNotebookAs(URI.revive(target), document.notebookDocument, token);
|
||||
return true;
|
||||
}
|
||||
|
||||
async $undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void> {
|
||||
const document = this._documents.get(URI.revive(uri));
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
private _backupIdPool: number = 0;
|
||||
|
||||
document.undo(editId, isDirty);
|
||||
async $backupNotebook(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string> {
|
||||
const document = this._getNotebookDocument(URI.revive(uri));
|
||||
const provider = this._getProviderData(viewType);
|
||||
|
||||
}
|
||||
const storagePath = this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension);
|
||||
const fileName = String(hash([document.uri.toString(), this._backupIdPool++]));
|
||||
const backupUri = URI.joinPath(storagePath, fileName);
|
||||
|
||||
async $redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void> {
|
||||
const document = this._documents.get(URI.revive(uri));
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.redo(editId, isDirty);
|
||||
}
|
||||
|
||||
|
||||
async $backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string | undefined> {
|
||||
const document = this._documents.get(URI.revive(uri));
|
||||
const provider = this._notebookContentProviders.get(viewType);
|
||||
|
||||
if (document && provider && provider.provider.backupNotebook) {
|
||||
const backup = await provider.provider.backupNotebook(document.notebookDocument, { destination: document.getNewBackupUri() }, cancellation);
|
||||
document.updateBackup(backup);
|
||||
return backup.id;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void {
|
||||
this._outputDisplayOrder = displayOrder;
|
||||
const backup = await provider.provider.backupNotebook(document.notebookDocument, { destination: backupUri }, cancellation);
|
||||
document.updateBackup(backup);
|
||||
return backup.id;
|
||||
}
|
||||
|
||||
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined; }) {
|
||||
if (event.providerHandle !== undefined) {
|
||||
this._withAdapter(event.providerHandle, event.uri, async (adapter, document) => {
|
||||
const kernel = event.kernelFriendlyId ? adapter.getKernelByFriendlyId(event.kernelFriendlyId) : undefined;
|
||||
const kernel = event.kernelFriendlyId ? adapter.getKernelByFriendlyId(URI.revive(event.uri), event.kernelFriendlyId) : undefined;
|
||||
this._editors.forEach(editor => {
|
||||
if (editor.editor.notebookData === document) {
|
||||
editor.editor._acceptKernel(kernel);
|
||||
@@ -595,36 +547,23 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
}
|
||||
|
||||
// TODO@rebornix: remove document - editor one on one mapping
|
||||
private _getEditorFromURI(uriComponents: UriComponents) {
|
||||
const uriStr = URI.revive(uriComponents).toString();
|
||||
let editor: { editor: ExtHostNotebookEditor; } | undefined;
|
||||
this._editors.forEach(e => {
|
||||
if (e.editor.document.uri.toString() === uriStr) {
|
||||
editor = e;
|
||||
}
|
||||
});
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
$onDidReceiveMessage(editorId: string, forRendererType: string | undefined, message: any): void {
|
||||
this._webviewComm.get(editorId)?.onDidReceiveMessage(forRendererType, message);
|
||||
}
|
||||
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void {
|
||||
const document = this._documents.get(URI.revive(uriComponents));
|
||||
if (document) {
|
||||
document.acceptModelChanged(event, isDirty);
|
||||
}
|
||||
$acceptModelChanged(uri: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void {
|
||||
const document = this._getNotebookDocument(URI.revive(uri));
|
||||
document.acceptModelChanged(event, isDirty);
|
||||
}
|
||||
|
||||
public $acceptModelSaved(uriComponents: UriComponents): void {
|
||||
const document = this._documents.get(URI.revive(uriComponents));
|
||||
if (document) {
|
||||
// this.$acceptDirtyStateChanged(uriComponents, false);
|
||||
this._onDidSaveNotebookDocument.fire(document.notebookDocument);
|
||||
}
|
||||
$acceptDirtyStateChanged(uri: UriComponents, isDirty: boolean): void {
|
||||
const document = this._getNotebookDocument(URI.revive(uri));
|
||||
document.acceptModelChanged({ rawEvents: [], versionId: document.notebookDocument.version }, isDirty);
|
||||
}
|
||||
|
||||
$acceptModelSaved(uri: UriComponents): void {
|
||||
const document = this._getNotebookDocument(URI.revive(uri));
|
||||
this._onDidSaveNotebookDocument.fire(document.notebookDocument);
|
||||
}
|
||||
|
||||
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void {
|
||||
@@ -642,42 +581,30 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
|
||||
if (data.visibleRanges) {
|
||||
editor.editor._acceptVisibleRanges(data.visibleRanges.ranges);
|
||||
editor.editor._acceptVisibleRanges(data.visibleRanges.ranges.map(typeConverters.NotebookCellRange.to));
|
||||
this._onDidChangeNotebookEditorVisibleRanges.fire({
|
||||
notebookEditor: editor.editor,
|
||||
visibleRanges: editor.editor.visibleRanges
|
||||
notebookEditor: editor.editor.editor,
|
||||
visibleRanges: editor.editor.editor.visibleRanges
|
||||
});
|
||||
}
|
||||
|
||||
if (data.selections) {
|
||||
if (data.selections.selections.length) {
|
||||
const firstCell = data.selections.selections[0];
|
||||
editor.editor.selection = editor.editor.notebookData.getCell(firstCell)?.cell;
|
||||
} else {
|
||||
editor.editor.selection = undefined;
|
||||
}
|
||||
editor.editor._acceptSelections(data.selections.selections);
|
||||
|
||||
this._onDidChangeNotebookEditorSelection.fire({
|
||||
notebookEditor: editor.editor,
|
||||
selection: editor.editor.selection
|
||||
notebookEditor: editor.editor.editor,
|
||||
selection: editor.editor.editor.selection
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void {
|
||||
this.logService.debug('ExtHostNotebook#$acceptDocumentPropertiesChanged', uriComponents.path, data);
|
||||
const editor = this._getEditorFromURI(uriComponents);
|
||||
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.metadata) {
|
||||
editor.editor.notebookData.acceptDocumentPropertiesChanged(data);
|
||||
}
|
||||
$acceptDocumentPropertiesChanged(uri: UriComponents, data: INotebookDocumentPropertiesChangeData): void {
|
||||
this.logService.debug('ExtHostNotebook#$acceptDocumentPropertiesChanged', uri.path, data);
|
||||
const document = this._getNotebookDocument(URI.revive(uri));
|
||||
document.acceptDocumentPropertiesChanged(data);
|
||||
}
|
||||
|
||||
private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[], visibleRanges: vscode.NotebookCellRange[]) {
|
||||
private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: ICellRange[], visibleRanges: extHostTypes.NotebookCellRange[]) {
|
||||
const revivedUri = document.uri;
|
||||
let webComm = this._webviewComm.get(editorId);
|
||||
|
||||
@@ -690,17 +617,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
editorId,
|
||||
document.notebookDocument.viewType,
|
||||
this._proxy,
|
||||
webComm.contentProviderComm,
|
||||
document
|
||||
);
|
||||
|
||||
if (selections.length) {
|
||||
const firstCell = selections[0];
|
||||
editor.selection = editor.notebookData.getCell(firstCell)?.cell;
|
||||
} else {
|
||||
editor.selection = undefined;
|
||||
}
|
||||
|
||||
editor._acceptSelections(selections);
|
||||
editor._acceptVisibleRanges(visibleRanges);
|
||||
|
||||
this._editors.get(editorId)?.editor.dispose();
|
||||
@@ -723,7 +643,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
|
||||
for (const e of this._editors.values()) {
|
||||
if (e.editor.document.uri.toString() === revivedUri.toString()) {
|
||||
if (e.editor.notebookData.uri.toString() === revivedUri.toString()) {
|
||||
e.editor.dispose();
|
||||
this._editors.delete(e.editor.id);
|
||||
editorChanged = true;
|
||||
@@ -739,31 +659,37 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
for (const modelData of delta.addedDocuments) {
|
||||
const uri = URI.revive(modelData.uri);
|
||||
const viewType = modelData.viewType;
|
||||
const entry = this._notebookContentProviders.get(viewType);
|
||||
const storageRoot = entry && (this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension));
|
||||
|
||||
if (this._documents.has(uri)) {
|
||||
throw new Error(`adding EXISTING notebook ${uri}`);
|
||||
}
|
||||
const that = this;
|
||||
|
||||
const document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, this._mainThreadBulkEdits, {
|
||||
emitModelChange(event: vscode.NotebookCellsChangeEvent): void {
|
||||
that._onDidChangeNotebookCells.fire(event);
|
||||
const document = new ExtHostNotebookDocument(
|
||||
this._proxy,
|
||||
this._documentsAndEditors,
|
||||
{
|
||||
emitModelChange(event: vscode.NotebookCellsChangeEvent): void {
|
||||
that._onDidChangeNotebookCells.fire(event);
|
||||
},
|
||||
emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void {
|
||||
that._onDidChangeCellOutputs.fire(event);
|
||||
},
|
||||
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void {
|
||||
that._onDidChangeCellLanguage.fire(event);
|
||||
},
|
||||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void {
|
||||
that._onDidChangeCellMetadata.fire(event);
|
||||
},
|
||||
emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void {
|
||||
that._onDidChangeNotebookDocumentMetadata.fire(event);
|
||||
}
|
||||
},
|
||||
emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void {
|
||||
that._onDidChangeCellOutputs.fire(event);
|
||||
},
|
||||
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void {
|
||||
that._onDidChangeCellLanguage.fire(event);
|
||||
},
|
||||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void {
|
||||
that._onDidChangeCellMetadata.fire(event);
|
||||
},
|
||||
emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void {
|
||||
that._onDidChangeNotebookDocumentMetadata.fire(event);
|
||||
}
|
||||
}, viewType, modelData.contentOptions, { ...notebookDocumentMetadataDefaults, ...modelData.metadata }, uri, storageRoot);
|
||||
viewType,
|
||||
modelData.contentOptions,
|
||||
modelData.metadata ? typeConverters.NotebookDocumentMetadata.to(modelData.metadata) : new extHostTypes.NotebookDocumentMetadata(),
|
||||
uri,
|
||||
);
|
||||
|
||||
document.acceptModelChanged({
|
||||
versionId: modelData.versionId,
|
||||
@@ -784,13 +710,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
|
||||
this._documents.get(uri)?.dispose();
|
||||
this._documents.set(uri, document);
|
||||
|
||||
// create editor if populated
|
||||
if (modelData.attachedEditor) {
|
||||
this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections, modelData.attachedEditor.visibleRanges);
|
||||
editorChanged = true;
|
||||
}
|
||||
|
||||
this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments });
|
||||
|
||||
this._onDidOpenNotebookDocument.fire(document.notebookDocument);
|
||||
@@ -807,7 +726,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
const document = this._documents.get(revivedUri);
|
||||
|
||||
if (document) {
|
||||
this._createExtHostEditor(document, editorModelData.id, editorModelData.selections, editorModelData.visibleRanges);
|
||||
this._createExtHostEditor(document, editorModelData.id, editorModelData.selections, editorModelData.visibleRanges.map(typeConverters.NotebookCellRange.to));
|
||||
editorChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -823,7 +742,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
editorChanged = true;
|
||||
this._editors.delete(editorid);
|
||||
|
||||
if (this.activeNotebookEditor?.id === editor.editor.id) {
|
||||
if (this._activeNotebookEditor?.id === editor.editor.id) {
|
||||
this._activeNotebookEditor = undefined;
|
||||
}
|
||||
|
||||
@@ -839,16 +758,16 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
|
||||
if (delta.visibleEditors) {
|
||||
this.visibleNotebookEditors = delta.visibleEditors.map(id => this._editors.get(id)!.editor).filter(editor => !!editor) as ExtHostNotebookEditor[];
|
||||
this._visibleNotebookEditors = delta.visibleEditors.map(id => this._editors.get(id)!.editor).filter(editor => !!editor) as ExtHostNotebookEditor[];
|
||||
const visibleEditorsSet = new Set<string>();
|
||||
this.visibleNotebookEditors.forEach(editor => visibleEditorsSet.add(editor.id));
|
||||
this._visibleNotebookEditors.forEach(editor => visibleEditorsSet.add(editor.id));
|
||||
|
||||
for (const e of this._editors.values()) {
|
||||
const newValue = visibleEditorsSet.has(e.editor.id);
|
||||
e.editor._acceptVisibility(newValue);
|
||||
}
|
||||
|
||||
this.visibleNotebookEditors = [...this._editors.values()].map(e => e.editor).filter(e => e.visible);
|
||||
this._visibleNotebookEditors = [...this._editors.values()].map(e => e.editor).filter(e => e.visible);
|
||||
this._onDidChangeVisibleNotebookEditors.fire(this.visibleNotebookEditors);
|
||||
}
|
||||
|
||||
@@ -857,7 +776,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor;
|
||||
this._activeNotebookEditor?._acceptActive(true);
|
||||
for (const e of this._editors.values()) {
|
||||
if (e.editor !== this.activeNotebookEditor) {
|
||||
if (e.editor !== this._activeNotebookEditor) {
|
||||
e.editor._acceptActive(false);
|
||||
}
|
||||
}
|
||||
@@ -869,7 +788,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
}
|
||||
|
||||
this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor);
|
||||
this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor?.editor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -892,11 +811,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
}
|
||||
|
||||
function isEditEvent(e: vscode.NotebookDocumentEditEvent | vscode.NotebookDocumentContentChangeEvent): e is vscode.NotebookDocumentEditEvent {
|
||||
return typeof (e as vscode.NotebookDocumentEditEvent).undo === 'function'
|
||||
&& typeof (e as vscode.NotebookDocumentEditEvent).redo === 'function';
|
||||
}
|
||||
|
||||
export class NotebookCellStatusBarItemInternal extends Disposable {
|
||||
private static NEXT_ID = 0;
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ 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 { ResourceMap } from 'vs/base/common/map';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
@@ -76,7 +75,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
const cellLengths: number[] = [];
|
||||
const cellLineCounts: number[] = [];
|
||||
for (const cell of this._notebook.cells) {
|
||||
if (cell.cellKind === CellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) {
|
||||
if (cell.cellKind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) {
|
||||
this._cellUris.set(cell.uri, this._cells.length);
|
||||
this._cells.push(cell);
|
||||
cellLengths.push(cell.document.getText().length + 1);
|
||||
|
||||
@@ -4,56 +4,31 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { ISplice } from 'vs/base/common/sequence';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { CellKind, INotebookDocumentPropertiesChangeData, IWorkspaceCellEditDto, MainThreadBulkEditsShape, MainThreadNotebookShape, NotebookCellOutputsSplice, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { CellKind, INotebookDocumentPropertiesChangeData, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { CellEditType, CellOutputKind, diff, IMainCellDto, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IMainCellDto, IOutputDto, IOutputItemDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
|
||||
|
||||
interface IObservable<T> {
|
||||
proxy: T;
|
||||
onDidChange: Event<void>;
|
||||
}
|
||||
|
||||
function getObservable<T extends Object>(obj: T): IObservable<T> {
|
||||
const onDidChange = new Emitter<void>();
|
||||
const proxy = new Proxy(obj, {
|
||||
set(target: T, p: PropertyKey, value: any, _receiver: any): boolean {
|
||||
target[p as keyof T] = value;
|
||||
onDidChange.fire();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
proxy,
|
||||
onDidChange: onDidChange.event
|
||||
};
|
||||
}
|
||||
|
||||
class RawContentChangeEvent {
|
||||
|
||||
constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: ExtHostCell[], readonly items: ExtHostCell[]) { }
|
||||
constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: vscode.NotebookCell[], readonly items: ExtHostCell[]) { }
|
||||
|
||||
static asApiEvent(event: RawContentChangeEvent): vscode.NotebookCellsChangeData {
|
||||
return Object.freeze({
|
||||
start: event.start,
|
||||
deletedCount: event.deletedCount,
|
||||
deletedItems: event.deletedItems.map(data => data.cell),
|
||||
deletedItems: event.deletedItems,
|
||||
items: event.items.map(data => data.cell)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostCell extends Disposable {
|
||||
export class ExtHostCell {
|
||||
|
||||
static asModelAddData(notebook: vscode.NotebookDocument, cell: IMainCellDto): IExtHostModelAddedData {
|
||||
return {
|
||||
@@ -70,14 +45,8 @@ export class ExtHostCell extends Disposable {
|
||||
private _onDidDispose = new Emitter<void>();
|
||||
readonly onDidDispose: Event<void> = this._onDidDispose.event;
|
||||
|
||||
private _onDidChangeOutputs = new Emitter<ISplice<IProcessedOutput>[]>();
|
||||
readonly onDidChangeOutputs: Event<ISplice<IProcessedOutput>[]> = this._onDidChangeOutputs.event;
|
||||
|
||||
private _outputs: any[];
|
||||
private _outputMapping = new WeakMap<vscode.CellOutput, string | undefined /* output ID */>();
|
||||
|
||||
private _metadata: vscode.NotebookCellMetadata;
|
||||
private _metadataChangeListener: IDisposable;
|
||||
private _outputs: extHostTypes.NotebookCellOutput[];
|
||||
private _metadata: extHostTypes.NotebookCellMetadata;
|
||||
|
||||
readonly handle: number;
|
||||
readonly uri: URI;
|
||||
@@ -86,28 +55,20 @@ export class ExtHostCell extends Disposable {
|
||||
private _cell: vscode.NotebookCell | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape,
|
||||
private readonly _notebook: ExtHostNotebookDocument,
|
||||
private readonly _extHostDocument: ExtHostDocumentsAndEditors,
|
||||
private readonly _cellData: IMainCellDto,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.handle = _cellData.handle;
|
||||
this.uri = URI.revive(_cellData.uri);
|
||||
this.cellKind = _cellData.cellKind;
|
||||
this._outputs = _cellData.outputs.map(extHostTypeConverters.NotebookCellOutput.to);
|
||||
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(_cellData.metadata ?? {});
|
||||
}
|
||||
|
||||
this._outputs = _cellData.outputs;
|
||||
for (const output of this._outputs) {
|
||||
this._outputMapping.set(output, output.outputId);
|
||||
delete output.outputId;
|
||||
}
|
||||
|
||||
const observableMetadata = getObservable(_cellData.metadata ?? {});
|
||||
this._metadata = observableMetadata.proxy;
|
||||
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
|
||||
this._updateMetadata();
|
||||
}));
|
||||
dispose() {
|
||||
this._onDidDispose.fire();
|
||||
this._onDidDispose.dispose();
|
||||
}
|
||||
|
||||
get cell(): vscode.NotebookCell {
|
||||
@@ -121,81 +82,35 @@ export class ExtHostCell extends Disposable {
|
||||
get index() { return that._notebook.getCellIndex(that); },
|
||||
notebook: that._notebook.notebookDocument,
|
||||
uri: that.uri,
|
||||
cellKind: this._cellData.cellKind,
|
||||
cellKind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind),
|
||||
document: data.document,
|
||||
get language() { return data!.document.languageId; },
|
||||
get outputs() { return that._outputs; },
|
||||
set outputs(value) { that._updateOutputs(value); },
|
||||
get outputs() { return that._outputs.slice(0); },
|
||||
set outputs(_value) { throw new Error('Use WorkspaceEdit to update cell outputs.'); },
|
||||
get metadata() { return that._metadata; },
|
||||
set metadata(value) {
|
||||
that.setMetadata(value);
|
||||
that._updateMetadata();
|
||||
},
|
||||
set metadata(_value) { throw new Error('Use WorkspaceEdit to update cell metadata.'); },
|
||||
});
|
||||
}
|
||||
return this._cell;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
super.dispose();
|
||||
this._onDidDispose.fire();
|
||||
setOutputs(newOutputs: IOutputDto[]): void {
|
||||
this._outputs = newOutputs.map(extHostTypeConverters.NotebookCellOutput.to);
|
||||
}
|
||||
|
||||
setOutputs(newOutputs: vscode.CellOutput[]): void {
|
||||
this._outputs = newOutputs;
|
||||
}
|
||||
|
||||
private _updateOutputs(newOutputs: vscode.CellOutput[]) {
|
||||
const rawDiffs = diff<vscode.CellOutput>(this._outputs || [], newOutputs || [], (a) => {
|
||||
return this._outputMapping.has(a);
|
||||
});
|
||||
|
||||
const transformedDiffs: ISplice<IProcessedOutput>[] = rawDiffs.map(diff => {
|
||||
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
|
||||
this._outputMapping.delete(this._outputs[i]);
|
||||
setOutputItems(outputId: string, append: boolean, newOutputItems: IOutputItemDto[]) {
|
||||
const newItems = newOutputItems.map(extHostTypeConverters.NotebookCellOutputItem.to);
|
||||
const output = this._outputs.find(op => op.id === outputId);
|
||||
if (output) {
|
||||
if (!append) {
|
||||
output.outputs.length = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
deleteCount: diff.deleteCount,
|
||||
start: diff.start,
|
||||
toInsert: diff.toInsert.map((output): IProcessedOutput => {
|
||||
if (output.outputKind === CellOutputKind.Rich) {
|
||||
const uuid = UUID.generateUuid();
|
||||
this._outputMapping.set(output, uuid);
|
||||
return { ...output, outputId: uuid };
|
||||
}
|
||||
|
||||
this._outputMapping.set(output, undefined);
|
||||
return output;
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
this._outputs = newOutputs;
|
||||
this._onDidChangeOutputs.fire(transformedDiffs);
|
||||
output.outputs.push(...newItems);
|
||||
}
|
||||
}
|
||||
|
||||
setMetadata(newMetadata: vscode.NotebookCellMetadata): void {
|
||||
// Don't apply metadata defaults here, 'undefined' means 'inherit from document metadata'
|
||||
this._metadataChangeListener.dispose();
|
||||
const observableMetadata = getObservable(newMetadata);
|
||||
this._metadata = observableMetadata.proxy;
|
||||
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
|
||||
this._updateMetadata();
|
||||
}));
|
||||
}
|
||||
|
||||
private _updateMetadata(): Promise<boolean> {
|
||||
const index = this._notebook.notebookDocument.cells.indexOf(this.cell);
|
||||
const edit: IWorkspaceCellEditDto = {
|
||||
_type: WorkspaceEditType.Cell,
|
||||
metadata: undefined,
|
||||
resource: this._notebook.uri,
|
||||
notebookVersionId: this._notebook.notebookDocument.version,
|
||||
edit: { editType: CellEditType.Metadata, index, metadata: this._metadata }
|
||||
};
|
||||
|
||||
return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit({ edits: [edit] });
|
||||
setMetadata(newMetadata: NotebookCellMetadata): void {
|
||||
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(newMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,10 +122,6 @@ export interface INotebookEventEmitter {
|
||||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void;
|
||||
}
|
||||
|
||||
function hashPath(resource: URI): string {
|
||||
const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
|
||||
return hash(str) + '';
|
||||
}
|
||||
|
||||
export class ExtHostNotebookDocument extends Disposable {
|
||||
|
||||
@@ -222,35 +133,21 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
private _cellDisposableMapping = new Map<number, DisposableStore>();
|
||||
|
||||
private _notebook: vscode.NotebookDocument | undefined;
|
||||
private _metadata: Required<vscode.NotebookDocumentMetadata>;
|
||||
private _metadataChangeListener: IDisposable;
|
||||
private _versionId = 0;
|
||||
private _versionId: number = 0;
|
||||
private _isDirty: boolean = false;
|
||||
private _backupCounter = 1;
|
||||
private _backup?: vscode.NotebookDocumentBackup;
|
||||
private _disposed = false;
|
||||
private _languages: string[] = [];
|
||||
|
||||
private readonly _edits = new Cache<vscode.NotebookDocumentEditEvent>('notebook documents');
|
||||
private _disposed: boolean = false;
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape,
|
||||
private readonly _emitter: INotebookEventEmitter,
|
||||
private readonly _viewType: string,
|
||||
private readonly _contentOptions: vscode.NotebookDocumentContentOptions,
|
||||
metadata: Required<vscode.NotebookDocumentMetadata>,
|
||||
public readonly uri: URI,
|
||||
private readonly _storagePath: URI | undefined
|
||||
private _metadata: extHostTypes.NotebookDocumentMetadata,
|
||||
readonly uri: URI,
|
||||
) {
|
||||
super();
|
||||
|
||||
const observableMetadata = getObservable(metadata);
|
||||
this._metadata = observableMetadata.proxy;
|
||||
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
|
||||
this._tryUpdateMetadata();
|
||||
}));
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -259,36 +156,6 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
dispose(this._cellDisposableMapping.values());
|
||||
}
|
||||
|
||||
private _updateMetadata(newMetadata: Required<vscode.NotebookDocumentMetadata>) {
|
||||
this._metadataChangeListener.dispose();
|
||||
newMetadata = {
|
||||
...notebookDocumentMetadataDefaults,
|
||||
...newMetadata
|
||||
};
|
||||
if (this._metadataChangeListener) {
|
||||
this._metadataChangeListener.dispose();
|
||||
}
|
||||
|
||||
const observableMetadata = getObservable(newMetadata);
|
||||
this._metadata = observableMetadata.proxy;
|
||||
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
|
||||
this._tryUpdateMetadata();
|
||||
}));
|
||||
|
||||
this._tryUpdateMetadata();
|
||||
}
|
||||
|
||||
private _tryUpdateMetadata() {
|
||||
const edit: IWorkspaceCellEditDto = {
|
||||
_type: WorkspaceEditType.Cell,
|
||||
metadata: undefined,
|
||||
edit: { editType: CellEditType.DocumentMetadata, metadata: this._metadata },
|
||||
resource: this.uri,
|
||||
notebookVersionId: this.notebookDocument.version,
|
||||
};
|
||||
|
||||
return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit({ edits: [edit] });
|
||||
}
|
||||
|
||||
get notebookDocument(): vscode.NotebookDocument {
|
||||
if (!this._notebook) {
|
||||
@@ -301,29 +168,15 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
get isDirty() { return that._isDirty; },
|
||||
get isUntitled() { return that.uri.scheme === Schemas.untitled; },
|
||||
get cells(): ReadonlyArray<vscode.NotebookCell> { return that._cells.map(cell => cell.cell); },
|
||||
get languages() { return that._languages; },
|
||||
set languages(value: string[]) { that._trySetLanguages(value); },
|
||||
get metadata() { return that._metadata; },
|
||||
set metadata(value: Required<vscode.NotebookDocumentMetadata>) { that._updateMetadata(value); },
|
||||
get contentOptions() { return that._contentOptions; }
|
||||
set metadata(_value: Required<vscode.NotebookDocumentMetadata>) { throw new Error('Use WorkspaceEdit to update metadata.'); },
|
||||
get contentOptions() { return that._contentOptions; },
|
||||
save() { return that._save(); }
|
||||
});
|
||||
}
|
||||
return this._notebook;
|
||||
}
|
||||
|
||||
private _trySetLanguages(newLanguages: string[]) {
|
||||
this._languages = newLanguages;
|
||||
this._proxy.$updateNotebookLanguages(this._viewType, this.uri, this._languages);
|
||||
}
|
||||
|
||||
getNewBackupUri(): URI {
|
||||
if (!this._storagePath) {
|
||||
throw new Error('Backup requires a valid storage path');
|
||||
}
|
||||
const fileName = hashPath(this.uri) + (this._backupCounter++);
|
||||
return joinPath(this._storagePath, fileName);
|
||||
}
|
||||
|
||||
updateBackup(backup: vscode.NotebookDocumentBackup): void {
|
||||
this._backup?.delete();
|
||||
this._backup = backup;
|
||||
@@ -339,17 +192,7 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
...notebookDocumentMetadataDefaults,
|
||||
...data.metadata
|
||||
};
|
||||
|
||||
if (this._metadataChangeListener) {
|
||||
this._metadataChangeListener.dispose();
|
||||
}
|
||||
|
||||
const observableMetadata = getObservable(newMetadata);
|
||||
this._metadata = observableMetadata.proxy;
|
||||
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
|
||||
this._tryUpdateMetadata();
|
||||
}));
|
||||
|
||||
this._metadata = this._metadata.with(newMetadata);
|
||||
this._emitter.emitDocumentMetadataChange({ document: this.notebookDocument });
|
||||
}
|
||||
|
||||
@@ -365,6 +208,8 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
this._moveCell(e.index, e.newIdx);
|
||||
} else if (e.kind === NotebookCellsChangeType.Output) {
|
||||
this._setCellOutputs(e.index, e.outputs);
|
||||
} else if (e.kind === NotebookCellsChangeType.OutputItem) {
|
||||
this._setCellOutputItems(e.index, e.outputId, e.append, e.outputItems);
|
||||
} else if (e.kind === NotebookCellsChangeType.ChangeLanguage) {
|
||||
this._changeCellLanguage(e.index, e.language);
|
||||
} else if (e.kind === NotebookCellsChangeType.ChangeCellMetadata) {
|
||||
@@ -373,6 +218,13 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private async _save(): Promise<boolean> {
|
||||
if (this._disposed) {
|
||||
return Promise.reject(new Error('Document has been closed'));
|
||||
}
|
||||
return this._proxy.$trySaveDocument(this.uri);
|
||||
}
|
||||
|
||||
private _spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
@@ -386,7 +238,7 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
const cellDtos = splice[2];
|
||||
const newCells = cellDtos.map(cell => {
|
||||
|
||||
const extCell = new ExtHostCell(this._mainThreadBulkEdits, this, this._documentsAndEditors, cell);
|
||||
const extCell = new ExtHostCell(this, this._documentsAndEditors, cell);
|
||||
|
||||
if (!initialization) {
|
||||
addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell));
|
||||
@@ -398,12 +250,6 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
this._cellDisposableMapping.set(extCell.handle, store);
|
||||
}
|
||||
|
||||
const store = this._cellDisposableMapping.get(extCell.handle)!;
|
||||
|
||||
store.add(extCell.onDidChangeOutputs((diffs) => {
|
||||
this.eventuallyUpdateCellOutputs(extCell, diffs);
|
||||
}));
|
||||
|
||||
return extCell;
|
||||
});
|
||||
|
||||
@@ -412,12 +258,14 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
this._cellDisposableMapping.delete(this._cells[j].handle);
|
||||
}
|
||||
|
||||
const changeEvent = new RawContentChangeEvent(splice[0], splice[1], [], newCells);
|
||||
const deletedItems = this._cells.splice(splice[0], splice[1], ...newCells);
|
||||
for (let cell of deletedItems) {
|
||||
removedCellDocuments.push(cell.uri);
|
||||
changeEvent.deletedItems.push(cell.cell);
|
||||
}
|
||||
|
||||
contentChangeEvents.push(new RawContentChangeEvent(splice[0], splice[1], deletedItems, newCells));
|
||||
contentChangeEvents.push(changeEvent);
|
||||
});
|
||||
|
||||
this._documentsAndEditors.acceptDocumentsAndEditorsDelta({
|
||||
@@ -453,12 +301,18 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private _setCellOutputs(index: number, outputs: IProcessedOutput[]): void {
|
||||
private _setCellOutputs(index: number, outputs: IOutputDto[]): void {
|
||||
const cell = this._cells[index];
|
||||
cell.setOutputs(outputs);
|
||||
this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] });
|
||||
}
|
||||
|
||||
private _setCellOutputItems(index: number, outputId: string, append: boolean, outputItems: IOutputItemDto[]): void {
|
||||
const cell = this._cells[index];
|
||||
cell.setOutputItems(outputId, append, outputItems);
|
||||
this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] });
|
||||
}
|
||||
|
||||
private _changeCellLanguage(index: number, language: string): void {
|
||||
const cell = this._cells[index];
|
||||
const event: vscode.NotebookCellLanguageChangeEvent = { document: this.notebookDocument, cell: cell.cell, language };
|
||||
@@ -472,21 +326,8 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
this._emitter.emitCellMetadataChange(event);
|
||||
}
|
||||
|
||||
async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<IProcessedOutput>[]) {
|
||||
const outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
|
||||
const outputs = diff.toInsert;
|
||||
return [diff.start, diff.deleteCount, outputs];
|
||||
});
|
||||
|
||||
if (!outputDtos.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._proxy.$spliceNotebookCellOutputs(this._viewType, this.uri, cell.handle, outputDtos);
|
||||
this._emitter.emitCellOutputsChange({
|
||||
document: this.notebookDocument,
|
||||
cells: [cell.cell]
|
||||
});
|
||||
getCellFromIndex(index: number): ExtHostCell | undefined {
|
||||
return this._cells[index];
|
||||
}
|
||||
|
||||
getCell(cellHandle: number): ExtHostCell | undefined {
|
||||
@@ -496,37 +337,4 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
getCellIndex(cell: ExtHostCell): number {
|
||||
return this._cells.indexOf(cell);
|
||||
}
|
||||
|
||||
addEdit(item: vscode.NotebookDocumentEditEvent): 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();
|
||||
// }
|
||||
}
|
||||
|
||||
private getEdit(editId: number): vscode.NotebookDocumentEditEvent {
|
||||
const edit = this._edits.get(editId, 0);
|
||||
if (!edit) {
|
||||
throw new Error('No edit found');
|
||||
}
|
||||
|
||||
return edit;
|
||||
}
|
||||
|
||||
disposeEdits(editIds: number[]): void {
|
||||
for (const id of editIds) {
|
||||
this._edits.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,18 @@
|
||||
|
||||
import { readonly } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { addIdToOutput, CellEditType, ICellEditOperation, ICellReplaceEdit, INotebookEditData, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as extHostConverter from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { CellEditType, ICellEditOperation, ICellRange, ICellReplaceEdit, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtHostNotebookDocument } from './extHostNotebookDocument';
|
||||
|
||||
interface INotebookEditData {
|
||||
documentVersionId: number;
|
||||
cellEdits: ICellEditOperation[];
|
||||
}
|
||||
|
||||
class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
|
||||
|
||||
private readonly _documentVersionId: number;
|
||||
@@ -54,17 +59,13 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
|
||||
});
|
||||
}
|
||||
|
||||
replaceCellOutput(index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[]): void {
|
||||
replaceCellOutput(index: number, outputs: vscode.NotebookCellOutput[]): void {
|
||||
this._throwIfFinalized();
|
||||
this._collectedEdits.push({
|
||||
editType: CellEditType.Output,
|
||||
index,
|
||||
outputs: outputs.map(output => {
|
||||
if (extHostTypes.NotebookCellOutput.isNotebookCellOutput(output)) {
|
||||
return addIdToOutput(output.toJSON());
|
||||
} else {
|
||||
return addIdToOutput(output);
|
||||
}
|
||||
return extHostConverter.NotebookCellOutput.from(output);
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -78,62 +79,80 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
|
||||
editType: CellEditType.Replace,
|
||||
index: from,
|
||||
count: to - from,
|
||||
cells: cells.map(data => {
|
||||
return {
|
||||
...data,
|
||||
outputs: data.outputs.map(output => addIdToOutput(output)),
|
||||
};
|
||||
})
|
||||
cells: cells.map(extHostConverter.NotebookCellData.from)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor {
|
||||
export class ExtHostNotebookEditor {
|
||||
private _selection?: vscode.NotebookCell;
|
||||
private _selections: vscode.NotebookCellRange[] = [];
|
||||
|
||||
//TODO@rebornix noop setter?
|
||||
selection?: vscode.NotebookCell;
|
||||
|
||||
private _visibleRanges: vscode.NotebookCellRange[] = [];
|
||||
private _visibleRanges: extHostTypes.NotebookCellRange[] = [];
|
||||
private _viewColumn?: vscode.ViewColumn;
|
||||
private _active: boolean = false;
|
||||
private _visible: boolean = false;
|
||||
private _kernel?: vscode.NotebookKernel;
|
||||
|
||||
private _onDidDispose = new Emitter<void>();
|
||||
private _onDidReceiveMessage = new Emitter<any>();
|
||||
|
||||
readonly onDidDispose: Event<void> = this._onDidDispose.event;
|
||||
readonly onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
|
||||
|
||||
private _hasDecorationsForKey: { [key: string]: boolean; } = Object.create(null);
|
||||
|
||||
private _editor: vscode.NotebookEditor | undefined;
|
||||
|
||||
constructor(
|
||||
readonly id: string,
|
||||
private readonly _viewType: string,
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
private readonly _webComm: vscode.NotebookCommunication,
|
||||
readonly notebookData: ExtHostNotebookDocument,
|
||||
) {
|
||||
super();
|
||||
this._register(this._webComm.onDidReceiveMessage(e => {
|
||||
this._onDidReceiveMessage.fire(e);
|
||||
}));
|
||||
}
|
||||
|
||||
get viewColumn(): vscode.ViewColumn | undefined {
|
||||
return this._viewColumn;
|
||||
dispose() {
|
||||
this._onDidDispose.fire();
|
||||
this._onDidDispose.dispose();
|
||||
}
|
||||
|
||||
set viewColumn(_value) {
|
||||
throw readonly('viewColumn');
|
||||
}
|
||||
|
||||
get kernel() {
|
||||
return this._kernel;
|
||||
}
|
||||
|
||||
set kernel(_kernel: vscode.NotebookKernel | undefined) {
|
||||
throw readonly('kernel');
|
||||
get editor(): vscode.NotebookEditor {
|
||||
if (!this._editor) {
|
||||
const that = this;
|
||||
this._editor = {
|
||||
get document() {
|
||||
return that.notebookData.notebookDocument;
|
||||
},
|
||||
get selection() {
|
||||
return that._selection;
|
||||
},
|
||||
get selections() {
|
||||
return that._selections;
|
||||
},
|
||||
get visibleRanges() {
|
||||
return that._visibleRanges;
|
||||
},
|
||||
revealRange(range, revealType) {
|
||||
that._proxy.$tryRevealRange(that.id, extHostConverter.NotebookCellRange.from(range), revealType ?? extHostTypes.NotebookEditorRevealType.Default);
|
||||
},
|
||||
get viewColumn() {
|
||||
return that._viewColumn;
|
||||
},
|
||||
get onDidDispose() {
|
||||
return that.onDidDispose;
|
||||
},
|
||||
edit(callback) {
|
||||
const edit = new NotebookEditorCellEditBuilder(this.document.version);
|
||||
callback(edit);
|
||||
return that._applyEdit(edit.finalize());
|
||||
},
|
||||
get kernel() {
|
||||
return that._kernel;
|
||||
},
|
||||
setDecorations(decorationType, range) {
|
||||
return that.setDecorations(decorationType, range);
|
||||
}
|
||||
};
|
||||
}
|
||||
return this._editor;
|
||||
}
|
||||
|
||||
_acceptKernel(kernel?: vscode.NotebookKernel) {
|
||||
@@ -152,18 +171,16 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
this._visible = value;
|
||||
}
|
||||
|
||||
get visibleRanges() {
|
||||
return this._visibleRanges;
|
||||
}
|
||||
|
||||
set visibleRanges(_range: vscode.NotebookCellRange[]) {
|
||||
throw readonly('visibleRanges');
|
||||
}
|
||||
|
||||
_acceptVisibleRanges(value: vscode.NotebookCellRange[]): void {
|
||||
_acceptVisibleRanges(value: extHostTypes.NotebookCellRange[]): void {
|
||||
this._visibleRanges = value;
|
||||
}
|
||||
|
||||
_acceptSelections(selections: ICellRange[]): void {
|
||||
const primarySelection = selections[0];
|
||||
this._selection = primarySelection ? this.notebookData.getCellFromIndex(primarySelection.start)?.cell : undefined;
|
||||
this._selections = selections.map(val => new extHostTypes.NotebookCellRange(val.start, val.end));
|
||||
}
|
||||
|
||||
get active(): boolean {
|
||||
return this._active;
|
||||
}
|
||||
@@ -176,16 +193,6 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
this._active = value;
|
||||
}
|
||||
|
||||
get document(): vscode.NotebookDocument {
|
||||
return this.notebookData.notebookDocument;
|
||||
}
|
||||
|
||||
edit(callback: (editBuilder: NotebookEditorCellEditBuilder) => void): Thenable<boolean> {
|
||||
const edit = new NotebookEditorCellEditBuilder(this.document.version);
|
||||
callback(edit);
|
||||
return this._applyEdit(edit.finalize());
|
||||
}
|
||||
|
||||
private _applyEdit(editData: INotebookEditData): Promise<boolean> {
|
||||
|
||||
// return when there is nothing to do
|
||||
@@ -208,7 +215,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
|
||||
if (prev.editType === CellEditType.Replace && editData.cellEdits[i].editType === CellEditType.Replace) {
|
||||
const edit = editData.cellEdits[i];
|
||||
if ((edit.editType !== CellEditType.DocumentMetadata && edit.editType !== CellEditType.Unknown) && prev.index === edit.index) {
|
||||
if ((edit.editType !== CellEditType.DocumentMetadata) && prev.index === edit.index) {
|
||||
prev.cells.push(...(editData.cellEdits[i] as ICellReplaceEdit).cells);
|
||||
prev.count += (editData.cellEdits[i] as ICellReplaceEdit).count;
|
||||
continue;
|
||||
@@ -219,7 +226,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
compressedEditsIndex++;
|
||||
}
|
||||
|
||||
return this._proxy.$tryApplyEdits(this._viewType, this.document.uri, editData.documentVersionId, compressedEdits);
|
||||
return this._proxy.$tryApplyEdits(this._viewType, this.notebookData.uri, editData.documentVersionId, compressedEdits);
|
||||
}
|
||||
|
||||
setDecorations(decorationType: vscode.NotebookEditorDecorationType, range: vscode.NotebookCellRange): void {
|
||||
@@ -236,25 +243,8 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
|
||||
return this._proxy.$trySetDecorations(
|
||||
this.id,
|
||||
range,
|
||||
extHostConverter.NotebookCellRange.from(range),
|
||||
decorationType.key
|
||||
);
|
||||
}
|
||||
|
||||
revealRange(range: vscode.NotebookCellRange, revealType?: extHostTypes.NotebookEditorRevealType) {
|
||||
this._proxy.$tryRevealRange(this.id, range, revealType || extHostTypes.NotebookEditorRevealType.Default);
|
||||
}
|
||||
|
||||
async postMessage(message: any): Promise<boolean> {
|
||||
return this._webComm.postMessage(message);
|
||||
}
|
||||
|
||||
asWebviewUri(localResource: vscode.Uri): vscode.Uri {
|
||||
return this._webComm.asWebviewUri(localResource);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._onDidDispose.fire();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,14 +14,12 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
|
||||
private static ID_GEN = 0;
|
||||
|
||||
private static ALLOWED_BACKGROUND_COLORS = (() => {
|
||||
const map = new Map<string, ThemeColor>();
|
||||
private static ALLOWED_BACKGROUND_COLORS = new Map<string, ThemeColor>(
|
||||
[['statusBarItem.errorBackground', new ThemeColor('statusBarItem.errorForeground')]]
|
||||
);
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/110214
|
||||
map.set('statusBarItem.errorBackground', new ThemeColor('statusBarItem.errorForeground'));
|
||||
|
||||
return map;
|
||||
})();
|
||||
#proxy: MainThreadStatusBarShape;
|
||||
#commands: CommandsConverter;
|
||||
|
||||
private _id: number;
|
||||
private _alignment: number;
|
||||
@@ -43,14 +41,13 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
|
||||
};
|
||||
|
||||
private _timeoutHandle: any;
|
||||
private _proxy: MainThreadStatusBarShape;
|
||||
private _commands: CommandsConverter;
|
||||
private _accessibilityInformation?: vscode.AccessibilityInformation;
|
||||
|
||||
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation) {
|
||||
this.#proxy = proxy;
|
||||
this.#commands = commands;
|
||||
|
||||
this._id = ExtHostStatusBarEntry.ID_GEN++;
|
||||
this._proxy = proxy;
|
||||
this._commands = commands;
|
||||
this._statusId = id;
|
||||
this._statusName = name;
|
||||
this._alignment = alignment;
|
||||
@@ -127,12 +124,12 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
|
||||
if (typeof command === 'string') {
|
||||
this._command = {
|
||||
fromApi: command,
|
||||
internal: this._commands.toInternal({ title: '', command }, this._internalCommandRegistration),
|
||||
internal: this.#commands.toInternal({ title: '', command }, this._internalCommandRegistration),
|
||||
};
|
||||
} else if (command) {
|
||||
this._command = {
|
||||
fromApi: command,
|
||||
internal: this._commands.toInternal(command, this._internalCommandRegistration),
|
||||
internal: this.#commands.toInternal(command, this._internalCommandRegistration),
|
||||
};
|
||||
} else {
|
||||
this._command = undefined;
|
||||
@@ -153,7 +150,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
|
||||
public hide(): void {
|
||||
clearTimeout(this._timeoutHandle);
|
||||
this._visible = false;
|
||||
this._proxy.$dispose(this.id);
|
||||
this.#proxy.$dispose(this.id);
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
@@ -174,7 +171,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
|
||||
}
|
||||
|
||||
// Set to status bar
|
||||
this._proxy.$setEntry(this.id, this._statusId, this._statusName, this._text, this._tooltip, this._command?.internal, color,
|
||||
this.#proxy.$setEntry(this.id, this._statusId, this._statusName, this._text, this._tooltip, this._command?.internal, color,
|
||||
this._backgroundColor, this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT,
|
||||
this._priority, this._accessibilityInformation);
|
||||
}, 0);
|
||||
|
||||
@@ -48,7 +48,7 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths {
|
||||
const storageUri = URI.joinPath(this._environment.workspaceStorageHome, storageName);
|
||||
|
||||
try {
|
||||
await this._extHostFileSystem.stat(storageUri);
|
||||
await this._extHostFileSystem.value.stat(storageUri);
|
||||
this._logService.trace('[ExtHostStorage] storage dir already exists', storageUri);
|
||||
return storageUri;
|
||||
} catch {
|
||||
@@ -57,8 +57,8 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths {
|
||||
|
||||
try {
|
||||
this._logService.trace('[ExtHostStorage] creating dir and metadata-file', storageUri);
|
||||
await this._extHostFileSystem.createDirectory(storageUri);
|
||||
await this._extHostFileSystem.writeFile(
|
||||
await this._extHostFileSystem.value.createDirectory(storageUri);
|
||||
await this._extHostFileSystem.value.writeFile(
|
||||
URI.joinPath(storageUri, 'meta.json'),
|
||||
new TextEncoder().encode(JSON.stringify({
|
||||
id: this._workspace.id,
|
||||
|
||||
@@ -341,7 +341,10 @@ export namespace TaskFilterDTO {
|
||||
|
||||
class TaskExecutionImpl implements vscode.TaskExecution {
|
||||
|
||||
constructor(private readonly _tasks: ExtHostTaskBase, readonly _id: string, private readonly _task: vscode.Task) {
|
||||
readonly #tasks: ExtHostTaskBase;
|
||||
|
||||
constructor(tasks: ExtHostTaskBase, readonly _id: string, private readonly _task: vscode.Task) {
|
||||
this.#tasks = tasks;
|
||||
}
|
||||
|
||||
public get task(): vscode.Task {
|
||||
@@ -349,7 +352,7 @@ class TaskExecutionImpl implements vscode.TaskExecution {
|
||||
}
|
||||
|
||||
public terminate(): void {
|
||||
this._tasks.terminateTask(this);
|
||||
this.#tasks.terminateTask(this);
|
||||
}
|
||||
|
||||
public fireDidStartProcess(value: tasks.TaskProcessStartedDTO): void {
|
||||
|
||||
@@ -9,18 +9,18 @@ import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShap
|
||||
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ITerminalChildProcess, ITerminalLaunchError, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
||||
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType } from './extHostTypes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { localize } from 'vs/nls';
|
||||
import { NotSupportedError } from 'vs/base/common/errors';
|
||||
import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
|
||||
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
|
||||
|
||||
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
|
||||
|
||||
@@ -47,7 +47,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID
|
||||
|
||||
export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');
|
||||
|
||||
export class ExtHostTerminal implements vscode.Terminal {
|
||||
export class ExtHostTerminal {
|
||||
private _disposed: boolean = false;
|
||||
private _pidPromise: Promise<number | undefined>;
|
||||
private _cols: number | undefined;
|
||||
@@ -57,6 +57,8 @@ export class ExtHostTerminal implements vscode.Terminal {
|
||||
|
||||
public isOpen: boolean = false;
|
||||
|
||||
readonly value: vscode.Terminal;
|
||||
|
||||
constructor(
|
||||
private _proxy: MainThreadTerminalServiceShape,
|
||||
public _id: TerminalIdentifier,
|
||||
@@ -65,6 +67,49 @@ export class ExtHostTerminal implements vscode.Terminal {
|
||||
) {
|
||||
this._creationOptions = Object.freeze(this._creationOptions);
|
||||
this._pidPromise = new Promise<number | undefined>(c => this._pidPromiseComplete = c);
|
||||
|
||||
const that = this;
|
||||
this.value = {
|
||||
get name(): string {
|
||||
return that._name || '';
|
||||
},
|
||||
get processId(): Promise<number | undefined> {
|
||||
return that._pidPromise;
|
||||
},
|
||||
get creationOptions(): Readonly<vscode.TerminalOptions | vscode.ExtensionTerminalOptions> {
|
||||
return that._creationOptions;
|
||||
},
|
||||
get exitStatus(): vscode.TerminalExitStatus | undefined {
|
||||
return that._exitStatus;
|
||||
},
|
||||
sendText(text: string, addNewLine: boolean = true): void {
|
||||
that._checkDisposed();
|
||||
that._proxy.$sendText(that._id, text, addNewLine);
|
||||
},
|
||||
show(preserveFocus: boolean): void {
|
||||
that._checkDisposed();
|
||||
that._proxy.$show(that._id, preserveFocus);
|
||||
},
|
||||
hide(): void {
|
||||
that._checkDisposed();
|
||||
that._proxy.$hide(that._id);
|
||||
},
|
||||
dispose(): void {
|
||||
if (!that._disposed) {
|
||||
that._disposed = true;
|
||||
that._proxy.$dispose(that._id);
|
||||
}
|
||||
},
|
||||
get dimensions(): vscode.TerminalDimensions | undefined {
|
||||
if (that._cols === undefined || that._rows === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
columns: that._cols,
|
||||
rows: that._rows
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public async create(
|
||||
@@ -75,12 +120,13 @@ export class ExtHostTerminal implements vscode.Terminal {
|
||||
waitOnExit?: boolean,
|
||||
strictEnv?: boolean,
|
||||
hideFromUser?: boolean,
|
||||
isFeatureTerminal?: boolean
|
||||
isFeatureTerminal?: boolean,
|
||||
isExtensionOwnedTerminal?: boolean
|
||||
): Promise<void> {
|
||||
if (typeof this._id !== 'string') {
|
||||
throw new Error('Terminal has already been created');
|
||||
}
|
||||
await this._proxy.$createTerminal(this._id, { name: this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser, isFeatureTerminal });
|
||||
await this._proxy.$createTerminal(this._id, { name: this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser, isFeatureTerminal, isExtensionOwnedTerminal });
|
||||
}
|
||||
|
||||
public async createExtensionTerminal(): Promise<number> {
|
||||
@@ -95,41 +141,16 @@ export class ExtHostTerminal implements vscode.Terminal {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (!this._disposed) {
|
||||
this._disposed = true;
|
||||
this._proxy.$dispose(this._id);
|
||||
}
|
||||
}
|
||||
|
||||
private _checkDisposed() {
|
||||
if (this._disposed) {
|
||||
throw new Error('Terminal has already been disposed');
|
||||
}
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this._name || '';
|
||||
}
|
||||
|
||||
public set name(name: string) {
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
public get exitStatus(): vscode.TerminalExitStatus | undefined {
|
||||
return this._exitStatus;
|
||||
}
|
||||
|
||||
public get dimensions(): vscode.TerminalDimensions | undefined {
|
||||
if (this._cols === undefined || this._rows === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
columns: this._cols,
|
||||
rows: this._rows
|
||||
};
|
||||
}
|
||||
|
||||
public setExitCode(code: number | undefined) {
|
||||
this._exitStatus = Object.freeze({ code });
|
||||
}
|
||||
@@ -147,29 +168,6 @@ export class ExtHostTerminal implements vscode.Terminal {
|
||||
return true;
|
||||
}
|
||||
|
||||
public get processId(): Promise<number | undefined> {
|
||||
return this._pidPromise;
|
||||
}
|
||||
|
||||
public get creationOptions(): Readonly<vscode.TerminalOptions | vscode.ExtensionTerminalOptions> {
|
||||
return this._creationOptions;
|
||||
}
|
||||
|
||||
public sendText(text: string, addNewLine: boolean = true): void {
|
||||
this._checkDisposed();
|
||||
this._proxy.$sendText(this._id, text, addNewLine);
|
||||
}
|
||||
|
||||
public show(preserveFocus: boolean): void {
|
||||
this._checkDisposed();
|
||||
this._proxy.$show(this._id, preserveFocus);
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
this._checkDisposed();
|
||||
this._proxy.$hide(this._id);
|
||||
}
|
||||
|
||||
public _setProcessId(processId: number | undefined): void {
|
||||
// The event may fire 2 times when the panel is restored
|
||||
if (this._pidPromiseComplete) {
|
||||
@@ -187,6 +185,9 @@ export class ExtHostTerminal implements vscode.Terminal {
|
||||
}
|
||||
|
||||
export class ExtHostPseudoterminal implements ITerminalChildProcess {
|
||||
readonly id = 0;
|
||||
readonly shouldPersist = false;
|
||||
|
||||
private readonly _onProcessData = new Emitter<string>();
|
||||
public readonly onProcessData: Event<string> = this._onProcessData.event;
|
||||
private readonly _onProcessExit = new Emitter<number | undefined>();
|
||||
@@ -284,8 +285,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
private readonly _terminalLinkCache: Map<number, Map<number, ICachedLinkEntry>> = new Map();
|
||||
private readonly _terminalLinkCancellationSource: Map<number, CancellationTokenSource> = new Map();
|
||||
|
||||
public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; }
|
||||
public get terminals(): ExtHostTerminal[] { return this._terminals; }
|
||||
public get activeTerminal(): vscode.Terminal | undefined { return this._activeTerminal?.value; }
|
||||
public get terminals(): vscode.Terminal[] { return this._terminals.map(term => term.value); }
|
||||
|
||||
protected readonly _onDidCloseTerminal: Emitter<vscode.Terminal> = new Emitter<vscode.Terminal>();
|
||||
public get onDidCloseTerminal(): Event<vscode.Terminal> { return this._onDidCloseTerminal && this._onDidCloseTerminal.event; }
|
||||
@@ -336,7 +337,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
this._terminalProcessDisposables[id] = disposable;
|
||||
});
|
||||
this._terminals.push(terminal);
|
||||
return terminal;
|
||||
return terminal.value;
|
||||
}
|
||||
|
||||
public attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void {
|
||||
@@ -362,7 +363,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
if (terminal) {
|
||||
this._activeTerminal = terminal;
|
||||
if (original !== this._activeTerminal) {
|
||||
this._onDidChangeActiveTerminal.fire(this._activeTerminal);
|
||||
this._onDidChangeActiveTerminal.fire(this._activeTerminal.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,7 +371,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
public async $acceptTerminalProcessData(id: number, data: string): Promise<void> {
|
||||
const terminal = this._getTerminalById(id);
|
||||
if (terminal) {
|
||||
this._onDidWriteTerminalData.fire({ terminal, data });
|
||||
this._onDidWriteTerminalData.fire({ terminal: terminal.value, data });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,8 +380,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
if (terminal) {
|
||||
if (terminal.setDimensions(cols, rows)) {
|
||||
this._onDidChangeTerminalDimensions.fire({
|
||||
terminal: terminal,
|
||||
dimensions: terminal.dimensions as vscode.TerminalDimensions
|
||||
terminal: terminal.value,
|
||||
dimensions: terminal.value.dimensions as vscode.TerminalDimensions
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -400,11 +401,11 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
}
|
||||
|
||||
public async $acceptTerminalClosed(id: number, exitCode: number | undefined): Promise<void> {
|
||||
const index = this._getTerminalObjectIndexById(this.terminals, id);
|
||||
const index = this._getTerminalObjectIndexById(this._terminals, id);
|
||||
if (index !== null) {
|
||||
const terminal = this._terminals.splice(index, 1)[0];
|
||||
terminal.setExitCode(exitCode);
|
||||
this._onDidCloseTerminal.fire(terminal);
|
||||
this._onDidCloseTerminal.fire(terminal.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,9 +415,9 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
const index = this._getTerminalObjectIndexById(this._terminals, extHostTerminalId);
|
||||
if (index !== null) {
|
||||
// The terminal has already been created (via createTerminal*), only fire the event
|
||||
this.terminals[index]._id = id;
|
||||
this._terminals[index]._id = id;
|
||||
this._onDidOpenTerminal.fire(this.terminals[index]);
|
||||
this.terminals[index].isOpen = true;
|
||||
this._terminals[index].isOpen = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -431,7 +432,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
};
|
||||
const terminal = new ExtHostTerminal(this._proxy, id, creationOptions, name);
|
||||
this._terminals.push(terminal);
|
||||
this._onDidOpenTerminal.fire(terminal);
|
||||
this._onDidOpenTerminal.fire(terminal.value);
|
||||
terminal.isOpen = true;
|
||||
}
|
||||
|
||||
@@ -455,7 +456,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
await new Promise<void>(r => {
|
||||
// Ensure open is called after onDidOpenTerminal
|
||||
const listener = this.onDidOpenTerminal(async e => {
|
||||
if (e === terminal) {
|
||||
if (e === terminal.value) {
|
||||
listener.dispose();
|
||||
r();
|
||||
}
|
||||
@@ -564,7 +565,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
this._terminalLinkCancellationSource.set(terminalId, cancellationSource);
|
||||
|
||||
const result: ITerminalLinkDto[] = [];
|
||||
const context: vscode.TerminalLinkContext = { terminal, line };
|
||||
const context: vscode.TerminalLinkContext = { terminal: terminal.value, line };
|
||||
const promises: vscode.ProviderResult<{ provider: vscode.TerminalLinkProvider, links: vscode.TerminalLink[] }>[] = [];
|
||||
|
||||
for (const provider of this._linkProviders) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { deepFreeze } from 'vs/base/common/objects';
|
||||
import { isDefined } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
@@ -16,11 +17,11 @@ import { ExtHostTestingResource, ExtHostTestingShape, MainContext, MainThreadTes
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { TestItem } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { TestItem, TestResults, TestState } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { OwnedTestCollection, SingleUseTestCollection } from 'vs/workbench/contrib/testing/common/ownedTestCollection';
|
||||
import { AbstractIncrementalTestCollection, EMPTY_TEST_RESULT, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, InternalTestItemWithChildren, InternalTestResults, RunTestForProviderRequest, RunTestsResult, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { OwnedTestCollection, SingleUseTestCollection, TestPosition } from 'vs/workbench/contrib/testing/common/ownedTestCollection';
|
||||
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, RunTestForProviderRequest, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`;
|
||||
@@ -39,8 +40,8 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
private workspaceObservers: WorkspaceFolderTestObserverFactory;
|
||||
private textDocumentObservers: TextDocumentTestObserverFactory;
|
||||
|
||||
public onLastResultsChanged = this.resultsChangedEmitter.event;
|
||||
public lastResults?: vscode.TestResults;
|
||||
public onResultsChanged = this.resultsChangedEmitter.event;
|
||||
public results: ReadonlyArray<vscode.TestResults> = [];
|
||||
|
||||
constructor(@IExtHostRpcService rpc: IExtHostRpcService, @IExtHostDocumentsAndEditors private readonly documents: IExtHostDocumentsAndEditors, @IExtHostWorkspace private readonly workspace: IExtHostWorkspace) {
|
||||
this.proxy = rpc.getProxy(MainContext.MainThreadTesting);
|
||||
@@ -88,30 +89,39 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
* Implements vscode.test.runTests
|
||||
*/
|
||||
public async runTests(req: vscode.TestRunOptions<vscode.TestItem>, token = CancellationToken.None) {
|
||||
await this.proxy.$runTests({
|
||||
tests: req.tests
|
||||
// Find workspace items first, then owned tests, then document tests.
|
||||
// If a test instance exists in both the workspace and document, prefer
|
||||
// the workspace because it's less ephemeral.
|
||||
.map(test => this.workspaceObservers.getMirroredTestDataByReference(test)
|
||||
?? mapFind(this.testSubscriptions.values(), c => c.collection.getTestByReference(test))
|
||||
?? this.textDocumentObservers.getMirroredTestDataByReference(test))
|
||||
const testListToProviders = (tests: ReadonlyArray<vscode.TestItem>) =>
|
||||
tests
|
||||
.map(this.getInternalTestForReference, this)
|
||||
.filter(isDefined)
|
||||
.map(item => ({ providerId: item.providerId, testId: item.id })),
|
||||
.map(t => ({ providerId: t.providerId, testId: t.item.extId }));
|
||||
|
||||
await this.proxy.$runTests({
|
||||
exclude: req.exclude ? testListToProviders(req.exclude).map(t => t.testId) : undefined,
|
||||
tests: testListToProviders(req.tests),
|
||||
debug: req.debug
|
||||
}, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements vscode.test.publishTestResults
|
||||
*/
|
||||
public publishExtensionProvidedResults(results: vscode.TestResults, persist: boolean): void {
|
||||
this.proxy.$publishExtensionProvidedResults(TestResults.from(generateUuid(), results), persist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates test results shown to extensions.
|
||||
* @override
|
||||
*/
|
||||
public $publishTestResults(results: InternalTestResults): void {
|
||||
const convert = (item: InternalTestItemWithChildren): vscode.RequiredTestItem =>
|
||||
({ ...TestItem.toShallow(item.item), children: item.children.map(convert) });
|
||||
public $publishTestResults(results: ISerializedTestResults[]): void {
|
||||
this.results = Object.freeze(
|
||||
results
|
||||
.map(r => deepFreeze(TestResults.to(r)))
|
||||
.concat(this.results)
|
||||
.sort((a, b) => b.completedAt - a.completedAt)
|
||||
.slice(0, 32),
|
||||
);
|
||||
|
||||
this.lastResults = { tests: results.tests.map(convert) };
|
||||
this.resultsChangedEmitter.fire();
|
||||
}
|
||||
|
||||
@@ -172,6 +182,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
collection.addRoot(hierarchy.root, id);
|
||||
Promise.resolve(hierarchy.discoveredInitialTests).then(() => collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, -1]));
|
||||
hierarchy.onDidChangeTest(e => collection.onItemChange(e, id));
|
||||
hierarchy.onDidInvalidateTest?.(e => {
|
||||
const internal = collection.getTestByReference(e);
|
||||
if (!internal) {
|
||||
console.warn(`Received a TestProvider.onDidInvalidateTest for a test that does not currently exist.`);
|
||||
} else {
|
||||
this.proxy.$retireTest(internal.item.extId);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -219,27 +237,49 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
* providers to be run.
|
||||
* @override
|
||||
*/
|
||||
public async $runTestsForProvider(req: RunTestForProviderRequest, cancellation: CancellationToken): Promise<RunTestsResult> {
|
||||
public async $runTestsForProvider(req: RunTestForProviderRequest, cancellation: CancellationToken): Promise<void> {
|
||||
const provider = this.providers.get(req.providerId);
|
||||
if (!provider || !provider.runTests) {
|
||||
return EMPTY_TEST_RESULT;
|
||||
return;
|
||||
}
|
||||
|
||||
const tests = req.ids.map(id => this.ownedTests.getTestById(id)?.actual)
|
||||
const includeTests = req.ids.map(id => this.ownedTests.getTestById(id)?.[1]).filter(isDefined);
|
||||
const excludeTests = req.excludeExtIds
|
||||
.map(id => this.ownedTests.getTestById(id))
|
||||
.filter(isDefined)
|
||||
// Only send the actual TestItem's to the user to run.
|
||||
.map(t => t instanceof TestItemFilteredWrapper ? t.actual : t);
|
||||
if (!tests.length) {
|
||||
return EMPTY_TEST_RESULT;
|
||||
.filter(([tree, exclude]) =>
|
||||
includeTests.some(include => tree.comparePositions(include, exclude) === TestPosition.IsChild),
|
||||
);
|
||||
|
||||
if (!includeTests.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await provider.runTests({ tests, debug: req.debug }, cancellation);
|
||||
await provider.runTests({
|
||||
setState: (test, state) => {
|
||||
// for test providers that don't support excluding natively,
|
||||
// make sure not to report excluded result otherwise summaries will be off.
|
||||
for (const [tree, exclude] of excludeTests) {
|
||||
const e = tree.comparePositions(exclude, test.id);
|
||||
if (e === TestPosition.IsChild || e === TestPosition.IsSame) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.flushCollectionDiffs();
|
||||
this.proxy.$updateTestStateInRun(req.runId, test.id, TestState.from(state));
|
||||
},
|
||||
tests: includeTests.map(t => TestItemFilteredWrapper.unwrap(t.actual)),
|
||||
exclude: excludeTests.map(([, t]) => TestItemFilteredWrapper.unwrap(t.actual)),
|
||||
debug: req.debug,
|
||||
}, cancellation);
|
||||
|
||||
for (const { collection } of this.testSubscriptions.values()) {
|
||||
collection.flushDiff(); // ensure all states are updated
|
||||
}
|
||||
|
||||
return EMPTY_TEST_RESULT;
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error(e); // so it appears to attached debuggers
|
||||
throw e;
|
||||
@@ -252,10 +292,32 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const { actual, previousChildren, previousEquals, ...item } = owned;
|
||||
const { actual, previousChildren, previousEquals, ...item } = owned[1];
|
||||
return Promise.resolve(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes diff information for all collections to ensure state in the
|
||||
* main thread is updated.
|
||||
*/
|
||||
private flushCollectionDiffs() {
|
||||
for (const { collection } of this.testSubscriptions.values()) {
|
||||
collection.flushDiff();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal test item associated with the reference from the extension.
|
||||
*/
|
||||
private getInternalTestForReference(test: vscode.TestItem) {
|
||||
// Find workspace items first, then owned tests, then document tests.
|
||||
// If a test instance exists in both the workspace and document, prefer
|
||||
// the workspace because it's less ephemeral.
|
||||
return this.workspaceObservers.getMirroredTestDataByReference(test)
|
||||
?? mapFind(this.testSubscriptions.values(), c => c.collection.getTestByReference(test))
|
||||
?? this.textDocumentObservers.getMirroredTestDataByReference(test);
|
||||
}
|
||||
|
||||
private createDefaultDocumentTestHierarchy(provider: vscode.TestProvider, document: vscode.TextDocument, folder: vscode.WorkspaceFolder | undefined): vscode.TestHierarchy<vscode.TestItem> | undefined {
|
||||
if (!folder) {
|
||||
return;
|
||||
@@ -266,6 +328,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
return;
|
||||
}
|
||||
|
||||
const onDidInvalidateTest = new Emitter<vscode.TestItem>();
|
||||
workspaceHierarchy.onDidInvalidateTest?.(node => {
|
||||
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(node, document);
|
||||
if (wrapper.hasNodeMatchingFilter) {
|
||||
onDidInvalidateTest.fire(wrapper);
|
||||
}
|
||||
});
|
||||
|
||||
const onDidChangeTest = new Emitter<vscode.TestItem>();
|
||||
workspaceHierarchy.onDidChangeTest(node => {
|
||||
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(node, document);
|
||||
@@ -308,6 +378,8 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
onDidChangeTest.dispose();
|
||||
TestItemFilteredWrapper.removeFilter(document);
|
||||
},
|
||||
discoveredInitialTests: workspaceHierarchy.discoveredInitialTests,
|
||||
onDidInvalidateTest: onDidInvalidateTest.event,
|
||||
onDidChangeTest: onDidChangeTest.event
|
||||
};
|
||||
}
|
||||
@@ -341,6 +413,14 @@ export class TestItemFilteredWrapper implements vscode.TestItem {
|
||||
return w;
|
||||
}
|
||||
|
||||
public static unwrap(item: vscode.TestItem) {
|
||||
return item instanceof TestItemFilteredWrapper ? item.actual : item;
|
||||
}
|
||||
|
||||
public get id() {
|
||||
return this.actual.id;
|
||||
}
|
||||
|
||||
public get label() {
|
||||
return this.actual.label;
|
||||
}
|
||||
@@ -361,10 +441,6 @@ export class TestItemFilteredWrapper implements vscode.TestItem {
|
||||
return this.actual.runnable;
|
||||
}
|
||||
|
||||
public get state() {
|
||||
return this.actual.state;
|
||||
}
|
||||
|
||||
public get children() {
|
||||
// We only want children that match the filter.
|
||||
return this.getWrappedChildren().filter(child => child.hasNodeMatchingFilter);
|
||||
@@ -462,7 +538,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
|
||||
this.updated.delete(node);
|
||||
|
||||
if (node.parent && this.alreadyRemoved.has(node.parent)) {
|
||||
this.alreadyRemoved.add(node.id);
|
||||
this.alreadyRemoved.add(node.item.extId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -602,8 +678,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
|
||||
* If the test item is a mirrored test item, returns its underlying ID.
|
||||
*/
|
||||
public getMirroredTestDataByReference(item: vscode.TestItem) {
|
||||
const id = getMirroredItemId(item);
|
||||
return id ? this.items.get(id) : undefined;
|
||||
return this.items.get(item.id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -632,12 +707,6 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
|
||||
}
|
||||
}
|
||||
|
||||
const getMirroredItemId = (item: vscode.TestItem) => {
|
||||
return (item as any)[MirroredItemId] as string | undefined;
|
||||
};
|
||||
|
||||
const MirroredItemId = Symbol('MirroredItemId');
|
||||
|
||||
class TestItemFromMirror implements vscode.RequiredTestItem {
|
||||
readonly #internal: MirroredCollectionTestItem;
|
||||
readonly #collection: MirroredTestCollection;
|
||||
@@ -645,7 +714,6 @@ class TestItemFromMirror implements vscode.RequiredTestItem {
|
||||
public get id() { return this.#internal.revived.id!; }
|
||||
public get label() { return this.#internal.revived.label; }
|
||||
public get description() { return this.#internal.revived.description; }
|
||||
public get state() { return this.#internal.revived.state; }
|
||||
public get location() { return this.#internal.revived.location; }
|
||||
public get runnable() { return this.#internal.revived.runnable ?? true; }
|
||||
public get debuggable() { return this.#internal.revived.debuggable ?? false; }
|
||||
@@ -653,8 +721,6 @@ class TestItemFromMirror implements vscode.RequiredTestItem {
|
||||
return this.#collection.getAllAsTestItem(this.#internal.children);
|
||||
}
|
||||
|
||||
get [MirroredItemId]() { return this.#internal.id; }
|
||||
|
||||
constructor(internal: MirroredCollectionTestItem, collection: MirroredTestCollection) {
|
||||
this.#internal = internal;
|
||||
this.#collection = collection;
|
||||
@@ -665,14 +731,13 @@ class TestItemFromMirror implements vscode.RequiredTestItem {
|
||||
id: this.id,
|
||||
label: this.label,
|
||||
description: this.description,
|
||||
state: this.state,
|
||||
location: this.location,
|
||||
runnable: this.runnable,
|
||||
debuggable: this.debuggable,
|
||||
children: this.children.map(c => (c as TestItemFromMirror).toJSON()),
|
||||
|
||||
providerId: this.#internal.providerId,
|
||||
testId: this.#internal.id,
|
||||
testId: this.id,
|
||||
};
|
||||
|
||||
return serialized;
|
||||
|
||||
@@ -10,28 +10,29 @@ import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/model';
|
||||
import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
|
||||
import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
|
||||
export class TextEditorDecorationType implements vscode.TextEditorDecorationType {
|
||||
export class TextEditorDecorationType {
|
||||
|
||||
private static readonly _Keys = new IdGenerator('TextEditorDecorationType');
|
||||
|
||||
private _proxy: MainThreadTextEditorsShape;
|
||||
public key: string;
|
||||
readonly value: vscode.TextEditorDecorationType;
|
||||
|
||||
constructor(proxy: MainThreadTextEditorsShape, options: vscode.DecorationRenderOptions) {
|
||||
this.key = TextEditorDecorationType._Keys.nextId();
|
||||
this._proxy = proxy;
|
||||
this._proxy.$registerTextEditorDecorationType(this.key, TypeConverters.DecorationRenderOptions.from(options));
|
||||
const key = TextEditorDecorationType._Keys.nextId();
|
||||
proxy.$registerTextEditorDecorationType(key, TypeConverters.DecorationRenderOptions.from(options));
|
||||
this.value = Object.freeze({
|
||||
key,
|
||||
dispose() {
|
||||
proxy.$removeTextEditorDecorationType(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._proxy.$removeTextEditorDecorationType(this.key);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITextEditOperation {
|
||||
@@ -134,36 +135,63 @@ export class TextEditorEdit {
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
|
||||
export class ExtHostTextEditorOptions {
|
||||
|
||||
private _proxy: MainThreadTextEditorsShape;
|
||||
private _id: string;
|
||||
private _logService: ILogService;
|
||||
|
||||
private _tabSize!: number;
|
||||
private _indentSize!: number;
|
||||
private _insertSpaces!: boolean;
|
||||
private _cursorStyle!: TextEditorCursorStyle;
|
||||
private _lineNumbers!: TextEditorLineNumbersStyle;
|
||||
|
||||
readonly value: vscode.TextEditorOptions;
|
||||
|
||||
constructor(proxy: MainThreadTextEditorsShape, id: string, source: IResolvedTextEditorConfiguration, logService: ILogService) {
|
||||
this._proxy = proxy;
|
||||
this._id = id;
|
||||
this._accept(source);
|
||||
this._logService = logService;
|
||||
|
||||
const that = this;
|
||||
|
||||
this.value = {
|
||||
get tabSize(): number | string {
|
||||
return that._tabSize;
|
||||
},
|
||||
set tabSize(value: number | string) {
|
||||
that._setTabSize(value);
|
||||
},
|
||||
get insertSpaces(): boolean | string {
|
||||
return that._insertSpaces;
|
||||
},
|
||||
set insertSpaces(value: boolean | string) {
|
||||
that._setInsertSpaces(value);
|
||||
},
|
||||
get cursorStyle(): TextEditorCursorStyle {
|
||||
return that._cursorStyle;
|
||||
},
|
||||
set cursorStyle(value: TextEditorCursorStyle) {
|
||||
that._setCursorStyle(value);
|
||||
},
|
||||
get lineNumbers(): TextEditorLineNumbersStyle {
|
||||
return that._lineNumbers;
|
||||
},
|
||||
set lineNumbers(value: TextEditorLineNumbersStyle) {
|
||||
that._setLineNumbers(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public _accept(source: IResolvedTextEditorConfiguration): void {
|
||||
this._tabSize = source.tabSize;
|
||||
this._indentSize = source.indentSize;
|
||||
this._insertSpaces = source.insertSpaces;
|
||||
this._cursorStyle = source.cursorStyle;
|
||||
this._lineNumbers = TypeConverters.TextEditorLineNumbersStyle.to(source.lineNumbers);
|
||||
}
|
||||
|
||||
public get tabSize(): number | string {
|
||||
return this._tabSize;
|
||||
}
|
||||
// --- internal: tabSize
|
||||
|
||||
private _validateTabSize(value: number | string): number | 'auto' | null {
|
||||
if (value === 'auto') {
|
||||
@@ -183,7 +211,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
|
||||
return null;
|
||||
}
|
||||
|
||||
public set tabSize(value: number | string) {
|
||||
private _setTabSize(value: number | string) {
|
||||
const tabSize = this._validateTabSize(value);
|
||||
if (tabSize === null) {
|
||||
// ignore invalid call
|
||||
@@ -202,50 +230,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
|
||||
}));
|
||||
}
|
||||
|
||||
public get indentSize(): number | string {
|
||||
return this._indentSize;
|
||||
}
|
||||
|
||||
private _validateIndentSize(value: number | string): number | 'tabSize' | null {
|
||||
if (value === 'tabSize') {
|
||||
return 'tabSize';
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
const r = Math.floor(value);
|
||||
return (r > 0 ? r : null);
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
const r = parseInt(value, 10);
|
||||
if (isNaN(r)) {
|
||||
return null;
|
||||
}
|
||||
return (r > 0 ? r : null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public set indentSize(value: number | string) {
|
||||
const indentSize = this._validateIndentSize(value);
|
||||
if (indentSize === null) {
|
||||
// ignore invalid call
|
||||
return;
|
||||
}
|
||||
if (typeof indentSize === 'number') {
|
||||
if (this._indentSize === indentSize) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
// reflect the new indentSize value immediately
|
||||
this._indentSize = indentSize;
|
||||
}
|
||||
this._warnOnError(this._proxy.$trySetOptions(this._id, {
|
||||
indentSize: indentSize
|
||||
}));
|
||||
}
|
||||
|
||||
public get insertSpaces(): boolean | string {
|
||||
return this._insertSpaces;
|
||||
}
|
||||
// --- internal: insert spaces
|
||||
|
||||
private _validateInsertSpaces(value: boolean | string): boolean | 'auto' {
|
||||
if (value === 'auto') {
|
||||
@@ -254,7 +239,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
|
||||
return (value === 'false' ? false : Boolean(value));
|
||||
}
|
||||
|
||||
public set insertSpaces(value: boolean | string) {
|
||||
private _setInsertSpaces(value: boolean | string) {
|
||||
const insertSpaces = this._validateInsertSpaces(value);
|
||||
if (typeof insertSpaces === 'boolean') {
|
||||
if (this._insertSpaces === insertSpaces) {
|
||||
@@ -269,11 +254,9 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
|
||||
}));
|
||||
}
|
||||
|
||||
public get cursorStyle(): TextEditorCursorStyle {
|
||||
return this._cursorStyle;
|
||||
}
|
||||
// --- internal: cursor style
|
||||
|
||||
public set cursorStyle(value: TextEditorCursorStyle) {
|
||||
private _setCursorStyle(value: TextEditorCursorStyle) {
|
||||
if (this._cursorStyle === value) {
|
||||
// nothing to do
|
||||
return;
|
||||
@@ -284,11 +267,9 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
|
||||
}));
|
||||
}
|
||||
|
||||
public get lineNumbers(): TextEditorLineNumbersStyle {
|
||||
return this._lineNumbers;
|
||||
}
|
||||
// --- internal: line number
|
||||
|
||||
public set lineNumbers(value: TextEditorLineNumbersStyle) {
|
||||
private _setLineNumbers(value: TextEditorLineNumbersStyle) {
|
||||
if (this._lineNumbers === value) {
|
||||
// nothing to do
|
||||
return;
|
||||
@@ -368,31 +349,170 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostTextEditor implements vscode.TextEditor {
|
||||
|
||||
private readonly _documentData: ExtHostDocumentData;
|
||||
export class ExtHostTextEditor {
|
||||
|
||||
private _selections: Selection[];
|
||||
private _options: ExtHostTextEditorOptions;
|
||||
private _visibleRanges: Range[];
|
||||
private _viewColumn: vscode.ViewColumn | undefined;
|
||||
private _disposed: boolean = false;
|
||||
private _hasDecorationsForKey: { [key: string]: boolean; };
|
||||
private _hasDecorationsForKey = new Set<string>();
|
||||
|
||||
readonly value: vscode.TextEditor;
|
||||
|
||||
constructor(
|
||||
readonly id: string,
|
||||
private readonly _proxy: MainThreadTextEditorsShape,
|
||||
private readonly _logService: ILogService,
|
||||
document: ExtHostDocumentData,
|
||||
document: Lazy<vscode.TextDocument>,
|
||||
selections: Selection[], options: IResolvedTextEditorConfiguration,
|
||||
visibleRanges: Range[], viewColumn: vscode.ViewColumn | undefined
|
||||
) {
|
||||
this._documentData = document;
|
||||
this._selections = selections;
|
||||
this._options = new ExtHostTextEditorOptions(this._proxy, this.id, options, _logService);
|
||||
this._visibleRanges = visibleRanges;
|
||||
this._viewColumn = viewColumn;
|
||||
this._hasDecorationsForKey = Object.create(null);
|
||||
|
||||
const that = this;
|
||||
|
||||
this.value = Object.freeze({
|
||||
get document(): vscode.TextDocument {
|
||||
return document.getValue();
|
||||
},
|
||||
set document(_value) {
|
||||
throw readonly('document');
|
||||
},
|
||||
// --- selection
|
||||
get selection(): Selection {
|
||||
return that._selections && that._selections[0];
|
||||
},
|
||||
set selection(value: Selection) {
|
||||
if (!(value instanceof Selection)) {
|
||||
throw illegalArgument('selection');
|
||||
}
|
||||
that._selections = [value];
|
||||
that._trySetSelection();
|
||||
},
|
||||
get selections(): Selection[] {
|
||||
return that._selections;
|
||||
},
|
||||
set selections(value: Selection[]) {
|
||||
if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) {
|
||||
throw illegalArgument('selections');
|
||||
}
|
||||
that._selections = value;
|
||||
that._trySetSelection();
|
||||
},
|
||||
// --- visible ranges
|
||||
get visibleRanges(): Range[] {
|
||||
return that._visibleRanges;
|
||||
},
|
||||
set visibleRanges(_value: Range[]) {
|
||||
throw readonly('visibleRanges');
|
||||
},
|
||||
// --- options
|
||||
get options(): vscode.TextEditorOptions {
|
||||
return that._options.value;
|
||||
},
|
||||
set options(value: vscode.TextEditorOptions) {
|
||||
if (!that._disposed) {
|
||||
that._options.assign(value);
|
||||
}
|
||||
},
|
||||
// --- view column
|
||||
get viewColumn(): vscode.ViewColumn | undefined {
|
||||
return that._viewColumn;
|
||||
},
|
||||
set viewColumn(_value) {
|
||||
throw readonly('viewColumn');
|
||||
},
|
||||
// --- edit
|
||||
edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
|
||||
if (that._disposed) {
|
||||
return Promise.reject(new Error('TextEditor#edit not possible on closed editors'));
|
||||
}
|
||||
const edit = new TextEditorEdit(document.getValue(), options);
|
||||
callback(edit);
|
||||
return that._applyEdit(edit);
|
||||
},
|
||||
// --- snippet edit
|
||||
insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
|
||||
if (that._disposed) {
|
||||
return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors'));
|
||||
}
|
||||
let ranges: IRange[];
|
||||
|
||||
if (!where || (Array.isArray(where) && where.length === 0)) {
|
||||
ranges = that._selections.map(range => TypeConverters.Range.from(range));
|
||||
|
||||
} else if (where instanceof Position) {
|
||||
const { lineNumber, column } = TypeConverters.Position.from(where);
|
||||
ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }];
|
||||
|
||||
} else if (where instanceof Range) {
|
||||
ranges = [TypeConverters.Range.from(where)];
|
||||
} else {
|
||||
ranges = [];
|
||||
for (const posOrRange of where) {
|
||||
if (posOrRange instanceof Range) {
|
||||
ranges.push(TypeConverters.Range.from(posOrRange));
|
||||
} else {
|
||||
const { lineNumber, column } = TypeConverters.Position.from(posOrRange);
|
||||
ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column });
|
||||
}
|
||||
}
|
||||
}
|
||||
return _proxy.$tryInsertSnippet(id, snippet.value, ranges, options);
|
||||
},
|
||||
setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void {
|
||||
const willBeEmpty = (ranges.length === 0);
|
||||
if (willBeEmpty && !that._hasDecorationsForKey.has(decorationType.key)) {
|
||||
// avoid no-op call to the renderer
|
||||
return;
|
||||
}
|
||||
if (willBeEmpty) {
|
||||
that._hasDecorationsForKey.delete(decorationType.key);
|
||||
} else {
|
||||
that._hasDecorationsForKey.add(decorationType.key);
|
||||
}
|
||||
that._runOnProxy(() => {
|
||||
if (TypeConverters.isDecorationOptionsArr(ranges)) {
|
||||
return _proxy.$trySetDecorations(
|
||||
id,
|
||||
decorationType.key,
|
||||
TypeConverters.fromRangeOrRangeWithMessage(ranges)
|
||||
);
|
||||
} else {
|
||||
const _ranges: number[] = new Array<number>(4 * ranges.length);
|
||||
for (let i = 0, len = ranges.length; i < len; i++) {
|
||||
const range = ranges[i];
|
||||
_ranges[4 * i] = range.start.line + 1;
|
||||
_ranges[4 * i + 1] = range.start.character + 1;
|
||||
_ranges[4 * i + 2] = range.end.line + 1;
|
||||
_ranges[4 * i + 3] = range.end.character + 1;
|
||||
}
|
||||
return _proxy.$trySetDecorationsFast(
|
||||
id,
|
||||
decorationType.key,
|
||||
_ranges
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
revealRange(range: Range, revealType: vscode.TextEditorRevealType): void {
|
||||
that._runOnProxy(() => _proxy.$tryRevealRange(
|
||||
id,
|
||||
TypeConverters.Range.from(range),
|
||||
(revealType || TextEditorRevealType.Default)
|
||||
));
|
||||
},
|
||||
show(column: vscode.ViewColumn) {
|
||||
_proxy.$tryShowEditor(id, TypeConverters.ViewColumn.from(column));
|
||||
},
|
||||
hide() {
|
||||
_proxy.$tryHideEditor(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -400,164 +520,32 @@ export class ExtHostTextEditor implements vscode.TextEditor {
|
||||
this._disposed = true;
|
||||
}
|
||||
|
||||
show(column: vscode.ViewColumn) {
|
||||
this._proxy.$tryShowEditor(this.id, TypeConverters.ViewColumn.from(column));
|
||||
}
|
||||
|
||||
hide() {
|
||||
this._proxy.$tryHideEditor(this.id);
|
||||
}
|
||||
|
||||
// ---- the document
|
||||
|
||||
get document(): vscode.TextDocument {
|
||||
return this._documentData.document;
|
||||
}
|
||||
|
||||
set document(value) {
|
||||
throw readonly('document');
|
||||
}
|
||||
|
||||
// ---- options
|
||||
|
||||
get options(): vscode.TextEditorOptions {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
set options(value: vscode.TextEditorOptions) {
|
||||
if (!this._disposed) {
|
||||
this._options.assign(value);
|
||||
}
|
||||
}
|
||||
// --- incoming: extension host MUST accept what the renderer says
|
||||
|
||||
_acceptOptions(options: IResolvedTextEditorConfiguration): void {
|
||||
ok(!this._disposed);
|
||||
this._options._accept(options);
|
||||
}
|
||||
|
||||
// ---- visible ranges
|
||||
|
||||
get visibleRanges(): Range[] {
|
||||
return this._visibleRanges;
|
||||
}
|
||||
|
||||
set visibleRanges(value: Range[]) {
|
||||
throw readonly('visibleRanges');
|
||||
}
|
||||
|
||||
_acceptVisibleRanges(value: Range[]): void {
|
||||
ok(!this._disposed);
|
||||
this._visibleRanges = value;
|
||||
}
|
||||
|
||||
// ---- view column
|
||||
|
||||
get viewColumn(): vscode.ViewColumn | undefined {
|
||||
return this._viewColumn;
|
||||
}
|
||||
|
||||
set viewColumn(value) {
|
||||
throw readonly('viewColumn');
|
||||
}
|
||||
|
||||
_acceptViewColumn(value: vscode.ViewColumn) {
|
||||
ok(!this._disposed);
|
||||
this._viewColumn = value;
|
||||
}
|
||||
|
||||
// ---- selections
|
||||
|
||||
get selection(): Selection {
|
||||
return this._selections && this._selections[0];
|
||||
}
|
||||
|
||||
set selection(value: Selection) {
|
||||
if (!(value instanceof Selection)) {
|
||||
throw illegalArgument('selection');
|
||||
}
|
||||
this._selections = [value];
|
||||
this._trySetSelection();
|
||||
}
|
||||
|
||||
get selections(): Selection[] {
|
||||
return this._selections;
|
||||
}
|
||||
|
||||
set selections(value: Selection[]) {
|
||||
if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) {
|
||||
throw illegalArgument('selections');
|
||||
}
|
||||
this._selections = value;
|
||||
this._trySetSelection();
|
||||
}
|
||||
|
||||
setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void {
|
||||
const willBeEmpty = (ranges.length === 0);
|
||||
if (willBeEmpty && !this._hasDecorationsForKey[decorationType.key]) {
|
||||
// avoid no-op call to the renderer
|
||||
return;
|
||||
}
|
||||
if (willBeEmpty) {
|
||||
delete this._hasDecorationsForKey[decorationType.key];
|
||||
} else {
|
||||
this._hasDecorationsForKey[decorationType.key] = true;
|
||||
}
|
||||
this._runOnProxy(
|
||||
() => {
|
||||
if (TypeConverters.isDecorationOptionsArr(ranges)) {
|
||||
return this._proxy.$trySetDecorations(
|
||||
this.id,
|
||||
decorationType.key,
|
||||
TypeConverters.fromRangeOrRangeWithMessage(ranges)
|
||||
);
|
||||
} else {
|
||||
const _ranges: number[] = new Array<number>(4 * ranges.length);
|
||||
for (let i = 0, len = ranges.length; i < len; i++) {
|
||||
const range = ranges[i];
|
||||
_ranges[4 * i] = range.start.line + 1;
|
||||
_ranges[4 * i + 1] = range.start.character + 1;
|
||||
_ranges[4 * i + 2] = range.end.line + 1;
|
||||
_ranges[4 * i + 3] = range.end.character + 1;
|
||||
}
|
||||
return this._proxy.$trySetDecorationsFast(
|
||||
this.id,
|
||||
decorationType.key,
|
||||
_ranges
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
revealRange(range: Range, revealType: vscode.TextEditorRevealType): void {
|
||||
this._runOnProxy(
|
||||
() => this._proxy.$tryRevealRange(
|
||||
this.id,
|
||||
TypeConverters.Range.from(range),
|
||||
(revealType || TextEditorRevealType.Default)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _trySetSelection(): Promise<vscode.TextEditor | null | undefined> {
|
||||
const selection = this._selections.map(TypeConverters.Selection.from);
|
||||
return this._runOnProxy(() => this._proxy.$trySetSelections(this.id, selection));
|
||||
}
|
||||
|
||||
_acceptSelections(selections: Selection[]): void {
|
||||
ok(!this._disposed);
|
||||
this._selections = selections;
|
||||
}
|
||||
|
||||
// ---- editing
|
||||
|
||||
edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
|
||||
if (this._disposed) {
|
||||
return Promise.reject(new Error('TextEditor#edit not possible on closed editors'));
|
||||
}
|
||||
const edit = new TextEditorEdit(this._documentData.document, options);
|
||||
callback(edit);
|
||||
return this._applyEdit(edit);
|
||||
private async _trySetSelection(): Promise<vscode.TextEditor | null | undefined> {
|
||||
const selection = this._selections.map(TypeConverters.Selection.from);
|
||||
await this._runOnProxy(() => this._proxy.$trySetSelections(this.id, selection));
|
||||
return this.value;
|
||||
}
|
||||
|
||||
private _applyEdit(editBuilder: TextEditorEdit): Promise<boolean> {
|
||||
@@ -613,44 +601,12 @@ export class ExtHostTextEditor implements vscode.TextEditor {
|
||||
undoStopAfter: editData.undoStopAfter
|
||||
});
|
||||
}
|
||||
|
||||
insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
|
||||
if (this._disposed) {
|
||||
return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors'));
|
||||
}
|
||||
let ranges: IRange[];
|
||||
|
||||
if (!where || (Array.isArray(where) && where.length === 0)) {
|
||||
ranges = this._selections.map(range => TypeConverters.Range.from(range));
|
||||
|
||||
} else if (where instanceof Position) {
|
||||
const { lineNumber, column } = TypeConverters.Position.from(where);
|
||||
ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }];
|
||||
|
||||
} else if (where instanceof Range) {
|
||||
ranges = [TypeConverters.Range.from(where)];
|
||||
} else {
|
||||
ranges = [];
|
||||
for (const posOrRange of where) {
|
||||
if (posOrRange instanceof Range) {
|
||||
ranges.push(TypeConverters.Range.from(posOrRange));
|
||||
} else {
|
||||
const { lineNumber, column } = TypeConverters.Position.from(posOrRange);
|
||||
ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this._proxy.$tryInsertSnippet(this.id, snippet.value, ranges, options);
|
||||
}
|
||||
|
||||
// ---- util
|
||||
|
||||
private _runOnProxy(callback: () => Promise<any>): Promise<ExtHostTextEditor | undefined | null> {
|
||||
if (this._disposed) {
|
||||
this._logService.warn('TextEditor is closed/disposed');
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return callback().then(() => this, err => {
|
||||
if (!(err instanceof Error && err.name === 'DISPOSED')) {
|
||||
this._logService.warn(err);
|
||||
@@ -659,4 +615,3 @@ export class ExtHostTextEditor implements vscode.TextEditor {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,12 +41,17 @@ export class ExtHostEditors implements ExtHostEditorsShape {
|
||||
this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(e => this._onDidChangeActiveTextEditor.fire(e));
|
||||
}
|
||||
|
||||
getActiveTextEditor(): ExtHostTextEditor | undefined {
|
||||
getActiveTextEditor(): vscode.TextEditor | undefined {
|
||||
return this._extHostDocumentsAndEditors.activeEditor();
|
||||
}
|
||||
|
||||
getVisibleTextEditors(): vscode.TextEditor[] {
|
||||
return this._extHostDocumentsAndEditors.allEditors();
|
||||
getVisibleTextEditors(): vscode.TextEditor[];
|
||||
getVisibleTextEditors(internal: true): ExtHostTextEditor[];
|
||||
getVisibleTextEditors(internal?: true): ExtHostTextEditor[] | vscode.TextEditor[] {
|
||||
const editors = this._extHostDocumentsAndEditors.allEditors();
|
||||
return internal
|
||||
? editors
|
||||
: editors.map(editor => editor.value);
|
||||
}
|
||||
|
||||
showTextDocument(document: vscode.TextDocument, column: vscode.ViewColumn, preserveFocus: boolean): Promise<vscode.TextEditor>;
|
||||
@@ -75,7 +80,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
|
||||
const editorId = await this._proxy.$tryShowTextDocument(document.uri, options);
|
||||
const editor = editorId && this._extHostDocumentsAndEditors.getEditor(editorId);
|
||||
if (editor) {
|
||||
return editor;
|
||||
return editor.value;
|
||||
}
|
||||
// we have no editor... having an id means that we had an editor
|
||||
// on the main side and that it isn't the current editor anymore...
|
||||
@@ -87,7 +92,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
|
||||
}
|
||||
|
||||
createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType {
|
||||
return new TextEditorDecorationType(this._proxy, options);
|
||||
return new TextEditorDecorationType(this._proxy, options).value;
|
||||
}
|
||||
|
||||
// --- called from main thread
|
||||
@@ -114,7 +119,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
|
||||
// (2) fire change events
|
||||
if (data.options) {
|
||||
this._onDidChangeTextEditorOptions.fire({
|
||||
textEditor: textEditor,
|
||||
textEditor: textEditor.value,
|
||||
options: { ...data.options, lineNumbers: TypeConverters.TextEditorLineNumbersStyle.to(data.options.lineNumbers) }
|
||||
});
|
||||
}
|
||||
@@ -122,7 +127,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
|
||||
const kind = TextEditorSelectionChangeKind.fromValue(data.selections.source);
|
||||
const selections = data.selections.selections.map(TypeConverters.Selection.to);
|
||||
this._onDidChangeTextEditorSelection.fire({
|
||||
textEditor,
|
||||
textEditor: textEditor.value,
|
||||
selections,
|
||||
kind
|
||||
});
|
||||
@@ -130,7 +135,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
|
||||
if (data.visibleRanges) {
|
||||
const visibleRanges = arrays.coalesce(data.visibleRanges.map(TypeConverters.Range.to));
|
||||
this._onDidChangeTextEditorVisibleRanges.fire({
|
||||
textEditor,
|
||||
textEditor: textEditor.value,
|
||||
visibleRanges
|
||||
});
|
||||
}
|
||||
@@ -143,9 +148,9 @@ export class ExtHostEditors implements ExtHostEditorsShape {
|
||||
throw new Error('Unknown text editor');
|
||||
}
|
||||
const viewColumn = TypeConverters.ViewColumn.to(data[id]);
|
||||
if (textEditor.viewColumn !== viewColumn) {
|
||||
if (textEditor.value.viewColumn !== viewColumn) {
|
||||
textEditor._acceptViewColumn(viewColumn);
|
||||
this._onDidChangeTextEditorViewColumn.fire({ textEditor, viewColumn });
|
||||
this._onDidChangeTextEditorViewColumn.fire({ textEditor: textEditor.value, viewColumn });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
if (!options || !options.treeDataProvider) {
|
||||
throw new Error('Options with treeDataProvider is mandatory');
|
||||
}
|
||||
|
||||
const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany });
|
||||
const treeView = this.createExtHostTreeView(viewId, options, extension);
|
||||
return {
|
||||
get onDidCollapseElement() { return treeView.onDidCollapseElement; },
|
||||
@@ -110,7 +110,9 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
reveal: (element: T, options?: IRevealOptions): Promise<void> => {
|
||||
return treeView.reveal(element, options);
|
||||
},
|
||||
dispose: () => {
|
||||
dispose: async () => {
|
||||
// Wait for the registration promise to finish before doing the dispose.
|
||||
await registerPromise;
|
||||
this.treeViews.delete(viewId);
|
||||
treeView.dispose();
|
||||
}
|
||||
@@ -240,7 +242,6 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
}
|
||||
}
|
||||
this.dataProvider = options.treeDataProvider;
|
||||
this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany });
|
||||
if (this.dataProvider.onDidChangeTreeData) {
|
||||
this._register(this.dataProvider.onDidChangeTreeData(element => this._onDidChangeData.fire({ message: false, element })));
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import * as types from './extHostTypes';
|
||||
import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { ITextEditorOptions, EditorOverride } from 'vs/platform/editor/common/editor';
|
||||
import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -22,7 +22,7 @@ import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/platform/markers/common/markers';
|
||||
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { isString, isNumber } from 'vs/base/common/types';
|
||||
import { isString, isNumber, isDefined } from 'vs/base/common/types';
|
||||
import * as marked from 'vs/base/common/marked/marked';
|
||||
import { parse } from 'vs/base/common/marshalling';
|
||||
import { cloneAndChange } from 'vs/base/common/objects';
|
||||
@@ -31,8 +31,8 @@ import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
|
||||
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { CellOutputKind, IDisplayOutput, INotebookDecorationRenderOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ITestItem, ITestState } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ISerializedTestResults, ITestItem, ITestState, SerializedTestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
|
||||
export interface PositionLike {
|
||||
line: number;
|
||||
@@ -509,7 +509,7 @@ export namespace TextEdit {
|
||||
}
|
||||
|
||||
export namespace WorkspaceEdit {
|
||||
export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors, notebooks?: ExtHostNotebookController): extHostProtocol.IWorkspaceEditDto {
|
||||
export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors, extHostNotebooks?: ExtHostNotebookController): extHostProtocol.IWorkspaceEditDto {
|
||||
const result: extHostProtocol.IWorkspaceEditDto = {
|
||||
edits: []
|
||||
};
|
||||
@@ -544,7 +544,61 @@ export namespace WorkspaceEdit {
|
||||
resource: entry.uri,
|
||||
edit: entry.edit,
|
||||
notebookMetadata: entry.notebookMetadata,
|
||||
notebookVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version
|
||||
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version
|
||||
});
|
||||
|
||||
} else if (entry._type === types.FileEditType.CellOutput) {
|
||||
if (entry.newOutputs) {
|
||||
result.edits.push({
|
||||
_type: extHostProtocol.WorkspaceEditType.Cell,
|
||||
metadata: entry.metadata,
|
||||
resource: entry.uri,
|
||||
edit: {
|
||||
editType: notebooks.CellEditType.Output,
|
||||
index: entry.index,
|
||||
append: entry.append,
|
||||
outputs: entry.newOutputs.map(NotebookCellOutput.from)
|
||||
}
|
||||
});
|
||||
}
|
||||
// todo@joh merge metadata and output edit?
|
||||
if (entry.newMetadata) {
|
||||
result.edits.push({
|
||||
_type: extHostProtocol.WorkspaceEditType.Cell,
|
||||
metadata: entry.metadata,
|
||||
resource: entry.uri,
|
||||
edit: {
|
||||
editType: notebooks.CellEditType.Metadata,
|
||||
index: entry.index,
|
||||
metadata: entry.newMetadata
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (entry._type === types.FileEditType.CellReplace) {
|
||||
result.edits.push({
|
||||
_type: extHostProtocol.WorkspaceEditType.Cell,
|
||||
metadata: entry.metadata,
|
||||
resource: entry.uri,
|
||||
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version,
|
||||
edit: {
|
||||
editType: notebooks.CellEditType.Replace,
|
||||
index: entry.index,
|
||||
count: entry.count,
|
||||
cells: entry.cells.map(NotebookCellData.from)
|
||||
}
|
||||
});
|
||||
} else if (entry._type === types.FileEditType.CellOutputItem) {
|
||||
result.edits.push({
|
||||
_type: extHostProtocol.WorkspaceEditType.Cell,
|
||||
metadata: entry.metadata,
|
||||
resource: entry.uri,
|
||||
edit: {
|
||||
editType: notebooks.CellEditType.OutputItems,
|
||||
index: entry.index,
|
||||
outputId: entry.outputId,
|
||||
items: entry.newOutputItems?.map(NotebookCellOutputItem.from) || [],
|
||||
append: entry.append
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -796,6 +850,66 @@ export namespace EvaluatableExpression {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace InlineValue {
|
||||
export function from(inlineValue: vscode.InlineValue): modes.InlineValue {
|
||||
if (inlineValue instanceof types.InlineValueText) {
|
||||
return <modes.InlineValueText>{
|
||||
type: 'text',
|
||||
range: Range.from(inlineValue.range),
|
||||
text: inlineValue.text
|
||||
};
|
||||
} else if (inlineValue instanceof types.InlineValueVariableLookup) {
|
||||
return <modes.InlineValueVariableLookup>{
|
||||
type: 'variable',
|
||||
range: Range.from(inlineValue.range),
|
||||
variableName: inlineValue.variableName,
|
||||
caseSensitiveLookup: inlineValue.caseSensitiveLookup
|
||||
};
|
||||
} else if (inlineValue instanceof types.InlineValueEvaluatableExpression) {
|
||||
return <modes.InlineValueExpression>{
|
||||
type: 'expression',
|
||||
range: Range.from(inlineValue.range),
|
||||
expression: inlineValue.expression
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Unknown 'InlineValue' type`);
|
||||
}
|
||||
}
|
||||
|
||||
export function to(inlineValue: modes.InlineValue): vscode.InlineValue {
|
||||
switch (inlineValue.type) {
|
||||
case 'text':
|
||||
return <vscode.InlineValueText>{
|
||||
range: Range.to(inlineValue.range),
|
||||
text: inlineValue.text
|
||||
};
|
||||
case 'variable':
|
||||
return <vscode.InlineValueVariableLookup>{
|
||||
range: Range.to(inlineValue.range),
|
||||
variableName: inlineValue.variableName,
|
||||
caseSensitiveLookup: inlineValue.caseSensitiveLookup
|
||||
};
|
||||
case 'expression':
|
||||
return <vscode.InlineValueEvaluatableExpression>{
|
||||
range: Range.to(inlineValue.range),
|
||||
expression: inlineValue.expression
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace InlineValueContext {
|
||||
export function from(inlineValueContext: vscode.InlineValueContext): extHostProtocol.IInlineValueContextDto {
|
||||
return <extHostProtocol.IInlineValueContextDto>{
|
||||
stoppedLocation: Range.from(inlineValueContext.stoppedLocation)
|
||||
};
|
||||
}
|
||||
|
||||
export function to(inlineValueContext: extHostProtocol.IInlineValueContextDto): types.InlineValueContext {
|
||||
return new types.InlineValueContext(Range.to(inlineValueContext.stoppedLocation));
|
||||
}
|
||||
}
|
||||
|
||||
export namespace DocumentHighlight {
|
||||
export function from(documentHighlight: vscode.DocumentHighlight): modes.DocumentHighlight {
|
||||
return {
|
||||
@@ -1021,6 +1135,7 @@ export namespace InlineHint {
|
||||
return {
|
||||
text: hint.text,
|
||||
range: Range.from(hint.range),
|
||||
kind: InlineHintKind.from(hint.kind ?? types.InlineHintKind.Other),
|
||||
description: hint.description && MarkdownString.fromStrict(hint.description),
|
||||
whitespaceBefore: hint.whitespaceBefore,
|
||||
whitespaceAfter: hint.whitespaceAfter
|
||||
@@ -1028,13 +1143,24 @@ export namespace InlineHint {
|
||||
}
|
||||
|
||||
export function to(hint: modes.InlineHint): vscode.InlineHint {
|
||||
return new types.InlineHint(
|
||||
const res = new types.InlineHint(
|
||||
hint.text,
|
||||
Range.to(hint.range),
|
||||
htmlContent.isMarkdownString(hint.description) ? MarkdownString.to(hint.description) : hint.description,
|
||||
hint.whitespaceBefore,
|
||||
hint.whitespaceAfter
|
||||
InlineHintKind.to(hint.kind)
|
||||
);
|
||||
res.whitespaceAfter = hint.whitespaceAfter;
|
||||
res.whitespaceBefore = hint.whitespaceBefore;
|
||||
res.description = htmlContent.isMarkdownString(hint.description) ? MarkdownString.to(hint.description) : hint.description;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace InlineHintKind {
|
||||
export function from(kind: vscode.InlineHintKind): modes.InlineHintKind {
|
||||
return kind;
|
||||
}
|
||||
export function to(kind: modes.InlineHintKind): vscode.InlineHintKind {
|
||||
return kind;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1218,7 +1344,7 @@ export namespace TextEditorOpenOptions {
|
||||
inactive: options.background,
|
||||
preserveFocus: options.preserveFocus,
|
||||
selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined,
|
||||
override: typeof options.override === 'boolean' ? false : undefined
|
||||
override: typeof options.override === 'boolean' ? EditorOverride.DISABLED : undefined
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1268,32 +1394,120 @@ export namespace LanguageSelector {
|
||||
} else if (typeof selector === 'string') {
|
||||
return selector;
|
||||
} else {
|
||||
const filter = selector as vscode.DocumentFilter; // TODO: microsoft/TypeScript#42768
|
||||
return <languageSelector.LanguageFilter>{
|
||||
language: selector.language,
|
||||
scheme: selector.scheme,
|
||||
pattern: typeof selector.pattern === 'undefined' ? undefined : GlobPattern.from(selector.pattern),
|
||||
exclusive: selector.exclusive
|
||||
language: filter.language,
|
||||
scheme: filter.scheme,
|
||||
pattern: typeof filter.pattern === 'undefined' ? undefined : GlobPattern.from(filter.pattern),
|
||||
exclusive: filter.exclusive
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellOutput {
|
||||
export function from(output: types.NotebookCellOutput): IDisplayOutput {
|
||||
return output.toJSON();
|
||||
export namespace NotebookCellRange {
|
||||
|
||||
export function from(range: vscode.NotebookCellRange): notebooks.ICellRange {
|
||||
return { start: range.start, end: range.end };
|
||||
}
|
||||
|
||||
export function to(range: notebooks.ICellRange): types.NotebookCellRange {
|
||||
return new types.NotebookCellRange(range.start, range.end);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellMetadata {
|
||||
|
||||
export function to(data: notebooks.NotebookCellMetadata): types.NotebookCellMetadata {
|
||||
return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.runnable, data.hasExecutionOrder, data.executionOrder, data.runState, data.runStartTime, data.statusMessage, data.lastRunDuration, data.inputCollapsed, data.outputCollapsed, data.custom);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookDocumentMetadata {
|
||||
|
||||
export function from(data: types.NotebookDocumentMetadata): notebooks.NotebookDocumentMetadata {
|
||||
return data;
|
||||
}
|
||||
|
||||
export function to(data: notebooks.NotebookDocumentMetadata): types.NotebookDocumentMetadata {
|
||||
return new types.NotebookDocumentMetadata(data.editable, data.runnable, data.cellEditable, data.cellRunnable, data.cellHasExecutionOrder, data.displayOrder, data.custom, data.runState, data.trusted);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace NotebookCellKind {
|
||||
export function from(data: vscode.NotebookCellKind): notebooks.CellKind {
|
||||
switch (data) {
|
||||
case types.NotebookCellKind.Markdown:
|
||||
return notebooks.CellKind.Markdown;
|
||||
case types.NotebookCellKind.Code:
|
||||
default:
|
||||
return notebooks.CellKind.Code;
|
||||
}
|
||||
}
|
||||
|
||||
export function to(data: notebooks.CellKind): vscode.NotebookCellKind {
|
||||
switch (data) {
|
||||
case notebooks.CellKind.Markdown:
|
||||
return types.NotebookCellKind.Markdown;
|
||||
case notebooks.CellKind.Code:
|
||||
default:
|
||||
return types.NotebookCellKind.Code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellData {
|
||||
|
||||
export function from(data: vscode.NotebookCellData): notebooks.ICellDto2 {
|
||||
return {
|
||||
cellKind: NotebookCellKind.from(data.cellKind),
|
||||
language: data.language,
|
||||
source: data.source,
|
||||
metadata: data.metadata,
|
||||
outputs: data.outputs.map(output => ({
|
||||
outputId: output.id,
|
||||
metadata: output.metadata,
|
||||
outputs: (output.outputs || []).map(op => ({
|
||||
mime: op.mime,
|
||||
value: op.value,
|
||||
metadata: op.metadata
|
||||
}))
|
||||
}))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellOutputItem {
|
||||
export function from(output: types.NotebookCellOutputItem): IDisplayOutput {
|
||||
export function from(item: types.NotebookCellOutputItem): notebooks.IOutputItemDto {
|
||||
return {
|
||||
outputKind: CellOutputKind.Rich,
|
||||
data: { [output.mime]: output.value },
|
||||
metadata: output.metadata && { custom: output.metadata }
|
||||
mime: item.mime,
|
||||
value: item.value,
|
||||
metadata: item.metadata
|
||||
};
|
||||
}
|
||||
|
||||
export function to(item: notebooks.IOutputItemDto): types.NotebookCellOutputItem {
|
||||
return new types.NotebookCellOutputItem(item.mime, item.value, item.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellOutput {
|
||||
export function from(output: types.NotebookCellOutput): notebooks.IOutputDto {
|
||||
return {
|
||||
outputId: output.id,
|
||||
outputs: output.outputs.map(NotebookCellOutputItem.from),
|
||||
metadata: output.metadata
|
||||
};
|
||||
}
|
||||
|
||||
export function to(output: notebooks.IOutputDto): vscode.NotebookCellOutput {
|
||||
const items = output.outputs.map(NotebookCellOutputItem.to);
|
||||
return new types.NotebookCellOutput(items, output.outputId, output.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace NotebookExclusiveDocumentPattern {
|
||||
export function from(pattern: { include: vscode.GlobPattern | undefined, exclude: vscode.GlobPattern | undefined }): { include: string | types.RelativePattern | undefined, exclude: string | types.RelativePattern | undefined };
|
||||
export function from(pattern: vscode.GlobPattern): string | types.RelativePattern;
|
||||
@@ -1368,7 +1582,7 @@ export namespace NotebookExclusiveDocumentPattern {
|
||||
}
|
||||
|
||||
export namespace NotebookDecorationRenderOptions {
|
||||
export function from(options: vscode.NotebookDecorationRenderOptions): INotebookDecorationRenderOptions {
|
||||
export function from(options: vscode.NotebookDecorationRenderOptions): notebooks.INotebookDecorationRenderOptions {
|
||||
return {
|
||||
backgroundColor: <string | types.ThemeColor>options.backgroundColor,
|
||||
borderColor: <string | types.ThemeColor>options.borderColor,
|
||||
@@ -1380,22 +1594,22 @@ export namespace NotebookDecorationRenderOptions {
|
||||
export namespace TestState {
|
||||
export function from(item: vscode.TestState): ITestState {
|
||||
return {
|
||||
runState: item.runState,
|
||||
state: item.state,
|
||||
duration: item.duration,
|
||||
messages: item.messages.map(message => ({
|
||||
messages: item.messages?.map(message => ({
|
||||
message: MarkdownString.fromStrict(message.message) || '',
|
||||
severity: message.severity,
|
||||
expectedOutput: message.expectedOutput,
|
||||
actualOutput: message.actualOutput,
|
||||
location: message.location ? location.from(message.location) : undefined,
|
||||
})),
|
||||
location: message.location ? location.from(message.location) as any : undefined,
|
||||
})) ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
export function to(item: ITestState): vscode.TestState {
|
||||
return new types.TestState(
|
||||
item.runState,
|
||||
item.messages.map(message => ({
|
||||
return {
|
||||
state: item.state,
|
||||
messages: item.messages.map(message => ({
|
||||
message: typeof message.message === 'string' ? message.message : MarkdownString.to(message.message),
|
||||
severity: message.severity,
|
||||
expectedOutput: message.expectedOutput,
|
||||
@@ -1405,22 +1619,20 @@ export namespace TestState {
|
||||
uri: URI.revive(message.location.uri)
|
||||
}),
|
||||
})),
|
||||
item.duration,
|
||||
);
|
||||
duration: item.duration,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace TestItem {
|
||||
export function from(item: vscode.TestItem, parentExtId?: string): ITestItem {
|
||||
export function from(item: vscode.TestItem): ITestItem {
|
||||
return {
|
||||
extId: item.id ?? (parentExtId ? `${parentExtId}\0${item.label}` : item.label),
|
||||
extId: item.id,
|
||||
label: item.label,
|
||||
location: item.location ? location.from(item.location) : undefined,
|
||||
location: item.location ? location.from(item.location) as any : undefined,
|
||||
debuggable: item.debuggable ?? false,
|
||||
description: item.description,
|
||||
runnable: item.runnable ?? true,
|
||||
state: TestState.from(item.state),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1435,7 +1647,68 @@ export namespace TestItem {
|
||||
debuggable: item.debuggable,
|
||||
description: item.description,
|
||||
runnable: item.runnable,
|
||||
state: TestState.to(item.state),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestResults {
|
||||
export function from(id: string, results: vscode.TestResults): ISerializedTestResults {
|
||||
const serialized: ISerializedTestResults = {
|
||||
completedAt: results.completedAt,
|
||||
id,
|
||||
items: [],
|
||||
};
|
||||
|
||||
const queue: [parent: SerializedTestResultItem | null, children: Iterable<vscode.TestItemWithResults>][] = [
|
||||
[null, results.results],
|
||||
];
|
||||
|
||||
while (queue.length) {
|
||||
const [parent, children] = queue.pop()!;
|
||||
for (const item of children) {
|
||||
const serializedItem: SerializedTestResultItem = {
|
||||
children: item.children?.map(c => c.id) ?? [],
|
||||
computedState: item.result.state,
|
||||
item: TestItem.from(item),
|
||||
state: TestState.from(item.result),
|
||||
retired: undefined,
|
||||
parent: parent?.item.extId ?? null,
|
||||
providerId: '',
|
||||
direct: !parent,
|
||||
};
|
||||
|
||||
serialized.items.push(serializedItem);
|
||||
if (item.children) {
|
||||
queue.push([serializedItem, item.children]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map<string, SerializedTestResultItem>): vscode.TestItemWithResults => ({
|
||||
...TestItem.toShallow(item.item),
|
||||
result: TestState.to(item.state),
|
||||
children: item.children
|
||||
.map(c => byInternalId.get(c))
|
||||
.filter(isDefined)
|
||||
.map(c => convertTestResultItem(c, byInternalId)),
|
||||
});
|
||||
|
||||
export function to(serialized: ISerializedTestResults): vscode.TestResults {
|
||||
const roots: SerializedTestResultItem[] = [];
|
||||
const byInternalId = new Map<string, SerializedTestResultItem>();
|
||||
for (const item of serialized.items) {
|
||||
byInternalId.set(item.item.extId, item);
|
||||
if (item.direct) {
|
||||
roots.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
completedAt: serialized.completedAt,
|
||||
results: roots.map(r => convertTestResultItem(r, byInternalId)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ 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 { addIdToOutput, CellEditType, ICellEditOperation, IDisplayOutput, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellEditType, ICellEditOperation, notebookDocumentMetadataDefaults, NOTEBOOK_DISPLAY_ORDER } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
function es5ClassCompat(target: Function): any {
|
||||
@@ -585,7 +585,10 @@ export interface IFileOperationOptions {
|
||||
export const enum FileEditType {
|
||||
File = 1,
|
||||
Text = 2,
|
||||
Cell = 3
|
||||
Cell = 3,
|
||||
CellOutput = 4,
|
||||
CellReplace = 5,
|
||||
CellOutputItem = 6
|
||||
}
|
||||
|
||||
export interface IFileOperation {
|
||||
@@ -611,13 +614,45 @@ export interface IFileCellEdit {
|
||||
metadata?: vscode.WorkspaceEditEntryMetadata;
|
||||
}
|
||||
|
||||
export interface ICellEdit {
|
||||
_type: FileEditType.CellReplace;
|
||||
metadata?: vscode.WorkspaceEditEntryMetadata;
|
||||
uri: URI;
|
||||
index: number;
|
||||
count: number;
|
||||
cells: vscode.NotebookCellData[];
|
||||
}
|
||||
|
||||
export interface ICellOutputEdit {
|
||||
_type: FileEditType.CellOutput;
|
||||
uri: URI;
|
||||
index: number;
|
||||
append: boolean;
|
||||
newOutputs?: NotebookCellOutput[];
|
||||
newMetadata?: vscode.NotebookCellMetadata;
|
||||
metadata?: vscode.WorkspaceEditEntryMetadata;
|
||||
}
|
||||
|
||||
export interface ICellOutputItemsEdit {
|
||||
_type: FileEditType.CellOutputItem;
|
||||
uri: URI;
|
||||
index: number;
|
||||
outputId: string;
|
||||
append: boolean;
|
||||
newOutputItems?: NotebookCellOutputItem[];
|
||||
metadata?: vscode.WorkspaceEditEntryMetadata;
|
||||
}
|
||||
|
||||
|
||||
type WorkspaceEditEntry = IFileOperation | IFileTextEdit | IFileCellEdit | ICellEdit | ICellOutputEdit | ICellOutputItemsEdit;
|
||||
|
||||
@es5ClassCompat
|
||||
export class WorkspaceEdit implements vscode.WorkspaceEdit {
|
||||
|
||||
private readonly _edits = new Array<IFileOperation | IFileTextEdit | IFileCellEdit>();
|
||||
private readonly _edits: WorkspaceEditEntry[] = [];
|
||||
|
||||
|
||||
_allEntries(): ReadonlyArray<IFileTextEdit | IFileOperation | IFileCellEdit> {
|
||||
_allEntries(): ReadonlyArray<WorkspaceEditEntry> {
|
||||
return this._edits;
|
||||
}
|
||||
|
||||
@@ -643,22 +678,32 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
|
||||
|
||||
replaceNotebookCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
if (start !== end || cells.length > 0) {
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Replace, index: start, count: end - start, cells: cells.map(cell => ({ ...cell, outputs: cell.outputs.map(output => addIdToOutput(output)) })) } });
|
||||
this._edits.push({ _type: FileEditType.CellReplace, uri, index: start, count: end - start, cells, metadata });
|
||||
}
|
||||
}
|
||||
|
||||
replaceNotebookCellOutput(uri: URI, index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._edits.push({
|
||||
_type: FileEditType.Cell, metadata, uri, edit: {
|
||||
editType: CellEditType.Output, index, outputs: outputs.map(output => {
|
||||
if (NotebookCellOutput.isNotebookCellOutput(output)) {
|
||||
return addIdToOutput(output.toJSON());
|
||||
} else {
|
||||
return addIdToOutput(output);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
replaceNotebookCellOutput(uri: URI, index: number, outputs: vscode.NotebookCellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._editNotebookCellOutput(uri, index, false, outputs, metadata);
|
||||
}
|
||||
|
||||
appendNotebookCellOutput(uri: URI, index: number, outputs: vscode.NotebookCellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._editNotebookCellOutput(uri, index, true, outputs, metadata);
|
||||
}
|
||||
|
||||
replaceNotebookCellOutputItems(uri: URI, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._editNotebookCellOutputItems(uri, index, outputId, false, items, metadata);
|
||||
}
|
||||
|
||||
appendNotebookCellOutputItems(uri: URI, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._editNotebookCellOutputItems(uri, index, outputId, true, items, metadata);
|
||||
}
|
||||
|
||||
private _editNotebookCellOutputItems(uri: URI, index: number, id: string, append: boolean, items: vscode.NotebookCellOutputItem[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
|
||||
this._edits.push({ _type: FileEditType.CellOutputItem, metadata, uri, index, outputId: id, append, newOutputItems: items });
|
||||
}
|
||||
|
||||
private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: vscode.NotebookCellOutput[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
|
||||
this._edits.push({ _type: FileEditType.CellOutput, metadata, uri, index, append, newOutputs: outputs, newMetadata: undefined });
|
||||
}
|
||||
|
||||
replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
@@ -1373,20 +1418,26 @@ export enum SignatureHelpTriggerKind {
|
||||
ContentChange = 3,
|
||||
}
|
||||
|
||||
|
||||
export enum InlineHintKind {
|
||||
Other = 0,
|
||||
Type = 1,
|
||||
Parameter = 2,
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class InlineHint {
|
||||
text: string;
|
||||
range: Range;
|
||||
kind?: vscode.InlineHintKind;
|
||||
description?: string | vscode.MarkdownString;
|
||||
whitespaceBefore?: boolean;
|
||||
whitespaceAfter?: boolean;
|
||||
|
||||
constructor(text: string, range: Range, description?: string | vscode.MarkdownString, whitespaceBefore?: boolean, whitespaceAfter?: boolean) {
|
||||
constructor(text: string, range: Range, kind?: vscode.InlineHintKind) {
|
||||
this.text = text;
|
||||
this.range = range;
|
||||
this.description = description;
|
||||
this.whitespaceBefore = whitespaceBefore;
|
||||
this.whitespaceAfter = whitespaceAfter;
|
||||
this.kind = kind;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2387,6 +2438,51 @@ export class EvaluatableExpression implements vscode.EvaluatableExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class InlineValueText implements vscode.InlineValueText {
|
||||
readonly range: Range;
|
||||
readonly text: string;
|
||||
|
||||
constructor(range: Range, text: string) {
|
||||
this.range = range;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class InlineValueVariableLookup implements vscode.InlineValueVariableLookup {
|
||||
readonly range: Range;
|
||||
readonly variableName?: string;
|
||||
readonly caseSensitiveLookup: boolean;
|
||||
|
||||
constructor(range: Range, variableName?: string, caseSensitiveLookup: boolean = true) {
|
||||
this.range = range;
|
||||
this.variableName = variableName;
|
||||
this.caseSensitiveLookup = caseSensitiveLookup;
|
||||
}
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class InlineValueEvaluatableExpression implements vscode.InlineValueEvaluatableExpression {
|
||||
readonly range: Range;
|
||||
readonly expression?: string;
|
||||
|
||||
constructor(range: Range, expression?: string) {
|
||||
this.range = range;
|
||||
this.expression = expression;
|
||||
}
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class InlineValueContext implements vscode.InlineValueContext {
|
||||
|
||||
readonly stoppedLocation: vscode.Range;
|
||||
|
||||
constructor(range: vscode.Range) {
|
||||
this.stoppedLocation = range;
|
||||
}
|
||||
}
|
||||
|
||||
//#region file api
|
||||
|
||||
export enum FileChangeType {
|
||||
@@ -2797,6 +2893,264 @@ export enum ColorThemeKind {
|
||||
|
||||
//#region Notebook
|
||||
|
||||
export class NotebookCellRange {
|
||||
|
||||
private _start: number;
|
||||
private _end: number;
|
||||
|
||||
get start() {
|
||||
return this._start;
|
||||
}
|
||||
|
||||
get end() {
|
||||
return this._end;
|
||||
}
|
||||
|
||||
constructor(start: number, end: number) {
|
||||
// todo@rebornix
|
||||
// if (start < 0) {
|
||||
// throw illegalArgument('start must be positive');
|
||||
// }
|
||||
// if (end < start) {
|
||||
// throw illegalArgument('end cannot be smaller than start');
|
||||
// }
|
||||
this._start = start;
|
||||
this._end = end;
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellMetadata {
|
||||
|
||||
constructor(
|
||||
readonly editable?: boolean,
|
||||
readonly breakpointMargin?: boolean,
|
||||
readonly runnable?: boolean,
|
||||
readonly hasExecutionOrder?: boolean,
|
||||
readonly executionOrder?: number,
|
||||
readonly runState?: NotebookCellRunState,
|
||||
readonly runStartTime?: number,
|
||||
readonly statusMessage?: string,
|
||||
readonly lastRunDuration?: number,
|
||||
readonly inputCollapsed?: boolean,
|
||||
readonly outputCollapsed?: boolean,
|
||||
readonly custom?: Record<string, any>,
|
||||
) { }
|
||||
|
||||
with(change: {
|
||||
editable?: boolean | null,
|
||||
breakpointMargin?: boolean | null,
|
||||
runnable?: boolean | null,
|
||||
hasExecutionOrder?: boolean | null,
|
||||
executionOrder?: number | null,
|
||||
runState?: NotebookCellRunState | null,
|
||||
runStartTime?: number | null,
|
||||
statusMessage?: string | null,
|
||||
lastRunDuration?: number | null,
|
||||
inputCollapsed?: boolean | null,
|
||||
outputCollapsed?: boolean | null,
|
||||
custom?: Record<string, any> | null,
|
||||
}): NotebookCellMetadata {
|
||||
|
||||
let { editable, breakpointMargin, runnable, hasExecutionOrder, executionOrder, runState, runStartTime, statusMessage, lastRunDuration, inputCollapsed, outputCollapsed, custom } = change;
|
||||
|
||||
if (editable === undefined) {
|
||||
editable = this.editable;
|
||||
} else if (editable === null) {
|
||||
editable = undefined;
|
||||
}
|
||||
if (breakpointMargin === undefined) {
|
||||
breakpointMargin = this.breakpointMargin;
|
||||
} else if (breakpointMargin === null) {
|
||||
breakpointMargin = undefined;
|
||||
}
|
||||
if (runnable === undefined) {
|
||||
runnable = this.runnable;
|
||||
} else if (runnable === null) {
|
||||
runnable = undefined;
|
||||
}
|
||||
if (hasExecutionOrder === undefined) {
|
||||
hasExecutionOrder = this.hasExecutionOrder;
|
||||
} else if (hasExecutionOrder === null) {
|
||||
hasExecutionOrder = undefined;
|
||||
}
|
||||
if (executionOrder === undefined) {
|
||||
executionOrder = this.executionOrder;
|
||||
} else if (executionOrder === null) {
|
||||
executionOrder = undefined;
|
||||
}
|
||||
if (runState === undefined) {
|
||||
runState = this.runState;
|
||||
} else if (runState === null) {
|
||||
runState = undefined;
|
||||
}
|
||||
if (runStartTime === undefined) {
|
||||
runStartTime = this.runStartTime;
|
||||
} else if (runStartTime === null) {
|
||||
runStartTime = undefined;
|
||||
}
|
||||
if (statusMessage === undefined) {
|
||||
statusMessage = this.statusMessage;
|
||||
} else if (statusMessage === null) {
|
||||
statusMessage = undefined;
|
||||
}
|
||||
if (lastRunDuration === undefined) {
|
||||
lastRunDuration = this.lastRunDuration;
|
||||
} else if (lastRunDuration === null) {
|
||||
lastRunDuration = undefined;
|
||||
}
|
||||
if (inputCollapsed === undefined) {
|
||||
inputCollapsed = this.inputCollapsed;
|
||||
} else if (inputCollapsed === null) {
|
||||
inputCollapsed = undefined;
|
||||
}
|
||||
if (outputCollapsed === undefined) {
|
||||
outputCollapsed = this.outputCollapsed;
|
||||
} else if (outputCollapsed === null) {
|
||||
outputCollapsed = undefined;
|
||||
}
|
||||
if (custom === undefined) {
|
||||
custom = this.custom;
|
||||
} else if (custom === null) {
|
||||
custom = undefined;
|
||||
}
|
||||
|
||||
if (editable === this.editable &&
|
||||
breakpointMargin === this.breakpointMargin &&
|
||||
runnable === this.runnable &&
|
||||
hasExecutionOrder === this.hasExecutionOrder &&
|
||||
executionOrder === this.executionOrder &&
|
||||
runState === this.runState &&
|
||||
runStartTime === this.runStartTime &&
|
||||
statusMessage === this.statusMessage &&
|
||||
lastRunDuration === this.lastRunDuration &&
|
||||
inputCollapsed === this.inputCollapsed &&
|
||||
outputCollapsed === this.outputCollapsed &&
|
||||
custom === this.custom
|
||||
) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new NotebookCellMetadata(
|
||||
editable,
|
||||
breakpointMargin,
|
||||
runnable,
|
||||
hasExecutionOrder,
|
||||
executionOrder,
|
||||
runState,
|
||||
runStartTime,
|
||||
statusMessage,
|
||||
lastRunDuration,
|
||||
inputCollapsed,
|
||||
outputCollapsed,
|
||||
custom,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookDocumentMetadata {
|
||||
|
||||
constructor(
|
||||
readonly editable: boolean = true,
|
||||
readonly runnable: boolean = true,
|
||||
readonly cellEditable: boolean = true,
|
||||
readonly cellRunnable: boolean = true,
|
||||
readonly cellHasExecutionOrder: boolean = true,
|
||||
readonly displayOrder: vscode.GlobPattern[] = NOTEBOOK_DISPLAY_ORDER,
|
||||
readonly custom: { [key: string]: any; } = {},
|
||||
readonly runState: NotebookRunState = NotebookRunState.Idle,
|
||||
readonly trusted: boolean = true,
|
||||
) { }
|
||||
|
||||
with(change: {
|
||||
editable?: boolean | null,
|
||||
runnable?: boolean | null,
|
||||
cellEditable?: boolean | null,
|
||||
cellRunnable?: boolean | null,
|
||||
cellHasExecutionOrder?: boolean | null,
|
||||
displayOrder?: vscode.GlobPattern[] | null,
|
||||
custom?: { [key: string]: any; } | null,
|
||||
runState?: NotebookRunState | null,
|
||||
trusted?: boolean | null,
|
||||
}): NotebookDocumentMetadata {
|
||||
|
||||
let { editable, runnable, cellEditable, cellRunnable, cellHasExecutionOrder, displayOrder, custom, runState, trusted } = change;
|
||||
|
||||
if (editable === undefined) {
|
||||
editable = this.editable;
|
||||
} else if (editable === null) {
|
||||
editable = undefined;
|
||||
}
|
||||
if (runnable === undefined) {
|
||||
runnable = this.runnable;
|
||||
} else if (runnable === null) {
|
||||
runnable = undefined;
|
||||
}
|
||||
if (cellEditable === undefined) {
|
||||
cellEditable = this.cellEditable;
|
||||
} else if (cellEditable === null) {
|
||||
cellEditable = undefined;
|
||||
}
|
||||
if (cellRunnable === undefined) {
|
||||
cellRunnable = this.cellRunnable;
|
||||
} else if (cellRunnable === null) {
|
||||
cellRunnable = undefined;
|
||||
}
|
||||
if (cellHasExecutionOrder === undefined) {
|
||||
cellHasExecutionOrder = this.cellHasExecutionOrder;
|
||||
} else if (cellHasExecutionOrder === null) {
|
||||
cellHasExecutionOrder = undefined;
|
||||
}
|
||||
if (displayOrder === undefined) {
|
||||
displayOrder = this.displayOrder;
|
||||
} else if (displayOrder === null) {
|
||||
displayOrder = undefined;
|
||||
}
|
||||
if (custom === undefined) {
|
||||
custom = this.custom;
|
||||
} else if (custom === null) {
|
||||
custom = undefined;
|
||||
}
|
||||
if (runState === undefined) {
|
||||
runState = this.runState;
|
||||
} else if (runState === null) {
|
||||
runState = undefined;
|
||||
}
|
||||
if (trusted === undefined) {
|
||||
trusted = this.trusted;
|
||||
} else if (trusted === null) {
|
||||
trusted = undefined;
|
||||
}
|
||||
|
||||
if (editable === this.editable &&
|
||||
runnable === this.runnable &&
|
||||
cellEditable === this.cellEditable &&
|
||||
cellRunnable === this.cellRunnable &&
|
||||
cellHasExecutionOrder === this.cellHasExecutionOrder &&
|
||||
displayOrder === this.displayOrder &&
|
||||
custom === this.custom &&
|
||||
runState === this.runState &&
|
||||
trusted === this.trusted
|
||||
) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
return new NotebookDocumentMetadata(
|
||||
editable,
|
||||
runnable,
|
||||
cellEditable,
|
||||
cellRunnable,
|
||||
cellHasExecutionOrder,
|
||||
displayOrder,
|
||||
custom,
|
||||
runState,
|
||||
trusted
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellOutputItem {
|
||||
|
||||
static isNotebookCellOutputItem(obj: unknown): obj is vscode.NotebookCellOutputItem {
|
||||
@@ -2806,52 +3160,37 @@ export class NotebookCellOutputItem {
|
||||
constructor(
|
||||
readonly mime: string,
|
||||
readonly value: unknown, // JSON'able
|
||||
readonly metadata?: Record<string, string | number | boolean>
|
||||
readonly metadata?: Record<string, any>
|
||||
) { }
|
||||
}
|
||||
|
||||
export class NotebookCellOutput {
|
||||
|
||||
static isNotebookCellOutput(obj: unknown): obj is vscode.NotebookCellOutput {
|
||||
return obj instanceof NotebookCellOutput;
|
||||
}
|
||||
readonly outputs: NotebookCellOutputItem[];
|
||||
readonly id: string;
|
||||
readonly metadata?: Record<string, any>;
|
||||
|
||||
constructor(
|
||||
readonly outputs: NotebookCellOutputItem[],
|
||||
readonly metadata?: Record<string, string | number | boolean>
|
||||
) { }
|
||||
|
||||
toJSON(): IDisplayOutput {
|
||||
let data: { [key: string]: unknown; } = {};
|
||||
let custom: { [key: string]: unknown; } = {};
|
||||
let hasMetadata = false;
|
||||
|
||||
for (let item of this.outputs) {
|
||||
data[item.mime] = item.value;
|
||||
if (item.metadata) {
|
||||
custom[item.mime] = item.metadata;
|
||||
hasMetadata = true;
|
||||
}
|
||||
outputs: NotebookCellOutputItem[],
|
||||
idOrMetadata?: string | Record<string, any>,
|
||||
metadata?: Record<string, any>
|
||||
) {
|
||||
this.outputs = outputs;
|
||||
if (typeof idOrMetadata === 'string') {
|
||||
this.id = idOrMetadata;
|
||||
this.metadata = metadata;
|
||||
} else {
|
||||
this.id = generateUuid();
|
||||
this.metadata = idOrMetadata ?? metadata;
|
||||
}
|
||||
return {
|
||||
outputKind: CellOutputKind.Rich,
|
||||
data,
|
||||
metadata: hasMetadata ? { custom } : undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export enum CellKind {
|
||||
export enum NotebookCellKind {
|
||||
Markdown = 1,
|
||||
Code = 2
|
||||
}
|
||||
|
||||
export enum CellOutputKind {
|
||||
Text = 1,
|
||||
Error = 2,
|
||||
Rich = 3
|
||||
}
|
||||
|
||||
export enum NotebookCellRunState {
|
||||
Running = 1,
|
||||
Idle = 2,
|
||||
@@ -2954,31 +3293,6 @@ export enum TestMessageSeverity {
|
||||
Hint = 3
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class TestState {
|
||||
#runState: TestRunState;
|
||||
#duration?: number;
|
||||
#messages: ReadonlyArray<Readonly<vscode.TestMessage>>;
|
||||
|
||||
public get runState() {
|
||||
return this.#runState;
|
||||
}
|
||||
|
||||
public get duration() {
|
||||
return this.#duration;
|
||||
}
|
||||
|
||||
public get messages() {
|
||||
return this.#messages;
|
||||
}
|
||||
|
||||
constructor(runState: TestRunState, messages: vscode.TestMessage[] = [], duration?: number) {
|
||||
this.#runState = runState;
|
||||
this.#messages = Object.freeze(messages.map(m => Object.freeze(m)));
|
||||
this.#duration = duration;
|
||||
}
|
||||
}
|
||||
|
||||
export type RequiredTestItem = vscode.RequiredTestItem;
|
||||
|
||||
export type TestItem = vscode.TestItem;
|
||||
@@ -2991,3 +3305,9 @@ export enum ExternalUriOpenerPriority {
|
||||
Default = 2,
|
||||
Preferred = 3,
|
||||
}
|
||||
|
||||
export enum WorkspaceTrustState {
|
||||
Untrusted = 0,
|
||||
Trusted = 1,
|
||||
Unknown = 2
|
||||
}
|
||||
|
||||
@@ -20,11 +20,12 @@ import { FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Severity } from 'vs/platform/notification/common/notification';
|
||||
import { WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Range, RelativePattern, WorkspaceTrustState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { IRawFileMatch2, resultIsMatch } from 'vs/workbench/services/search/common/search';
|
||||
import * as vscode from 'vscode';
|
||||
@@ -168,6 +169,9 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
|
||||
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
|
||||
|
||||
private readonly _onDidChangeWorkspaceTrustState = new Emitter<vscode.WorkspaceTrustStateChangeEvent>();
|
||||
readonly onDidChangeWorkspaceTrustState: Event<vscode.WorkspaceTrustStateChangeEvent> = this._onDidChangeWorkspaceTrustState.event;
|
||||
|
||||
private readonly _logService: ILogService;
|
||||
private readonly _requestIdProvider: Counter;
|
||||
private readonly _barrier: Barrier;
|
||||
@@ -181,6 +185,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
|
||||
private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = [];
|
||||
|
||||
private _workspaceTrustState: WorkspaceTrustState = WorkspaceTrustState.Unknown;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@@ -198,7 +204,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo)) : undefined;
|
||||
}
|
||||
|
||||
$initializeWorkspace(data: IWorkspaceData | null): void {
|
||||
$initializeWorkspace(data: IWorkspaceData | null, trustState: WorkspaceTrustState): void {
|
||||
this._workspaceTrustState = trustState;
|
||||
this.$acceptWorkspaceData(data);
|
||||
this._barrier.open();
|
||||
}
|
||||
@@ -549,6 +556,21 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
resolveProxy(url: string): Promise<string | undefined> {
|
||||
return this._proxy.$resolveProxy(url);
|
||||
}
|
||||
|
||||
// --- trust ---
|
||||
|
||||
get trustState(): WorkspaceTrustState {
|
||||
return this._workspaceTrustState;
|
||||
}
|
||||
|
||||
requireWorkspaceTrust(modal?: boolean): Promise<WorkspaceTrustState> {
|
||||
return this._proxy.$requireWorkspaceTrust(modal);
|
||||
}
|
||||
|
||||
$onDidChangeWorkspaceTrustState(state: WorkspaceTrustStateChangeEvent): void {
|
||||
this._workspaceTrustState = state.currentTrustState;
|
||||
this._onDidChangeWorkspaceTrustState.fire(Object.freeze(state));
|
||||
}
|
||||
}
|
||||
|
||||
export const IExtHostWorkspace = createDecorator<IExtHostWorkspace>('IExtHostWorkspace');
|
||||
|
||||
@@ -43,6 +43,11 @@ const apiMenus: IAPIMenu[] = [
|
||||
id: MenuId.EditorTitle,
|
||||
description: localize('menus.editorTitle', "The editor title menu")
|
||||
},
|
||||
{
|
||||
key: 'editor/title/run',
|
||||
id: MenuId.EditorTitleRun,
|
||||
description: localize('menus.editorTitleRun', "Run submenu inside the editor title menu")
|
||||
},
|
||||
{
|
||||
key: 'editor/context',
|
||||
id: MenuId.EditorContext,
|
||||
@@ -161,6 +166,12 @@ const apiMenus: IAPIMenu[] = [
|
||||
description: localize('notebook.cell.title', "The contributed notebook cell title menu"),
|
||||
proposed: true
|
||||
},
|
||||
{
|
||||
key: 'testing/item/context',
|
||||
id: MenuId.TestItem,
|
||||
description: localize('testing.item.title', "The contributed test item menu"),
|
||||
proposed: true
|
||||
},
|
||||
{
|
||||
key: 'extension/context',
|
||||
id: MenuId.ExtensionContext,
|
||||
@@ -176,6 +187,16 @@ const apiMenus: IAPIMenu[] = [
|
||||
id: MenuId.TimelineItemContext,
|
||||
description: localize('view.timelineContext', "The Timeline view item context menu")
|
||||
},
|
||||
{
|
||||
key: 'ports/item/context',
|
||||
id: MenuId.TunnelContext,
|
||||
description: localize('view.tunnelContext', "The Ports view item context menu")
|
||||
},
|
||||
{
|
||||
key: 'ports/item/origin/inline',
|
||||
id: MenuId.TunnelOriginInline,
|
||||
description: localize('view.tunnelOriginInline', "The Ports view item origin inline menu")
|
||||
}
|
||||
];
|
||||
|
||||
namespace schema {
|
||||
@@ -337,7 +358,7 @@ namespace schema {
|
||||
type: 'string'
|
||||
},
|
||||
icon: {
|
||||
description: localize('vscode.extension.contributes.submenu.icon', '(Optional) Icon which is used to represent the submenu in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `\\$(zap)`'),
|
||||
description: localize({ key: 'vscode.extension.contributes.submenu.icon', comment: ['do not translate or change `\\$(zap)`, \\ in front of $ is important.'] }, '(Optional) Icon which is used to represent the submenu in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `\\$(zap)`'),
|
||||
anyOf: [{
|
||||
type: 'string'
|
||||
},
|
||||
@@ -465,7 +486,7 @@ namespace schema {
|
||||
type: 'string'
|
||||
},
|
||||
icon: {
|
||||
description: localize('vscode.extension.contributes.commandType.icon', '(Optional) Icon which is used to represent the command in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `\\$(zap)`'),
|
||||
description: localize({ key: 'vscode.extension.contributes.commandType.icon', comment: ['do not translate or change `\\$(zap)`, \\ in front of $ is important.'] }, '(Optional) Icon which is used to represent the command in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `\\$(zap)`'),
|
||||
anyOf: [{
|
||||
type: 'string'
|
||||
},
|
||||
|
||||
@@ -163,8 +163,10 @@ export class CLIServerBase {
|
||||
}
|
||||
|
||||
private async openExternal(data: OpenExternalCommandPipeArgs, res: http.ServerResponse) {
|
||||
for (const uri of data.uris) {
|
||||
await this._commands.executeCommand('_remoteCLI.openExternal', URI.parse(uri), { allowTunneling: true });
|
||||
for (const uriString of data.uris) {
|
||||
const uri = URI.parse(uriString);
|
||||
const urioOpen = uri.scheme === 'file' ? uri : uriString; // workaround for #112577
|
||||
await this._commands.executeCommand('_remoteCLI.openExternal', urioOpen);
|
||||
}
|
||||
res.writeHead(200);
|
||||
res.end();
|
||||
|
||||
@@ -25,7 +25,6 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
|
||||
import { createCancelablePromise, firstParallel } from 'vs/base/common/async';
|
||||
|
||||
|
||||
export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
@@ -68,7 +67,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
return new SignService();
|
||||
}
|
||||
|
||||
public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined> {
|
||||
public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {
|
||||
|
||||
if (args.kind === 'integrated') {
|
||||
|
||||
@@ -116,13 +115,21 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
const command = prepareCommand(shell, args.args, cwdForPrepareCommand, args.env);
|
||||
terminal.sendText(command, true);
|
||||
|
||||
// Mark terminal as unused when its session ends, see #112055
|
||||
const sessionListener = this.onDidTerminateDebugSession(s => {
|
||||
if (s.id === sessionId) {
|
||||
this._integratedTerminalInstances.free(terminal!);
|
||||
sessionListener.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
return shellProcessId;
|
||||
|
||||
} else if (args.kind === 'external') {
|
||||
|
||||
return runInExternalTerminal(args, await this._configurationService.getConfigProvider());
|
||||
}
|
||||
return super.$runInTerminal(args);
|
||||
return super.$runInTerminal(args, sessionId);
|
||||
}
|
||||
|
||||
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
|
||||
@@ -139,17 +146,15 @@ class DebugTerminalCollection {
|
||||
private _terminalInstances = new Map<vscode.Terminal, { lastUsedAt: number, config: string }>();
|
||||
|
||||
public async checkout(config: string) {
|
||||
const entries = [...this._terminalInstances.keys()];
|
||||
const promises = entries.map((terminal) => createCancelablePromise(async ct => {
|
||||
const pid = await terminal.processId;
|
||||
if (await hasChildProcesses(pid)) {
|
||||
const entries = [...this._terminalInstances.entries()];
|
||||
const promises = entries.map(([terminal, termInfo]) => createCancelablePromise(async ct => {
|
||||
if (termInfo.lastUsedAt !== -1 && await hasChildProcesses(await terminal.processId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// important: date check and map operations must be synchronous
|
||||
const now = Date.now();
|
||||
const termInfo = this._terminalInstances.get(terminal);
|
||||
if (!termInfo || termInfo.lastUsedAt + DebugTerminalCollection.minUseDelay > now || ct.isCancellationRequested) {
|
||||
if (termInfo.lastUsedAt + DebugTerminalCollection.minUseDelay > now || ct.isCancellationRequested) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -168,6 +173,13 @@ class DebugTerminalCollection {
|
||||
this._terminalInstances.set(terminal, { lastUsedAt: Date.now(), config: termConfig });
|
||||
}
|
||||
|
||||
public free(terminal: vscode.Terminal) {
|
||||
const info = this._terminalInstances.get(terminal);
|
||||
if (info) {
|
||||
info.lastUsedAt = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public onTerminalClosed(terminal: vscode.Terminal) {
|
||||
this._terminalInstances.delete(terminal);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHost
|
||||
import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver';
|
||||
import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadService';
|
||||
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
|
||||
|
||||
class NodeModuleRequireInterceptor extends RequireInterceptor {
|
||||
|
||||
@@ -23,7 +23,7 @@ class NodeModuleRequireInterceptor extends RequireInterceptor {
|
||||
const that = this;
|
||||
const node_module = <any>require.__$__nodeRequire('module');
|
||||
const original = node_module._load;
|
||||
node_module._load = function load(request: string, parent: { filename: string; }, isMain: any) {
|
||||
node_module._load = function load(request: string, parent: { filename: string; }, isMain: boolean) {
|
||||
for (let alternativeModuleName of that._alternatives) {
|
||||
let alternative = alternativeModuleName(request);
|
||||
if (alternative) {
|
||||
|
||||
@@ -3,21 +3,20 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ILogService, DelegatedLogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { ILogService, LogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
|
||||
|
||||
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
|
||||
export class ExtHostLogService extends LogService implements ILogService, ExtHostLogServiceShape {
|
||||
|
||||
constructor(
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
) {
|
||||
if (initData.logFile.scheme !== Schemas.file) { throw new Error('Only file-logging supported'); }
|
||||
super(new SpdLogService(ExtensionHostLogFileName, dirname(initData.logFile).fsPath, initData.logLevel));
|
||||
super(new SpdLogLogger(ExtensionHostLogFileName, initData.logFile.fsPath, true, initData.logLevel));
|
||||
}
|
||||
|
||||
$setLevel(level: LogLevel): void {
|
||||
|
||||
@@ -7,14 +7,36 @@ import { MainThreadOutputServiceShape } from '../common/extHost.protocol';
|
||||
import type * as vscode from 'vscode';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender';
|
||||
import { toLocalISOString } from 'vs/base/common/date';
|
||||
import { dirExists, mkdirp } from 'vs/base/node/pfs';
|
||||
import { SymlinkSupport } from 'vs/base/node/pfs';
|
||||
import { promises } from 'fs';
|
||||
import { AbstractExtHostOutputChannel, ExtHostPushOutputChannel, ExtHostOutputService, LazyOutputChannel } from 'vs/workbench/api/common/extHostOutput';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { createRotatingLogger } from 'vs/platform/log/node/spdlogLog';
|
||||
import { RotatingLogger } from 'spdlog';
|
||||
import { ByteSize } from 'vs/platform/files/common/files';
|
||||
|
||||
class OutputAppender {
|
||||
|
||||
private appender: RotatingLogger;
|
||||
|
||||
constructor(name: string, readonly file: string) {
|
||||
this.appender = createRotatingLogger(name, file, 30 * ByteSize.MB, 1);
|
||||
this.appender.clearFormatters();
|
||||
}
|
||||
|
||||
append(content: string): void {
|
||||
this.appender.critical(content);
|
||||
}
|
||||
|
||||
flush(): void {
|
||||
this.appender.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChannel {
|
||||
|
||||
@@ -85,9 +107,9 @@ export class ExtHostOutputService2 extends ExtHostOutputService {
|
||||
private async _doCreateOutChannel(name: string): Promise<AbstractExtHostOutputChannel> {
|
||||
try {
|
||||
const outputDirPath = join(this._logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
|
||||
const exists = await dirExists(outputDirPath);
|
||||
const exists = await SymlinkSupport.existsDirectory(outputDirPath);
|
||||
if (!exists) {
|
||||
await mkdirp(outputDirPath);
|
||||
await promises.mkdir(outputDirPath, { recursive: true });
|
||||
}
|
||||
const fileName = `${this._namePool++}-${name.replace(/[\\/:\*\?"<>\|]/g, '')}`;
|
||||
const file = URI.file(join(outputDirPath, `${fileName}.log`));
|
||||
|
||||
@@ -11,8 +11,7 @@ import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/termi
|
||||
import { IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostConfiguration, ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IShellLaunchConfig, ITerminalEnvironment, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
|
||||
import { TerminalProcess } from 'vs/platform/terminal/node/terminalProcess';
|
||||
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
@@ -26,6 +25,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { getSystemShell, getSystemShellSync } from 'vs/base/node/shell';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IShellLaunchConfig, ITerminalEnvironment, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
|
||||
|
||||
export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
|
||||
@@ -60,7 +60,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
const terminal = new ExtHostTerminal(this._proxy, generateUuid(), { name, shellPath, shellArgs }, name);
|
||||
this._terminals.push(terminal);
|
||||
terminal.create(shellPath, shellArgs);
|
||||
return terminal;
|
||||
return terminal.value;
|
||||
}
|
||||
|
||||
public createTerminalFromOptions(options: vscode.TerminalOptions, isFeatureTerminal?: boolean): vscode.Terminal {
|
||||
@@ -74,8 +74,10 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
/*options.waitOnExit*/ undefined,
|
||||
withNullAsUndefined(options.strictEnv),
|
||||
withNullAsUndefined(options.hideFromUser),
|
||||
withNullAsUndefined(isFeatureTerminal));
|
||||
return terminal;
|
||||
withNullAsUndefined(isFeatureTerminal),
|
||||
true
|
||||
);
|
||||
return terminal.value;
|
||||
}
|
||||
|
||||
public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string {
|
||||
@@ -201,10 +203,11 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
|
||||
const envFromConfig = this._apiInspectConfigToPlain(configProvider.getConfiguration('terminal.integrated').inspect<ITerminalEnvironment>(`env.${platformKey}`));
|
||||
const baseEnv = terminalConfig.get<boolean>('inheritEnv', true) ? process.env as platform.IProcessEnvironment : await this._getNonInheritedEnv();
|
||||
const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._variableResolver);
|
||||
const env = terminalEnvironment.createTerminalEnvironment(
|
||||
shellLaunchConfig,
|
||||
envFromConfig,
|
||||
terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._variableResolver),
|
||||
variableResolver,
|
||||
isWorkspaceShellAllowed,
|
||||
this._extHostInitDataService.version,
|
||||
terminalConfig.get<'auto' | 'off' | 'on'>('detectLocale', 'auto'),
|
||||
@@ -212,9 +215,9 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
);
|
||||
|
||||
// Apply extension environment variable collections to the environment
|
||||
if (!shellLaunchConfig.strictEnv) {
|
||||
if (!shellLaunchConfig.strictEnv && !shellLaunchConfig.hideFromUser) {
|
||||
const mergedCollection = new MergedEnvironmentVariableCollection(this._environmentVariableCollections);
|
||||
mergedCollection.applyToProcessEnvironment(env);
|
||||
mergedCollection.applyToProcessEnvironment(env, variableResolver);
|
||||
}
|
||||
|
||||
this._proxy.$sendResolvedLaunchConfig(id, shellLaunchConfig);
|
||||
|
||||
@@ -18,7 +18,6 @@ import { IExtHostTunnelService, TunnelDto } from 'vs/workbench/api/common/extHos
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { TunnelOptions, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { promisify } from 'util';
|
||||
import { MovingAverage } from 'vs/base/common/numbers';
|
||||
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
@@ -148,7 +147,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
super();
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService);
|
||||
if (isLinux && initData.remote.isRemote && initData.remote.authority) {
|
||||
this._proxy.$setCandidateFinder();
|
||||
this._proxy.$setRemoteTunnelService(process.pid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +196,9 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
|
||||
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
|
||||
if (provider) {
|
||||
if (provider.candidatePortSource !== undefined) {
|
||||
await this._proxy.$setCandidatePortSource(provider.candidatePortSource);
|
||||
}
|
||||
if (provider.showCandidatePort) {
|
||||
this._showCandidatePort = provider.showCandidatePort;
|
||||
await this._proxy.$setCandidateFilter();
|
||||
@@ -267,8 +269,8 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
let tcp: string = '';
|
||||
let tcp6: string = '';
|
||||
try {
|
||||
tcp = await pfs.readFile('/proc/net/tcp', 'utf8');
|
||||
tcp6 = await pfs.readFile('/proc/net/tcp6', 'utf8');
|
||||
tcp = await fs.promises.readFile('/proc/net/tcp', 'utf8');
|
||||
tcp6 = await fs.promises.readFile('/proc/net/tcp6', 'utf8');
|
||||
} catch (e) {
|
||||
// File reading error. No additional handling needed.
|
||||
}
|
||||
@@ -286,10 +288,10 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
try {
|
||||
const pid: number = Number(childName);
|
||||
const childUri = resources.joinPath(URI.file('/proc'), childName);
|
||||
const childStat = await pfs.stat(childUri.fsPath);
|
||||
const childStat = await fs.promises.stat(childUri.fsPath);
|
||||
if (childStat.isDirectory() && !isNaN(pid)) {
|
||||
const cwd = await promisify(fs.readlink)(resources.joinPath(childUri, 'cwd').fsPath);
|
||||
const cmd = await pfs.readFile(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8');
|
||||
const cwd = await fs.promises.readlink(resources.joinPath(childUri, 'cwd').fsPath);
|
||||
const cmd = await fs.promises.readFile(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8');
|
||||
processes.push({ pid, cwd, cmd });
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log';
|
||||
import { ILogService, LogLevel, AbstractLogger } from 'vs/platform/log/common/log';
|
||||
import { ExtHostLogServiceShape, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { UriComponents } from 'vs/base/common/uri';
|
||||
|
||||
export class ExtHostLogService extends AbstractLogService implements ILogService, ExtHostLogServiceShape {
|
||||
export class ExtHostLogService extends AbstractLogger implements ILogService, ExtHostLogServiceShape {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user