mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-08 01:28:26 -05:00
Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898 (#15681)
* Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898 * Fixes and cleanup * Distro * Fix hygiene yarn * delete no yarn lock changes file * Fix hygiene * Fix layer check * Fix CI * Skip lib checks * Remove tests deleted in vs code * Fix tests * Distro * Fix tests and add removed extension point * Skip failing notebook tests for now * Disable broken tests and cleanup build folder * Update yarn.lock and fix smoke tests * Bump sqlite * fix contributed actions and file spacing * Fix user data path * Update yarn.locks Co-authored-by: ADS Merger <karlb@microsoft.com>
This commit is contained in:
20
src/vs/workbench/api/browser/apiCommands.ts
Normal file
20
src/vs/workbench/api/browser/apiCommands.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
|
||||
if (isWeb) {
|
||||
CommandsRegistry.registerCommand('_workbench.fetchJSON', async function (accessor: ServicesAccessor, url: string, method: string) {
|
||||
const result = await fetch(url, { method, headers: { Accept: 'application/json' } });
|
||||
|
||||
if (result.ok) {
|
||||
return result.json();
|
||||
} else {
|
||||
throw new Error(result.statusText);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -63,6 +63,8 @@ import './mainThreadWebviewManager';
|
||||
import './mainThreadWorkspace';
|
||||
import './mainThreadComments';
|
||||
import './mainThreadNotebook';
|
||||
import './mainThreadNotebookKernels';
|
||||
import './mainThreadNotebookDocumentsAndEditors';
|
||||
import './mainThreadTask';
|
||||
import './mainThreadLabelService';
|
||||
import './mainThreadTunnelService';
|
||||
@@ -71,6 +73,7 @@ import './mainThreadTimeline';
|
||||
import './mainThreadTesting';
|
||||
import './mainThreadSecretState';
|
||||
import 'vs/workbench/api/common/apiCommands';
|
||||
import 'vs/workbench/api/browser/apiCommands';
|
||||
|
||||
export class ExtensionPoints implements IWorkbenchContribution {
|
||||
|
||||
|
||||
@@ -18,9 +18,6 @@ import { fromNow } from 'vs/base/common/date';
|
||||
import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
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,
|
||||
@@ -33,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);
|
||||
|
||||
@@ -65,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");
|
||||
|
||||
@@ -83,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."));
|
||||
}
|
||||
}
|
||||
@@ -175,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 => {
|
||||
@@ -195,7 +146,6 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -211,16 +161,9 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
this.authenticationService.sessionsUpdate(id, event);
|
||||
}
|
||||
|
||||
$logout(providerId: string, sessionId: string): Promise<void> {
|
||||
return this.authenticationService.logout(providerId, sessionId);
|
||||
$removeSession(providerId: string, sessionId: string): Promise<void> {
|
||||
return this.authenticationService.removeSession(providerId, sessionId);
|
||||
}
|
||||
|
||||
private isAccessAllowed(providerId: string, accountName: string, extensionId: string): boolean {
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
const extensionData = allowList.find(extension => extension.id === extensionId);
|
||||
return !!extensionData;
|
||||
}
|
||||
|
||||
private async loginPrompt(providerName: string, extensionName: string): Promise<boolean> {
|
||||
const { choice } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
@@ -235,17 +178,12 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
}
|
||||
|
||||
private 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);
|
||||
}
|
||||
|
||||
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, potentialSessions: modes.AuthenticationSession[], clearSessionPreference: boolean): Promise<modes.AuthenticationSession> {
|
||||
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');
|
||||
}
|
||||
@@ -257,52 +195,66 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
if (existingSessionPreference) {
|
||||
const matchingSession = potentialSessions.find(session => session.id === existingSessionPreference);
|
||||
if (matchingSession) {
|
||||
const allowed = await this.authenticationService.showGetSessionPrompt(providerId, matchingSession.account.label, extensionId, extensionName);
|
||||
if (allowed) {
|
||||
return 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.authenticationService.selectSession(providerId, extensionId, extensionName, potentialSessions);
|
||||
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 orderedScopes = scopes.sort().join(' ');
|
||||
const sessions = (await this.authenticationService.getSessions(providerId)).filter(session => session.scopes.slice().sort().join(' ') === orderedScopes);
|
||||
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.isAccessAllowed(providerId, session.account.label, extensionId);
|
||||
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 {
|
||||
this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, [session]);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!silent) {
|
||||
session = await this.selectSession(providerId, extensionId, extensionName, sessions, !!options.clearSessionPreference);
|
||||
} else {
|
||||
this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, sessions);
|
||||
}
|
||||
return this.selectSession(providerId, extensionId, extensionName, scopes, sessions, !!options.clearSessionPreference, silent);
|
||||
}
|
||||
} else {
|
||||
if (!silent) {
|
||||
const isAllowed = await this.loginPrompt(providerId, extensionName);
|
||||
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.login(providerId, scopes);
|
||||
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);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { IExtHostContext, IWorkspaceEditDto, MainThreadBulkEditsShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { reviveWorkspaceEditDto2 } from 'vs/workbench/api/browser/mainThreadEditors';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadBulkEdits)
|
||||
export class MainThreadBulkEdits implements MainThreadBulkEditsShape {
|
||||
@@ -13,12 +14,16 @@ export class MainThreadBulkEdits implements MainThreadBulkEditsShape {
|
||||
constructor(
|
||||
_extHostContext: IExtHostContext,
|
||||
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) { }
|
||||
|
||||
dispose(): void { }
|
||||
|
||||
$tryApplyWorkspaceEdit(dto: IWorkspaceEditDto, undoRedoGroupId?: number): Promise<boolean> {
|
||||
const edits = reviveWorkspaceEditDto2(dto);
|
||||
return this._bulkEditService.apply(edits, { undoRedoGroupId }).then(() => true, _err => false);
|
||||
return this._bulkEditService.apply(edits, { undoRedoGroupId }).then(() => true, err => {
|
||||
this._logService.warn('IGNORING workspace edit', err);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { CLIOutput, IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagementCLIService';
|
||||
@@ -15,21 +15,30 @@ import { getExtensionId } from 'vs/platform/extensionManagement/common/extension
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IOpenWindowOptions, IWindowOpenable } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { canExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
|
||||
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 });
|
||||
return openerService.open(isString(uri) ? uri : URI.revive(uri), { openExternal: true, allowTunneling: true });
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('_remoteCLI.windowOpen', function (accessor: ServicesAccessor, toOpen: IWindowOpenable[], options?: IOpenWindowOptions) {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
return commandService.executeCommand('_files.windowOpen', toOpen, options);
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('_remoteCLI.getSystemStatus', function (accessor: ServicesAccessor) {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
return commandService.executeCommand('_issues.getSystemStatus');
|
||||
});
|
||||
|
||||
interface ManageExtensionsArgs {
|
||||
@@ -81,25 +90,25 @@ class RemoteExtensionCLIManagementService extends ExtensionManagementCLIService
|
||||
|
||||
constructor(
|
||||
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IProductService productService: IProductService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
|
||||
@ILocalizationsService localizationsService: ILocalizationsService,
|
||||
@ILabelService labelService: ILabelService,
|
||||
@IWorkbenchEnvironmentService envService: IWorkbenchEnvironmentService
|
||||
@IWorkbenchEnvironmentService envService: IWorkbenchEnvironmentService,
|
||||
@IExtensionManifestPropertiesService private readonly _extensionManifestPropertiesService: IExtensionManifestPropertiesService,
|
||||
) {
|
||||
super(extensionManagementService, extensionGalleryService, localizationsService);
|
||||
super(extensionManagementService, extensionGalleryService);
|
||||
|
||||
const remoteAuthority = envService.remoteAuthority;
|
||||
this._location = remoteAuthority ? labelService.getHostLabel(Schemas.vscodeRemote, remoteAuthority) : undefined;
|
||||
}
|
||||
|
||||
protected get location(): string | undefined {
|
||||
protected override get location(): string | undefined {
|
||||
return this._location;
|
||||
}
|
||||
|
||||
protected validateExtensionKind(manifest: IExtensionManifest, output: CLIOutput): boolean {
|
||||
if (!canExecuteOnWorkspace(manifest, this.productService, this.configurationService)) {
|
||||
protected override validateExtensionKind(manifest: IExtensionManifest, output: CLIOutput): boolean {
|
||||
if (!this._extensionManifestPropertiesService.canExecuteOnWorkspace(manifest)) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { UriComponents, URI } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { MainContext, MainThreadEditorInsetsShape, IExtHostContext, ExtHostEditorInsetsShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from '../common/extHostCustomers';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { reviveWebviewContentOptions } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
import { ExtHostContext, ExtHostEditorInsetsShape, IExtHostContext, IWebviewOptions, MainContext, MainThreadEditorInsetsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { extHostNamedCustomer } from '../common/extHostCustomers';
|
||||
|
||||
// todo@jrieken move these things back into something like contrib/insets
|
||||
class EditorWebviewZone implements IViewZone {
|
||||
@@ -70,7 +70,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void> {
|
||||
async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void> {
|
||||
|
||||
let editor: IActiveCodeEditor | undefined;
|
||||
id = id.substr(0, id.indexOf(',')); //todo@jrieken HACK
|
||||
@@ -91,10 +91,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
|
||||
const webview = this._webviewService.createWebviewElement('' + handle, {
|
||||
enableFindWidget: false,
|
||||
}, {
|
||||
allowScripts: options.enableScripts,
|
||||
localResourceRoots: options.localResourceRoots ? options.localResourceRoots.map(uri => URI.revive(uri)) : undefined
|
||||
}, { id: extensionId, location: URI.revive(extensionLocation) });
|
||||
}, reviveWebviewContentOptions(options), { id: extensionId, location: URI.revive(extensionLocation) });
|
||||
|
||||
const webviewZone = new EditorWebviewZone(editor, line, height, webview);
|
||||
|
||||
@@ -108,7 +105,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
disposables.add(editor.onDidDispose(remove));
|
||||
disposables.add(webviewZone);
|
||||
disposables.add(webview);
|
||||
disposables.add(webview.onMessage(msg => this._proxy.$onDidReceiveMessage(handle, msg)));
|
||||
disposables.add(webview.onMessage(msg => this._proxy.$onDidReceiveMessage(handle, msg.message)));
|
||||
|
||||
this._insets.set(handle, webviewZone);
|
||||
}
|
||||
@@ -117,7 +114,6 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
const inset = this.getInset(handle);
|
||||
this._insets.delete(handle);
|
||||
inset.dispose();
|
||||
|
||||
}
|
||||
|
||||
$setHtml(handle: number, value: string): void {
|
||||
@@ -125,12 +121,9 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
inset.webview.html = value;
|
||||
}
|
||||
|
||||
$setOptions(handle: number, options: modes.IWebviewOptions): void {
|
||||
$setOptions(handle: number, options: IWebviewOptions): void {
|
||||
const inset = this.getInset(handle);
|
||||
inset.webview.contentOptions = {
|
||||
...options,
|
||||
localResourceRoots: options.localResourceRoots?.map(components => URI.from(components)),
|
||||
};
|
||||
inset.webview.contentOptions = reviveWebviewContentOptions(options);
|
||||
}
|
||||
|
||||
async $postMessage(handle: number, value: any): Promise<boolean> {
|
||||
|
||||
@@ -535,7 +535,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
this._commentService.updateComments(providerId, event);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
this._workspaceProviders.forEach(value => dispose(value));
|
||||
this._workspaceProviders.clear();
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { multibyteAwareBtoa } from 'vs/base/browser/dom';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
@@ -13,10 +14,9 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { isEqual, isEqualOrParent, toLocalResource } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { FileChangesEvent, FileChangeType, FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
@@ -31,13 +31,13 @@ import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/
|
||||
import { WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput';
|
||||
import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
|
||||
import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { IWorkingCopy, IWorkingCopyBackup, NO_TYPE_ID, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopy';
|
||||
|
||||
const enum CustomEditorModelType {
|
||||
Custom,
|
||||
@@ -60,8 +60,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
@ICustomEditorService private readonly _customEditorService: ICustomEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
|
||||
@IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IBackupFileService private readonly _backupService: IBackupFileService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -92,28 +91,29 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
}));
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
|
||||
dispose(this._editorProviders.values());
|
||||
this._editorProviders.clear();
|
||||
}
|
||||
|
||||
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void {
|
||||
this.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true);
|
||||
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities, serializeBuffersForPostMessage: boolean): void {
|
||||
this.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true, serializeBuffersForPostMessage);
|
||||
}
|
||||
|
||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void {
|
||||
this.registerEditorProvider(CustomEditorModelType.Custom, reviveWebviewExtension(extensionData), viewType, options, {}, supportsMultipleEditorsPerDocument);
|
||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean, serializeBuffersForPostMessage: boolean): void {
|
||||
this.registerEditorProvider(CustomEditorModelType.Custom, reviveWebviewExtension(extensionData), viewType, options, {}, supportsMultipleEditorsPerDocument, serializeBuffersForPostMessage);
|
||||
}
|
||||
|
||||
private registerEditorProvider(
|
||||
modelType: CustomEditorModelType,
|
||||
extension: WebviewExtensionDescription,
|
||||
viewType: string,
|
||||
options: modes.IWebviewPanelOptions,
|
||||
options: extHostProtocol.IWebviewPanelOptions,
|
||||
capabilities: extHostProtocol.CustomTextEditorCapabilities,
|
||||
supportsMultipleEditorsPerDocument: boolean,
|
||||
serializeBuffersForPostMessage: boolean,
|
||||
): void {
|
||||
if (this._editorProviders.has(viewType)) {
|
||||
throw new Error(`Provider for ${viewType} already registered`);
|
||||
@@ -133,7 +133,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
const handle = webviewInput.id;
|
||||
const resource = webviewInput.resource;
|
||||
|
||||
this.mainThreadWebviewPanels.addWebviewInput(handle, webviewInput);
|
||||
this.mainThreadWebviewPanels.addWebviewInput(handle, webviewInput, { serializeBuffersForPostMessage });
|
||||
webviewInput.webview.options = options;
|
||||
webviewInput.webview.extension = extension;
|
||||
|
||||
@@ -176,7 +176,11 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
}
|
||||
|
||||
try {
|
||||
await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation);
|
||||
await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, {
|
||||
title: webviewInput.getTitle(),
|
||||
webviewOptions: webviewInput.webview.contentOptions,
|
||||
panelOptions: webviewInput.webview.options,
|
||||
}, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), cancellation);
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
webviewInput.webview.html = this.mainThreadWebview.getWebviewResolvedFailedContent(viewType);
|
||||
@@ -224,7 +228,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxyCustomEditors, viewType, resource, options, () => {
|
||||
return Array.from(this.mainThreadWebviewPanels.webviewInputs)
|
||||
.filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[];
|
||||
}, cancellation, this._backupService);
|
||||
}, cancellation);
|
||||
return this._customEditorService.models.add(resource, viewType, model);
|
||||
}
|
||||
}
|
||||
@@ -274,6 +278,8 @@ namespace HotExitState {
|
||||
|
||||
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
|
||||
|
||||
#isDisposed = false;
|
||||
|
||||
private _fromBackup: boolean = false;
|
||||
private _hotExitState: HotExitState.State = HotExitState.Allowed;
|
||||
private _backupId: string | undefined;
|
||||
@@ -282,9 +288,25 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
private _savePoint: number = -1;
|
||||
private readonly _edits: Array<number> = [];
|
||||
private _isDirtyFromContentChange = false;
|
||||
private _inOrphaned = false;
|
||||
|
||||
private _ongoingSave?: CancelablePromise<void>;
|
||||
|
||||
private readonly _onDidChangeOrphaned = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeOrphaned = this._onDidChangeOrphaned.event;
|
||||
|
||||
// TODO@mjbvz consider to enable a `typeId` that is specific for custom
|
||||
// editors. Using a distinct `typeId` allows the working copy to have
|
||||
// any resource (including file based resources) even if other working
|
||||
// copies exist with the same resource.
|
||||
//
|
||||
// IMPORTANT: changing the `typeId` has an impact on backups for this
|
||||
// working copy. Any value that is not the empty string will be used
|
||||
// as seed to the backup. Only change the `typeId` if you have implemented
|
||||
// a fallback solution to resolve any existing backups that do not have
|
||||
// this seed.
|
||||
readonly typeId = NO_TYPE_ID;
|
||||
|
||||
public static async create(
|
||||
instantiationService: IInstantiationService,
|
||||
proxy: extHostProtocol.ExtHostCustomEditorsShape,
|
||||
@@ -293,9 +315,13 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
options: { backupId?: string },
|
||||
getEditors: () => CustomEditorInput[],
|
||||
cancellation: CancellationToken,
|
||||
_backupFileService: IBackupFileService,
|
||||
) {
|
||||
const { editable } = await proxy.$createCustomDocument(resource, viewType, options.backupId, cancellation);
|
||||
): Promise<MainThreadCustomEditorModel> {
|
||||
const editors = getEditors();
|
||||
let untitledDocumentData: VSBuffer | undefined;
|
||||
if (editors.length !== 0) {
|
||||
untitledDocumentData = editors[0].untitledDocumentData;
|
||||
}
|
||||
const { editable } = await proxy.$createCustomDocument(resource, viewType, options.backupId, untitledDocumentData, cancellation);
|
||||
return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, !!options.backupId, editable, getEditors);
|
||||
}
|
||||
|
||||
@@ -321,17 +347,23 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
if (_editable) {
|
||||
this._register(workingCopyService.registerWorkingCopy(this));
|
||||
}
|
||||
|
||||
this._register(_fileService.onDidFilesChange(e => this.onDidFilesChange(e)));
|
||||
}
|
||||
|
||||
get editorResource() {
|
||||
return this._editorResource;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override dispose() {
|
||||
this.#isDisposed = true;
|
||||
|
||||
if (this._editable) {
|
||||
this._undoService.removeElements(this._editorResource);
|
||||
}
|
||||
|
||||
this._proxy.$disposeCustomDocument(this._editorResource, this._viewType);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -371,6 +403,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
return this._fromBackup;
|
||||
}
|
||||
|
||||
public isOrphaned(): boolean {
|
||||
return this._inOrphaned;
|
||||
}
|
||||
|
||||
private isUntitled() {
|
||||
return this._editorResource.scheme === Schemas.untitled;
|
||||
}
|
||||
@@ -383,8 +419,64 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
|
||||
//#endregion
|
||||
|
||||
public isReadonly() {
|
||||
return !this._editable;
|
||||
private async onDidFilesChange(e: FileChangesEvent): Promise<void> {
|
||||
let fileEventImpactsModel = false;
|
||||
let newInOrphanModeGuess: boolean | undefined;
|
||||
|
||||
// If we are currently orphaned, we check if the model file was added back
|
||||
if (this._inOrphaned) {
|
||||
const modelFileAdded = e.contains(this.editorResource, FileChangeType.ADDED);
|
||||
if (modelFileAdded) {
|
||||
newInOrphanModeGuess = false;
|
||||
fileEventImpactsModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise we check if the model file was deleted
|
||||
else {
|
||||
const modelFileDeleted = e.contains(this.editorResource, FileChangeType.DELETED);
|
||||
if (modelFileDeleted) {
|
||||
newInOrphanModeGuess = true;
|
||||
fileEventImpactsModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileEventImpactsModel && this._inOrphaned !== newInOrphanModeGuess) {
|
||||
let newInOrphanModeValidated: boolean = false;
|
||||
if (newInOrphanModeGuess) {
|
||||
// We have received reports of users seeing delete events even though the file still
|
||||
// exists (network shares issue: https://github.com/microsoft/vscode/issues/13665).
|
||||
// Since we do not want to mark the model as orphaned, we have to check if the
|
||||
// file is really gone and not just a faulty file event.
|
||||
await timeout(100);
|
||||
|
||||
if (this.#isDisposed) {
|
||||
newInOrphanModeValidated = true;
|
||||
} else {
|
||||
const exists = await this._fileService.exists(this.editorResource);
|
||||
newInOrphanModeValidated = !exists;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inOrphaned !== newInOrphanModeValidated && !this.#isDisposed) {
|
||||
this.setOrphaned(newInOrphanModeValidated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setOrphaned(orphaned: boolean): void {
|
||||
if (this._inOrphaned !== orphaned) {
|
||||
this._inOrphaned = orphaned;
|
||||
this._onDidChangeOrphaned.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public isEditable(): boolean {
|
||||
return this._editable;
|
||||
}
|
||||
|
||||
public isOnReadonlyFileSystem(): boolean {
|
||||
return this._fileService.hasCapability(this.editorResource, FileSystemProviderCapabilities.Readonly);
|
||||
}
|
||||
|
||||
public get viewType() {
|
||||
@@ -570,23 +662,25 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
}
|
||||
const primaryEditor = editors[0];
|
||||
|
||||
const backupData: IWorkingCopyBackup<CustomDocumentBackupData> = {
|
||||
meta: {
|
||||
viewType: this.viewType,
|
||||
editorResource: this._editorResource,
|
||||
backupId: '',
|
||||
extension: primaryEditor.extension ? {
|
||||
id: primaryEditor.extension.id.value,
|
||||
location: primaryEditor.extension.location,
|
||||
} : undefined,
|
||||
webview: {
|
||||
id: primaryEditor.id,
|
||||
options: primaryEditor.webview.options,
|
||||
state: primaryEditor.webview.state,
|
||||
}
|
||||
const backupMeta: CustomDocumentBackupData = {
|
||||
viewType: this.viewType,
|
||||
editorResource: this._editorResource,
|
||||
backupId: '',
|
||||
extension: primaryEditor.extension ? {
|
||||
id: primaryEditor.extension.id.value,
|
||||
location: primaryEditor.extension.location,
|
||||
} : undefined,
|
||||
webview: {
|
||||
id: primaryEditor.id,
|
||||
options: primaryEditor.webview.options,
|
||||
state: primaryEditor.webview.state,
|
||||
}
|
||||
};
|
||||
|
||||
const backupData: IWorkingCopyBackup = {
|
||||
meta: backupMeta
|
||||
};
|
||||
|
||||
if (!this._editable) {
|
||||
return backupData;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
} else if (dto.type === 'function') {
|
||||
this.debugService.addFunctionBreakpoint(dto.functionName, dto.id);
|
||||
} else if (dto.type === 'data') {
|
||||
this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes);
|
||||
this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes, dto.accessType);
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
|
||||
@@ -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 = () => {
|
||||
@@ -158,10 +157,12 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen
|
||||
}));
|
||||
|
||||
this._register(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => {
|
||||
if (e.operation === FileOperation.MOVE || e.operation === FileOperation.DELETE) {
|
||||
for (const { source } of e.files) {
|
||||
if (source) {
|
||||
this._modelReferenceCollection.remove(source);
|
||||
const isMove = e.operation === FileOperation.MOVE;
|
||||
if (isMove || e.operation === FileOperation.DELETE) {
|
||||
for (const pair of e.files) {
|
||||
const removed = isMove ? pair.source : pair.target;
|
||||
if (removed) {
|
||||
this._modelReferenceCollection.remove(removed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,7 +171,7 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen
|
||||
this._modelTrackers = Object.create(null);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
public override dispose(): void {
|
||||
Object.keys(this._modelTrackers).forEach((modelUrl) => {
|
||||
this._modelTrackers[modelUrl].dispose();
|
||||
});
|
||||
@@ -203,12 +204,12 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen
|
||||
}
|
||||
|
||||
private _onModelModeChanged(event: { model: ITextModel; oldModeId: string; }): void {
|
||||
let { model, oldModeId } = event;
|
||||
let { model } = event;
|
||||
const modelUrl = model.uri;
|
||||
if (!this._modelIsSynced.has(modelUrl.toString())) {
|
||||
return;
|
||||
}
|
||||
this._proxy.$acceptModelModeChanged(model.uri, oldModeId, model.getLanguageIdentifier().language);
|
||||
this._proxy.$acceptModelModeChanged(model.uri, model.getLanguageIdentifier().language);
|
||||
}
|
||||
|
||||
private _onModelRemoved(modelUrl: URI): void {
|
||||
@@ -267,7 +268,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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -518,7 +518,7 @@ export class MainThreadTextEditor {
|
||||
|
||||
const snippetController = SnippetController2.get(this._codeEditor);
|
||||
|
||||
// // cancel previous snippet mode
|
||||
// cancel previous snippet mode
|
||||
// snippetController.leaveSnippet();
|
||||
|
||||
// set selection, focus editor
|
||||
|
||||
@@ -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: uri?.fsPath?.toLowerCase().endsWith('ipynb') || uri?.fsPath?.toLowerCase().endsWith('sql') ? undefined : false // {{SQL CARBON EDIT}}
|
||||
override: uri?.fsPath?.toLowerCase().endsWith('ipynb') || uri?.fsPath?.toLowerCase().endsWith('sql') ? undefined : EditorOverride.DISABLED // {{SQL CARBON EDIT}}
|
||||
};
|
||||
|
||||
const input: IResourceEditorInput = {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { SerializedError } from 'vs/base/common/errors';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { IExtHostContext, MainContext, MainThreadExtensionServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IExtensionService, ExtensionActivationError, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionService, ExtensionHostKind, MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -15,11 +15,12 @@ import { Action } from 'vs/base/common/actions';
|
||||
import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
import { ITimerService } from 'vs/workbench/services/timer/browser/timerService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadExtensionService)
|
||||
export class MainThreadExtensionService implements MainThreadExtensionServiceShape {
|
||||
@@ -34,6 +35,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
||||
@IHostService private readonly _hostService: IHostService,
|
||||
@IWorkbenchExtensionEnablementService private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,
|
||||
@ITimerService private readonly _timerService: ITimerService,
|
||||
@IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService,
|
||||
) {
|
||||
this._extensionHostKind = extHostContext.extensionHostKind;
|
||||
}
|
||||
@@ -59,25 +61,36 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
||||
console.error(`[${extensionId}]${error.message}`);
|
||||
console.error(error.stack);
|
||||
}
|
||||
async $onExtensionActivationError(extensionId: ExtensionIdentifier, activationError: ExtensionActivationError): Promise<void> {
|
||||
if (typeof activationError === 'string') {
|
||||
this._extensionService._logOrShowMessage(Severity.Error, activationError);
|
||||
} else {
|
||||
this._handleMissingDependency(extensionId, activationError.dependency);
|
||||
}
|
||||
}
|
||||
async $onExtensionActivationError(extensionId: ExtensionIdentifier, data: SerializedError, missingExtensionDependency: MissingExtensionDependency | null): Promise<void> {
|
||||
const error = new Error();
|
||||
error.name = data.name;
|
||||
error.message = data.message;
|
||||
error.stack = data.stack;
|
||||
|
||||
private async _handleMissingDependency(extensionId: ExtensionIdentifier, missingDependency: string): Promise<void> {
|
||||
const extension = await this._extensionService.getExtension(extensionId.value);
|
||||
if (extension) {
|
||||
const local = await this._extensionsWorkbenchService.queryLocal();
|
||||
const installedDependency = local.filter(i => areSameExtensions(i.identifier, { id: missingDependency }))[0];
|
||||
if (installedDependency) {
|
||||
await this._handleMissingInstalledDependency(extension, installedDependency.local!);
|
||||
} else {
|
||||
await this._handleMissingNotInstalledDependency(extension, missingDependency);
|
||||
this._extensionService._onDidActivateExtensionError(extensionId, error);
|
||||
|
||||
if (missingExtensionDependency) {
|
||||
const extension = await this._extensionService.getExtension(extensionId.value);
|
||||
if (extension) {
|
||||
const local = await this._extensionsWorkbenchService.queryLocal();
|
||||
const installedDependency = local.filter(i => areSameExtensions(i.identifier, { id: missingExtensionDependency.dependency }))[0];
|
||||
if (installedDependency) {
|
||||
await this._handleMissingInstalledDependency(extension, installedDependency.local!);
|
||||
return;
|
||||
} else {
|
||||
await this._handleMissingNotInstalledDependency(extension, missingExtensionDependency.dependency);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment;
|
||||
if (isDev) {
|
||||
this._notificationService.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(error.message);
|
||||
}
|
||||
|
||||
private async _handleMissingInstalledDependency(extension: IExtensionDescription, missingInstalledDependency: ILocalExtension): Promise<void> {
|
||||
@@ -106,26 +119,26 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
||||
|
||||
private async _handleMissingNotInstalledDependency(extension: IExtensionDescription, missingDependency: string): Promise<void> {
|
||||
const extName = extension.displayName || extension.name;
|
||||
const dependencyExtension = (await this._extensionsWorkbenchService.queryGallery({ names: [missingDependency] }, CancellationToken.None)).firstPage[0];
|
||||
let dependencyExtension: IExtension | null = null;
|
||||
try {
|
||||
dependencyExtension = (await this._extensionsWorkbenchService.queryGallery({ names: [missingDependency] }, CancellationToken.None)).firstPage[0];
|
||||
} catch (err) {
|
||||
}
|
||||
if (dependencyExtension) {
|
||||
this._notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('uninstalledDep', "Cannot activate the '{0}' extension because it depends on the '{1}' extension, which is not installed. Would you like to install the extension and reload the window?", extName, dependencyExtension.displayName),
|
||||
actions: {
|
||||
primary: [new Action('install', localize('install missing dep', "Install and Reload"), '', true,
|
||||
() => this._extensionsWorkbenchService.install(dependencyExtension)
|
||||
() => this._extensionsWorkbenchService.install(dependencyExtension!)
|
||||
.then(() => this._hostService.reload(), e => this._notificationService.error(e)))]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._notificationService.error(localize('unknownDep', "Cannot activate the '{0}' extension because it depends on an unknown '{1}' extension .", extName, missingDependency));
|
||||
this._notificationService.error(localize('unknownDep', "Cannot activate the '{0}' extension because it depends on an unknown '{1}' extension.", extName, missingDependency));
|
||||
}
|
||||
}
|
||||
|
||||
async $onExtensionHostExit(code: number): Promise<void> {
|
||||
this._extensionService._onExtensionHostExit(code);
|
||||
}
|
||||
|
||||
async $setPerformanceMarks(marks: PerformanceMark[]): Promise<void> {
|
||||
if (this._extensionHostKind === ExtensionHostKind.LocalProcess) {
|
||||
this._timerService.setPerformanceMarks('localExtHost', marks);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -251,6 +251,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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -66,9 +66,12 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
|
||||
primaryActions.push(new MessageItemAction('_extension_message_handle_' + command.handle, command.title, command.handle));
|
||||
});
|
||||
|
||||
let source: string | undefined;
|
||||
let source: string | { label: string, id: string } | undefined;
|
||||
if (extension) {
|
||||
source = nls.localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
|
||||
source = {
|
||||
label: nls.localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name),
|
||||
id: extension.identifier.value
|
||||
};
|
||||
}
|
||||
|
||||
if (!source) {
|
||||
@@ -118,7 +121,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
|
||||
cancelId = buttons.length - 1;
|
||||
}
|
||||
|
||||
const { choice } = await this._dialogService.show(severity, message, buttons, { cancelId, useCustom });
|
||||
const { choice } = await this._dialogService.show(severity, message, buttons, { cancelId, custom: useCustom });
|
||||
return choice === commands.length ? undefined : commands[choice].handle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,475 +3,71 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
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 { ResourceMap } from 'vs/base/common/map';
|
||||
import { IExtUri } 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 { ILogService } from 'vs/platform/log/common/log';
|
||||
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
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 { 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 { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, 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,
|
||||
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 removedAPIEditors = editorDelta.removed.map(removed => removed.getId());
|
||||
|
||||
// const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined;
|
||||
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
|
||||
|
||||
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
|
||||
};
|
||||
}),
|
||||
removedDocuments: documentDelta.removed.map(e => e.uri),
|
||||
addedEditors: addedAPIEditors,
|
||||
removedEditors: removedAPIEditors,
|
||||
newActiveEditor: newActiveEditor,
|
||||
visibleEditors: visibleEditorDelta.added.length === 0 && visibleEditorDelta.removed.length === 0
|
||||
? undefined
|
||||
: [...after.visibleEditors].map(editor => editor[0])
|
||||
};
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly documents: Set<NotebookTextModel>,
|
||||
readonly textEditors: Map<string, IEditor>,
|
||||
readonly activeEditor: string | null | undefined,
|
||||
readonly visibleEditors: Map<string, IEditor>
|
||||
) {
|
||||
//
|
||||
}
|
||||
}
|
||||
import { NotebookSelector } from 'vs/workbench/contrib/notebook/common/notebookSelector';
|
||||
import { INotebookCellStatusBarItemProvider, INotebookExclusiveDocumentFilter, NotebookDataDto, TransientCellMetadata, TransientDocumentMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookContentProvider, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, MainContext, MainThreadNotebookShape, NotebookExtensionDescription } from '../common/extHost.protocol';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadNotebook)
|
||||
export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape {
|
||||
private readonly _notebookProviders = new Map<string, { controller: IMainNotebookController, disposable: IDisposable }>();
|
||||
private readonly _notebookKernelProviders = new Map<number, { extension: NotebookExtensionDescription, emitter: Emitter<URI | undefined>, provider: IDisposable }>();
|
||||
export class MainThreadNotebooks implements MainThreadNotebookShape {
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
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 _cellStatusBarEntries: Map<number, IDisposable> = new Map();
|
||||
private readonly _modelReferenceCollection: BoundModelReferenceCollection;
|
||||
private readonly _notebookProviders = new Map<string, { controller: INotebookContentProvider, disposable: IDisposable }>();
|
||||
private readonly _notebookSerializer = new Map<number, IDisposable>();
|
||||
private readonly _notebookCellStatusBarRegistrations = new Map<number, IDisposable>();
|
||||
|
||||
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,
|
||||
@INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService,
|
||||
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@INotebookCellStatusBarService private readonly _cellStatusBarService: INotebookCellStatusBarService,
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
this._modelReferenceCollection = new BoundModelReferenceCollection(this._uriIdentityService.extUri);
|
||||
this._register(this._modelReferenceCollection);
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
async $tryApplyEdits(_viewType: string, resource: UriComponents, modelVersionId: number, cellEdits: ICellEditOperation[]): Promise<boolean> {
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
if (!textModel) {
|
||||
return false;
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
// remove all notebook providers
|
||||
for (const item of this._notebookProviders.values()) {
|
||||
item.disposable.dispose();
|
||||
}
|
||||
return textModel.applyEdits(modelVersionId, cellEdits, true, undefined, () => undefined, undefined);
|
||||
dispose(this._notebookSerializer.values());
|
||||
}
|
||||
|
||||
private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta) {
|
||||
if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delta.removedDocuments !== undefined && delta.removedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delta.addedEditors !== undefined && delta.addedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delta.removedEditors !== undefined && delta.removedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delta.visibleEditors !== undefined && delta.visibleEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delta.newActiveEditor !== undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private _emitDelta(delta: INotebookDocumentsAndEditorsDelta) {
|
||||
if (this._isDeltaEmpty(delta)) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
}
|
||||
|
||||
return this._proxy.$acceptDocumentAndEditorsDelta(delta);
|
||||
}
|
||||
|
||||
registerListeners() {
|
||||
this._notebookService.listNotebookEditors().forEach((e) => {
|
||||
this._addNotebookEditor(e);
|
||||
});
|
||||
|
||||
this._register(this._notebookService.onDidChangeActiveEditor(e => {
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onDidChangeVisibleEditors(e => {
|
||||
if (this._notebookProviders.size > 0) {
|
||||
if (!this._currentState) {
|
||||
// no current state means we didn't even create editors in ext host yet.
|
||||
return;
|
||||
}
|
||||
|
||||
// we can't simply update visibleEditors as we need to check if we should create editors first.
|
||||
this._updateState();
|
||||
}
|
||||
}));
|
||||
|
||||
const notebookEditorAddedHandler = (editor: IEditor) => {
|
||||
if (!this._editorEventListenersMapping.has(editor.getId())) {
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(editor.onDidChangeVisibleRanges(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: { ranges: editor.visibleRanges }, selections: null });
|
||||
}));
|
||||
|
||||
disposableStore.add(editor.onDidChangeSelection(() => {
|
||||
const selectionHandles = editor.getSelectionHandles();
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: null, selections: { selections: selectionHandles } });
|
||||
}));
|
||||
|
||||
this._editorEventListenersMapping.set(editor.getId(), disposableStore);
|
||||
}
|
||||
};
|
||||
|
||||
this._register(this._notebookService.onNotebookEditorAdd(editor => {
|
||||
notebookEditorAddedHandler(editor);
|
||||
this._addNotebookEditor(editor);
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onNotebookEditorsRemove(editors => {
|
||||
this._removeNotebookEditor(editors);
|
||||
|
||||
editors.forEach(editor => {
|
||||
this._editorEventListenersMapping.get(editor.getId())?.dispose();
|
||||
this._editorEventListenersMapping.delete(editor.getId());
|
||||
});
|
||||
}));
|
||||
|
||||
this._notebookService.listNotebookEditors().forEach(editor => {
|
||||
notebookEditorAddedHandler(editor);
|
||||
});
|
||||
|
||||
const cellToDto = (cell: NotebookCellTextModel): IMainCellDto => {
|
||||
return {
|
||||
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
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const notebookDocumentAddedHandler = (textModel: NotebookTextModel) => {
|
||||
if (!this._documentEventListenersMapping.has(textModel.uri)) {
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(textModel!.onDidChangeContent(event => {
|
||||
const dto = event.rawEvents.map(e => {
|
||||
const data =
|
||||
e.kind === NotebookCellsChangeType.ModelChange || e.kind === NotebookCellsChangeType.Initialize
|
||||
? {
|
||||
kind: e.kind,
|
||||
versionId: event.versionId,
|
||||
changes: e.changes.map(diff => [diff[0], diff[1], diff[2].map(cell => cellToDto(cell as NotebookCellTextModel))] as [number, number, IMainCellDto[]])
|
||||
}
|
||||
: (
|
||||
e.kind === NotebookCellsChangeType.Move
|
||||
? {
|
||||
kind: e.kind,
|
||||
index: e.index,
|
||||
length: e.length,
|
||||
newIdx: e.newIdx,
|
||||
versionId: event.versionId,
|
||||
cells: e.cells.map(cell => cellToDto(cell as NotebookCellTextModel))
|
||||
}
|
||||
: e
|
||||
);
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO@rebornix, @jrieken
|
||||
* When a document is modified, it will trigger onDidChangeContent events.
|
||||
* The first event listener is this one, which doesn't know if the text model is dirty or not. It can ask `workingCopyService` but get the wrong result
|
||||
* The second event listener is `NotebookEditorModel`, which will then set `isDirty` to `true`.
|
||||
* Since `e.transient` decides if the model should be dirty or not, we will use the same logic here.
|
||||
*/
|
||||
const hasNonTransientEvent = event.rawEvents.find(e => !e.transient);
|
||||
this._proxy.$acceptModelChanged(textModel.uri, {
|
||||
rawEvents: dto,
|
||||
versionId: event.versionId
|
||||
}, !!hasNonTransientEvent);
|
||||
|
||||
const hasDocumentMetadataChangeEvent = event.rawEvents.find(e => e.kind === NotebookCellsChangeType.ChangeDocumentMetadata);
|
||||
if (!!hasDocumentMetadataChangeEvent) {
|
||||
this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: textModel.metadata });
|
||||
}
|
||||
}));
|
||||
this._documentEventListenersMapping.set(textModel!.uri, disposableStore);
|
||||
}
|
||||
};
|
||||
|
||||
this._notebookService.listNotebookDocuments().forEach(notebookDocumentAddedHandler);
|
||||
this._register(this._notebookService.onDidAddNotebookDocument(document => {
|
||||
notebookDocumentAddedHandler(document);
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onDidRemoveNotebookDocument(uri => {
|
||||
this._documentEventListenersMapping.get(uri)?.dispose();
|
||||
this._documentEventListenersMapping.delete(uri);
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onDidChangeNotebookActiveKernel(e => {
|
||||
this._proxy.$acceptNotebookActiveKernelChange(e);
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onNotebookDocumentSaved(e => {
|
||||
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 notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined;
|
||||
this._updateState(notebookEditor);
|
||||
}
|
||||
|
||||
private _addNotebookEditor(e: IEditor) {
|
||||
this._toDisposeOnEditorRemove.set(e.getId(), combinedDisposable(
|
||||
e.onDidChangeModel(() => this._updateState()),
|
||||
e.onDidFocusEditorWidget(() => {
|
||||
this._updateState(e);
|
||||
}),
|
||||
));
|
||||
|
||||
const activeEditorPane = this.editorService.activeEditorPane as any | undefined;
|
||||
const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined;
|
||||
this._updateState(notebookEditor);
|
||||
}
|
||||
|
||||
private _removeNotebookEditor(editors: IEditor[]) {
|
||||
editors.forEach(e => {
|
||||
const sub = this._toDisposeOnEditorRemove.get(e.getId());
|
||||
if (sub) {
|
||||
this._toDisposeOnEditorRemove.delete(e.getId());
|
||||
sub.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
private async _updateState(focusedNotebookEditor?: IEditor) {
|
||||
let activeEditor: string | null = null;
|
||||
|
||||
const activeEditorPane = this.editorService.activeEditorPane as any | undefined;
|
||||
if (activeEditorPane?.isNotebookEditor) {
|
||||
const notebookEditor = (activeEditorPane.getControl() as INotebookEditor);
|
||||
activeEditor = notebookEditor && notebookEditor.hasModel() ? notebookEditor!.getId() : null;
|
||||
}
|
||||
|
||||
const documentEditorsMap = new Map<string, IEditor>();
|
||||
|
||||
const editors = new Map<string, IEditor>();
|
||||
this._notebookService.listNotebookEditors().forEach(editor => {
|
||||
if (editor.textModel) {
|
||||
editors.set(editor.getId(), editor);
|
||||
documentEditorsMap.set(editor.textModel.uri.toString(), editor);
|
||||
}
|
||||
});
|
||||
|
||||
const visibleEditorsMap = new Map<string, IEditor>();
|
||||
this.editorService.visibleEditorPanes.forEach(editor => {
|
||||
if ((editor as any).isNotebookEditor) {
|
||||
const nbEditorWidget = (editor as any).getControl() as INotebookEditor;
|
||||
if (nbEditorWidget && editors.has(nbEditorWidget.getId())) {
|
||||
visibleEditorsMap.set(nbEditorWidget.getId(), nbEditorWidget);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const documents = new Set<NotebookTextModel>();
|
||||
this._notebookService.listNotebookDocuments().forEach(document => {
|
||||
documents.add(document);
|
||||
});
|
||||
|
||||
if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.textModel) {
|
||||
activeEditor = focusedNotebookEditor.getId();
|
||||
}
|
||||
|
||||
// editors always have view model attached, which means there is already a document in exthost.
|
||||
const newState = new DocumentAndEditorState(documents, editors, activeEditor, visibleEditorsMap);
|
||||
const delta = DocumentAndEditorState.compute(this._currentState, newState);
|
||||
// const isEmptyChange = (!delta.addedDocuments || delta.addedDocuments.length === 0)
|
||||
// && (!delta.removedDocuments || delta.removedDocuments.length === 0)
|
||||
// && (!delta.addedEditors || delta.addedEditors.length === 0)
|
||||
// && (!delta.removedEditors || delta.removedEditors.length === 0)
|
||||
// && (delta.newActiveEditor === undefined)
|
||||
|
||||
// if (!isEmptyChange) {
|
||||
this._currentState = newState;
|
||||
await this._emitDelta(delta);
|
||||
// }
|
||||
}
|
||||
|
||||
async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, options: {
|
||||
async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, options: {
|
||||
transientOutputs: boolean;
|
||||
transientMetadata: TransientMetadata;
|
||||
transientCellMetadata: TransientCellMetadata;
|
||||
transientDocumentMetadata: TransientDocumentMetadata;
|
||||
viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; };
|
||||
}): Promise<void> {
|
||||
let contentOptions = { transientOutputs: options.transientOutputs, transientMetadata: options.transientMetadata };
|
||||
let contentOptions = { transientOutputs: options.transientOutputs, transientCellMetadata: options.transientCellMetadata, transientDocumentMetadata: options.transientDocumentMetadata };
|
||||
|
||||
const controller: IMainNotebookController = {
|
||||
supportBackup,
|
||||
const controller: INotebookContentProvider = {
|
||||
get options() {
|
||||
return contentOptions;
|
||||
},
|
||||
set options(newOptions) {
|
||||
contentOptions.transientMetadata = newOptions.transientMetadata;
|
||||
contentOptions.transientCellMetadata = newOptions.transientCellMetadata;
|
||||
contentOptions.transientDocumentMetadata = newOptions.transientDocumentMetadata;
|
||||
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);
|
||||
open: async (uri: URI, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken) => {
|
||||
const data = await this._proxy.$openNotebook(viewType, uri, backupId, untitledDocumentData, token);
|
||||
return {
|
||||
data,
|
||||
transientOptions: contentOptions
|
||||
};
|
||||
},
|
||||
resolveNotebookEditor: async (viewType: string, uri: URI, editorId: string) => {
|
||||
await this._proxy.$resolveNotebookEditor(viewType, uri, editorId);
|
||||
},
|
||||
onDidReceiveMessage: (editorId: string, rendererType: string | undefined, message: unknown) => {
|
||||
this._proxy.$onDidReceiveMessage(editorId, rendererType, message);
|
||||
},
|
||||
save: async (uri: URI, token: CancellationToken) => {
|
||||
return this._proxy.$saveNotebook(viewType, uri, token);
|
||||
},
|
||||
@@ -479,16 +75,15 @@ 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> {
|
||||
async $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; }): Promise<void> {
|
||||
const provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (provider && options) {
|
||||
@@ -509,254 +104,69 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
}
|
||||
|
||||
async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void> {
|
||||
const emitter = new Emitter<URI | undefined>();
|
||||
$registerNotebookSerializer(handle: number, extension: NotebookExtensionDescription, viewType: string, options: TransientOptions): void {
|
||||
const registration = this._notebookService.registerNotebookSerializer(viewType, extension, {
|
||||
options,
|
||||
dataToNotebook: (data: VSBuffer): Promise<NotebookDataDto> => {
|
||||
return this._proxy.$dataToNotebook(handle, data, CancellationToken.None);
|
||||
},
|
||||
notebookToData: (data: NotebookDataDto): Promise<VSBuffer> => {
|
||||
return this._proxy.$notebookToData(handle, data, CancellationToken.None);
|
||||
}
|
||||
});
|
||||
this._notebookSerializer.set(handle, registration);
|
||||
}
|
||||
|
||||
$unregisterNotebookSerializer(handle: number): void {
|
||||
this._notebookSerializer.get(handle)?.dispose();
|
||||
this._notebookSerializer.delete(handle);
|
||||
}
|
||||
|
||||
$emitCellStatusBarEvent(eventHandle: number): void {
|
||||
const emitter = this._notebookCellStatusBarRegistrations.get(eventHandle);
|
||||
if (emitter instanceof Emitter) {
|
||||
emitter.fire(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
async $registerNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined, selector: NotebookSelector): Promise<void> {
|
||||
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
|
||||
};
|
||||
});
|
||||
const provider: INotebookCellStatusBarItemProvider = {
|
||||
async provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken) {
|
||||
const result = await that._proxy.$provideNotebookCellStatusBarItems(handle, uri, index, token);
|
||||
return {
|
||||
items: result?.items ?? [],
|
||||
dispose() {
|
||||
if (result) {
|
||||
that._proxy.$releaseNotebookCellStatusBarItems(result.cacheId);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
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);
|
||||
},
|
||||
});
|
||||
this._notebookKernelProviders.set(handle, {
|
||||
extension,
|
||||
emitter,
|
||||
provider
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async $unregisterNotebookKernelProvider(handle: number): Promise<void> {
|
||||
const entry = this._notebookKernelProviders.get(handle);
|
||||
|
||||
if (entry) {
|
||||
entry.emitter.dispose();
|
||||
entry.provider.dispose();
|
||||
this._notebookKernelProviders.delete(handle);
|
||||
}
|
||||
}
|
||||
|
||||
$onNotebookKernelChange(handle: number, uriComponents: UriComponents): void {
|
||||
const entry = this._notebookKernelProviders.get(handle);
|
||||
|
||||
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) {
|
||||
editor.postMessage(forRendererId, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType) {
|
||||
const editor = this._notebookService.listNotebookEditors().find(editor => editor.getId() === id);
|
||||
if (editor && editor.isNotebookEditor) {
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
if (!notebookEditor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const viewModel = notebookEditor.viewModel;
|
||||
const cell = viewModel.viewCells[range.start];
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (revealType) {
|
||||
case NotebookEditorRevealType.Default:
|
||||
return notebookEditor.revealCellRangeInView(range);
|
||||
case NotebookEditorRevealType.InCenter:
|
||||
return notebookEditor.revealInCenter(cell);
|
||||
case NotebookEditorRevealType.InCenterIfOutsideViewport:
|
||||
return notebookEditor.revealInCenterIfOutsideViewport(cell);
|
||||
case NotebookEditorRevealType.AtTop:
|
||||
return notebookEditor.revealInViewAtTop(cell);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions) {
|
||||
this._notebookService.registerEditorDecorationType(key, options);
|
||||
}
|
||||
|
||||
$removeNotebookEditorDecorationType(key: string) {
|
||||
this._notebookService.removeEditorDecorationType(key);
|
||||
}
|
||||
|
||||
$trySetDecorations(id: string, range: ICellRange, key: string) {
|
||||
const editor = this._notebookService.listNotebookEditors().find(editor => editor.getId() === id);
|
||||
if (editor && editor.isNotebookEditor) {
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
notebookEditor.setEditorDecorations(key, range);
|
||||
}
|
||||
}
|
||||
|
||||
async $setStatusBarEntry(id: number, rawStatusBarEntry: INotebookCellStatusBarEntryDto): Promise<void> {
|
||||
const statusBarEntry = {
|
||||
...rawStatusBarEntry,
|
||||
...{ cellResource: URI.revive(rawStatusBarEntry.cellResource) }
|
||||
selector: selector
|
||||
};
|
||||
|
||||
const existingEntry = this._cellStatusBarEntries.get(id);
|
||||
if (existingEntry) {
|
||||
existingEntry.dispose();
|
||||
if (typeof eventHandle === 'number') {
|
||||
const emitter = new Emitter<void>();
|
||||
this._notebookCellStatusBarRegistrations.set(eventHandle, emitter);
|
||||
provider.onDidChangeStatusBarItems = emitter.event;
|
||||
}
|
||||
|
||||
if (statusBarEntry.visible) {
|
||||
this._cellStatusBarEntries.set(
|
||||
id,
|
||||
this.cellStatusBarService.addEntry(statusBarEntry));
|
||||
}
|
||||
const disposable = this._cellStatusBarService.registerCellStatusBarItemProvider(provider);
|
||||
this._notebookCellStatusBarRegistrations.set(handle, disposable);
|
||||
}
|
||||
|
||||
|
||||
async $tryOpenDocument(uriComponents: UriComponents, viewType?: string): Promise<URI> {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const ref = await this._notebookModelResolverService.resolve(uri, viewType);
|
||||
this._modelReferenceCollection.add(uri, ref);
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
async $tryShowNotebookDocument(resource: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string> {
|
||||
const editorOptions: ITextEditorOptions = {
|
||||
preserveFocus: options.preserveFocus,
|
||||
pinned: options.pinned,
|
||||
// selection: options.selection,
|
||||
// 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,
|
||||
async $unregisterNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined): Promise<void> {
|
||||
const unregisterThing = (handle: number) => {
|
||||
const entry = this._notebookCellStatusBarRegistrations.get(handle);
|
||||
if (entry) {
|
||||
this._notebookCellStatusBarRegistrations.get(handle)?.dispose();
|
||||
this._notebookCellStatusBarRegistrations.delete(handle);
|
||||
}
|
||||
};
|
||||
|
||||
const columnArg = viewColumnToEditorGroup(this._editorGroupService, options.position);
|
||||
|
||||
let group: IEditorGroup | undefined = undefined;
|
||||
|
||||
if (columnArg === SIDE_GROUP) {
|
||||
const direction = preferredSideBySideGroupDirection(this.configurationService);
|
||||
|
||||
let neighbourGroup = this.editorGroupsService.findGroup({ direction });
|
||||
if (!neighbourGroup) {
|
||||
neighbourGroup = this.editorGroupsService.addGroup(this.editorGroupsService.activeGroup, direction);
|
||||
}
|
||||
group = neighbourGroup;
|
||||
} else {
|
||||
group = this.editorGroupsService.getGroup(viewColumnToEditorGroup(this.editorGroupsService, columnArg)) ?? this.editorGroupsService.activeGroup;
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
return notebookEditor.getId();
|
||||
} else {
|
||||
throw new Error(`Notebook Editor creation failure for documenet ${resource}`);
|
||||
unregisterThing(handle);
|
||||
if (typeof eventHandle === 'number') {
|
||||
unregisterThing(eventHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
151
src/vs/workbench/api/browser/mainThreadNotebookDocuments.ts
Normal file
151
src/vs/workbench/api/browser/mainThreadNotebookDocuments.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThreadDocuments';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { IImmediateCellEditOperation, IMainCellDto, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, MainThreadNotebookDocumentsShape } from '../common/extHost.protocol';
|
||||
import { MainThreadNotebooksAndEditors } from 'vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
export class MainThreadNotebookDocuments implements MainThreadNotebookDocumentsShape {
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
private readonly _documentEventListenersMapping = new ResourceMap<DisposableStore>();
|
||||
private readonly _modelReferenceCollection: BoundModelReferenceCollection;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
notebooksAndEditors: MainThreadNotebooksAndEditors,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@INotebookEditorModelResolverService private readonly _notebookEditorModelResolverService: INotebookEditorModelResolverService,
|
||||
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
this._modelReferenceCollection = new BoundModelReferenceCollection(this._uriIdentityService.extUri);
|
||||
|
||||
notebooksAndEditors.onDidAddNotebooks(this._handleNotebooksAdded, this, this._disposables);
|
||||
notebooksAndEditors.onDidRemoveNotebooks(this._handleNotebooksRemoved, this, this._disposables);
|
||||
|
||||
// forward dirty and save events
|
||||
this._disposables.add(this._notebookEditorModelResolverService.onDidChangeDirty(model => this._proxy.$acceptDirtyStateChanged(model.resource, model.isDirty())));
|
||||
this._disposables.add(this._notebookEditorModelResolverService.onDidSaveNotebook(e => this._proxy.$acceptModelSaved(e)));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
this._modelReferenceCollection.dispose();
|
||||
dispose(this._documentEventListenersMapping.values());
|
||||
|
||||
}
|
||||
|
||||
private _handleNotebooksAdded(notebooks: readonly NotebookTextModel[]): void {
|
||||
|
||||
for (const textModel of notebooks) {
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(textModel.onDidChangeContent(event => {
|
||||
const dto = event.rawEvents.map(e => {
|
||||
const data =
|
||||
e.kind === NotebookCellsChangeType.ModelChange || e.kind === NotebookCellsChangeType.Initialize
|
||||
? {
|
||||
kind: e.kind,
|
||||
versionId: event.versionId,
|
||||
changes: e.changes.map(diff => [diff[0], diff[1], diff[2].map(cell => MainThreadNotebookDocuments._cellToDto(cell as NotebookCellTextModel))] as [number, number, IMainCellDto[]])
|
||||
}
|
||||
: (
|
||||
e.kind === NotebookCellsChangeType.Move
|
||||
? {
|
||||
kind: e.kind,
|
||||
index: e.index,
|
||||
length: e.length,
|
||||
newIdx: e.newIdx,
|
||||
versionId: event.versionId,
|
||||
cells: e.cells.map(cell => MainThreadNotebookDocuments._cellToDto(cell as NotebookCellTextModel))
|
||||
}
|
||||
: e
|
||||
);
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
// using the model resolver service to know if the model is dirty or not.
|
||||
// assuming this is the first listener it can mean that at first the model
|
||||
// is marked as dirty and that another event is fired
|
||||
this._proxy.$acceptModelChanged(
|
||||
textModel.uri,
|
||||
{ rawEvents: dto, versionId: event.versionId },
|
||||
this._notebookEditorModelResolverService.isDirty(textModel.uri)
|
||||
);
|
||||
|
||||
const hasDocumentMetadataChangeEvent = event.rawEvents.find(e => e.kind === NotebookCellsChangeType.ChangeDocumentMetadata);
|
||||
if (hasDocumentMetadataChangeEvent) {
|
||||
this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: textModel.metadata });
|
||||
}
|
||||
}));
|
||||
|
||||
this._documentEventListenersMapping.set(textModel.uri, disposableStore);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleNotebooksRemoved(uris: URI[]): void {
|
||||
for (const uri of uris) {
|
||||
this._documentEventListenersMapping.get(uri)?.dispose();
|
||||
this._documentEventListenersMapping.delete(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private static _cellToDto(cell: NotebookCellTextModel): IMainCellDto {
|
||||
return {
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
async $tryOpenDocument(uriComponents: UriComponents): Promise<URI> {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const ref = await this._notebookEditorModelResolverService.resolve(uri, undefined);
|
||||
this._modelReferenceCollection.add(uri, ref);
|
||||
return uri;
|
||||
}
|
||||
|
||||
async $trySaveDocument(uriComponents: UriComponents) {
|
||||
const uri = URI.revive(uriComponents);
|
||||
|
||||
const ref = await this._notebookEditorModelResolverService.resolve(uri);
|
||||
const saveResult = await ref.object.save();
|
||||
ref.dispose();
|
||||
return saveResult;
|
||||
}
|
||||
|
||||
async $applyEdits(resource: UriComponents, cellEdits: IImmediateCellEditOperation[], computeUndoRedo = true): Promise<void> {
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
if (!textModel) {
|
||||
throw new Error(`Can't apply edits to unknown notebook model: ${URI.revive(resource).toString()}`);
|
||||
}
|
||||
|
||||
try {
|
||||
textModel.applyEdits(cellEdits, true, undefined, () => undefined, undefined, computeUndoRedo);
|
||||
} catch (e) {
|
||||
// Clearing outputs at the same time as the EH calling append/replaceOutputItems is an expected race, and it should be a no-op.
|
||||
// And any other failure should not throw back to the extension.
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { diffMaps, diffSets } from 'vs/base/common/collections';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { combinedDisposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MainThreadNotebookDocuments } from 'vs/workbench/api/browser/mainThreadNotebookDocuments';
|
||||
import { MainThreadNotebookEditors } from 'vs/workbench/api/browser/mainThreadNotebookEditors';
|
||||
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { editorGroupToViewColumn } from 'vs/workbench/common/editor';
|
||||
import { getNotebookEditorFromEditorPane, IActiveNotebookEditor, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookDocumentsAndEditorsDelta, INotebookEditorAddData, INotebookModelAddedData, MainContext } from '../common/extHost.protocol';
|
||||
|
||||
interface INotebookAndEditorDelta {
|
||||
removedDocuments: URI[];
|
||||
addedDocuments: NotebookTextModel[];
|
||||
removedEditors: string[];
|
||||
addedEditors: IActiveNotebookEditor[];
|
||||
newActiveEditor?: string | null;
|
||||
visibleEditors?: string[];
|
||||
}
|
||||
|
||||
class NotebookAndEditorState {
|
||||
static compute(before: NotebookAndEditorState | undefined, after: NotebookAndEditorState): INotebookAndEditorDelta {
|
||||
if (!before) {
|
||||
return {
|
||||
addedDocuments: [...after.documents],
|
||||
removedDocuments: [],
|
||||
addedEditors: [...after.textEditors.values()],
|
||||
removedEditors: [],
|
||||
visibleEditors: [...after.visibleEditors].map(editor => editor[0])
|
||||
};
|
||||
}
|
||||
const documentDelta = diffSets(before.documents, after.documents);
|
||||
const editorDelta = diffMaps(before.textEditors, after.textEditors);
|
||||
|
||||
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
|
||||
const visibleEditorDelta = diffMaps(before.visibleEditors, after.visibleEditors);
|
||||
|
||||
return {
|
||||
addedDocuments: documentDelta.added,
|
||||
removedDocuments: documentDelta.removed.map(e => e.uri),
|
||||
addedEditors: editorDelta.added,
|
||||
removedEditors: editorDelta.removed.map(removed => removed.getId()),
|
||||
newActiveEditor: newActiveEditor,
|
||||
visibleEditors: visibleEditorDelta.added.length === 0 && visibleEditorDelta.removed.length === 0
|
||||
? undefined
|
||||
: [...after.visibleEditors].map(editor => editor[0])
|
||||
};
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly documents: Set<NotebookTextModel>,
|
||||
readonly textEditors: Map<string, IActiveNotebookEditor>,
|
||||
readonly activeEditor: string | null | undefined,
|
||||
readonly visibleEditors: Map<string, IActiveNotebookEditor>
|
||||
) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
@extHostCustomer
|
||||
export class MainThreadNotebooksAndEditors {
|
||||
|
||||
private readonly _onDidAddNotebooks = new Emitter<NotebookTextModel[]>();
|
||||
private readonly _onDidRemoveNotebooks = new Emitter<URI[]>();
|
||||
private readonly _onDidAddEditors = new Emitter<IActiveNotebookEditor[]>();
|
||||
private readonly _onDidRemoveEditors = new Emitter<string[]>();
|
||||
|
||||
readonly onDidAddNotebooks: Event<NotebookTextModel[]> = this._onDidAddNotebooks.event;
|
||||
readonly onDidRemoveNotebooks: Event<URI[]> = this._onDidRemoveNotebooks.event;
|
||||
readonly onDidAddEditors: Event<IActiveNotebookEditor[]> = this._onDidAddEditors.event;
|
||||
readonly onDidRemoveEditors: Event<string[]> = this._onDidRemoveEditors.event;
|
||||
|
||||
private readonly _proxy: Pick<ExtHostNotebookShape, '$acceptDocumentAndEditorsDelta'>;
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly _editorListeners = new Map<string, IDisposable>();
|
||||
|
||||
private _currentState?: NotebookAndEditorState;
|
||||
|
||||
private readonly _mainThreadNotebooks: MainThreadNotebookDocuments;
|
||||
private readonly _mainThreadEditors: MainThreadNotebookEditors;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@INotebookEditorService private readonly _notebookEditorService: INotebookEditorService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
|
||||
this._mainThreadNotebooks = instantiationService.createInstance(MainThreadNotebookDocuments, extHostContext, this);
|
||||
this._mainThreadEditors = instantiationService.createInstance(MainThreadNotebookEditors, extHostContext, this);
|
||||
|
||||
extHostContext.set(MainContext.MainThreadNotebookDocuments, this._mainThreadNotebooks);
|
||||
extHostContext.set(MainContext.MainThreadNotebookEditors, this._mainThreadEditors);
|
||||
|
||||
this._notebookService.onDidCreateNotebookDocument(() => this._updateState(), this, this._disposables);
|
||||
this._notebookService.onDidRemoveNotebookDocument(() => this._updateState(), this, this._disposables);
|
||||
this._editorService.onDidActiveEditorChange(() => this._updateState(), this, this._disposables);
|
||||
this._editorService.onDidVisibleEditorsChange(() => this._updateState(), this, this._disposables);
|
||||
this._notebookEditorService.onDidAddNotebookEditor(this._handleEditorAdd, this, this._disposables);
|
||||
this._notebookEditorService.onDidRemoveNotebookEditor(this._handleEditorRemove, this, this._disposables);
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._mainThreadNotebooks.dispose();
|
||||
this._mainThreadEditors.dispose();
|
||||
this._onDidAddEditors.dispose();
|
||||
this._onDidRemoveEditors.dispose();
|
||||
this._onDidAddNotebooks.dispose();
|
||||
this._onDidRemoveNotebooks.dispose();
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
private _handleEditorAdd(editor: INotebookEditor): void {
|
||||
this._editorListeners.set(editor.getId(), combinedDisposable(
|
||||
editor.onDidChangeModel(() => this._updateState()),
|
||||
editor.onDidFocusEditorWidget(() => this._updateState(editor)),
|
||||
));
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
private _handleEditorRemove(editor: INotebookEditor): void {
|
||||
this._editorListeners.get(editor.getId())?.dispose();
|
||||
this._editorListeners.delete(editor.getId());
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
private _updateState(focusedEditor?: INotebookEditor): void {
|
||||
|
||||
const editors = new Map<string, IActiveNotebookEditor>();
|
||||
const visibleEditorsMap = new Map<string, IActiveNotebookEditor>();
|
||||
|
||||
for (const editor of this._notebookEditorService.listNotebookEditors()) {
|
||||
if (editor.hasModel()) {
|
||||
editors.set(editor.getId(), editor);
|
||||
}
|
||||
}
|
||||
|
||||
const activeNotebookEditor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane);
|
||||
let activeEditor: string | null = null;
|
||||
if (activeNotebookEditor) {
|
||||
activeEditor = activeNotebookEditor.getId();
|
||||
} else if (focusedEditor?.textModel) {
|
||||
activeEditor = focusedEditor.getId();
|
||||
}
|
||||
if (activeEditor && !editors.has(activeEditor)) {
|
||||
activeEditor = null;
|
||||
}
|
||||
|
||||
for (const editorPane of this._editorService.visibleEditorPanes) {
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(editorPane);
|
||||
if (notebookEditor?.hasModel() && editors.has(notebookEditor.getId())) {
|
||||
visibleEditorsMap.set(notebookEditor.getId(), notebookEditor);
|
||||
}
|
||||
}
|
||||
|
||||
const newState = new NotebookAndEditorState(new Set(this._notebookService.listNotebookDocuments()), editors, activeEditor, visibleEditorsMap);
|
||||
this._onDelta(NotebookAndEditorState.compute(this._currentState, newState));
|
||||
this._currentState = newState;
|
||||
}
|
||||
|
||||
private _onDelta(delta: INotebookAndEditorDelta): void {
|
||||
if (MainThreadNotebooksAndEditors._isDeltaEmpty(delta)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dto: INotebookDocumentsAndEditorsDelta = {
|
||||
removedDocuments: delta.removedDocuments,
|
||||
removedEditors: delta.removedEditors,
|
||||
newActiveEditor: delta.newActiveEditor,
|
||||
visibleEditors: delta.visibleEditors,
|
||||
addedDocuments: delta.addedDocuments.map(MainThreadNotebooksAndEditors._asModelAddData),
|
||||
addedEditors: delta.addedEditors.map(this._asEditorAddData, this),
|
||||
};
|
||||
|
||||
// send to extension FIRST
|
||||
this._proxy.$acceptDocumentAndEditorsDelta(dto);
|
||||
|
||||
// handle internally
|
||||
this._onDidRemoveEditors.fire(delta.removedEditors);
|
||||
this._onDidRemoveNotebooks.fire(delta.removedDocuments);
|
||||
this._onDidAddNotebooks.fire(delta.addedDocuments);
|
||||
this._onDidAddEditors.fire(delta.addedEditors);
|
||||
}
|
||||
|
||||
private static _isDeltaEmpty(delta: INotebookAndEditorDelta): boolean {
|
||||
if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.removedDocuments !== undefined && delta.removedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.addedEditors !== undefined && delta.addedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.removedEditors !== undefined && delta.removedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.visibleEditors !== undefined && delta.visibleEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.newActiveEditor !== undefined) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
private _asEditorAddData(add: IActiveNotebookEditor): INotebookEditorAddData {
|
||||
|
||||
const pane = this._editorService.visibleEditorPanes.find(pane => getNotebookEditorFromEditorPane(pane) === add);
|
||||
|
||||
return {
|
||||
id: add.getId(),
|
||||
documentUri: add.viewModel.uri,
|
||||
selections: add.getSelections(),
|
||||
visibleRanges: add.visibleRanges,
|
||||
viewColumn: pane && editorGroupToViewColumn(this._editorGroupService, pane.group)
|
||||
};
|
||||
}
|
||||
}
|
||||
191
src/vs/workbench/api/browser/mainThreadNotebookEditors.ts
Normal file
191
src/vs/workbench/api/browser/mainThreadNotebookEditors.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { getNotebookEditorFromEditorPane, INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookDocumentShowOptions, INotebookEditorViewColumnInfo, MainThreadNotebookEditorsShape, NotebookEditorRevealType } from '../common/extHost.protocol';
|
||||
import { MainThreadNotebooksAndEditors } from 'vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors';
|
||||
import { ICellEditOperation, INotebookDecorationRenderOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor';
|
||||
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { editorGroupToViewColumn } from 'vs/workbench/common/editor';
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
|
||||
class MainThreadNotebook {
|
||||
|
||||
constructor(
|
||||
readonly editor: INotebookEditor,
|
||||
readonly disposables: DisposableStore
|
||||
) { }
|
||||
|
||||
dispose() {
|
||||
this.disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadNotebookEditors implements MainThreadNotebookEditorsShape {
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
private readonly _mainThreadEditors = new Map<string, MainThreadNotebook>();
|
||||
|
||||
private _currentViewColumnInfo?: INotebookEditorViewColumnInfo;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
notebooksAndEditors: MainThreadNotebooksAndEditors,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@INotebookEditorService private readonly _notebookEditorService: INotebookEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
|
||||
notebooksAndEditors.onDidAddEditors(this._handleEditorsAdded, this, this._disposables);
|
||||
notebooksAndEditors.onDidRemoveEditors(this._handleEditorsRemoved, this, this._disposables);
|
||||
|
||||
this._editorService.onDidActiveEditorChange(() => this._updateEditorViewColumns(), this, this._disposables);
|
||||
this._editorGroupService.onDidRemoveGroup(() => this._updateEditorViewColumns(), this, this._disposables);
|
||||
this._editorGroupService.onDidMoveGroup(() => this._updateEditorViewColumns(), this, this._disposables);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
dispose(this._mainThreadEditors.values());
|
||||
}
|
||||
|
||||
private _handleEditorsAdded(editors: readonly INotebookEditor[]): void {
|
||||
|
||||
for (const editor of editors) {
|
||||
|
||||
const editorDisposables = new DisposableStore();
|
||||
editorDisposables.add(editor.onDidChangeVisibleRanges(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: { ranges: editor.visibleRanges } });
|
||||
}));
|
||||
|
||||
editorDisposables.add(editor.onDidChangeSelection(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { selections: { selections: editor.getSelections() } });
|
||||
}));
|
||||
|
||||
const wrapper = new MainThreadNotebook(editor, editorDisposables);
|
||||
this._mainThreadEditors.set(editor.getId(), wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleEditorsRemoved(editorIds: readonly string[]): void {
|
||||
for (const id of editorIds) {
|
||||
this._mainThreadEditors.get(id)?.dispose();
|
||||
this._mainThreadEditors.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateEditorViewColumns(): void {
|
||||
const result: INotebookEditorViewColumnInfo = Object.create(null);
|
||||
for (let editorPane of this._editorService.visibleEditorPanes) {
|
||||
const candidate = getNotebookEditorFromEditorPane(editorPane);
|
||||
if (candidate && this._mainThreadEditors.has(candidate.getId())) {
|
||||
result[candidate.getId()] = editorGroupToViewColumn(this._editorGroupService, editorPane.group);
|
||||
}
|
||||
}
|
||||
if (!equals(result, this._currentViewColumnInfo)) {
|
||||
this._currentViewColumnInfo = result;
|
||||
this._proxy.$acceptEditorViewColumns(result);
|
||||
}
|
||||
}
|
||||
|
||||
async $tryApplyEdits(editorId: string, modelVersionId: number, cellEdits: ICellEditOperation[]): Promise<boolean> {
|
||||
const wrapper = this._mainThreadEditors.get(editorId);
|
||||
if (!wrapper) {
|
||||
return false;
|
||||
}
|
||||
const { editor } = wrapper;
|
||||
if (!editor.textModel) {
|
||||
this._logService.warn('Notebook editor has NO model', editorId);
|
||||
return false;
|
||||
}
|
||||
if (editor.textModel.versionId !== modelVersionId) {
|
||||
return false;
|
||||
}
|
||||
//todo@jrieken use proper selection logic!
|
||||
return editor.textModel.applyEdits(cellEdits, true, undefined, () => undefined, undefined);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async $tryShowNotebookDocument(resource: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string> {
|
||||
const editorOptions = new NotebookEditorOptions({
|
||||
cellSelections: options.selections,
|
||||
preserveFocus: options.preserveFocus,
|
||||
pinned: options.pinned,
|
||||
// selection: options.selection,
|
||||
// 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: EditorOverride.DISABLED,
|
||||
});
|
||||
|
||||
const input = NotebookEditorInput.create(this._instantiationService, URI.revive(resource), viewType);
|
||||
const editorPane = await this._editorService.openEditor(input, editorOptions, options.position);
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(editorPane);
|
||||
|
||||
if (notebookEditor) {
|
||||
return notebookEditor.getId();
|
||||
} else {
|
||||
throw new Error(`Notebook Editor creation failure for documenet ${resource}`);
|
||||
}
|
||||
}
|
||||
|
||||
async $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise<void> {
|
||||
const editor = this._notebookEditorService.getNotebookEditor(id);
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
if (!notebookEditor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const viewModel = notebookEditor.viewModel;
|
||||
const cell = viewModel.cellAt(range.start);
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (revealType) {
|
||||
case NotebookEditorRevealType.Default:
|
||||
return notebookEditor.revealCellRangeInView(range);
|
||||
case NotebookEditorRevealType.InCenter:
|
||||
return notebookEditor.revealInCenter(cell);
|
||||
case NotebookEditorRevealType.InCenterIfOutsideViewport:
|
||||
return notebookEditor.revealInCenterIfOutsideViewport(cell);
|
||||
case NotebookEditorRevealType.AtTop:
|
||||
return notebookEditor.revealInViewAtTop(cell);
|
||||
}
|
||||
}
|
||||
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void {
|
||||
this._notebookEditorService.registerEditorDecorationType(key, options);
|
||||
}
|
||||
|
||||
$removeNotebookEditorDecorationType(key: string): void {
|
||||
this._notebookEditorService.removeEditorDecorationType(key);
|
||||
}
|
||||
|
||||
$trySetDecorations(id: string, range: ICellRange, key: string): void {
|
||||
const editor = this._notebookEditorService.getNotebookEditor(id);
|
||||
if (editor) {
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
notebookEditor.setEditorDecorations(key, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
225
src/vs/workbench/api/browser/mainThreadNotebookKernels.ts
Normal file
225
src/vs/workbench/api/browser/mainThreadNotebookKernels.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { flatten, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { combinedDisposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { INotebookKernel, INotebookKernelChangeEvent } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
import { ExtHostContext, ExtHostNotebookKernelsShape, IExtHostContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol';
|
||||
|
||||
abstract class MainThreadKernel implements INotebookKernel {
|
||||
|
||||
private readonly _onDidChange = new Emitter<INotebookKernelChangeEvent>();
|
||||
private readonly preloads: { uri: URI, provides: string[] }[];
|
||||
readonly onDidChange: Event<INotebookKernelChangeEvent> = this._onDidChange.event;
|
||||
|
||||
readonly id: string;
|
||||
readonly viewType: string;
|
||||
readonly extension: ExtensionIdentifier;
|
||||
|
||||
implementsInterrupt: boolean;
|
||||
label: string;
|
||||
description?: string;
|
||||
detail?: string;
|
||||
supportedLanguages: string[];
|
||||
implementsExecutionOrder: boolean;
|
||||
localResourceRoot: URI;
|
||||
|
||||
public get preloadUris() {
|
||||
return this.preloads.map(p => p.uri);
|
||||
}
|
||||
|
||||
public get preloadProvides() {
|
||||
return flatten(this.preloads.map(p => p.provides));
|
||||
}
|
||||
|
||||
constructor(data: INotebookKernelDto2, private _modeService: IModeService) {
|
||||
this.id = data.id;
|
||||
this.viewType = data.viewType;
|
||||
this.extension = data.extensionId;
|
||||
|
||||
this.implementsInterrupt = data.supportsInterrupt ?? false;
|
||||
this.label = data.label;
|
||||
this.description = data.description;
|
||||
this.detail = data.detail;
|
||||
this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : _modeService.getRegisteredModes();
|
||||
this.implementsExecutionOrder = data.hasExecutionOrder ?? false;
|
||||
this.localResourceRoot = URI.revive(data.extensionLocation);
|
||||
this.preloads = data.preloads?.map(u => ({ uri: URI.revive(u.uri), provides: u.provides })) ?? [];
|
||||
}
|
||||
|
||||
|
||||
update(data: Partial<INotebookKernelDto2>) {
|
||||
|
||||
const event: INotebookKernelChangeEvent = Object.create(null);
|
||||
if (data.label !== undefined) {
|
||||
this.label = data.label;
|
||||
event.label = true;
|
||||
}
|
||||
if (data.description !== undefined) {
|
||||
this.description = data.description;
|
||||
event.description = true;
|
||||
}
|
||||
if (data.detail !== undefined) {
|
||||
this.detail = data.detail;
|
||||
event.detail = true;
|
||||
}
|
||||
if (data.supportedLanguages !== undefined) {
|
||||
this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : this._modeService.getRegisteredModes();
|
||||
event.supportedLanguages = true;
|
||||
}
|
||||
if (data.hasExecutionOrder !== undefined) {
|
||||
this.implementsExecutionOrder = data.hasExecutionOrder;
|
||||
event.hasExecutionOrder = true;
|
||||
}
|
||||
this._onDidChange.fire(event);
|
||||
}
|
||||
|
||||
abstract executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise<void>;
|
||||
abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise<void>;
|
||||
}
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadNotebookKernels)
|
||||
export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape {
|
||||
|
||||
private readonly _editors = new Map<INotebookEditor, IDisposable>();
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly _kernels = new Map<number, [kernel: MainThreadKernel, registraion: IDisposable]>();
|
||||
private readonly _proxy: ExtHostNotebookKernelsShape;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@INotebookKernelService private readonly _notebookKernelService: INotebookKernelService,
|
||||
@INotebookEditorService notebookEditorService: INotebookEditorService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebookKernels);
|
||||
|
||||
notebookEditorService.listNotebookEditors().forEach(this._onEditorAdd, this);
|
||||
notebookEditorService.onDidAddNotebookEditor(this._onEditorAdd, this, this._disposables);
|
||||
notebookEditorService.onDidRemoveNotebookEditor(this._onEditorRemove, this, this._disposables);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
for (let [, registration] of this._kernels.values()) {
|
||||
registration.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// --- kernel ipc
|
||||
|
||||
private _onEditorAdd(editor: INotebookEditor) {
|
||||
|
||||
const ipcListener = editor.onDidReceiveMessage(e => {
|
||||
if (e.forRenderer) {
|
||||
return;
|
||||
}
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const { selected } = this._notebookKernelService.getMatchingKernel(editor.viewModel.notebookDocument);
|
||||
if (!selected) {
|
||||
return;
|
||||
}
|
||||
for (let [handle, candidate] of this._kernels) {
|
||||
if (candidate[0] === selected) {
|
||||
this._proxy.$acceptRendererMessage(handle, editor.getId(), e.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
this._editors.set(editor, ipcListener);
|
||||
}
|
||||
|
||||
private _onEditorRemove(editor: INotebookEditor) {
|
||||
this._editors.get(editor)?.dispose();
|
||||
this._editors.delete(editor);
|
||||
}
|
||||
|
||||
async $postMessage(handle: number, editorId: string | undefined, message: any): Promise<boolean> {
|
||||
const tuple = this._kernels.get(handle);
|
||||
if (!tuple) {
|
||||
throw new Error('kernel already disposed');
|
||||
}
|
||||
const [kernel] = tuple;
|
||||
let didSend = false;
|
||||
for (const [editor] of this._editors) {
|
||||
if (!editor.hasModel()) {
|
||||
continue;
|
||||
}
|
||||
if (this._notebookKernelService.getMatchingKernel(editor.viewModel.notebookDocument).selected !== kernel) {
|
||||
// different kernel
|
||||
continue;
|
||||
}
|
||||
if (editorId === undefined) {
|
||||
// all editors
|
||||
editor.postMessage(undefined, message);
|
||||
didSend = true;
|
||||
} else if (editor.getId() === editorId) {
|
||||
// selected editors
|
||||
editor.postMessage(undefined, message);
|
||||
didSend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return didSend;
|
||||
}
|
||||
|
||||
// --- kernel adding/updating/removal
|
||||
|
||||
async $addKernel(handle: number, data: INotebookKernelDto2): Promise<void> {
|
||||
const that = this;
|
||||
const kernel = new class extends MainThreadKernel {
|
||||
async executeNotebookCellsRequest(uri: URI, handles: number[]): Promise<void> {
|
||||
await that._proxy.$executeCells(handle, uri, handles);
|
||||
}
|
||||
async cancelNotebookCellExecution(uri: URI, handles: number[]): Promise<void> {
|
||||
await that._proxy.$cancelCells(handle, uri, handles);
|
||||
}
|
||||
}(data, this._modeService);
|
||||
const registration = this._notebookKernelService.registerKernel(kernel);
|
||||
|
||||
const listener = this._notebookKernelService.onDidChangeNotebookKernelBinding(e => {
|
||||
if (e.oldKernel === kernel.id) {
|
||||
this._proxy.$acceptSelection(handle, e.notebook, false);
|
||||
} else if (e.newKernel === kernel.id) {
|
||||
this._proxy.$acceptSelection(handle, e.notebook, true);
|
||||
}
|
||||
});
|
||||
|
||||
this._kernels.set(handle, [kernel, combinedDisposable(listener, registration)]);
|
||||
}
|
||||
|
||||
$updateKernel(handle: number, data: Partial<INotebookKernelDto2>): void {
|
||||
const tuple = this._kernels.get(handle);
|
||||
if (tuple) {
|
||||
tuple[0].update(data);
|
||||
}
|
||||
}
|
||||
|
||||
$removeKernel(handle: number): void {
|
||||
const tuple = this._kernels.get(handle);
|
||||
if (tuple) {
|
||||
tuple[1].dispose();
|
||||
this._kernels.delete(handle);
|
||||
}
|
||||
}
|
||||
|
||||
$updateNotebookPriority(handle: number, notebook: UriComponents, value: number | undefined): void {
|
||||
const tuple = this._kernels.get(handle);
|
||||
if (tuple) {
|
||||
this._notebookKernelService.updateKernelNotebookAffinity(tuple[0], URI.revive(notebook), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,6 +100,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
const inputOptions: IInputOptions = Object.create(null);
|
||||
|
||||
if (options) {
|
||||
inputOptions.title = options.title;
|
||||
inputOptions.password = options.password;
|
||||
inputOptions.placeHolder = options.placeHolder;
|
||||
inputOptions.valueSelection = options.valueSelection;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService, InputValidationType } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../common/extHost.protocol';
|
||||
import { Command } from 'vs/editor/common/modes';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
@@ -242,6 +242,7 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||
|
||||
delete this._groupsByHandle[handle];
|
||||
this.groups.splice(this.groups.elements.indexOf(group), 1);
|
||||
this._onDidChangeResources.fire();
|
||||
}
|
||||
|
||||
async getOriginalResource(uri: URI): Promise<URI | null> {
|
||||
@@ -423,6 +424,24 @@ export class MainThreadSCM implements MainThreadSCMShape {
|
||||
repository.input.visible = visible;
|
||||
}
|
||||
|
||||
$setInputBoxFocus(sourceControlHandle: number): void {
|
||||
const repository = this._repositories.get(sourceControlHandle);
|
||||
if (!repository) {
|
||||
return;
|
||||
}
|
||||
|
||||
repository.input.setFocus();
|
||||
}
|
||||
|
||||
$showValidationMessage(sourceControlHandle: number, message: string, type: InputValidationType) {
|
||||
const repository = this._repositories.get(sourceControlHandle);
|
||||
if (!repository) {
|
||||
return;
|
||||
}
|
||||
|
||||
repository.input.showValidationMessage(message, type);
|
||||
}
|
||||
|
||||
$setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void {
|
||||
const repository = this._repositories.get(sourceControlHandle);
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable {
|
||||
|
||||
return Promise.resolve(searchP).then((result: ISearchCompleteStats) => {
|
||||
this._searches.delete(search.id);
|
||||
return { results: Array.from(search.matches.values()), stats: result.stats, limitHit: result.limitHit };
|
||||
return { results: Array.from(search.matches.values()), stats: result.stats, limitHit: result.limitHit, messages: result.messages };
|
||||
}, err => {
|
||||
this._searches.delete(search.id);
|
||||
return Promise.reject(err);
|
||||
|
||||
@@ -10,12 +10,12 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { Command } from 'vs/editor/common/modes';
|
||||
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { getCodiconAriaLabel } from 'vs/base/common/codicons';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadStatusBar)
|
||||
export class MainThreadStatusBar implements MainThreadStatusBarShape {
|
||||
|
||||
private readonly entries: Map<number, { accessor: IStatusbarEntryAccessor, alignment: MainThreadStatusBarAlignment, priority: number }> = new Map();
|
||||
static readonly CODICON_REGEXP = /\$\((.*?)\)/g;
|
||||
|
||||
constructor(
|
||||
_extHostContext: IExtHostContext,
|
||||
@@ -35,7 +35,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
|
||||
ariaLabel = accessibilityInformation.label;
|
||||
role = accessibilityInformation.role;
|
||||
} else {
|
||||
ariaLabel = text ? text.replace(MainThreadStatusBar.CODICON_REGEXP, (_match, codiconName) => codiconName) : '';
|
||||
ariaLabel = getCodiconAriaLabel(text);
|
||||
}
|
||||
const entry: IStatusbarEntry = { text, tooltip, command, color, backgroundColor, ariaLabel, role };
|
||||
|
||||
|
||||
@@ -422,7 +422,7 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
if (execution.task?.execution && CustomExecutionDTO.is(execution.task.execution) && event.resolvedVariables) {
|
||||
const dictionary: IStringDictionary<string> = {};
|
||||
Array.from(event.resolvedVariables.entries()).forEach(entry => dictionary[entry[0]] = entry[1]);
|
||||
resolvedDefinition = await this._configurationResolverService.resolveAny(task.getWorkspaceFolder(),
|
||||
resolvedDefinition = await this._configurationResolverService.resolveAnyAsync(task.getWorkspaceFolder(),
|
||||
execution.task.definition, dictionary);
|
||||
}
|
||||
this._proxy.$onDidStartTask(execution, event.terminalId!, resolvedDefinition);
|
||||
|
||||
@@ -4,24 +4,46 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { MainThreadTelemetryShape, MainContext, IExtHostContext } from '../common/extHost.protocol';
|
||||
import { MainThreadTelemetryShape, MainContext, IExtHostContext, ExtHostTelemetryShape, ExtHostContext } from '../common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTelemetry)
|
||||
export class MainThreadTelemetry implements MainThreadTelemetryShape {
|
||||
export class MainThreadTelemetry extends Disposable implements MainThreadTelemetryShape {
|
||||
private readonly _proxy: ExtHostTelemetryShape;
|
||||
|
||||
private static readonly _name = 'pluginHostTelemetry';
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IEnvironmentService private readonly _environmenService: IEnvironmentService,
|
||||
) {
|
||||
//
|
||||
super();
|
||||
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTelemetry);
|
||||
|
||||
if (!this._environmenService.disableTelemetry) {
|
||||
this._register(this._configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectedKeys.includes('telemetry.enableTelemetry')) {
|
||||
this._proxy.$onDidChangeTelemetryEnabled(this.telemetryEnabled);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
this._proxy.$initializeTelemetryEnabled(this.telemetryEnabled);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
//
|
||||
private get telemetryEnabled(): boolean {
|
||||
if (this._environmenService.disableTelemetry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!this._configurationService.getValue('telemetry.enableTelemetry');
|
||||
}
|
||||
|
||||
$publicLog(eventName: string, data: any = Object.create(null)): void {
|
||||
|
||||
@@ -3,20 +3,22 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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 { 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';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
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 { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensions } from 'vs/platform/terminal/common/terminal';
|
||||
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
|
||||
import { ExtHostContext, ExtHostTerminalServiceShape, IExtHostContext, ITerminalDimensionsDto, MainContext, MainThreadTerminalServiceShape, TerminalIdentifier, TerminalLaunchConfig } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ITerminalExternalLinkProvider, ITerminalInstance, ITerminalInstanceService, ITerminalLink, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy';
|
||||
import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
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 { IAvailableProfilesRequest as IAvailableProfilesRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
|
||||
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
|
||||
@@ -32,6 +34,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
private readonly _toDispose = new DisposableStore();
|
||||
private readonly _terminalProcessProxies = new Map<number, ITerminalProcessExtHostProxy>();
|
||||
private _dataEventTracker: TerminalDataEventTracker | undefined;
|
||||
private _extHostKind: ExtensionHostKind;
|
||||
/**
|
||||
* A single shared terminal link provider for the exthost. When an ext registers a link
|
||||
* provider, this is registered with the terminal on the renderer side and all links are
|
||||
@@ -47,7 +50,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
|
||||
@@ -59,16 +61,16 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
this._onInstanceDimensionsChanged(instance);
|
||||
}));
|
||||
|
||||
this._extHostKind = extHostContext.extensionHostKind;
|
||||
|
||||
this._toDispose.add(_terminalService.onInstanceDisposed(instance => this._onTerminalDisposed(instance)));
|
||||
this._toDispose.add(_terminalService.onInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance)));
|
||||
this._toDispose.add(_terminalService.onInstanceDimensionsChanged(instance => this._onInstanceDimensionsChanged(instance)));
|
||||
this._toDispose.add(_terminalService.onInstanceMaximumDimensionsChanged(instance => this._onInstanceMaximumDimensionsChanged(instance)));
|
||||
this._toDispose.add(_terminalService.onInstanceRequestSpawnExtHostProcess(request => this._onRequestSpawnExtHostProcess(request)));
|
||||
this._toDispose.add(_terminalService.onInstanceRequestStartExtensionTerminal(e => this._onRequestStartExtensionTerminal(e)));
|
||||
this._toDispose.add(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : null)));
|
||||
this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => instance && this._onTitleChanged(instance.id, instance.title)));
|
||||
this._toDispose.add(_terminalService.configHelper.onWorkspacePermissionsChanged(isAllowed => this._onWorkspacePermissionsChanged(isAllowed)));
|
||||
this._toDispose.add(_terminalService.onRequestAvailableShells(e => this._onRequestAvailableShells(e)));
|
||||
this._toDispose.add(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.instanceId : null)));
|
||||
this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => instance && this._onTitleChanged(instance.instanceId, instance.title)));
|
||||
this._toDispose.add(_terminalService.onRequestAvailableProfiles(e => this._onRequestAvailableProfiles(e)));
|
||||
|
||||
// ITerminalInstanceService listeners
|
||||
if (terminalInstanceService.onRequestDefaultShellAndArgs) {
|
||||
@@ -82,7 +84,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
});
|
||||
const activeInstance = this._terminalService.getActiveInstance();
|
||||
if (activeInstance) {
|
||||
this._proxy.$acceptActiveTerminalChanged(activeInstance.id);
|
||||
this._proxy.$acceptActiveTerminalChanged(activeInstance.instanceId);
|
||||
}
|
||||
if (this._environmentVariableService.collections.size > 0) {
|
||||
const collectionAsArray = [...this._environmentVariableService.collections.entries()];
|
||||
@@ -98,9 +100,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
public dispose(): void {
|
||||
this._toDispose.dispose();
|
||||
this._linkProvider?.dispose();
|
||||
|
||||
// TODO@Daniel: Should all the previously created terminals be disposed
|
||||
// when the extension host process goes down ?
|
||||
}
|
||||
|
||||
private _getTerminalId(id: TerminalIdentifier): number | undefined {
|
||||
@@ -124,17 +123,22 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
executable: launchConfig.shellPath,
|
||||
args: launchConfig.shellArgs,
|
||||
cwd: typeof launchConfig.cwd === 'string' ? launchConfig.cwd : URI.revive(launchConfig.cwd),
|
||||
icon: launchConfig.icon,
|
||||
initialText: launchConfig.initialText,
|
||||
waitOnExit: launchConfig.waitOnExit,
|
||||
ignoreConfigurationCwd: true,
|
||||
env: launchConfig.env,
|
||||
strictEnv: launchConfig.strictEnv,
|
||||
hideFromUser: launchConfig.hideFromUser,
|
||||
isExtensionTerminal: launchConfig.isExtensionTerminal,
|
||||
customPtyImplementation: launchConfig.isExtensionCustomPtyTerminal
|
||||
? (id, cols, rows) => new TerminalProcessExtHostProxy(id, cols, rows, this._terminalService)
|
||||
: undefined,
|
||||
extHostTerminalId: extHostTerminalId,
|
||||
isFeatureTerminal: launchConfig.isFeatureTerminal
|
||||
isFeatureTerminal: launchConfig.isFeatureTerminal,
|
||||
isExtensionOwnedTerminal: launchConfig.isExtensionOwnedTerminal
|
||||
};
|
||||
const terminal = this._terminalService.createTerminal(shellLaunchConfig);
|
||||
this._extHostTerminalIds.set(extHostTerminalId, terminal.id);
|
||||
this._extHostTerminalIds.set(extHostTerminalId, terminal.instanceId);
|
||||
}
|
||||
|
||||
public $show(id: TerminalIdentifier, preserveFocus: boolean): void {
|
||||
@@ -148,23 +152,17 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
public $hide(id: TerminalIdentifier): void {
|
||||
const rendererId = this._getTerminalId(id);
|
||||
const instance = this._terminalService.getActiveInstance();
|
||||
if (instance && instance.id === rendererId) {
|
||||
if (instance && instance.instanceId === rendererId) {
|
||||
this._terminalService.hidePanel();
|
||||
}
|
||||
}
|
||||
|
||||
public $dispose(id: TerminalIdentifier): void {
|
||||
const terminalInstance = this._getTerminalInstance(id);
|
||||
if (terminalInstance) {
|
||||
terminalInstance.dispose();
|
||||
}
|
||||
this._getTerminalInstance(id)?.dispose();
|
||||
}
|
||||
|
||||
public $sendText(id: TerminalIdentifier, text: string, addNewLine: boolean): void {
|
||||
const terminalInstance = this._getTerminalInstance(id);
|
||||
if (terminalInstance) {
|
||||
terminalInstance.sendText(text, addNewLine);
|
||||
}
|
||||
this._getTerminalInstance(id)?.sendText(text, addNewLine);
|
||||
}
|
||||
|
||||
public $startSendingDataEvents(): void {
|
||||
@@ -174,16 +172,14 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
});
|
||||
// Send initial events if they exist
|
||||
this._terminalService.terminalInstances.forEach(t => {
|
||||
t.initialDataEvents?.forEach(d => this._onTerminalData(t.id, d));
|
||||
t.initialDataEvents?.forEach(d => this._onTerminalData(t.instanceId, d));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public $stopSendingDataEvents(): void {
|
||||
if (this._dataEventTracker) {
|
||||
this._dataEventTracker.dispose();
|
||||
this._dataEventTracker = undefined;
|
||||
}
|
||||
this._dataEventTracker?.dispose();
|
||||
this._dataEventTracker = undefined;
|
||||
}
|
||||
|
||||
public $startLinkProvider(): void {
|
||||
@@ -212,12 +208,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
this._proxy.$acceptTerminalTitleChange(terminalId, name);
|
||||
}
|
||||
|
||||
private _onWorkspacePermissionsChanged(isAllowed: boolean): void {
|
||||
this._proxy.$acceptWorkspacePermissionsChanged(isAllowed);
|
||||
}
|
||||
|
||||
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
|
||||
this._proxy.$acceptTerminalClosed(terminalInstance.id, terminalInstance.exitCode);
|
||||
this._proxy.$acceptTerminalClosed(terminalInstance.instanceId, terminalInstance.exitCode);
|
||||
}
|
||||
|
||||
private _onTerminalOpened(terminalInstance: ITerminalInstance): void {
|
||||
@@ -230,63 +222,28 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
env: terminalInstance.shellLaunchConfig.env,
|
||||
hideFromUser: terminalInstance.shellLaunchConfig.hideFromUser
|
||||
};
|
||||
this._proxy.$acceptTerminalOpened(terminalInstance.id, extHostTerminalId, terminalInstance.title, shellLaunchConfigDto);
|
||||
this._proxy.$acceptTerminalOpened(terminalInstance.instanceId, extHostTerminalId, terminalInstance.title, shellLaunchConfigDto);
|
||||
}
|
||||
|
||||
private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void {
|
||||
if (terminalInstance.processId === undefined) {
|
||||
return;
|
||||
}
|
||||
this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
|
||||
this._proxy.$acceptTerminalProcessId(terminalInstance.instanceId, terminalInstance.processId);
|
||||
}
|
||||
|
||||
private _onInstanceDimensionsChanged(instance: ITerminalInstance): void {
|
||||
this._proxy.$acceptTerminalDimensions(instance.id, instance.cols, instance.rows);
|
||||
this._proxy.$acceptTerminalDimensions(instance.instanceId, instance.cols, instance.rows);
|
||||
}
|
||||
|
||||
private _onInstanceMaximumDimensionsChanged(instance: ITerminalInstance): void {
|
||||
this._proxy.$acceptTerminalMaximumDimensions(instance.id, instance.maxCols, instance.maxRows);
|
||||
this._proxy.$acceptTerminalMaximumDimensions(instance.instanceId, instance.maxCols, instance.maxRows);
|
||||
}
|
||||
|
||||
private _onRequestSpawnExtHostProcess(request: ISpawnExtHostProcessRequest): void {
|
||||
// Only allow processes on remote ext hosts
|
||||
if (!this._remoteAuthority) {
|
||||
return;
|
||||
}
|
||||
|
||||
const proxy = request.proxy;
|
||||
this._terminalProcessProxies.set(proxy.terminalId, proxy);
|
||||
const shellLaunchConfigDto: IShellLaunchConfigDto = {
|
||||
name: request.shellLaunchConfig.name,
|
||||
executable: request.shellLaunchConfig.executable,
|
||||
args: request.shellLaunchConfig.args,
|
||||
cwd: request.shellLaunchConfig.cwd,
|
||||
env: request.shellLaunchConfig.env,
|
||||
flowControl: this._configurationService.getValue<ITerminalConfiguration>(TERMINAL_CONFIG_SECTION).flowControl
|
||||
};
|
||||
|
||||
this._logService.trace('Spawning ext host process', { terminalId: proxy.terminalId, shellLaunchConfigDto, request });
|
||||
this._proxy.$spawnExtHostProcess(
|
||||
proxy.terminalId,
|
||||
shellLaunchConfigDto,
|
||||
request.activeWorkspaceRootUri,
|
||||
request.cols,
|
||||
request.rows,
|
||||
request.isWorkspaceShellAllowed
|
||||
).then(request.callback, request.callback);
|
||||
|
||||
proxy.onAcknowledgeDataEvent(charCount => this._proxy.$acceptProcessAckDataEvent(proxy.terminalId, charCount));
|
||||
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data));
|
||||
proxy.onResize(dimensions => this._proxy.$acceptProcessResize(proxy.terminalId, dimensions.cols, dimensions.rows));
|
||||
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate));
|
||||
proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.terminalId));
|
||||
proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.terminalId));
|
||||
proxy.onRequestLatency(() => this._onRequestLatency(proxy.terminalId));
|
||||
}
|
||||
|
||||
private _onRequestStartExtensionTerminal(request: IStartExtensionTerminalRequest): void {
|
||||
const proxy = request.proxy;
|
||||
this._terminalProcessProxies.set(proxy.terminalId, proxy);
|
||||
this._terminalProcessProxies.set(proxy.instanceId, proxy);
|
||||
|
||||
// Note that onReisze is not being listened to here as it needs to fire when max dimensions
|
||||
// change, excluding the dimension override
|
||||
@@ -296,72 +253,47 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
} : undefined;
|
||||
|
||||
this._proxy.$startExtensionTerminal(
|
||||
proxy.terminalId,
|
||||
proxy.instanceId,
|
||||
initialDimensions
|
||||
).then(request.callback);
|
||||
|
||||
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data));
|
||||
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate));
|
||||
proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.terminalId));
|
||||
proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.terminalId));
|
||||
proxy.onRequestLatency(() => this._onRequestLatency(proxy.terminalId));
|
||||
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.instanceId, data));
|
||||
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.instanceId, immediate));
|
||||
proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.instanceId));
|
||||
proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.instanceId));
|
||||
proxy.onRequestLatency(() => this._onRequestLatency(proxy.instanceId));
|
||||
}
|
||||
|
||||
public $sendProcessTitle(terminalId: number, title: string): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitTitle(title);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitTitle(title);
|
||||
}
|
||||
|
||||
public $sendProcessData(terminalId: number, data: string): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitData(data);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitData(data);
|
||||
}
|
||||
|
||||
public $sendProcessReady(terminalId: number, pid: number, cwd: string): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitReady(pid, cwd);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitReady(pid, cwd);
|
||||
}
|
||||
|
||||
public $sendProcessExit(terminalId: number, exitCode: number | undefined): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitExit(exitCode);
|
||||
this._terminalProcessProxies.delete(terminalId);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitExit(exitCode);
|
||||
}
|
||||
|
||||
public $sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitOverrideDimensions(dimensions);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitOverrideDimensions(dimensions);
|
||||
}
|
||||
|
||||
public $sendProcessInitialCwd(terminalId: number, initialCwd: string): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitInitialCwd(initialCwd);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitInitialCwd(initialCwd);
|
||||
}
|
||||
|
||||
public $sendProcessCwd(terminalId: number, cwd: string): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitCwd(cwd);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitCwd(cwd);
|
||||
}
|
||||
|
||||
public $sendResolvedLaunchConfig(terminalId: number, shellLaunchConfig: IShellLaunchConfig): void {
|
||||
const instance = this._terminalService.getInstanceFromId(terminalId);
|
||||
if (instance) {
|
||||
this._getTerminalProcess(terminalId)?.emitResolvedShellLaunchConfig(shellLaunchConfig);
|
||||
}
|
||||
this._getTerminalProcess(terminalId)?.emitResolvedShellLaunchConfig(shellLaunchConfig);
|
||||
}
|
||||
|
||||
private async _onRequestLatency(terminalId: number): Promise<void> {
|
||||
@@ -382,12 +314,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
if (conn) {
|
||||
return this._remoteAuthority === conn.remoteAuthority;
|
||||
}
|
||||
return true;
|
||||
return this._extHostKind !== ExtensionHostKind.LocalWebWorker;
|
||||
}
|
||||
|
||||
private async _onRequestAvailableShells(req: IAvailableShellsRequest): Promise<void> {
|
||||
private async _onRequestAvailableProfiles(req: IAvailableProfilesRequest): Promise<void> {
|
||||
if (this._isPrimaryExtHost()) {
|
||||
req.callback(await this._proxy.$getAvailableShells());
|
||||
req.callback(await this._proxy.$getAvailableProfiles(req.configuredProfilesOnly));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,12 +369,12 @@ class TerminalDataEventTracker extends Disposable {
|
||||
|
||||
this._terminalService.terminalInstances.forEach(instance => this._registerInstance(instance));
|
||||
this._register(this._terminalService.onInstanceCreated(instance => this._registerInstance(instance)));
|
||||
this._register(this._terminalService.onInstanceDisposed(instance => this._bufferer.stopBuffering(instance.id)));
|
||||
this._register(this._terminalService.onInstanceDisposed(instance => this._bufferer.stopBuffering(instance.instanceId)));
|
||||
}
|
||||
|
||||
private _registerInstance(instance: ITerminalInstance): void {
|
||||
// Buffer data events to reduce the amount of messages going to the extension host
|
||||
this._register(this._bufferer.startBuffering(instance.id, instance.onData));
|
||||
this._register(this._bufferer.startBuffering(instance.instanceId, instance.onData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,13 +386,13 @@ class ExtensionTerminalLinkProvider implements ITerminalExternalLinkProvider {
|
||||
|
||||
async provideLinks(instance: ITerminalInstance, line: string): Promise<ITerminalLink[] | undefined> {
|
||||
const proxy = this._proxy;
|
||||
const extHostLinks = await proxy.$provideLinks(instance.id, line);
|
||||
const extHostLinks = await proxy.$provideLinks(instance.instanceId, line);
|
||||
return extHostLinks.map(dto => ({
|
||||
id: dto.id,
|
||||
startIndex: dto.startIndex,
|
||||
length: dto.length,
|
||||
label: dto.label,
|
||||
activate: () => proxy.$activateLink(instance.id, dto.id)
|
||||
activate: () => proxy.$activateLink(instance.instanceId, dto.id)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,58 +3,64 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { 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 { TestResultState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ExtensionRunTestsRequest, getTestSubscriptionKey, ITestItem, ITestMessage, ITestRunTask, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult';
|
||||
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { ITestRootProvider, ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol';
|
||||
|
||||
const reviveDiff = (diff: TestsDiff) => {
|
||||
for (const entry of diff) {
|
||||
if (entry[0] === TestDiffOpType.Add || entry[0] === TestDiffOpType.Update) {
|
||||
const item = entry[1];
|
||||
if (item.item.location) {
|
||||
item.item.location.uri = URI.revive(item.item.location.uri);
|
||||
if (item.item?.uri) {
|
||||
item.item.uri = URI.revive(item.item.uri);
|
||||
}
|
||||
|
||||
for (const message of item.item.state.messages) {
|
||||
if (message.location) {
|
||||
message.location.uri = URI.revive(message.location.uri);
|
||||
}
|
||||
if (item.item?.range) {
|
||||
item.item.range = Range.lift(item.item.range);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTesting)
|
||||
export class MainThreadTesting extends Disposable implements MainThreadTestingShape {
|
||||
export class MainThreadTesting extends Disposable implements MainThreadTestingShape, ITestRootProvider {
|
||||
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 });
|
||||
}
|
||||
this._register(testService.registerRootProvider(this));
|
||||
|
||||
for (const { resource, uri } of this.testService.subscriptions) {
|
||||
this.proxy.$subscribeToTests(resource, uri);
|
||||
@@ -64,24 +70,100 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $registerTestProvider(id: string) {
|
||||
this.testService.registerTestController(id, {
|
||||
$addTestsToRun(runId: string, tests: ITestItem[]): void {
|
||||
for (const test of tests) {
|
||||
test.uri = URI.revive(test.uri);
|
||||
if (test.range) {
|
||||
test.range = Range.lift(test.range);
|
||||
}
|
||||
}
|
||||
|
||||
this.withLiveRun(runId, r => r.addTestChainToRun(tests));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$startedExtensionTestRun(req: ExtensionRunTestsRequest): void {
|
||||
this.resultService.createLiveResult(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$startedTestRunTask(runId: string, task: ITestRunTask): void {
|
||||
this.withLiveRun(runId, r => r.addTask(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$finishedTestRunTask(runId: string, taskId: string): void {
|
||||
this.withLiveRun(runId, r => r.markTaskComplete(taskId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$finishedExtensionTestRun(runId: string): void {
|
||||
this.withLiveRun(runId, r => r.markComplete());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $updateTestStateInRun(runId: string, taskId: string, testId: string, state: TestResultState, duration?: number): void {
|
||||
this.withLiveRun(runId, r => r.updateState(testId, taskId, state, duration));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $appendOutputToRun(runId: string, _taskId: string, output: VSBuffer): void {
|
||||
this.withLiveRun(runId, r => r.output.append(output));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $appendTestMessageInRun(runId: string, taskId: string, testId: string, message: ITestMessage): void {
|
||||
const r = this.resultService.getResult(runId);
|
||||
if (r && r instanceof LiveTestResult) {
|
||||
if (message.location) {
|
||||
message.location.uri = URI.revive(message.location.uri);
|
||||
message.location.range = Range.lift(message.location.range);
|
||||
}
|
||||
|
||||
r.appendMessage(testId, taskId, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $registerTestController(id: string) {
|
||||
const disposable = this.testService.registerTestController(id, {
|
||||
runTests: (req, token) => this.proxy.$runTestsForProvider(req, token),
|
||||
lookupTest: test => this.proxy.$lookupTest(test),
|
||||
expandTest: (src, levels) => this.proxy.$expandTest(src, isFinite(levels) ? levels : -1),
|
||||
});
|
||||
|
||||
this.testProviderRegistrations.set(id, disposable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $unregisterTestProvider(id: string) {
|
||||
this.testService.unregisterTestController(id);
|
||||
public $unregisterTestController(id: string) {
|
||||
this.testProviderRegistrations.get(id)?.dispose();
|
||||
this.testProviderRegistrations.delete(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$subscribeToDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void {
|
||||
public $subscribeToDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const disposable = this.testService.subscribeToDiffs(resource, uri,
|
||||
diff => this.proxy.$acceptDiff(resource, uriComponents, diff));
|
||||
@@ -105,15 +187,21 @@ 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() {
|
||||
this.testService.updateRootProviderCount(-1);
|
||||
public override dispose() {
|
||||
super.dispose();
|
||||
for (const subscription of this.testSubscriptions.values()) {
|
||||
subscription.dispose();
|
||||
}
|
||||
this.testSubscriptions.clear();
|
||||
}
|
||||
|
||||
private withLiveRun<T>(runId: string, fn: (run: LiveTestResult) => T): T | undefined {
|
||||
const r = this.resultService.getResult(runId);
|
||||
return r && r instanceof LiveTestResult ? fn(r) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,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(() => {
|
||||
@@ -156,7 +156,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
return viewDescriptor ? viewDescriptor.treeView : null;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
this._dataProviders.forEach((dataProvider, treeViewId) => {
|
||||
const treeView = this.getTreeView(treeViewId);
|
||||
if (treeView) {
|
||||
|
||||
@@ -4,22 +4,26 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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, PortAttributesProviderSelector } 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 { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, isPortPrivileged } from 'vs/platform/remote/common/tunnel';
|
||||
import { CandidatePort, IRemoteExplorerService, makeAddress, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, PORT_AUTO_SOURCE_SETTING_PROCESS } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, isPortPrivileged, ProvidedPortAttributes, PortAttributesProvider } 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 { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTunnelService)
|
||||
export class MainThreadTunnelService extends Disposable implements MainThreadTunnelServiceShape {
|
||||
export class MainThreadTunnelService extends Disposable implements MainThreadTunnelServiceShape, PortAttributesProvider {
|
||||
private readonly _proxy: ExtHostTunnelServiceShape;
|
||||
private elevateionRetry: boolean = false;
|
||||
private portsAttributesProviders: Map<number, PortAttributesProviderSelector> = new Map();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@@ -27,7 +31,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,19 +40,58 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
this._register(tunnelService.onTunnelClosed(() => this._proxy.$onDidTunnelsChange()));
|
||||
}
|
||||
|
||||
async $setCandidateFinder(): Promise<void> {
|
||||
private processFindingEnabled(): boolean {
|
||||
return (!!this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING)) && (this.configurationService.getValue(PORT_AUTO_SOURCE_SETTING) === PORT_AUTO_SOURCE_SETTING_PROCESS);
|
||||
}
|
||||
|
||||
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));
|
||||
this._proxy.$registerCandidateFinder(this.processFindingEnabled());
|
||||
} else {
|
||||
this._register(this.remoteExplorerService.onEnabledPortsFeatures(() => this._proxy.$registerCandidateFinder(this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING))));
|
||||
}
|
||||
this._register(this.configurationService.onDidChangeConfiguration(async (e) => {
|
||||
if (e.affectsConfiguration(PORT_AUTO_FORWARD_SETTING)) {
|
||||
return this._proxy.$registerCandidateFinder((this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING)));
|
||||
if (e.affectsConfiguration(PORT_AUTO_FORWARD_SETTING) || e.affectsConfiguration(PORT_AUTO_SOURCE_SETTING)) {
|
||||
return this._proxy.$registerCandidateFinder(this.processFindingEnabled());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private _alreadyRegistered: boolean = false;
|
||||
async $registerPortsAttributesProvider(selector: PortAttributesProviderSelector, providerHandle: number): Promise<void> {
|
||||
this.portsAttributesProviders.set(providerHandle, selector);
|
||||
if (!this._alreadyRegistered) {
|
||||
this.remoteExplorerService.tunnelModel.addAttributesProvider(this);
|
||||
this._alreadyRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
async $unregisterPortsAttributesProvider(providerHandle: number): Promise<void> {
|
||||
this.portsAttributesProviders.delete(providerHandle);
|
||||
}
|
||||
|
||||
async providePortAttributes(ports: number[], pid: number | undefined, commandLine: string | undefined, token: CancellationToken): Promise<ProvidedPortAttributes[]> {
|
||||
if (this.portsAttributesProviders.size === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Check all the selectors to make sure it's worth going to the extension host.
|
||||
const appropriateHandles = Array.from(this.portsAttributesProviders.entries()).filter(entry => {
|
||||
const selector = entry[1];
|
||||
const portRange = selector.portRange;
|
||||
const portInRange = portRange ? ports.some(port => portRange[0] <= port && port < portRange[1]) : true;
|
||||
const pidMatches = !selector.pid || (selector.pid === pid);
|
||||
const commandMatches = !selector.commandMatcher || (commandLine && (commandLine.match(selector.commandMatcher)));
|
||||
return portInRange && pidMatches && commandMatches;
|
||||
}).map(entry => entry[0]);
|
||||
|
||||
if (appropriateHandles.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return this._proxy.$providePortAttributes(appropriateHandles, ports, pid, commandLine, token);
|
||||
}
|
||||
|
||||
async $openTunnel(tunnelOptions: TunnelOptions, source: string): Promise<TunnelDto | undefined> {
|
||||
const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remoteAddress, tunnelOptions.localAddressPort, tunnelOptions.label, source, false);
|
||||
if (tunnel) {
|
||||
@@ -100,26 +144,23 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
const tunnelProvider: ITunnelProvider = {
|
||||
forwardPort: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => {
|
||||
const forward = this._proxy.$forwardPort(tunnelOptions, tunnelCreationOptions);
|
||||
if (forward) {
|
||||
return forward.then(tunnel => {
|
||||
this.logService.trace(`MainThreadTunnelService: New tunnel established by tunnel provider: ${tunnel?.remoteAddress.host}:${tunnel?.remoteAddress.port}`);
|
||||
if (!tunnel) {
|
||||
return undefined;
|
||||
return forward.then(tunnel => {
|
||||
this.logService.trace(`ForwardedPorts: (MainThreadTunnelService) New tunnel established by tunnel provider: ${tunnel?.remoteAddress.host}:${tunnel?.remoteAddress.port}`);
|
||||
if (!tunnel) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
tunnelRemotePort: tunnel.remoteAddress.port,
|
||||
tunnelRemoteHost: tunnel.remoteAddress.host,
|
||||
localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : makeAddress(tunnel.localAddress.host, tunnel.localAddress.port),
|
||||
tunnelLocalPort: typeof tunnel.localAddress !== 'string' ? tunnel.localAddress.port : undefined,
|
||||
public: tunnel.public,
|
||||
dispose: async (silent?: boolean) => {
|
||||
this.logService.trace(`ForwardedPorts: (MainThreadTunnelService) Closing tunnel from tunnel provider: ${tunnel?.remoteAddress.host}:${tunnel?.remoteAddress.port}`);
|
||||
return this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port }, silent);
|
||||
}
|
||||
return {
|
||||
tunnelRemotePort: tunnel.remoteAddress.port,
|
||||
tunnelRemoteHost: tunnel.remoteAddress.host,
|
||||
localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : makeAddress(tunnel.localAddress.host, tunnel.localAddress.port),
|
||||
tunnelLocalPort: typeof tunnel.localAddress !== 'string' ? tunnel.localAddress.port : undefined,
|
||||
public: tunnel.public,
|
||||
dispose: async (silent?: boolean) => {
|
||||
this.logService.trace(`MainThreadTunnelService: Closing tunnel from tunnel provider: ${tunnel?.remoteAddress.host}:${tunnel?.remoteAddress.port}`);
|
||||
return this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port }, silent);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
this.tunnelService.setTunnelProvider(tunnelProvider, features);
|
||||
@@ -131,8 +172,24 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
dispose(): void {
|
||||
|
||||
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: {
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
.registerDefaultConfigurations([{ 'remote.autoForwardPorts': false }]);
|
||||
break;
|
||||
}
|
||||
case CandidatePortSource.Output: {
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
.registerDefaultConfigurations([{ 'remote.autoForwardPortsSource': PORT_AUTO_SOURCE_SETTING_OUTPUT }]);
|
||||
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.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpe
|
||||
await this.proxy.$openUri(id, { resolvedUri: uri, sourceUri: ctx.sourceUri }, token);
|
||||
} catch (e) {
|
||||
if (!isPromiseCanceledError(e)) {
|
||||
const openDefaultAction = new Action('default', localize('openerFailedUseDefault', "Open using default opener"), undefined, undefined, () => {
|
||||
return this.openerService.open(uri, {
|
||||
const openDefaultAction = new Action('default', localize('openerFailedUseDefault', "Open using default opener"), undefined, undefined, async () => {
|
||||
await this.openerService.open(uri, {
|
||||
allowTunneling: false,
|
||||
allowContributedOpeners: defaultExternalUriOpenerId,
|
||||
});
|
||||
@@ -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
|
||||
@@ -125,7 +128,7 @@ export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpe
|
||||
this._contributedExternalUriOpenersStore.delete(id);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
this._registeredOpeners.clear();
|
||||
}
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { MainThreadWebviews, reviveWebviewExtension, reviveWebviewOptions } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
import { MainThreadWebviews, reviveWebviewContentOptions, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { editorGroupToViewColumn, EditorGroupColumn, IEditorInput, viewColumnToEditorGroup } from 'vs/workbench/common/editor';
|
||||
import { EditorGroupColumn, editorGroupToViewColumn, IEditorInput, viewColumnToEditorGroup } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput';
|
||||
import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService';
|
||||
import { WebviewIcons } from 'vs/workbench/contrib/webviewPanel/browser/webviewIconManager';
|
||||
import { ICreateWebViewShowOptions, IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
@@ -80,7 +81,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
private readonly _webviewInputs = new WebviewInputStore();
|
||||
|
||||
private readonly _editorProviders = new Map<string, IDisposable>();
|
||||
private readonly _webviewFromDiffEditorHandles = new Set<string>();
|
||||
|
||||
private readonly _revivers = new Map<string, IDisposable>();
|
||||
|
||||
@@ -98,18 +98,17 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewPanels);
|
||||
|
||||
this._register(_editorService.onDidActiveEditorChange(() => {
|
||||
const activeInput = this._editorService.activeEditor;
|
||||
if (activeInput instanceof DiffEditorInput && activeInput.primary instanceof WebviewInput && activeInput.secondary instanceof WebviewInput) {
|
||||
this.registerWebviewFromDiffEditorListeners(activeInput);
|
||||
}
|
||||
|
||||
this.updateWebviewViewStates(activeInput);
|
||||
this.updateWebviewViewStates(this._editorService.activeEditor);
|
||||
}));
|
||||
|
||||
this._register(_editorService.onDidVisibleEditorsChange(() => {
|
||||
this.updateWebviewViewStates(this._editorService.activeEditor);
|
||||
}));
|
||||
|
||||
this._register(_webviewWorkbenchService.onDidChangeActiveWebviewEditor(input => {
|
||||
this.updateWebviewViewStates(input);
|
||||
}));
|
||||
|
||||
// This reviver's only job is to activate extensions.
|
||||
// This should trigger the real reviver to be registered from the extension host side.
|
||||
this._register(_webviewWorkbenchService.registerResolver({
|
||||
@@ -124,7 +123,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
}));
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
|
||||
dispose(this._editorProviders.values());
|
||||
@@ -136,9 +135,9 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
|
||||
public get webviewInputs(): Iterable<WebviewInput> { return this._webviewInputs; }
|
||||
|
||||
public addWebviewInput(handle: extHostProtocol.WebviewHandle, input: WebviewInput): void {
|
||||
public addWebviewInput(handle: extHostProtocol.WebviewHandle, input: WebviewInput, options: { serializeBuffersForPostMessage: boolean }): void {
|
||||
this._webviewInputs.add(handle, input);
|
||||
this._mainThreadWebviews.addWebview(handle, input.webview);
|
||||
this._mainThreadWebviews.addWebview(handle, input.webview, options);
|
||||
|
||||
input.webview.onDidDispose(() => {
|
||||
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
|
||||
@@ -151,9 +150,13 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
extensionData: extHostProtocol.WebviewExtensionDescription,
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: extHostProtocol.IWebviewOptions;
|
||||
panelOptions: extHostProtocol.IWebviewPanelOptions;
|
||||
serializeBuffersForPostMessage: boolean;
|
||||
},
|
||||
showOptions: { viewColumn?: EditorGroupColumn, preserveFocus?: boolean; },
|
||||
options: WebviewInputOptions
|
||||
): void {
|
||||
const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null);
|
||||
if (showOptions) {
|
||||
@@ -163,8 +166,8 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
|
||||
const extension = reviveWebviewExtension(extensionData);
|
||||
|
||||
const webview = this._webviewWorkbenchService.createWebview(handle, this.webviewPanelViewType.fromExternal(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), extension);
|
||||
this.addWebviewInput(handle, webview);
|
||||
const webview = this._webviewWorkbenchService.createWebview(handle, this.webviewPanelViewType.fromExternal(viewType), initData.title, mainThreadShowOptions, reviveWebviewOptions(initData.panelOptions), reviveWebviewContentOptions(initData.webviewOptions), extension);
|
||||
this.addWebviewInput(handle, webview, { serializeBuffersForPostMessage: initData.serializeBuffersForPostMessage });
|
||||
|
||||
/* __GDPR__
|
||||
"webviews:createWebviewPanel" : {
|
||||
@@ -201,7 +204,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
}
|
||||
}
|
||||
|
||||
public $registerSerializer(viewType: string): void {
|
||||
public $registerSerializer(viewType: string, options: { serializeBuffersForPostMessage: boolean }): void {
|
||||
if (this._revivers.has(viewType)) {
|
||||
throw new Error(`Reviver for ${viewType} already registered`);
|
||||
}
|
||||
@@ -219,7 +222,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
|
||||
const handle = webviewInput.id;
|
||||
|
||||
this.addWebviewInput(handle, webviewInput);
|
||||
this.addWebviewInput(handle, webviewInput, options);
|
||||
|
||||
let state = undefined;
|
||||
if (webviewInput.webview.state) {
|
||||
@@ -231,7 +234,12 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
}
|
||||
|
||||
try {
|
||||
await this._proxy.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options);
|
||||
await this._proxy.$deserializeWebviewPanel(handle, viewType, {
|
||||
title: webviewInput.getTitle(),
|
||||
state,
|
||||
panelOptions: webviewInput.webview.options,
|
||||
webviewOptions: webviewInput.webview.contentOptions,
|
||||
}, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0));
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
webviewInput.webview.html = this._mainThreadWebviews.getWebviewResolvedFailedContent(viewType);
|
||||
@@ -250,27 +258,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
this._revivers.delete(viewType);
|
||||
}
|
||||
|
||||
private registerWebviewFromDiffEditorListeners(diffEditorInput: DiffEditorInput): void {
|
||||
const primary = diffEditorInput.primary as WebviewInput;
|
||||
const secondary = diffEditorInput.secondary as WebviewInput;
|
||||
|
||||
if (this._webviewFromDiffEditorHandles.has(primary.id) || this._webviewFromDiffEditorHandles.has(secondary.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._webviewFromDiffEditorHandles.add(primary.id);
|
||||
this._webviewFromDiffEditorHandles.add(secondary.id);
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
disposables.add(primary.webview.onDidFocus(() => this.updateWebviewViewStates(primary)));
|
||||
disposables.add(secondary.webview.onDidFocus(() => this.updateWebviewViewStates(secondary)));
|
||||
disposables.add(diffEditorInput.onDispose(() => {
|
||||
this._webviewFromDiffEditorHandles.delete(primary.id);
|
||||
this._webviewFromDiffEditorHandles.delete(secondary.id);
|
||||
dispose(disposables);
|
||||
}));
|
||||
}
|
||||
|
||||
private updateWebviewViewStates(activeEditorInput: IEditorInput | undefined) {
|
||||
if (!this._webviewInputs.size) {
|
||||
return;
|
||||
@@ -324,7 +311,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function reviveWebviewIcon(
|
||||
value: { light: UriComponents, dark: UriComponents; } | undefined
|
||||
): WebviewIcons | undefined {
|
||||
@@ -333,3 +319,9 @@ function reviveWebviewIcon(
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function reviveWebviewOptions(panelOptions: extHostProtocol.IWebviewPanelOptions): WebviewOptions {
|
||||
return {
|
||||
enableFindWidget: panelOptions.enableFindWidget,
|
||||
retainContextWhenHidden: panelOptions.retainContextWhenHidden,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc
|
||||
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
|
||||
dispose(this._webviewViewProviders.values());
|
||||
@@ -55,7 +55,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc
|
||||
public $registerWebviewViewProvider(
|
||||
extensionData: extHostProtocol.WebviewExtensionDescription,
|
||||
viewType: string,
|
||||
options?: { retainContextWhenHidden?: boolean }
|
||||
options: { retainContextWhenHidden?: boolean, serializeBuffersForPostMessage: boolean }
|
||||
): void {
|
||||
if (this._webviewViewProviders.has(viewType)) {
|
||||
throw new Error(`View provider for ${viewType} already registered`);
|
||||
@@ -68,7 +68,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc
|
||||
const handle = webviewView.webview.id;
|
||||
|
||||
this._webviewViews.set(handle, webviewView);
|
||||
this.mainThreadWebviews.addWebview(handle, webviewView.webview);
|
||||
this.mainThreadWebviews.addWebview(handle, webviewView.webview, { serializeBuffersForPostMessage: options.serializeBuffersForPostMessage });
|
||||
|
||||
let state = undefined;
|
||||
if (webviewView.webview.state) {
|
||||
|
||||
@@ -3,19 +3,20 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWebviewOptions } from 'vs/editor/common/modes';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { Webview, WebviewExtensionDescription, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInputOptions } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService';
|
||||
import { serializeMessage } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { deserializeWebviewMessage } from 'vs/workbench/api/common/extHostWebviewMessaging';
|
||||
import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
|
||||
export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape {
|
||||
|
||||
@@ -41,9 +42,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews);
|
||||
}
|
||||
|
||||
public addWebview(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay): void {
|
||||
public addWebview(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay, options: { serializeBuffersForPostMessage: boolean }): void {
|
||||
if (this._webviews.has(handle)) {
|
||||
throw new Error('Webview already registered');
|
||||
}
|
||||
|
||||
this._webviews.set(handle, webview);
|
||||
this.hookupWebviewEventDelegate(handle, webview);
|
||||
this.hookupWebviewEventDelegate(handle, webview, options);
|
||||
}
|
||||
|
||||
public $setHtml(handle: extHostProtocol.WebviewHandle, value: string): void {
|
||||
@@ -51,22 +56,28 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
webview.html = value;
|
||||
}
|
||||
|
||||
public $setOptions(handle: extHostProtocol.WebviewHandle, options: IWebviewOptions): void {
|
||||
public $setOptions(handle: extHostProtocol.WebviewHandle, options: extHostProtocol.IWebviewOptions): void {
|
||||
const webview = this.getWebview(handle);
|
||||
webview.contentOptions = reviveWebviewOptions(options);
|
||||
webview.contentOptions = reviveWebviewContentOptions(options);
|
||||
}
|
||||
|
||||
public async $postMessage(handle: extHostProtocol.WebviewHandle, message: any): Promise<boolean> {
|
||||
public async $postMessage(handle: extHostProtocol.WebviewHandle, jsonMessage: string, ...buffers: VSBuffer[]): Promise<boolean> {
|
||||
const webview = this.getWebview(handle);
|
||||
webview.postMessage(message);
|
||||
const { message, arrayBuffers } = deserializeWebviewMessage(jsonMessage, buffers);
|
||||
webview.postMessage(message, arrayBuffers);
|
||||
return true;
|
||||
}
|
||||
|
||||
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay) {
|
||||
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay, options: { serializeBuffersForPostMessage: boolean }) {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
disposables.add(webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri)));
|
||||
disposables.add(webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); }));
|
||||
|
||||
disposables.add(webview.onMessage((message) => {
|
||||
const serialized = serializeMessage(message.message, options);
|
||||
this._proxy.$onMessage(handle, serialized.message, ...serialized.buffers);
|
||||
}));
|
||||
|
||||
disposables.add(webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value)));
|
||||
|
||||
disposables.add(webview.onDidDispose(() => {
|
||||
@@ -116,10 +127,11 @@ export function reviveWebviewExtension(extensionData: extHostProtocol.WebviewExt
|
||||
return { id: extensionData.id, location: URI.revive(extensionData.location) };
|
||||
}
|
||||
|
||||
export function reviveWebviewOptions(options: IWebviewOptions): WebviewInputOptions {
|
||||
export function reviveWebviewContentOptions(webviewOptions: extHostProtocol.IWebviewOptions): WebviewContentOptions {
|
||||
return {
|
||||
...options,
|
||||
allowScripts: options.enableScripts,
|
||||
localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined,
|
||||
allowScripts: webviewOptions.enableScripts,
|
||||
enableCommandUris: webviewOptions.enableCommandUris,
|
||||
localResourceRoots: Array.isArray(webviewOptions.localResourceRoots) ? webviewOptions.localResourceRoots.map(r => URI.revive(r)) : undefined,
|
||||
portMapping: webviewOptions.portMapping,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 { WorkspaceTrustRequestOptions, IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } 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,22 @@ 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,
|
||||
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
|
||||
@IWorkspaceTrustRequestService private readonly _workspaceTrustRequestService: IWorkspaceTrustRequestService
|
||||
) {
|
||||
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.isWorkspaceTrusted());
|
||||
} else {
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace)));
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace), this.isWorkspaceTrusted()));
|
||||
}
|
||||
this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
this._contextService.onDidChangeWorkbenchState(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
this._workspaceTrustManagementService.onDidChangeTrust(this._onDidGrantWorkspaceTrust, this, this._toDispose);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -202,4 +206,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
$resolveProxy(url: string): Promise<string | undefined> {
|
||||
return this._requestService.resolveProxy(url);
|
||||
}
|
||||
|
||||
// --- trust ---
|
||||
|
||||
$requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean | undefined> {
|
||||
return this._workspaceTrustRequestService.requestWorkspaceTrust(options);
|
||||
}
|
||||
|
||||
private isWorkspaceTrusted(): boolean {
|
||||
return this._workspaceTrustManagementService.isWorkpaceTrusted();
|
||||
}
|
||||
|
||||
private _onDidGrantWorkspaceTrust(): void {
|
||||
this._proxy.$onDidGrantWorkspaceTrust();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ const viewDescriptor: IJSONSchema = {
|
||||
type: 'string'
|
||||
},
|
||||
contextualTitle: {
|
||||
description: localize('vscode.extension.contributes.view.contextualTitle', "Human-readable context for when the view is moved out of its original location. By default, the view's container name will be used. Will be shown"),
|
||||
description: localize('vscode.extension.contributes.view.contextualTitle', "Human-readable context for when the view is moved out of its original location. By default, the view's container name will be used."),
|
||||
type: 'string'
|
||||
},
|
||||
visibility: {
|
||||
@@ -157,6 +157,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`.'),
|
||||
@@ -311,8 +312,8 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
const removedExtensions: Set<string> = extensionPoints.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set<string>());
|
||||
for (const viewContainer of viewContainersRegistry.all) {
|
||||
if (viewContainer.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(viewContainer.extensionId))) {
|
||||
// move only those views that do not belong to the removed extension
|
||||
const views = this.viewsRegistry.getViews(viewContainer).filter(view => !removedExtensions.has(ExtensionIdentifier.toKey((view as ICustomViewDescriptor).extensionId)));
|
||||
// move all views in this container into default view container
|
||||
const views = this.viewsRegistry.getViews(viewContainer);
|
||||
if (views.length) {
|
||||
this.viewsRegistry.moveViews(views, this.getDefaultViewContainer());
|
||||
}
|
||||
|
||||
@@ -32,7 +32,11 @@ function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) =>
|
||||
|
||||
interface INewWindowAPICommandOptions {
|
||||
reuseWindow?: boolean;
|
||||
remoteAuthority?: string;
|
||||
/**
|
||||
* If set, defines the remoteAuthority of the new window. `null` will open a local window.
|
||||
* If not set, defaults to remoteAuthority of the current window.
|
||||
*/
|
||||
remoteAuthority?: string | null;
|
||||
}
|
||||
|
||||
export class NewWindowAPICommand {
|
||||
@@ -95,6 +99,7 @@ interface RecentEntry {
|
||||
uri: URI;
|
||||
type: 'workspace' | 'folder' | 'file';
|
||||
label?: string;
|
||||
remoteAuthority?: string;
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async function (accessor: ServicesAccessor, recentEntry: RecentEntry) {
|
||||
@@ -102,13 +107,14 @@ CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async functio
|
||||
let recent: IRecent | undefined = undefined;
|
||||
const uri = recentEntry.uri;
|
||||
const label = recentEntry.label;
|
||||
const remoteAuthority = recentEntry.remoteAuthority;
|
||||
if (recentEntry.type === 'workspace') {
|
||||
const workspace = await workspacesService.getWorkspaceIdentifier(uri);
|
||||
recent = { workspace, label };
|
||||
recent = { workspace, label, remoteAuthority };
|
||||
} else if (recentEntry.type === 'folder') {
|
||||
recent = { folderUri: uri, label };
|
||||
recent = { folderUri: uri, label, remoteAuthority };
|
||||
} else {
|
||||
recent = { fileUri: uri, label };
|
||||
recent = { fileUri: uri, label, remoteAuthority };
|
||||
}
|
||||
return workspacesService.addRecentlyOpened([recent]);
|
||||
});
|
||||
|
||||
@@ -170,7 +170,7 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => {
|
||||
validateProperties(configuration, extension);
|
||||
|
||||
configuration.id = node.id || extension.description.identifier.value;
|
||||
configuration.extensionInfo = { id: extension.description.identifier.value };
|
||||
configuration.extensionInfo = { id: extension.description.identifier.value, restrictedConfigurations: extension.description.capabilities?.untrustedWorkspaces?.supported === 'limited' ? extension.description.capabilities?.untrustedWorkspaces.restrictedConfigurations : undefined };
|
||||
configuration.title = configuration.title || extension.description.displayName || extension.description.identifier.value;
|
||||
configurations.push(configuration);
|
||||
return configurations;
|
||||
@@ -181,10 +181,10 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => {
|
||||
for (let extension of added) {
|
||||
const configurations: IConfigurationNode[] = [];
|
||||
const value = <IConfigurationNode | IConfigurationNode[]>extension.value;
|
||||
if (!Array.isArray(value)) {
|
||||
configurations.push(...handleConfiguration(value, extension));
|
||||
} else {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(v => configurations.push(...handleConfiguration(v, extension)));
|
||||
} else {
|
||||
configurations.push(...handleConfiguration(value, extension));
|
||||
}
|
||||
extensionConfigurations.set(ExtensionIdentifier.toKey(extension.description.identifier), configurations);
|
||||
addedConfigurations.push(...configurations);
|
||||
@@ -213,7 +213,7 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten
|
||||
const propertyConfiguration = properties[key];
|
||||
if (!isObject(propertyConfiguration)) {
|
||||
delete properties[key];
|
||||
extension.collector.error(nls.localize('invalid.property', "'configuration.property' must be an object"));
|
||||
extension.collector.error(nls.localize('invalid.property', "configuration.properties property '{0}' must be an object", key));
|
||||
continue;
|
||||
}
|
||||
if (propertyConfiguration.scope) {
|
||||
@@ -320,7 +320,7 @@ jsonRegistry.registerSchema('vscode://schemas/workspaceConfig', {
|
||||
'remoteAuthority': {
|
||||
type: 'string',
|
||||
doNotSuggest: true,
|
||||
description: nls.localize('workspaceConfig.remoteAuthority', "The remote server where the workspace is located. Only used by unsaved remote workspaces."),
|
||||
description: nls.localize('workspaceConfig.remoteAuthority', "The remote server where the workspace is located."),
|
||||
}
|
||||
},
|
||||
errorMessage: nls.localize('unknownWorkspaceProperty', "Unknown workspace configuration property")
|
||||
|
||||
@@ -7,7 +7,6 @@ import * as nls from 'vs/nls';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
|
||||
@@ -15,7 +14,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';
|
||||
@@ -27,7 +26,7 @@ import { ExtHostDocumentContentProvider } from 'vs/workbench/api/common/extHostD
|
||||
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { Extension, IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem';
|
||||
import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService';
|
||||
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
|
||||
@@ -35,7 +34,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';
|
||||
@@ -52,8 +51,7 @@ import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/ser
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { originalFSPath } from 'vs/base/common/resources';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
|
||||
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
|
||||
@@ -85,6 +83,10 @@ import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
|
||||
import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
|
||||
import { IExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
|
||||
import { ExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry';
|
||||
import { ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels';
|
||||
import { RemoteTrustOption } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
|
||||
@@ -101,6 +103,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostConsumerFileSystem = accessor.get(IExtHostConsumerFileSystem);
|
||||
const extensionService = accessor.get(IExtHostExtensionService);
|
||||
const extHostWorkspace = accessor.get(IExtHostWorkspace);
|
||||
const extHostTelemetry = accessor.get(IExtHostTelemetry);
|
||||
const extHostConfiguration = accessor.get(IExtHostConfiguration);
|
||||
const uriTransformer = accessor.get(IURITransformerService);
|
||||
const rpcProtocol = accessor.get(IExtHostRpcService);
|
||||
@@ -122,6 +125,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService);
|
||||
rpcProtocol.set(ExtHostContext.ExtHostWindow, extHostWindow);
|
||||
rpcProtocol.set(ExtHostContext.ExtHostSecretState, extHostSecretState);
|
||||
rpcProtocol.set(ExtHostContext.ExtHostTelemetry, extHostTelemetry);
|
||||
|
||||
// automatically create and register addressable instances
|
||||
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, accessor.get(IExtHostDecorations));
|
||||
@@ -139,7 +143,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
|
||||
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadBulkEdits)));
|
||||
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extHostLogService, extensionStoragePaths));
|
||||
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, extHostLogService, extensionStoragePaths));
|
||||
const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook));
|
||||
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
|
||||
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment));
|
||||
@@ -147,7 +152,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)));
|
||||
@@ -170,7 +175,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
rpcProtocol.assertRegistered(expected);
|
||||
|
||||
// Other instances
|
||||
const extHostBulkEdits = new ExtHostBulkEdits(rpcProtocol, extHostDocumentsAndEditors, extHostNotebook);
|
||||
const extHostBulkEdits = new ExtHostBulkEdits(rpcProtocol, extHostDocumentsAndEditors);
|
||||
const extHostClipboard = new ExtHostClipboard(rpcProtocol);
|
||||
const extHostMessageService = new ExtHostMessageService(rpcProtocol, extHostLogService);
|
||||
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
|
||||
@@ -201,10 +206,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);
|
||||
}
|
||||
}
|
||||
@@ -220,7 +226,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> {
|
||||
@@ -233,7 +238,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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -295,6 +300,16 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
get shell() {
|
||||
return extHostTerminalService.getDefaultShell(false, configProvider);
|
||||
},
|
||||
get isTelemetryEnabled() {
|
||||
return extHostTelemetry.getTelemetryEnabled();
|
||||
},
|
||||
get onDidChangeTelemetryEnabled(): Event<boolean> {
|
||||
return extHostTelemetry.onDidChangeTelemetryEnabled;
|
||||
},
|
||||
get isNewAppInstall() {
|
||||
const installAge = Date.now() - new Date(initData.telemetryInfo.firstSessionDate).getTime();
|
||||
return isNaN(installAge) ? false : installAge < 1000 * 60 * 60 * 24; // install age is less than a day
|
||||
},
|
||||
openExternal(uri: URI, options?: { allowContributedOpeners?: boolean | string; }) {
|
||||
return extHostWindow.openUri(uri, {
|
||||
allowTunneling: !!initData.remote.authority,
|
||||
@@ -325,9 +340,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
: extHostTypes.ExtensionKind.UI;
|
||||
|
||||
const test: typeof vscode.test = {
|
||||
registerTestProvider(provider) {
|
||||
registerTestController(provider) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.registerTestProvider(provider);
|
||||
return extHostTesting.registerTestController(extension.identifier.value, provider);
|
||||
},
|
||||
createDocumentTestObserver(document) {
|
||||
checkProposedApiEnabled(extension);
|
||||
@@ -341,26 +356,36 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.runTests(provider);
|
||||
},
|
||||
createTestItem<T>(options: vscode.TestItemOptions, data?: T) {
|
||||
return new extHostTypes.TestItemImpl(options.id, options.label, options.uri, data);
|
||||
},
|
||||
createTestRun(request, name, persist) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.createTestRun(extension.identifier.value, request, name, persist);
|
||||
},
|
||||
get onDidChangeTestResults() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.onLastResultsChanged;
|
||||
return extHostTesting.onResultsChanged;
|
||||
},
|
||||
get testResults() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.lastResults;
|
||||
return extHostTesting.results;
|
||||
},
|
||||
};
|
||||
|
||||
// todo@connor4312: backwards compatibility for a short period
|
||||
(test as any).createTestRunTask = test.createTestRun;
|
||||
|
||||
// namespace: extensions
|
||||
const extensions: typeof vscode.extensions = {
|
||||
getExtension(extensionId: string): Extension<any> | undefined {
|
||||
getExtension(extensionId: string): vscode.Extension<any> | undefined {
|
||||
const desc = extensionRegistry.getExtensionDescription(extensionId);
|
||||
if (desc) {
|
||||
return new Extension(extensionService, extension.identifier, desc, extensionKind);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
get all(): Extension<any>[] {
|
||||
get all(): vscode.Extension<any>[] {
|
||||
return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, extension.identifier, desc, extensionKind));
|
||||
},
|
||||
get onDidChange() {
|
||||
@@ -412,6 +437,9 @@ 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 {
|
||||
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);
|
||||
},
|
||||
@@ -571,7 +599,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;
|
||||
@@ -619,6 +647,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
if ('pty' in nameOrOptions) {
|
||||
return extHostTerminalService.createExtensionTerminal(nameOrOptions);
|
||||
}
|
||||
if (nameOrOptions.message) {
|
||||
checkProposedApiEnabled(extension);
|
||||
}
|
||||
if (nameOrOptions.icon) {
|
||||
checkProposedApiEnabled(extension);
|
||||
}
|
||||
return extHostTerminalService.createTerminalFromOptions(nameOrOptions);
|
||||
}
|
||||
return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs);
|
||||
@@ -687,9 +721,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables);
|
||||
},
|
||||
showNotebookDocument(document, options?) {
|
||||
showNotebookDocument(uriOrDocument, options?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.showNotebookDocument(document, options);
|
||||
return extHostNotebook.showNotebookDocument(uriOrDocument, options);
|
||||
},
|
||||
registerExternalUriOpener(id: string, opener: vscode.ExternalUriOpener, metadata: vscode.ExternalUriOpenerMetadata) {
|
||||
checkProposedApiEnabled(extension);
|
||||
@@ -885,11 +919,24 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
onDidChangeTunnels: (listener, thisArg?, disposables?) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables);
|
||||
|
||||
},
|
||||
registerPortAttributesProvider: (portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: vscode.PortAttributesProvider) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.registerPortsAttributesProvider(portSelector, provider);
|
||||
},
|
||||
registerTimelineProvider: (scheme: string | string[], provider: vscode.TimelineProvider) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTimeline.registerTimelineProvider(scheme, provider, extension.identifier, extHostCommands.converter);
|
||||
},
|
||||
get isTrusted() {
|
||||
return extHostWorkspace.trusted;
|
||||
},
|
||||
requestWorkspaceTrust: (options?: vscode.WorkspaceTrustRequestOptions) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostWorkspace.requestWorkspaceTrust(options);
|
||||
},
|
||||
onDidGrantWorkspaceTrust: (listener, thisArgs?, disposables?) => {
|
||||
return extHostWorkspace.onDidGrantWorkspaceTrust(listener, thisArgs, disposables);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1009,18 +1056,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
};
|
||||
|
||||
// namespace: notebook
|
||||
const notebook: (typeof vscode.notebook & {
|
||||
// to ensure that notebook extensions not break before they update APIs.
|
||||
visibleNotebookEditors: vscode.NotebookEditor[];
|
||||
onDidChangeVisibleNotebookEditors: Event<vscode.NotebookEditor[]>;
|
||||
activeNotebookEditor: vscode.NotebookEditor | undefined;
|
||||
onDidChangeActiveNotebookEditor: Event<vscode.NotebookEditor | undefined>;
|
||||
onDidChangeNotebookEditorSelection: Event<vscode.NotebookEditorSelectionChangeEvent>;
|
||||
onDidChangeNotebookEditorVisibleRanges: Event<vscode.NotebookEditorVisibleRangesChangeEvent>;
|
||||
}) = {
|
||||
openNotebookDocument: (uriComponents, viewType) => {
|
||||
const notebook: typeof vscode.notebook = {
|
||||
openNotebookDocument: (uriComponents) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.openNotebookDocument(uriComponents, viewType);
|
||||
return extHostNotebook.openNotebookDocument(uriComponents);
|
||||
},
|
||||
get onDidOpenNotebookDocument(): Event<vscode.NotebookDocument> {
|
||||
checkProposedApiEnabled(extension);
|
||||
@@ -1036,43 +1075,24 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
},
|
||||
get notebookDocuments(): vscode.NotebookDocument[] {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.notebookDocuments.map(d => d.notebookDocument);
|
||||
return extHostNotebook.notebookDocuments.map(d => d.apiNotebook);
|
||||
},
|
||||
get visibleNotebookEditors(): vscode.NotebookEditor[] {
|
||||
registerNotebookSerializer(viewType, serializer, options) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.visibleNotebookEditors;
|
||||
return extHostNotebook.registerNotebookSerializer(extension, viewType, serializer, options);
|
||||
},
|
||||
get onDidChangeVisibleNotebookEditors() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeVisibleNotebookEditors;
|
||||
},
|
||||
get onDidChangeActiveNotebookKernel() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeActiveNotebookKernel;
|
||||
},
|
||||
registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider, options?: {
|
||||
transientOutputs: boolean;
|
||||
transientMetadata: { [K in keyof vscode.NotebookCellMetadata]?: boolean }
|
||||
}) => {
|
||||
registerNotebookContentProvider: (viewType, provider, options) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options);
|
||||
},
|
||||
registerNotebookKernelProvider: (selector: vscode.NotebookDocumentFilter, provider: vscode.NotebookKernelProvider) => {
|
||||
registerNotebookCellStatusBarItemProvider: (selector: vscode.NotebookSelector, provider: vscode.NotebookCellStatusBarItemProvider) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.registerNotebookKernelProvider(extension, selector, provider);
|
||||
return extHostNotebook.registerNotebookCellStatusBarItemProvider(extension, selector, provider);
|
||||
},
|
||||
createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.createNotebookEditorDecorationType(options);
|
||||
},
|
||||
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.activeNotebookEditor;
|
||||
},
|
||||
onDidChangeActiveNotebookEditor(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeActiveNotebookEditor(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeNotebookDocumentMetadata(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookDocumentMetadata(listener, thisArgs, disposables);
|
||||
@@ -1081,22 +1101,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookCells(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeNotebookEditorSelection(listener, thisArgs?, disposables?) {
|
||||
onDidChangeCellExecutionState(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookEditorSelection(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeNotebookEditorVisibleRanges(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables);
|
||||
return extHostNotebook.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeCellOutputs(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeCellOutputs(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeCellLanguage(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeCellLanguage(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeCellMetadata(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeCellMetadata(listener, thisArgs, disposables);
|
||||
@@ -1105,9 +1117,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return new ExtHostNotebookConcatDocument(extHostNotebook, extHostDocuments, notebook, selector);
|
||||
},
|
||||
createCellStatusBarItem(cell: vscode.NotebookCell, alignment?: vscode.NotebookCellStatusBarAlignment, priority?: number): vscode.NotebookCellStatusBarItem {
|
||||
createNotebookCellExecutionTask(uri: vscode.Uri, index: number, kernelId: string): vscode.NotebookCellExecutionTask | undefined {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.createNotebookCellStatusBarItemInternal(cell, alignment, priority);
|
||||
return extHostNotebook.createNotebookCellExecution(uri, index, kernelId);
|
||||
},
|
||||
createNotebookController(id, viewType, label, executeHandler, preloads) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebookKernels.createNotebookController(extension, id, viewType, label, executeHandler, preloads);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1135,9 +1151,10 @@ 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,
|
||||
CodeActionTriggerKind: extHostTypes.CodeActionTriggerKind,
|
||||
CodeLens: extHostTypes.CodeLens,
|
||||
Color: extHostTypes.Color,
|
||||
ColorInformation: extHostTypes.ColorInformation,
|
||||
@@ -1171,6 +1188,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,
|
||||
@@ -1184,11 +1204,11 @@ 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,
|
||||
ParameterInformation: extHostTypes.ParameterInformation,
|
||||
PortAutoForwardAction: extHostTypes.PortAutoForwardAction,
|
||||
Position: extHostTypes.Position,
|
||||
ProcessExecution: extHostTypes.ProcessExecution,
|
||||
ProgressLocation: extHostTypes.ProgressLocation,
|
||||
@@ -1234,113 +1254,34 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
ViewColumn: extHostTypes.ViewColumn,
|
||||
WorkspaceEdit: extHostTypes.WorkspaceEdit,
|
||||
// proposed api types
|
||||
get RemoteAuthorityResolverError() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.RemoteAuthorityResolverError;
|
||||
},
|
||||
get ResolvedAuthority() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.ResolvedAuthority;
|
||||
},
|
||||
get SourceControlInputBoxValidationType() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.SourceControlInputBoxValidationType;
|
||||
},
|
||||
get ExtensionRuntime() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.ExtensionRuntime;
|
||||
},
|
||||
get TimelineItem() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TimelineItem;
|
||||
},
|
||||
get CellKind() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.CellKind;
|
||||
},
|
||||
get CellOutputKind() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.CellOutputKind;
|
||||
},
|
||||
get NotebookCellRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellRunState;
|
||||
},
|
||||
get NotebookRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookRunState;
|
||||
},
|
||||
get NotebookCellStatusBarAlignment() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellStatusBarAlignment;
|
||||
},
|
||||
get NotebookEditorRevealType() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookEditorRevealType;
|
||||
},
|
||||
get NotebookCellOutput() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellOutput;
|
||||
},
|
||||
get NotebookCellOutputItem() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellOutputItem;
|
||||
},
|
||||
get LinkedEditingRanges() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.LinkedEditingRanges;
|
||||
},
|
||||
get TestRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TestRunState;
|
||||
},
|
||||
get TestMessageSeverity() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TestMessageSeverity;
|
||||
},
|
||||
get TestState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TestState;
|
||||
},
|
||||
InlineHint: extHostTypes.InlineHint,
|
||||
InlineHintKind: extHostTypes.InlineHintKind,
|
||||
RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError,
|
||||
RemoteTrustOption: RemoteTrustOption,
|
||||
ResolvedAuthority: extHostTypes.ResolvedAuthority,
|
||||
SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType,
|
||||
ExtensionRuntime: extHostTypes.ExtensionRuntime,
|
||||
TimelineItem: extHostTypes.TimelineItem,
|
||||
NotebookRange: extHostTypes.NotebookRange,
|
||||
NotebookCellKind: extHostTypes.NotebookCellKind,
|
||||
NotebookCellExecutionState: extHostTypes.NotebookCellExecutionState,
|
||||
NotebookDocumentMetadata: extHostTypes.NotebookDocumentMetadata,
|
||||
NotebookCellMetadata: extHostTypes.NotebookCellMetadata,
|
||||
NotebookCellData: extHostTypes.NotebookCellData,
|
||||
NotebookData: extHostTypes.NotebookData,
|
||||
NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment,
|
||||
NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType,
|
||||
NotebookCellOutput: extHostTypes.NotebookCellOutput,
|
||||
NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem,
|
||||
NotebookCellStatusBarItem: extHostTypes.NotebookCellStatusBarItem,
|
||||
NotebookControllerAffinity: extHostTypes.NotebookControllerAffinity,
|
||||
LinkedEditingRanges: extHostTypes.LinkedEditingRanges,
|
||||
TestItemStatus: extHostTypes.TestItemStatus,
|
||||
TestResultState: extHostTypes.TestResultState,
|
||||
TestMessage: extHostTypes.TestMessage,
|
||||
TextSearchCompleteMessageType: TextSearchCompleteMessageType,
|
||||
TestMessageSeverity: extHostTypes.TestMessageSeverity,
|
||||
WorkspaceTrustState: extHostTypes.WorkspaceTrustState
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
class Extension<T> implements vscode.Extension<T> {
|
||||
|
||||
#extensionService: IExtHostExtensionService;
|
||||
#originExtensionId: ExtensionIdentifier;
|
||||
#identifier: ExtensionIdentifier;
|
||||
|
||||
readonly id: string;
|
||||
readonly extensionUri: URI;
|
||||
readonly extensionPath: string;
|
||||
readonly packageJSON: IExtensionDescription;
|
||||
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.id = description.identifier.value;
|
||||
this.extensionUri = description.extensionLocation;
|
||||
this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
|
||||
this.packageJSON = description;
|
||||
this.extensionKind = kind;
|
||||
}
|
||||
|
||||
get isActive(): boolean {
|
||||
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);
|
||||
}
|
||||
|
||||
activate(): Thenable<T> {
|
||||
return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { IExtHostWindow, ExtHostWindow } from 'vs/workbench/api/common/extHostWi
|
||||
import { IExtHostConsumerFileSystem, ExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
|
||||
import { IExtHostFileSystemInfo, ExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
import { IExtHostSecretState, ExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
|
||||
import { ExtHostTelemetry, IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry';
|
||||
|
||||
registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths);
|
||||
registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService);
|
||||
@@ -41,3 +42,4 @@ registerSingleton(IExtHostTunnelService, ExtHostTunnelService);
|
||||
registerSingleton(IExtHostWindow, ExtHostWindow);
|
||||
registerSingleton(IExtHostWorkspace, ExtHostWorkspace);
|
||||
registerSingleton(IExtHostSecretState, ExtHostSecretState);
|
||||
registerSingleton(IExtHostTelemetry, ExtHostTelemetry);
|
||||
|
||||
@@ -41,25 +41,30 @@ 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 { ActivationKind, MissingExtensionDependency, 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';
|
||||
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, ProvidedPortAttributes } 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, TransientCellMetadata, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation, INotebookCellStatusBarItem, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
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, TestResultState } 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, RunTestForProviderRequest, RunTestsRequest, TestIdWithSrc, TestsDiff, ISerializedTestResults, ITestMessage, ITestItem, ITestRunTask, ExtensionRunTestsRequest } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { WorkspaceTrustRequestOptions } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensions, ITerminalEnvironment, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
|
||||
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { NotebookSelector } from 'vs/workbench/contrib/notebook/common/notebookSelector';
|
||||
import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
|
||||
@@ -171,7 +176,7 @@ export interface MainThreadAuthenticationShape extends IDisposable {
|
||||
$ensureProvider(id: string): Promise<void>;
|
||||
$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>;
|
||||
$logout(providerId: string, sessionId: string): Promise<void>;
|
||||
$removeSession(providerId: string, sessionId: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadSecretStateShape extends IDisposable {
|
||||
@@ -288,7 +293,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;
|
||||
@@ -381,6 +386,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;
|
||||
@@ -454,12 +461,15 @@ export interface TerminalLaunchConfig {
|
||||
shellPath?: string;
|
||||
shellArgs?: string[] | string;
|
||||
cwd?: string | UriComponents;
|
||||
env?: { [key: string]: string | null; };
|
||||
env?: ITerminalEnvironment;
|
||||
icon?: string;
|
||||
initialText?: string;
|
||||
waitOnExit?: boolean;
|
||||
strictEnv?: boolean;
|
||||
hideFromUser?: boolean;
|
||||
isExtensionTerminal?: boolean;
|
||||
isExtensionCustomPtyTerminal?: boolean;
|
||||
isFeatureTerminal?: boolean;
|
||||
isExtensionOwnedTerminal?: boolean;
|
||||
}
|
||||
|
||||
export interface MainThreadTerminalServiceShape extends IDisposable {
|
||||
@@ -504,6 +514,8 @@ export interface BaseTransferQuickInput {
|
||||
|
||||
id: number;
|
||||
|
||||
title?: string;
|
||||
|
||||
type?: 'quickPick' | 'inputBox';
|
||||
|
||||
enabled?: boolean;
|
||||
@@ -558,6 +570,7 @@ export interface TransferInputBox extends BaseTransferQuickInput {
|
||||
}
|
||||
|
||||
export interface IInputBoxOptions {
|
||||
title?: string;
|
||||
value?: string;
|
||||
valueSelection?: [number, number];
|
||||
prompt?: string;
|
||||
@@ -592,11 +605,11 @@ export interface MainThreadTelemetryShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadEditorInsetsShape extends IDisposable {
|
||||
$createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void>;
|
||||
$createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void>;
|
||||
$disposeEditorInset(handle: number): void;
|
||||
|
||||
$setHtml(handle: number, value: string): void;
|
||||
$setOptions(handle: number, options: modes.IWebviewOptions): void;
|
||||
$setOptions(handle: number, options: IWebviewOptions): void;
|
||||
$postMessage(handle: number, value: any): Promise<boolean>;
|
||||
}
|
||||
|
||||
@@ -646,30 +659,87 @@ export enum WebviewEditorCapabilities {
|
||||
SupportsHotExit,
|
||||
}
|
||||
|
||||
export interface IWebviewPortMapping {
|
||||
readonly webviewPort: number;
|
||||
readonly extensionHostPort: number;
|
||||
}
|
||||
|
||||
export interface IWebviewOptions {
|
||||
readonly enableScripts?: boolean;
|
||||
readonly enableCommandUris?: boolean;
|
||||
readonly localResourceRoots?: ReadonlyArray<UriComponents>;
|
||||
readonly portMapping?: ReadonlyArray<IWebviewPortMapping>;
|
||||
}
|
||||
|
||||
export interface IWebviewPanelOptions {
|
||||
readonly enableFindWidget?: boolean;
|
||||
readonly retainContextWhenHidden?: boolean;
|
||||
}
|
||||
|
||||
export interface CustomTextEditorCapabilities {
|
||||
readonly supportsMove?: boolean;
|
||||
}
|
||||
|
||||
export const enum WebviewMessageArrayBufferViewType {
|
||||
Int8Array = 1,
|
||||
Uint8Array = 2,
|
||||
Uint8ClampedArray = 3,
|
||||
Int16Array = 4,
|
||||
Uint16Array = 5,
|
||||
Int32Array = 6,
|
||||
Uint32Array = 7,
|
||||
Float32Array = 8,
|
||||
Float64Array = 9,
|
||||
BigInt64Array = 10,
|
||||
BigUint64Array = 11,
|
||||
}
|
||||
|
||||
export interface WebviewMessageArrayBufferReference {
|
||||
readonly $$vscode_array_buffer_reference$$: true,
|
||||
|
||||
readonly index: number;
|
||||
|
||||
/**
|
||||
* Tracks if the reference is to a view instead of directly to an ArrayBuffer.
|
||||
*/
|
||||
readonly view?: {
|
||||
readonly type: WebviewMessageArrayBufferViewType;
|
||||
readonly byteLength: number;
|
||||
readonly byteOffset: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewsShape extends IDisposable {
|
||||
$setHtml(handle: WebviewHandle, value: string): void;
|
||||
$setOptions(handle: WebviewHandle, options: modes.IWebviewOptions): void;
|
||||
$postMessage(handle: WebviewHandle, value: any): Promise<boolean>
|
||||
$setOptions(handle: WebviewHandle, options: IWebviewOptions): void;
|
||||
$postMessage(handle: WebviewHandle, value: any, ...buffers: VSBuffer[]): Promise<boolean>
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewPanelsShape extends IDisposable {
|
||||
$createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void;
|
||||
$createWebviewPanel(
|
||||
extension: WebviewExtensionDescription,
|
||||
handle: WebviewHandle,
|
||||
viewType: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: IWebviewOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
serializeBuffersForPostMessage: boolean;
|
||||
},
|
||||
showOptions: WebviewPanelShowOptions,
|
||||
): void;
|
||||
$disposeWebview(handle: WebviewHandle): void;
|
||||
$reveal(handle: WebviewHandle, showOptions: WebviewPanelShowOptions): void;
|
||||
$setTitle(handle: WebviewHandle, value: string): void;
|
||||
$setIconPath(handle: WebviewHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void;
|
||||
|
||||
$registerSerializer(viewType: string): void;
|
||||
$registerSerializer(viewType: string, options: { serializeBuffersForPostMessage: boolean }): void;
|
||||
$unregisterSerializer(viewType: string): void;
|
||||
}
|
||||
|
||||
export interface MainThreadCustomEditorsShape extends IDisposable {
|
||||
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
|
||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void;
|
||||
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities, serializeBuffersForPostMessage: boolean): void;
|
||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean, serializeBuffersForPostMessage: boolean): void;
|
||||
$unregisterEditorProvider(viewType: string): void;
|
||||
|
||||
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
|
||||
@@ -677,7 +747,7 @@ export interface MainThreadCustomEditorsShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewViewsShape extends IDisposable {
|
||||
$registerWebviewViewProvider(extension: WebviewExtensionDescription, viewType: string, options?: { retainContextWhenHidden?: boolean }): void;
|
||||
$registerWebviewViewProvider(extension: WebviewExtensionDescription, viewType: string, options: { retainContextWhenHidden?: boolean, serializeBuffersForPostMessage: boolean }): void;
|
||||
$unregisterWebviewViewProvider(viewType: string): void;
|
||||
|
||||
$setWebviewViewTitle(handle: WebviewHandle, value: string | undefined): void;
|
||||
@@ -695,19 +765,40 @@ export interface WebviewPanelViewStateData {
|
||||
}
|
||||
|
||||
export interface ExtHostWebviewsShape {
|
||||
$onMessage(handle: WebviewHandle, message: any): void;
|
||||
$onMessage(handle: WebviewHandle, jsonSerializedMessage: string, ...buffers: VSBuffer[]): void;
|
||||
$onMissingCsp(handle: WebviewHandle, extensionId: string): void;
|
||||
}
|
||||
|
||||
export interface ExtHostWebviewPanelsShape {
|
||||
$onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void;
|
||||
$onDidDisposeWebviewPanel(handle: WebviewHandle): Promise<void>;
|
||||
$deserializeWebviewPanel(newWebviewHandle: WebviewHandle, viewType: string, title: string, state: any, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$deserializeWebviewPanel(
|
||||
newWebviewHandle: WebviewHandle,
|
||||
viewType: string,
|
||||
initData: {
|
||||
title: string;
|
||||
state: any;
|
||||
webviewOptions: IWebviewOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ExtHostCustomEditorsShape {
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewHandle, viewType: string, title: string, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise<void>;
|
||||
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
|
||||
$resolveWebviewEditor(
|
||||
resource: UriComponents,
|
||||
newWebviewHandle: WebviewHandle,
|
||||
viewType: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: IWebviewOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn,
|
||||
cancellation: CancellationToken
|
||||
): Promise<void>;
|
||||
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
|
||||
$disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void>;
|
||||
|
||||
$undo(resource: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void>;
|
||||
@@ -748,7 +839,7 @@ export interface ICellDto {
|
||||
source: string[];
|
||||
language: string;
|
||||
cellKind: CellKind;
|
||||
outputs: IProcessedOutput[];
|
||||
outputs: IOutputDto[];
|
||||
metadata?: NotebookCellMetadata;
|
||||
}
|
||||
|
||||
@@ -761,7 +852,7 @@ export type NotebookCellsSplice = [
|
||||
export type NotebookCellOutputsSplice = [
|
||||
number /* start */,
|
||||
number /* delete count */,
|
||||
IProcessedOutput[]
|
||||
IOutputDto[]
|
||||
];
|
||||
|
||||
export enum NotebookEditorRevealType {
|
||||
@@ -775,33 +866,69 @@ export interface INotebookDocumentShowOptions {
|
||||
position?: EditorGroupColumn;
|
||||
preserveFocus?: boolean;
|
||||
pinned?: boolean;
|
||||
selection?: ICellRange;
|
||||
selections?: ICellRange[];
|
||||
}
|
||||
|
||||
export type INotebookCellStatusBarEntryDto = Dto<INotebookCellStatusBarEntry>;
|
||||
export type INotebookCellStatusBarEntryDto = Dto<INotebookCellStatusBarItem>;
|
||||
|
||||
export interface INotebookCellStatusBarListDto {
|
||||
items: INotebookCellStatusBarEntryDto[];
|
||||
cacheId: number;
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookShape extends IDisposable {
|
||||
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, options: {
|
||||
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, options: {
|
||||
transientOutputs: boolean;
|
||||
transientMetadata: TransientMetadata;
|
||||
transientCellMetadata: TransientCellMetadata;
|
||||
transientDocumentMetadata: TransientDocumentMetadata;
|
||||
viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; };
|
||||
}): Promise<void>;
|
||||
$updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise<void>;
|
||||
$updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; }): Promise<void>;
|
||||
$unregisterNotebookProvider(viewType: string): Promise<void>;
|
||||
$registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void>;
|
||||
$unregisterNotebookKernelProvider(handle: number): Promise<void>;
|
||||
$onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void;
|
||||
$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>;
|
||||
|
||||
$registerNotebookSerializer(handle: number, extension: NotebookExtensionDescription, viewType: string, options: TransientOptions): void;
|
||||
$unregisterNotebookSerializer(handle: number): void;
|
||||
|
||||
$registerNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined, selector: NotebookSelector): Promise<void>;
|
||||
$unregisterNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined): Promise<void>;
|
||||
$emitCellStatusBarEvent(eventHandle: number): void;
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookEditorsShape extends IDisposable {
|
||||
$tryShowNotebookDocument(uriComponents: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string>;
|
||||
$tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise<void>;
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void;
|
||||
$removeNotebookEditorDecorationType(key: string): void;
|
||||
$trySetDecorations(id: string, range: ICellRange, decorationKey: string): void;
|
||||
$tryApplyEdits(editorId: string, modelVersionId: number, cellEdits: ICellEditOperation[]): Promise<boolean>
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookDocumentsShape extends IDisposable {
|
||||
$tryOpenDocument(uriComponents: UriComponents): Promise<UriComponents>;
|
||||
$trySaveDocument(uri: UriComponents): Promise<boolean>;
|
||||
$applyEdits(resource: UriComponents, edits: IImmediateCellEditOperation[], computeUndoRedo?: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
export interface INotebookKernelDto2 {
|
||||
id: string;
|
||||
viewType: string;
|
||||
extensionId: ExtensionIdentifier;
|
||||
extensionLocation: UriComponents;
|
||||
label: string;
|
||||
detail?: string;
|
||||
description?: string;
|
||||
supportedLanguages?: string[];
|
||||
supportsInterrupt?: boolean;
|
||||
hasExecutionOrder?: boolean;
|
||||
preloads?: { uri: UriComponents; provides: string[] }[];
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookKernelsShape extends IDisposable {
|
||||
$postMessage(handle: number, editorId: string | undefined, message: any): Promise<boolean>;
|
||||
$addKernel(handle: number, data: INotebookKernelDto2): Promise<void>;
|
||||
$updateKernel(handle: number, data: Partial<INotebookKernelDto2>): void;
|
||||
$removeKernel(handle: number): void;
|
||||
$updateNotebookPriority(handle: number, uri: UriComponents, value: number | undefined): void;
|
||||
}
|
||||
|
||||
export interface MainThreadUrlsShape extends IDisposable {
|
||||
@@ -835,6 +962,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>;
|
||||
$requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean | undefined>;
|
||||
}
|
||||
|
||||
export interface IFileChangeDto {
|
||||
@@ -888,9 +1016,8 @@ export interface MainThreadExtensionServiceShape extends IDisposable {
|
||||
$activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
|
||||
$onWillActivateExtension(extensionId: ExtensionIdentifier): Promise<void>;
|
||||
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
|
||||
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise<void>;
|
||||
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: SerializedError, missingExtensionDependency: MissingExtensionDependency | null): Promise<void>;
|
||||
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void;
|
||||
$onExtensionHostExit(code: number): Promise<void>;
|
||||
$setPerformanceMarks(marks: performance.PerformanceMark[]): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -943,6 +1070,8 @@ export interface MainThreadSCMShape extends IDisposable {
|
||||
$setInputBoxValue(sourceControlHandle: number, value: string): void;
|
||||
$setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void;
|
||||
$setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void;
|
||||
$setInputBoxFocus(sourceControlHandle: number): void;
|
||||
$showValidationMessage(sourceControlHandle: number, message: string, type: InputValidationType): void;
|
||||
$setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void;
|
||||
}
|
||||
|
||||
@@ -994,14 +1123,29 @@ export interface MainThreadWindowShape extends IDisposable {
|
||||
$asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise<UriComponents>;
|
||||
}
|
||||
|
||||
export enum CandidatePortSource {
|
||||
None = 0,
|
||||
Process = 1,
|
||||
Output = 2
|
||||
}
|
||||
|
||||
export interface PortAttributesProviderSelector {
|
||||
pid?: number;
|
||||
portRange?: [number, number];
|
||||
commandMatcher?: RegExp;
|
||||
}
|
||||
|
||||
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>;
|
||||
$onFoundNewCandidates(candidates: CandidatePort[]): Promise<void>;
|
||||
$setCandidatePortSource(source: CandidatePortSource): Promise<void>;
|
||||
$registerPortsAttributesProvider(selector: PortAttributesProviderSelector, providerHandle: number): Promise<void>;
|
||||
$unregisterPortsAttributesProvider(providerHandle: number): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadTimelineShape extends IDisposable {
|
||||
@@ -1039,7 +1183,7 @@ export interface IModelAddedData {
|
||||
isDirty: boolean;
|
||||
}
|
||||
export interface ExtHostDocumentsShape {
|
||||
$acceptModelModeChanged(strURL: UriComponents, oldModeId: string, newModeId: string): void;
|
||||
$acceptModelModeChanged(strURL: UriComponents, newModeId: string): void;
|
||||
$acceptModelSaved(strURL: UriComponents): void;
|
||||
$acceptDirtyStateChanged(strURL: UriComponents, isDirty: boolean): void;
|
||||
$acceptModelChanged(strURL: UriComponents, e: IModelChangedEvent, isDirty: boolean): void;
|
||||
@@ -1098,9 +1242,10 @@ export interface ExtHostTreeViewsShape {
|
||||
}
|
||||
|
||||
export interface ExtHostWorkspaceShape {
|
||||
$initializeWorkspace(workspace: IWorkspaceData | null): void;
|
||||
$initializeWorkspace(workspace: IWorkspaceData | null, trusted: boolean): void;
|
||||
$acceptWorkspaceData(workspace: IWorkspaceData | null): void;
|
||||
$handleTextSearchResult(result: search.IRawFileMatch2, requestId: number): void;
|
||||
$onDidGrantWorkspaceTrust(): void;
|
||||
}
|
||||
|
||||
export interface ExtHostFileSystemInfoShape {
|
||||
@@ -1129,11 +1274,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>;
|
||||
}
|
||||
@@ -1167,6 +1311,8 @@ export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAut
|
||||
export interface ExtHostExtensionServiceShape {
|
||||
$resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
|
||||
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
|
||||
$extensionTestsExecute(): Promise<number>;
|
||||
$extensionTestsExit(code: number): Promise<void>;
|
||||
$activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void>;
|
||||
$activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean>;
|
||||
$setRemoteEnvironment(env: { [key: string]: string | null; }): Promise<void>;
|
||||
@@ -1197,8 +1343,8 @@ export interface IWillRunFileOperationParticipation {
|
||||
|
||||
export interface ExtHostFileSystemEventServiceShape {
|
||||
$onFileEvent(events: FileSystemEvents): void;
|
||||
$onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined>;
|
||||
$onDidRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[]): void;
|
||||
$onWillRunFileOperation(operation: files.FileOperation, files: readonly SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined>;
|
||||
$onDidRunFileOperation(operation: files.FileOperation, files: readonly SourceTargetPair[]): void;
|
||||
}
|
||||
|
||||
export interface ObjectIdentifier {
|
||||
@@ -1305,9 +1451,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 {
|
||||
@@ -1376,9 +1523,6 @@ export interface IWorkspaceCellEditDto {
|
||||
|
||||
export interface IWorkspaceEditDto {
|
||||
edits: Array<IWorkspaceFileEditDto | IWorkspaceTextEditDto | IWorkspaceCellEditDto>;
|
||||
|
||||
// todo@jrieken reject should go into rename
|
||||
rejectReason?: string;
|
||||
}
|
||||
|
||||
export function reviveWorkspaceEditDto(data: IWorkspaceEditDto | undefined): modes.WorkspaceEdit {
|
||||
@@ -1470,6 +1614,11 @@ export interface ILinkedEditingRangesDto {
|
||||
wordPattern?: IRegExpDto;
|
||||
}
|
||||
|
||||
export interface IInlineValueContextDto {
|
||||
frameId: number;
|
||||
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>;
|
||||
@@ -1481,6 +1630,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>;
|
||||
@@ -1493,7 +1643,7 @@ export interface ExtHostLanguageFeaturesShape {
|
||||
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<IWorkspaceSymbolsDto>;
|
||||
$resolveWorkspaceSymbol(handle: number, symbol: IWorkspaceSymbolDto, token: CancellationToken): Promise<IWorkspaceSymbolDto | undefined>;
|
||||
$releaseWorkspaceSymbols(handle: number, id: number): void;
|
||||
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto | undefined>;
|
||||
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto & { rejectReason?: string } | undefined>;
|
||||
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined>;
|
||||
$provideDocumentSemanticTokens(handle: number, resource: UriComponents, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null>;
|
||||
$releaseDocumentSemanticTokens(handle: number, semanticColoringResultId: number): void;
|
||||
@@ -1529,19 +1679,9 @@ export interface ExtHostQuickOpenShape {
|
||||
$onDidHide(sessionId: number): void;
|
||||
}
|
||||
|
||||
export interface IShellLaunchConfigDto {
|
||||
name?: string;
|
||||
executable?: string;
|
||||
args?: string[] | string;
|
||||
cwd?: string | UriComponents;
|
||||
env?: { [key: string]: string | null; };
|
||||
hideFromUser?: boolean;
|
||||
flowControl?: boolean;
|
||||
}
|
||||
|
||||
export interface IShellDefinitionDto {
|
||||
label: string;
|
||||
path: string;
|
||||
export interface ExtHostTelemetryShape {
|
||||
$initializeTelemetryEnabled(enabled: boolean): void;
|
||||
$onDidChangeTelemetryEnabled(enabled: boolean): void;
|
||||
}
|
||||
|
||||
export interface IShellAndArgsDto {
|
||||
@@ -1574,7 +1714,6 @@ export interface ExtHostTerminalServiceShape {
|
||||
$acceptTerminalTitleChange(id: number, name: string): void;
|
||||
$acceptTerminalDimensions(id: number, cols: number, rows: number): void;
|
||||
$acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void;
|
||||
$spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined>;
|
||||
$startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<ITerminalLaunchError | undefined>;
|
||||
$acceptProcessAckDataEvent(id: number, charCount: number): void;
|
||||
$acceptProcessInput(id: number, data: string): void;
|
||||
@@ -1583,8 +1722,7 @@ export interface ExtHostTerminalServiceShape {
|
||||
$acceptProcessRequestInitialCwd(id: number): void;
|
||||
$acceptProcessRequestCwd(id: number): void;
|
||||
$acceptProcessRequestLatency(id: number): number;
|
||||
$acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
|
||||
$getAvailableShells(): Promise<IShellDefinitionDto[]>;
|
||||
$getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]>;
|
||||
$getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||
$provideLinks(id: number, line: string): Promise<ITerminalLinkDto[]>;
|
||||
$activateLink(id: number, linkId: number): void;
|
||||
@@ -1632,6 +1770,7 @@ export interface IDataBreakpointDto extends IBreakpointDto {
|
||||
canPersist: boolean;
|
||||
label: string;
|
||||
accessTypes?: DebugProtocol.DataBreakpointAccessType[];
|
||||
accessType: DebugProtocol.DataBreakpointAccessType;
|
||||
}
|
||||
|
||||
export interface ISourceBreakpointDto extends IBreakpointDto {
|
||||
@@ -1731,26 +1870,20 @@ 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 {
|
||||
visibleRanges: INotebookVisibleRangesEvent | null;
|
||||
selections: INotebookSelectionChangeEvent | null;
|
||||
visibleRanges?: INotebookVisibleRangesEvent;
|
||||
selections?: INotebookSelectionChangeEvent;
|
||||
}
|
||||
|
||||
export interface INotebookDocumentPropertiesChangeData {
|
||||
metadata: NotebookDocumentMetadata | null;
|
||||
metadata?: NotebookDocumentMetadata;
|
||||
}
|
||||
|
||||
export interface INotebookModelAddedData {
|
||||
@@ -1759,15 +1892,14 @@ 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[];
|
||||
viewColumn?: number
|
||||
}
|
||||
|
||||
export interface INotebookDocumentsAndEditorsDelta {
|
||||
@@ -1779,26 +1911,59 @@ export interface INotebookDocumentsAndEditorsDelta {
|
||||
visibleEditors?: string[];
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookShape {
|
||||
$resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto>;
|
||||
$resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise<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>;
|
||||
export interface INotebookKernelInfoDto2 {
|
||||
id?: string;
|
||||
friendlyId: string;
|
||||
label: string;
|
||||
extension: ExtensionIdentifier;
|
||||
extensionLocation: UriComponents;
|
||||
providerHandle?: number;
|
||||
description?: string;
|
||||
detail?: string;
|
||||
isPreferred?: boolean;
|
||||
preloads?: { uri: UriComponents; provides: string[] }[];
|
||||
supportedLanguages?: string[]
|
||||
implementsInterrupt?: boolean;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookShape extends ExtHostNotebookDocumentsAndEditorsShape, ExtHostNotebookDocumentsShape, ExtHostNotebookEditorsShape {
|
||||
$provideNotebookCellStatusBarItems(handle: number, uri: UriComponents, index: number, token: CancellationToken): Promise<INotebookCellStatusBarListDto | undefined>;
|
||||
$releaseNotebookCellStatusBarItems(id: number): void;
|
||||
|
||||
$openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): 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;
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void;
|
||||
$acceptModelSaved(uriComponents: UriComponents): void;
|
||||
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void;
|
||||
$acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void;
|
||||
$backupNotebook(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string>;
|
||||
|
||||
$dataToNotebook(handle: number, data: VSBuffer, token: CancellationToken): Promise<NotebookDataDto>;
|
||||
$notebookToData(handle: number, data: NotebookDataDto, token: CancellationToken): Promise<VSBuffer>;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookDocumentsAndEditorsShape {
|
||||
$acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookDocumentsShape {
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void;
|
||||
$acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void;
|
||||
$acceptModelSaved(uriComponents: UriComponents): void;
|
||||
$acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void;
|
||||
}
|
||||
|
||||
export type INotebookEditorViewColumnInfo = Record<string, number>;
|
||||
|
||||
export interface ExtHostNotebookEditorsShape {
|
||||
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void;
|
||||
$acceptEditorViewColumns(data: INotebookEditorViewColumnInfo): void;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookKernelsShape {
|
||||
$acceptSelection(handle: number, uri: UriComponents, value: boolean): void;
|
||||
$executeCells(handle: number, uri: UriComponents, handles: number[]): Promise<void>;
|
||||
$cancelCells(handle: number, uri: UriComponents, handles: number[]): Promise<void>;
|
||||
$acceptRendererMessage(handle: number, editorId: string, message: any): void;
|
||||
}
|
||||
|
||||
export interface ExtHostStorageShape {
|
||||
$acceptValue(shared: boolean, key: string, value: object | undefined): void;
|
||||
}
|
||||
@@ -1816,6 +1981,7 @@ export interface ExtHostTunnelServiceShape {
|
||||
$onDidTunnelsChange(): Promise<void>;
|
||||
$registerCandidateFinder(enable: boolean): Promise<void>;
|
||||
$applyCandidateFilter(candidates: CandidatePort[]): Promise<CandidatePort[]>;
|
||||
$providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: CancellationToken): Promise<ProvidedPortAttributes[]>;
|
||||
}
|
||||
|
||||
export interface ExtHostTimelineShape {
|
||||
@@ -1828,21 +1994,49 @@ 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>;
|
||||
$lookupTest(test: TestIdWithSrc): Promise<InternalTestItem | undefined>;
|
||||
$acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
|
||||
$publishTestResults(results: InternalTestResults): void;
|
||||
$publishTestResults(results: ISerializedTestResults[]): void;
|
||||
$expandTest(src: TestIdWithSrc, levels: number): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadTestingShape {
|
||||
$registerTestProvider(id: string): void;
|
||||
$unregisterTestProvider(id: string): void;
|
||||
/** Registeres that there's a test controller with the given ID */
|
||||
$registerTestController(id: string): void;
|
||||
/** Diposes of the test controller with the given ID */
|
||||
$unregisterTestController(id: string): void;
|
||||
/** Requests tests from the given resource/uri, from the observer API. */
|
||||
$subscribeToDiffs(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
/** Stops requesting tests from the given resource/uri, from the observer API. */
|
||||
$unsubscribeFromDiffs(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
/** Publishes that new tests were available on the given source. */
|
||||
$publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
|
||||
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<RunTestsResult>;
|
||||
/** Request by an extension to run tests. */
|
||||
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>;
|
||||
|
||||
// --- test run handling:
|
||||
/**
|
||||
* Adds tests to the run. The tests are given in descending depth. The first
|
||||
* item will be a previously-known test, or a test root.
|
||||
*/
|
||||
$addTestsToRun(runId: string, tests: ITestItem[]): void;
|
||||
/** Updates the state of a test run in the given run. */
|
||||
$updateTestStateInRun(runId: string, taskId: string, testId: string, state: TestResultState, duration?: number): void;
|
||||
/** Appends a message to a test in the run. */
|
||||
$appendTestMessageInRun(runId: string, taskId: string, testId: string, message: ITestMessage): void;
|
||||
/** Appends raw output to the test run.. */
|
||||
$appendOutputToRun(runId: string, taskId: string, output: VSBuffer): void;
|
||||
/** Signals a task in a test run started. */
|
||||
$startedTestRunTask(runId: string, task: ITestRunTask): void;
|
||||
/** Signals a task in a test run ended. */
|
||||
$finishedTestRunTask(runId: string, taskId: string): void;
|
||||
/** Start a new extension-provided test run. */
|
||||
$startedExtensionTestRun(req: ExtensionRunTestsRequest): void;
|
||||
/** Signals that an extension-provided test run finished. */
|
||||
$finishedExtensionTestRun(runId: string): void;
|
||||
}
|
||||
|
||||
// --- proxy identifiers
|
||||
@@ -1895,6 +2089,9 @@ export const MainContext = {
|
||||
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
|
||||
MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService'),
|
||||
MainThreadNotebook: createMainId<MainThreadNotebookShape>('MainThreadNotebook'),
|
||||
MainThreadNotebookDocuments: createMainId<MainThreadNotebookDocumentsShape>('MainThreadNotebookDocumentsShape'),
|
||||
MainThreadNotebookEditors: createMainId<MainThreadNotebookEditorsShape>('MainThreadNotebookEditorsShape'),
|
||||
MainThreadNotebookKernels: createMainId<MainThreadNotebookKernelsShape>('MainThreadNotebookKernels'),
|
||||
MainThreadTheming: createMainId<MainThreadThemingShape>('MainThreadTheming'),
|
||||
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
|
||||
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline'),
|
||||
@@ -1941,9 +2138,11 @@ export const ExtHostContext = {
|
||||
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
|
||||
ExtHosLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService'),
|
||||
ExtHostNotebook: createMainId<ExtHostNotebookShape>('ExtHostNotebook'),
|
||||
ExtHostNotebookKernels: createMainId<ExtHostNotebookKernelsShape>('ExtHostNotebookKernels'),
|
||||
ExtHostTheming: createMainId<ExtHostThemingShape>('ExtHostTheming'),
|
||||
ExtHostTunnelService: createMainId<ExtHostTunnelServiceShape>('ExtHostTunnelService'),
|
||||
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),
|
||||
ExtHostTimeline: createMainId<ExtHostTimelineShape>('ExtHostTimeline'),
|
||||
ExtHostTesting: createMainId<ExtHostTestingShape>('ExtHostTesting'),
|
||||
ExtHostTelemetry: createMainId<ExtHostTelemetryShape>('ExtHostTelemetry'),
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ import { ICommandsExecutor, RemoveFromRecentlyOpenedAPICommand, OpenIssueReporte
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { TransientCellMetadata, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto';
|
||||
@@ -30,13 +30,13 @@ const newCommands: ApiCommand[] = [
|
||||
new ApiCommand(
|
||||
'vscode.executeDocumentHighlights', '_executeDocumentHighlights', 'Execute document highlight provider.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
|
||||
new ApiCommandResult<modes.DocumentHighlight[], types.DocumentHighlight[] | undefined>('A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.', tryMapWith(typeConverters.DocumentHighlight.to))
|
||||
new ApiCommandResult<modes.DocumentHighlight[], types.DocumentHighlight[] | undefined>('A promise that resolves to an array of DocumentHighlight-instances.', tryMapWith(typeConverters.DocumentHighlight.to))
|
||||
),
|
||||
// -- document symbols
|
||||
new ApiCommand(
|
||||
'vscode.executeDocumentSymbolProvider', '_executeDocumentSymbolProvider', 'Execute document symbol provider.',
|
||||
[ApiCommandArgument.Uri],
|
||||
new ApiCommandResult<modes.DocumentSymbol[], vscode.SymbolInformation[] | undefined>('A promise that resolves to an array of DocumentHighlight-instances.', (value, apiArgs) => {
|
||||
new ApiCommandResult<modes.DocumentSymbol[], vscode.SymbolInformation[] | undefined>('A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.', (value, apiArgs) => {
|
||||
|
||||
if (isFalsyOrEmpty(value)) {
|
||||
return undefined;
|
||||
@@ -60,7 +60,7 @@ const newCommands: ApiCommand[] = [
|
||||
range!: vscode.Range;
|
||||
selectionRange!: vscode.Range;
|
||||
children!: vscode.DocumentSymbol[];
|
||||
containerName!: string;
|
||||
override containerName!: string;
|
||||
}
|
||||
return value.map(MergedInfo.to);
|
||||
|
||||
@@ -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;
|
||||
@@ -162,7 +162,7 @@ const newCommands: ApiCommand[] = [
|
||||
new ApiCommand(
|
||||
'vscode.executeDocumentRenameProvider', '_executeDocumentRenameProvider', 'Execute rename provider.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position, ApiCommandArgument.String.with('newName', 'The new symbol name')],
|
||||
new ApiCommandResult<IWorkspaceEditDto, types.WorkspaceEdit | undefined>('A promise that resolves to a WorkspaceEdit.', value => {
|
||||
new ApiCommandResult<IWorkspaceEditDto & { rejectReason?: string }, types.WorkspaceEdit | undefined>('A promise that resolves to a WorkspaceEdit.', value => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -343,25 +343,37 @@ const newCommands: ApiCommand[] = [
|
||||
new ApiCommandResult<{
|
||||
viewType: string;
|
||||
displayName: string;
|
||||
options: { transientOutputs: boolean; transientMetadata: TransientMetadata };
|
||||
options: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; };
|
||||
filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[]
|
||||
}[], {
|
||||
viewType: string;
|
||||
displayName: string;
|
||||
filenamePattern: vscode.NotebookFilenamePattern[];
|
||||
filenamePattern: (vscode.GlobPattern | { include: vscode.GlobPattern; exclude: vscode.GlobPattern; })[];
|
||||
options: vscode.NotebookDocumentContentOptions;
|
||||
}[] | undefined>('A promise that resolves to an array of NotebookContentProvider static info objects.', tryMapWith(item => {
|
||||
return {
|
||||
viewType: item.viewType,
|
||||
displayName: item.displayName,
|
||||
options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata },
|
||||
options: {
|
||||
transientOutputs: item.options.transientOutputs,
|
||||
transientCellMetadata: item.options.transientCellMetadata,
|
||||
transientDocumentMetadata: item.options.transientDocumentMetadata
|
||||
},
|
||||
filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern))
|
||||
};
|
||||
}))
|
||||
),
|
||||
// --- 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 +400,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',
|
||||
|
||||
@@ -87,13 +87,13 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
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 {
|
||||
@@ -111,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);
|
||||
@@ -129,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();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { MainContext, MainThreadBulkEditsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { WorkspaceEdit } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -17,13 +16,12 @@ export class ExtHostBulkEdits {
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private readonly _extHostNotebooks: ExtHostNotebookController,
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadBulkEdits);
|
||||
}
|
||||
|
||||
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Promise<boolean> {
|
||||
const dto = WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors, this._extHostNotebooks);
|
||||
const dto = WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
|
||||
return this._proxy.$tryApplyWorkspaceEdit(dto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,6 +217,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.`);
|
||||
}
|
||||
@@ -261,7 +269,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;
|
||||
|
||||
@@ -271,8 +279,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;
|
||||
@@ -315,7 +322,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);
|
||||
@@ -339,8 +346,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) {
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { ExtHostWebviews, shouldSerializeBuffersForPostMessage, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
|
||||
import { EditorGroupColumn } from 'vs/workbench/common/editor';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -183,7 +183,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider));
|
||||
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, {
|
||||
supportsMove: !!provider.moveCustomTextEditor,
|
||||
});
|
||||
}, shouldSerializeBuffersForPostMessage(extension));
|
||||
} else {
|
||||
disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider));
|
||||
|
||||
@@ -199,7 +199,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
}));
|
||||
}
|
||||
|
||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument);
|
||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument, shouldSerializeBuffersForPostMessage(extension));
|
||||
}
|
||||
|
||||
return extHostTypes.Disposable.from(
|
||||
@@ -209,7 +209,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
}));
|
||||
}
|
||||
|
||||
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) {
|
||||
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, cancellation: CancellationToken) {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
@@ -220,7 +220,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
}
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation);
|
||||
const document = await entry.provider.openCustomDocument(revivedResource, { backupId, untitledDocumentData: untitledDocumentData?.buffer }, cancellation);
|
||||
|
||||
let storageRoot: URI | undefined;
|
||||
if (this.supportEditing(entry.provider) && this._extensionStoragePaths) {
|
||||
@@ -251,9 +251,12 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
resource: UriComponents,
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: extHostProtocol.IWebviewOptions;
|
||||
panelOptions: extHostProtocol.IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn,
|
||||
options: modes.IWebviewOptions & modes.IWebviewPanelOptions,
|
||||
cancellation: CancellationToken,
|
||||
): Promise<void> {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
@@ -263,8 +266,8 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
|
||||
const viewColumn = typeConverters.ViewColumn.to(position);
|
||||
|
||||
const webview = this._extHostWebview.createNewWebview(handle, options, entry.extension);
|
||||
const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview);
|
||||
const webview = this._extHostWebview.createNewWebview(handle, initData.webviewOptions, entry.extension);
|
||||
const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, initData.title, viewColumn, initData.panelOptions, webview);
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import type * as vscode from 'vscode';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import * as process from 'vs/base/common/process';
|
||||
|
||||
export const IExtHostDebugService = createDecorator<IExtHostDebugService>('IExtHostDebugService');
|
||||
|
||||
@@ -384,7 +384,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
}
|
||||
};
|
||||
}
|
||||
return this._variableResolver.resolveAny(ws, config);
|
||||
return this._variableResolver.resolveAnyAsync(ws, config);
|
||||
}
|
||||
|
||||
protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
|
||||
@@ -930,7 +930,7 @@ export class ExtHostDebugConsole {
|
||||
|
||||
export class ExtHostVariableResolverService extends AbstractVariableResolverService {
|
||||
|
||||
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment, workspaceService?: IExtHostWorkspace) {
|
||||
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, workspaceService?: IExtHostWorkspace) {
|
||||
super({
|
||||
getFolderUri: (folderName: string): URI | undefined => {
|
||||
const found = folders.filter(f => f.name === folderName);
|
||||
@@ -945,8 +945,11 @@ 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 process.cwd();
|
||||
},
|
||||
getExecPath: (): string | undefined => {
|
||||
return env ? env['VSCODE_EXEC_PATH'] : undefined;
|
||||
return process.env['VSCODE_EXEC_PATH'];
|
||||
},
|
||||
getFilePath: (): string | undefined => {
|
||||
if (editorService) {
|
||||
@@ -987,7 +990,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}, undefined, env);
|
||||
}, undefined, Promise.resolve(process.env));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import type * as vscode from 'vscode';
|
||||
import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol';
|
||||
import { DiagnosticSeverity } from './extHostTypes';
|
||||
import * as converter from './extHostTypeConverters';
|
||||
import { mergeSort } from 'vs/base/common/arrays';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
@@ -78,7 +77,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
|
||||
let lastUri: vscode.Uri | undefined;
|
||||
|
||||
// ensure stable-sort
|
||||
first = mergeSort([...first], DiagnosticCollection._compareIndexedTuplesByUri);
|
||||
first = [...first].sort(DiagnosticCollection._compareIndexedTuplesByUri);
|
||||
|
||||
for (const tuple of first) {
|
||||
const [uri, diagnostics] = tuple;
|
||||
@@ -289,7 +288,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
|
||||
super(name!, owner, ExtHostDiagnostics._maxDiagnosticsPerFile, loggingProxy, _onDidChangeDiagnostics);
|
||||
_collections.set(owner, this);
|
||||
}
|
||||
dispose() {
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
_collections.delete(owner);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
|
||||
super(uri, lines, eol, versionId);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
// we don't really dispose documents but let
|
||||
// extensions still read from them. some
|
||||
// operations, live saving, will now error tho
|
||||
|
||||
@@ -102,7 +102,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
|
||||
return this._proxy.$tryCreateDocument(options).then(data => URI.revive(data));
|
||||
}
|
||||
|
||||
public $acceptModelModeChanged(uriComponents: UriComponents, oldModeId: string, newModeId: string): void {
|
||||
public $acceptModelModeChanged(uriComponents: UriComponents, newModeId: string): void {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const data = this._documentsAndEditors.getDocument(uri);
|
||||
if (!data) {
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import type * as vscode from 'vscode';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionActivationError, MissingDependencyError } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
|
||||
@@ -158,7 +157,7 @@ export class FailedExtension extends ActivatedExtension {
|
||||
}
|
||||
|
||||
export interface IExtensionsActivatorHost {
|
||||
onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): void;
|
||||
onExtensionActivationError(extensionId: ExtensionIdentifier, error: Error | null, missingExtensionDependency: MissingExtensionDependency | null): void;
|
||||
actualActivateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension>;
|
||||
}
|
||||
|
||||
@@ -255,8 +254,12 @@ export class ExtensionsActivator {
|
||||
const currentExtension = this._registry.getExtensionDescription(currentActivation.id);
|
||||
if (!currentExtension) {
|
||||
// Error condition 0: unknown extension
|
||||
this._host.onExtensionActivationError(currentActivation.id, new MissingDependencyError(currentActivation.id.value));
|
||||
const error = new Error(`Unknown dependency '${currentActivation.id.value}'`);
|
||||
const error = new Error(`Cannot activate unknown extension '${currentActivation.id.value}'`);
|
||||
this._host.onExtensionActivationError(
|
||||
currentActivation.id,
|
||||
error,
|
||||
new MissingExtensionDependency(currentActivation.id.value)
|
||||
);
|
||||
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentActivation.id), new FailedExtension(error));
|
||||
return;
|
||||
}
|
||||
@@ -280,9 +283,16 @@ export class ExtensionsActivator {
|
||||
|
||||
if (dep && dep.activationFailed) {
|
||||
// Error condition 2: a dependency has already failed activation
|
||||
this._host.onExtensionActivationError(currentExtension.identifier, nls.localize('failedDep1', "Cannot activate extension '{0}' because it depends on extension '{1}', which failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId));
|
||||
const error = new Error(`Dependency ${depId} failed to activate`);
|
||||
const currentExtensionFriendlyName = currentExtension.displayName || currentExtension.identifier.value;
|
||||
const depDesc = this._registry.getExtensionDescription(depId);
|
||||
const depFriendlyName = (depDesc ? depDesc.displayName || depId : depId);
|
||||
const error = new Error(`Cannot activate the '${currentExtensionFriendlyName}' extension because its dependency '${depFriendlyName}' failed to activate`);
|
||||
(<any>error).detail = dep.activationFailedError;
|
||||
this._host.onExtensionActivationError(
|
||||
currentExtension.identifier,
|
||||
error,
|
||||
null
|
||||
);
|
||||
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
|
||||
return;
|
||||
}
|
||||
@@ -309,8 +319,13 @@ export class ExtensionsActivator {
|
||||
}
|
||||
|
||||
// Error condition 1: unknown dependency
|
||||
this._host.onExtensionActivationError(currentExtension.identifier, new MissingDependencyError(depId));
|
||||
const error = new Error(`Unknown dependency '${depId}'`);
|
||||
const currentExtensionFriendlyName = currentExtension.displayName || currentExtension.identifier.value;
|
||||
const error = new Error(`Cannot activate the '${currentExtensionFriendlyName}' extension because it depends on unknown extension '${depId}'`);
|
||||
this._host.onExtensionActivationError(
|
||||
currentExtension.identifier,
|
||||
error,
|
||||
new MissingExtensionDependency(depId)
|
||||
);
|
||||
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
|
||||
return;
|
||||
}
|
||||
@@ -372,7 +387,25 @@ export class ExtensionsActivator {
|
||||
}
|
||||
|
||||
const newlyActivatingExtension = this._host.actualActivateExtension(extensionId, reason).then(undefined, (err) => {
|
||||
this._host.onExtensionActivationError(extensionId, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionId.value, err.message));
|
||||
|
||||
const error = new Error();
|
||||
if (err && err.name) {
|
||||
error.name = err.name;
|
||||
}
|
||||
if (err && err.message) {
|
||||
error.message = `Activating extension '${extensionId.value}' failed: ${err.message}.`;
|
||||
} else {
|
||||
error.message = `Activating extension '${extensionId.value}' failed: ${err}.`;
|
||||
}
|
||||
if (err && err.stack) {
|
||||
error.stack = err.stack;
|
||||
}
|
||||
|
||||
this._host.onExtensionActivationError(
|
||||
extensionId,
|
||||
error,
|
||||
null
|
||||
);
|
||||
this._logService.error(`Activating extension ${extensionId.value} failed due to an error:`);
|
||||
this._logService.error(err);
|
||||
// Treat the extension as being empty
|
||||
|
||||
@@ -17,15 +17,14 @@ import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/co
|
||||
import { ActivatedExtension, EmptyExtension, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { ExtensionActivationError, checkProposedApiEnabled, ActivationKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { MissingExtensionDependency, checkProposedApiEnabled, ActivationKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { ExtensionGlobalMemento, ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
|
||||
import { RemoteAuthorityResolverError, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { RemoteAuthorityResolverError, ExtensionKind, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
@@ -95,6 +94,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
private readonly _almostReadyToRunExtensions: Barrier;
|
||||
private readonly _readyToStartExtensionHost: Barrier;
|
||||
private readonly _readyToRunExtensions: Barrier;
|
||||
private readonly _eagerExtensionsActivated: Barrier;
|
||||
|
||||
protected readonly _registry: ExtensionDescriptionRegistry;
|
||||
private readonly _storage: ExtHostStorage;
|
||||
private readonly _secretState: ExtHostSecretState;
|
||||
@@ -140,6 +141,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
this._almostReadyToRunExtensions = new Barrier();
|
||||
this._readyToStartExtensionHost = new Barrier();
|
||||
this._readyToRunExtensions = new Barrier();
|
||||
this._eagerExtensionsActivated = new Barrier();
|
||||
this._registry = new ExtensionDescriptionRegistry(this._initData.extensions);
|
||||
this._storage = new ExtHostStorage(this._extHostContext);
|
||||
this._secretState = new ExtHostSecretState(this._extHostContext);
|
||||
@@ -158,8 +160,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
this._initData.resolvedExtensions,
|
||||
this._initData.hostExtensions,
|
||||
{
|
||||
onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => {
|
||||
this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error);
|
||||
onExtensionActivationError: (extensionId: ExtensionIdentifier, error: Error, missingExtensionDependency: MissingExtensionDependency | null): void => {
|
||||
this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, errors.transformErrorForSerialization(error), missingExtensionDependency);
|
||||
},
|
||||
|
||||
actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
|
||||
@@ -388,6 +390,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
const extensionMode = extensionDescription.isUnderDevelopment
|
||||
? (this._initData.environment.extensionTestsLocationURI ? ExtensionMode.Test : ExtensionMode.Development)
|
||||
: ExtensionMode.Production;
|
||||
const extensionKind = this._initData.remote.isRemote ? ExtensionKind.Workspace : ExtensionKind.UI;
|
||||
|
||||
this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
|
||||
|
||||
@@ -397,6 +400,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
this._storagePath.whenReady
|
||||
]).then(() => {
|
||||
const that = this;
|
||||
let extension: vscode.Extension<any> | undefined;
|
||||
|
||||
return Object.freeze<vscode.ExtensionContext>({
|
||||
globalState,
|
||||
workspaceState,
|
||||
@@ -412,6 +417,12 @@ 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 extension() {
|
||||
if (extension === undefined) {
|
||||
extension = new Extension(that, extensionDescription.identifier, extensionDescription, extensionKind);
|
||||
}
|
||||
return extension;
|
||||
},
|
||||
get extensionRuntime() {
|
||||
checkProposedApiEnabled(extensionDescription);
|
||||
return that.extensionRuntime;
|
||||
@@ -537,84 +548,61 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
);
|
||||
}
|
||||
|
||||
private _handleExtensionTests(): Promise<void> {
|
||||
return this._doHandleExtensionTests().then(undefined, error => {
|
||||
public async $extensionTestsExecute(): Promise<number> {
|
||||
await this._eagerExtensionsActivated.wait();
|
||||
try {
|
||||
return this._doHandleExtensionTests();
|
||||
} catch (error) {
|
||||
console.error(error); // ensure any error message makes it onto the console
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async _doHandleExtensionTests(): Promise<void> {
|
||||
private async _doHandleExtensionTests(): Promise<number> {
|
||||
const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
|
||||
if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) {
|
||||
return Promise.resolve(undefined);
|
||||
if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI) {
|
||||
throw new Error(nls.localize('extensionTestError1', "Cannot load test runner."));
|
||||
}
|
||||
|
||||
const extensionTestsPath = originalFSPath(extensionTestsLocationURI);
|
||||
|
||||
// Require the test runner via node require from the provided path
|
||||
let testRunner: ITestRunner | INewTestRunner | undefined;
|
||||
let requireError: Error | undefined;
|
||||
try {
|
||||
testRunner = await this._loadCommonJSModule(null, URI.file(extensionTestsPath), new ExtensionActivationTimesBuilder(false));
|
||||
} catch (error) {
|
||||
requireError = error;
|
||||
const testRunner: ITestRunner | INewTestRunner | undefined = await this._loadCommonJSModule(null, extensionTestsLocationURI, new ExtensionActivationTimesBuilder(false));
|
||||
|
||||
if (!testRunner || typeof testRunner.run !== 'function') {
|
||||
throw new Error(nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsLocationURI.toString()));
|
||||
}
|
||||
|
||||
// Execute the runner if it follows the old `run` spec
|
||||
if (testRunner && typeof testRunner.run === 'function') {
|
||||
return new Promise<void>((c, e) => {
|
||||
const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
|
||||
if (error) {
|
||||
e(error.toString());
|
||||
} else {
|
||||
c(undefined);
|
||||
}
|
||||
|
||||
// after tests have run, we shutdown the host
|
||||
this._testRunnerExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
|
||||
};
|
||||
|
||||
const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);
|
||||
|
||||
// Using the new API `run(): Promise<void>`
|
||||
if (runResult && runResult.then) {
|
||||
runResult
|
||||
.then(() => {
|
||||
c();
|
||||
this._testRunnerExit(0);
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
e(err.toString());
|
||||
this._testRunnerExit(1);
|
||||
});
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve((typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Otherwise make sure to shutdown anyway even in case of an error
|
||||
else {
|
||||
this._testRunnerExit(1 /* ERROR */);
|
||||
}
|
||||
const extensionTestsPath = originalFSPath(extensionTestsLocationURI); // for the old test runner API
|
||||
|
||||
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath)));
|
||||
const runResult = testRunner.run(extensionTestsPath, oldTestRunnerCallback);
|
||||
|
||||
// Using the new API `run(): Promise<void>`
|
||||
if (runResult && runResult.then) {
|
||||
runResult
|
||||
.then(() => {
|
||||
resolve(0);
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
reject(err.toString());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _testRunnerExit(code: number): void {
|
||||
public async $extensionTestsExit(code: number): Promise<void> {
|
||||
this._logService.info(`extension host terminating: test runner requested exit with code ${code}`);
|
||||
this._logService.info(`exiting with code ${code}`);
|
||||
this._logService.flush();
|
||||
|
||||
// wait at most 5000ms for the renderer to confirm our exit request and for the renderer socket to drain
|
||||
// (this is to ensure all outstanding messages reach the renderer)
|
||||
const exitPromise = this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
|
||||
const drainPromise = this._extHostContext.drain();
|
||||
Promise.race([Promise.all([exitPromise, drainPromise]), timeout(5000)]).then(() => {
|
||||
this._logService.info(`exiting with code ${code}`);
|
||||
this._logService.flush();
|
||||
|
||||
this._hostUtils.exit(code);
|
||||
});
|
||||
this._hostUtils.exit(code);
|
||||
}
|
||||
|
||||
private _startExtensionHost(): Promise<void> {
|
||||
@@ -626,8 +614,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
return this._readyToStartExtensionHost.wait()
|
||||
.then(() => this._readyToRunExtensions.open())
|
||||
.then(() => this._handleEagerExtensions())
|
||||
.then(() => this._handleExtensionTests())
|
||||
.then(() => {
|
||||
this._eagerExtensionsActivated.open();
|
||||
this._logService.info(`eager extensions activated`);
|
||||
});
|
||||
}
|
||||
@@ -666,10 +654,10 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
}
|
||||
|
||||
try {
|
||||
this._disposables.add(await this._extHostTunnelService.setTunnelExtensionFunctions(resolver));
|
||||
performance.mark(`code/extHost/willResolveAuthority/${authorityPrefix}`);
|
||||
const result = await resolver.resolve(remoteAuthority, { resolveAttempt });
|
||||
performance.mark(`code/extHost/didResolveAuthorityOK/${authorityPrefix}`);
|
||||
this._disposables.add(await this._extHostTunnelService.setTunnelExtensionFunctions(resolver));
|
||||
|
||||
// Split merged API result into separate authority/options
|
||||
const authority: ResolvedAuthority = {
|
||||
@@ -679,7 +667,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
connectionToken: result.connectionToken
|
||||
};
|
||||
const options: ResolvedOptions = {
|
||||
extensionHostEnv: result.extensionHostEnv
|
||||
extensionHostEnv: result.extensionHostEnv,
|
||||
trust: result.trust
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -827,3 +816,42 @@ export interface IExtHostExtensionService extends AbstractExtHostExtensionServic
|
||||
onDidChangeRemoteConnectionData: Event<void>;
|
||||
getRemoteConnectionData(): IRemoteConnectionData | null;
|
||||
}
|
||||
|
||||
export class Extension<T> implements vscode.Extension<T> {
|
||||
|
||||
#extensionService: IExtHostExtensionService;
|
||||
#originExtensionId: ExtensionIdentifier;
|
||||
#identifier: ExtensionIdentifier;
|
||||
|
||||
readonly id: string;
|
||||
readonly extensionUri: URI;
|
||||
readonly extensionPath: string;
|
||||
readonly packageJSON: IExtensionDescription;
|
||||
readonly extensionKind: vscode.ExtensionKind;
|
||||
|
||||
constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind) {
|
||||
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));
|
||||
this.packageJSON = description;
|
||||
this.extensionKind = kind;
|
||||
}
|
||||
|
||||
get isActive(): boolean {
|
||||
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);
|
||||
}
|
||||
|
||||
activate(): Thenable<T> {
|
||||
return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
@@ -394,7 +412,8 @@ class CodeActionAdapter {
|
||||
|
||||
const codeActionContext: vscode.CodeActionContext = {
|
||||
diagnostics: allDiagnostics,
|
||||
only: context.only ? new CodeActionKind(context.only) : undefined
|
||||
only: context.only ? new CodeActionKind(context.only) : undefined,
|
||||
triggerKind: typeConvert.CodeActionTriggerKind.to(context.trigger),
|
||||
};
|
||||
|
||||
return asPromise(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then((commandsOrActions): extHostProtocol.ICodeActionListDto | undefined => {
|
||||
@@ -1334,7 +1353,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 +1588,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 {
|
||||
|
||||
@@ -7,6 +7,7 @@ import type * as vscode from 'vscode';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { DeferredPromise, RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
export class ExtensionMemento implements vscode.Memento {
|
||||
|
||||
@@ -18,6 +19,9 @@ export class ExtensionMemento implements vscode.Memento {
|
||||
private _value?: { [n: string]: any; };
|
||||
private readonly _storageListener: IDisposable;
|
||||
|
||||
private _deferredPromises: Map<string, DeferredPromise<void>> = new Map();
|
||||
private _scheduler: RunOnceScheduler;
|
||||
|
||||
constructor(id: string, global: boolean, storage: ExtHostStorage) {
|
||||
this._id = id;
|
||||
this._shared = global;
|
||||
@@ -33,6 +37,23 @@ export class ExtensionMemento implements vscode.Memento {
|
||||
this._value = e.value;
|
||||
}
|
||||
});
|
||||
|
||||
this._scheduler = new RunOnceScheduler(() => {
|
||||
const records = this._deferredPromises;
|
||||
this._deferredPromises = new Map();
|
||||
(async () => {
|
||||
try {
|
||||
await this._storage.setValue(this._shared, this._id, this._value!);
|
||||
for (const value of records.values()) {
|
||||
value.complete();
|
||||
}
|
||||
} catch (e) {
|
||||
for (const value of records.values()) {
|
||||
value.error(e);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
get whenReady(): Promise<ExtensionMemento> {
|
||||
@@ -51,7 +72,20 @@ export class ExtensionMemento implements vscode.Memento {
|
||||
|
||||
update(key: string, value: any): Promise<void> {
|
||||
this._value![key] = value;
|
||||
return this._storage.setValue(this._shared, this._id, this._value!);
|
||||
|
||||
let record = this._deferredPromises.get(key);
|
||||
if (record !== undefined) {
|
||||
return record.p;
|
||||
}
|
||||
|
||||
const promise = new DeferredPromise<void>();
|
||||
this._deferredPromises.set(key, promise);
|
||||
|
||||
if (!this._scheduler.isScheduled()) {
|
||||
this._scheduler.schedule();
|
||||
}
|
||||
|
||||
return promise.p;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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';
|
||||
@@ -57,7 +56,6 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
}
|
||||
};
|
||||
|
||||
this._disposables.add(extHostNotebooks.onDidChangeCellLanguage(e => documentChange(e.document)));
|
||||
this._disposables.add(extHostNotebooks.onDidChangeNotebookCells(e => documentChange(e.document)));
|
||||
}
|
||||
|
||||
@@ -75,9 +73,9 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
this._cellUris = new ResourceMap();
|
||||
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))) {
|
||||
this._cellUris.set(cell.uri, this._cells.length);
|
||||
for (const cell of this._notebook.getCells()) {
|
||||
if (cell.kind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.document.uri, cell.document.languageId, true))) {
|
||||
this._cellUris.set(cell.document.uri, this._cells.length);
|
||||
this._cells.push(cell);
|
||||
cellLengths.push(cell.document.getText().length + 1);
|
||||
cellLineCounts.push(cell.document.lineCount);
|
||||
@@ -163,7 +161,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
const range = new types.Range(startPos, endPos);
|
||||
|
||||
const startCell = this._cells[startIdx.index];
|
||||
return new types.Location(startCell.uri, <types.Range>startCell.document.validateRange(range));
|
||||
return new types.Location(startCell.document.uri, <types.Range>startCell.document.validateRange(range));
|
||||
}
|
||||
|
||||
contains(uri: vscode.Uri): boolean {
|
||||
|
||||
@@ -3,56 +3,34 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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 { Schemas } from 'vs/base/common/network';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { ISplice } from 'vs/base/common/sequence';
|
||||
import { deepFreeze, equals } from 'vs/base/common/objects';
|
||||
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, MainThreadNotebookDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { 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 } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
|
||||
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),
|
||||
items: event.items.map(data => data.cell)
|
||||
static asApiEvents(events: RawContentChangeEvent[]): readonly vscode.NotebookCellsChangeData[] {
|
||||
return events.map(event => {
|
||||
return {
|
||||
start: event.start,
|
||||
deletedCount: event.deletedCount,
|
||||
deletedItems: event.deletedItems,
|
||||
items: event.items.map(data => data.apiCell)
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostCell extends Disposable {
|
||||
export class ExtHostCell {
|
||||
|
||||
static asModelAddData(notebook: vscode.NotebookDocument, cell: IMainCellDto): IExtHostModelAddedData {
|
||||
return {
|
||||
@@ -66,18 +44,11 @@ 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;
|
||||
private _previousResult: vscode.NotebookCellExecutionSummary | undefined;
|
||||
|
||||
private _internalMetadata: NotebookCellMetadata;
|
||||
readonly handle: number;
|
||||
readonly uri: URI;
|
||||
readonly cellKind: CellKind;
|
||||
@@ -85,242 +56,128 @@ 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;
|
||||
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();
|
||||
}));
|
||||
this._outputs = _cellData.outputs.map(extHostTypeConverters.NotebookCellOutput.to);
|
||||
this._internalMetadata = _cellData.metadata ?? {};
|
||||
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(this._internalMetadata);
|
||||
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(this._internalMetadata);
|
||||
}
|
||||
|
||||
get cell(): vscode.NotebookCell {
|
||||
get internalMetadata(): NotebookCellMetadata {
|
||||
return this._internalMetadata;
|
||||
}
|
||||
|
||||
get apiCell(): vscode.NotebookCell {
|
||||
if (!this._cell) {
|
||||
const that = this;
|
||||
const data = this._extHostDocument.getDocument(this.uri);
|
||||
if (!data) {
|
||||
throw new Error(`MISSING extHostDocument for notebook cell: ${this.uri}`);
|
||||
}
|
||||
this._cell = Object.freeze({
|
||||
this._cell = Object.freeze<vscode.NotebookCell>({
|
||||
get index() { return that._notebook.getCellIndex(that); },
|
||||
notebook: that._notebook.notebookDocument,
|
||||
uri: that.uri,
|
||||
cellKind: this._cellData.cellKind,
|
||||
notebook: that._notebook.apiNotebook,
|
||||
kind: 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); },
|
||||
get metadata() { return that._metadata; },
|
||||
set metadata(value) {
|
||||
that.setMetadata(value);
|
||||
that._updateMetadata();
|
||||
},
|
||||
get latestExecutionSummary() { return that._previousResult; }
|
||||
});
|
||||
}
|
||||
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._internalMetadata = newMetadata;
|
||||
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(newMetadata);
|
||||
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(newMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
export interface INotebookEventEmitter {
|
||||
emitModelChange(events: vscode.NotebookCellsChangeEvent): void;
|
||||
emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void;
|
||||
emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void;
|
||||
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void;
|
||||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void;
|
||||
emitCellExecutionStateChange(event: vscode.NotebookCellExecutionStateChangeEvent): 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 {
|
||||
export class ExtHostNotebookDocument {
|
||||
|
||||
private static _handlePool: number = 0;
|
||||
readonly handle = ExtHostNotebookDocument._handlePool++;
|
||||
|
||||
private _cells: ExtHostCell[] = [];
|
||||
|
||||
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 _disposed: boolean = false;
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape,
|
||||
private readonly _proxy: MainThreadNotebookDocumentsShape,
|
||||
private readonly _textDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private readonly _textDocuments: ExtHostDocuments,
|
||||
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
|
||||
) {
|
||||
super();
|
||||
|
||||
const observableMetadata = getObservable(metadata);
|
||||
this._metadata = observableMetadata.proxy;
|
||||
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
|
||||
this._tryUpdateMetadata();
|
||||
}));
|
||||
}
|
||||
private _metadata: extHostTypes.NotebookDocumentMetadata,
|
||||
readonly uri: URI,
|
||||
) { }
|
||||
|
||||
dispose() {
|
||||
this._disposed = true;
|
||||
super.dispose();
|
||||
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 {
|
||||
get apiNotebook(): vscode.NotebookDocument {
|
||||
if (!this._notebook) {
|
||||
const that = this;
|
||||
this._notebook = Object.freeze({
|
||||
this._notebook = {
|
||||
get uri() { return that.uri; },
|
||||
get version() { return that._versionId; },
|
||||
get fileName() { return that.uri.fsPath; },
|
||||
get viewType() { return that._viewType; },
|
||||
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 isClosed() { return that._disposed; },
|
||||
get metadata() { return that._metadata; },
|
||||
set metadata(value: Required<vscode.NotebookDocumentMetadata>) { that._updateMetadata(value); },
|
||||
get contentOptions() { return that._contentOptions; }
|
||||
});
|
||||
get cellCount() { return that._cells.length; },
|
||||
cellAt(index) {
|
||||
index = that._validateIndex(index);
|
||||
return that._cells[index].apiCell;
|
||||
},
|
||||
getCells(range) {
|
||||
const cells = range ? that._getCells(range) : that._cells;
|
||||
return cells.map(cell => cell.apiCell);
|
||||
},
|
||||
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;
|
||||
@@ -332,42 +189,68 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
}
|
||||
|
||||
acceptDocumentPropertiesChanged(data: INotebookDocumentPropertiesChangeData) {
|
||||
const newMetadata = {
|
||||
...notebookDocumentMetadataDefaults,
|
||||
...data.metadata
|
||||
};
|
||||
|
||||
if (this._metadataChangeListener) {
|
||||
this._metadataChangeListener.dispose();
|
||||
if (data.metadata) {
|
||||
this._metadata = this._metadata.with(data.metadata);
|
||||
}
|
||||
|
||||
const observableMetadata = getObservable(newMetadata);
|
||||
this._metadata = observableMetadata.proxy;
|
||||
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
|
||||
this._tryUpdateMetadata();
|
||||
}));
|
||||
|
||||
this._emitter.emitDocumentMetadataChange({ document: this.notebookDocument });
|
||||
}
|
||||
|
||||
acceptModelChanged(event: NotebookCellsChangedEventDto, isDirty: boolean): void {
|
||||
this._versionId = event.versionId;
|
||||
this._isDirty = isDirty;
|
||||
event.rawEvents.forEach(e => {
|
||||
if (e.kind === NotebookCellsChangeType.Initialize) {
|
||||
this._spliceNotebookCells(e.changes, true);
|
||||
} if (e.kind === NotebookCellsChangeType.ModelChange) {
|
||||
this._spliceNotebookCells(e.changes, false);
|
||||
} else if (e.kind === NotebookCellsChangeType.Move) {
|
||||
this._moveCell(e.index, e.newIdx);
|
||||
} else if (e.kind === NotebookCellsChangeType.Output) {
|
||||
this._setCellOutputs(e.index, e.outputs);
|
||||
} else if (e.kind === NotebookCellsChangeType.ChangeLanguage) {
|
||||
this._changeCellLanguage(e.index, e.language);
|
||||
} else if (e.kind === NotebookCellsChangeType.ChangeCellMetadata) {
|
||||
this._changeCellMetadata(e.index, e.metadata);
|
||||
|
||||
for (const rawEvent of event.rawEvents) {
|
||||
if (rawEvent.kind === NotebookCellsChangeType.Initialize) {
|
||||
this._spliceNotebookCells(rawEvent.changes, true);
|
||||
} if (rawEvent.kind === NotebookCellsChangeType.ModelChange) {
|
||||
this._spliceNotebookCells(rawEvent.changes, false);
|
||||
} else if (rawEvent.kind === NotebookCellsChangeType.Move) {
|
||||
this._moveCell(rawEvent.index, rawEvent.newIdx);
|
||||
} else if (rawEvent.kind === NotebookCellsChangeType.Output) {
|
||||
this._setCellOutputs(rawEvent.index, rawEvent.outputs);
|
||||
} else if (rawEvent.kind === NotebookCellsChangeType.OutputItem) {
|
||||
this._setCellOutputItems(rawEvent.index, rawEvent.outputId, rawEvent.append, rawEvent.outputItems);
|
||||
} else if (rawEvent.kind === NotebookCellsChangeType.ChangeLanguage) {
|
||||
this._changeCellLanguage(rawEvent.index, rawEvent.language);
|
||||
} else if (rawEvent.kind === NotebookCellsChangeType.ChangeCellMetadata) {
|
||||
this._changeCellMetadata(rawEvent.index, rawEvent.metadata);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _validateIndex(index: number): number {
|
||||
if (index < 0) {
|
||||
return 0;
|
||||
} else if (index >= this._cells.length) {
|
||||
return this._cells.length - 1;
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
private _validateRange(range: vscode.NotebookRange): vscode.NotebookRange {
|
||||
if (range.start < 0) {
|
||||
range = range.with({ start: 0 });
|
||||
}
|
||||
if (range.end > this._cells.length) {
|
||||
range = range.with({ end: this._cells.length });
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
private _getCells(range: vscode.NotebookRange): ExtHostCell[] {
|
||||
range = this._validateRange(range);
|
||||
const result: ExtHostCell[] = [];
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
result.push(this._cells[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async _save(): Promise<boolean> {
|
||||
if (this._disposed) {
|
||||
return Promise.reject(new Error('Notebook has been closed'));
|
||||
}
|
||||
return this._proxy.$trySaveDocument(this.uri);
|
||||
}
|
||||
|
||||
private _spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void {
|
||||
@@ -383,107 +266,88 @@ 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._textDocumentsAndEditors, cell);
|
||||
if (!initialization) {
|
||||
addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell));
|
||||
addedCellDocuments.push(ExtHostCell.asModelAddData(this.apiNotebook, cell));
|
||||
}
|
||||
|
||||
if (!this._cellDisposableMapping.has(extCell.handle)) {
|
||||
const store = new DisposableStore();
|
||||
store.add(extCell);
|
||||
this._cellDisposableMapping.set(extCell.handle, store);
|
||||
}
|
||||
|
||||
const store = this._cellDisposableMapping.get(extCell.handle)!;
|
||||
|
||||
store.add(extCell.onDidChangeOutputs((diffs) => {
|
||||
this.eventuallyUpdateCellOutputs(extCell, diffs);
|
||||
}));
|
||||
|
||||
return extCell;
|
||||
});
|
||||
|
||||
for (let j = splice[0]; j < splice[0] + splice[1]; j++) {
|
||||
this._cellDisposableMapping.get(this._cells[j].handle)?.dispose();
|
||||
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.apiCell);
|
||||
}
|
||||
|
||||
contentChangeEvents.push(new RawContentChangeEvent(splice[0], splice[1], deletedItems, newCells));
|
||||
contentChangeEvents.push(changeEvent);
|
||||
});
|
||||
|
||||
this._documentsAndEditors.acceptDocumentsAndEditorsDelta({
|
||||
this._textDocumentsAndEditors.acceptDocumentsAndEditorsDelta({
|
||||
addedDocuments: addedCellDocuments,
|
||||
removedDocuments: removedCellDocuments
|
||||
});
|
||||
|
||||
if (!initialization) {
|
||||
this._emitter.emitModelChange({
|
||||
document: this.notebookDocument,
|
||||
changes: contentChangeEvents.map(RawContentChangeEvent.asApiEvent)
|
||||
});
|
||||
this._emitter.emitModelChange(deepFreeze({
|
||||
document: this.apiNotebook,
|
||||
changes: RawContentChangeEvent.asApiEvents(contentChangeEvents)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private _moveCell(index: number, newIdx: number): void {
|
||||
const cells = this._cells.splice(index, 1);
|
||||
this._cells.splice(newIdx, 0, ...cells);
|
||||
const changes: vscode.NotebookCellsChangeData[] = [{
|
||||
start: index,
|
||||
deletedCount: 1,
|
||||
deletedItems: cells.map(data => data.cell),
|
||||
items: []
|
||||
}, {
|
||||
start: newIdx,
|
||||
deletedCount: 0,
|
||||
deletedItems: [],
|
||||
items: cells.map(data => data.cell)
|
||||
}];
|
||||
this._emitter.emitModelChange({
|
||||
document: this.notebookDocument,
|
||||
changes
|
||||
});
|
||||
const changes = [
|
||||
new RawContentChangeEvent(index, 1, cells.map(c => c.apiCell), []),
|
||||
new RawContentChangeEvent(newIdx, 0, [], cells)
|
||||
];
|
||||
this._emitter.emitModelChange(deepFreeze({
|
||||
document: this.apiNotebook,
|
||||
changes: RawContentChangeEvent.asApiEvents(changes)
|
||||
}));
|
||||
}
|
||||
|
||||
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] });
|
||||
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.apiNotebook, cells: [cell.apiCell] }));
|
||||
}
|
||||
|
||||
private _changeCellLanguage(index: number, language: string): void {
|
||||
private _setCellOutputItems(index: number, outputId: string, append: boolean, outputItems: IOutputItemDto[]): void {
|
||||
const cell = this._cells[index];
|
||||
const event: vscode.NotebookCellLanguageChangeEvent = { document: this.notebookDocument, cell: cell.cell, language };
|
||||
this._emitter.emitCellLanguageChange(event);
|
||||
cell.setOutputItems(outputId, append, outputItems);
|
||||
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.apiNotebook, cells: [cell.apiCell] }));
|
||||
}
|
||||
|
||||
private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata | undefined): void {
|
||||
private _changeCellLanguage(index: number, newModeId: string): void {
|
||||
const cell = this._cells[index];
|
||||
cell.setMetadata(newMetadata || {});
|
||||
const event: vscode.NotebookCellMetadataChangeEvent = { document: this.notebookDocument, cell: cell.cell };
|
||||
this._emitter.emitCellMetadataChange(event);
|
||||
if (cell.apiCell.document.languageId !== newModeId) {
|
||||
this._textDocuments.$acceptModelModeChanged(cell.uri, newModeId);
|
||||
}
|
||||
}
|
||||
|
||||
async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<IProcessedOutput>[]) {
|
||||
const outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
|
||||
const outputs = diff.toInsert;
|
||||
return [diff.start, diff.deleteCount, outputs];
|
||||
});
|
||||
private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void {
|
||||
const cell = this._cells[index];
|
||||
|
||||
if (!outputDtos.length) {
|
||||
return;
|
||||
const originalInternalMetadata = cell.internalMetadata;
|
||||
const originalExtMetadata = cell.apiCell.metadata;
|
||||
cell.setMetadata(newMetadata);
|
||||
const newExtMetadata = cell.apiCell.metadata;
|
||||
|
||||
if (!equals(originalExtMetadata, newExtMetadata)) {
|
||||
this._emitter.emitCellMetadataChange(deepFreeze({ document: this.apiNotebook, cell: cell.apiCell }));
|
||||
}
|
||||
|
||||
await this._proxy.$spliceNotebookCellOutputs(this._viewType, this.uri, cell.handle, outputDtos);
|
||||
this._emitter.emitCellOutputsChange({
|
||||
document: this.notebookDocument,
|
||||
cells: [cell.cell]
|
||||
});
|
||||
if (originalInternalMetadata.runState !== newMetadata.runState) {
|
||||
const executionState = newMetadata.runState ?? extHostTypes.NotebookCellExecutionState.Idle;
|
||||
this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.apiNotebook, cell: cell.apiCell, executionState }));
|
||||
}
|
||||
}
|
||||
|
||||
getCellFromIndex(index: number): ExtHostCell | undefined {
|
||||
return this._cells[index];
|
||||
}
|
||||
|
||||
getCell(cellHandle: number): ExtHostCell | undefined {
|
||||
|
||||
@@ -3,15 +3,18 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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 { MainThreadNotebookEditorsShape } 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, ICellReplaceEdit } 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;
|
||||
@@ -41,7 +44,7 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
|
||||
this._throwIfFinalized();
|
||||
this._collectedEdits.push({
|
||||
editType: CellEditType.DocumentMetadata,
|
||||
metadata: { ...notebookDocumentMetadataDefaults, ...value }
|
||||
metadata: value
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,21 +57,6 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
|
||||
});
|
||||
}
|
||||
|
||||
replaceCellOutput(index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[]): 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);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
replaceCells(from: number, to: number, cells: vscode.NotebookCellData[]): void {
|
||||
this._throwIfFinalized();
|
||||
if (from === to && cells.length === 0) {
|
||||
@@ -78,112 +66,89 @@ 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 {
|
||||
|
||||
//TODO@rebornix noop setter?
|
||||
selection?: vscode.NotebookCell;
|
||||
|
||||
private _visibleRanges: vscode.NotebookCellRange[] = [];
|
||||
private _selections: vscode.NotebookRange[] = [];
|
||||
private _visibleRanges: vscode.NotebookRange[] = [];
|
||||
private _viewColumn?: vscode.ViewColumn;
|
||||
private _active: boolean = false;
|
||||
|
||||
private _visible: boolean = false;
|
||||
private _kernel?: vscode.NotebookKernel;
|
||||
private readonly _hasDecorationsForKey = new Set<string>();
|
||||
|
||||
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;
|
||||
|
||||
constructor(
|
||||
readonly id: string,
|
||||
private readonly _viewType: string,
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
private readonly _webComm: vscode.NotebookCommunication,
|
||||
private readonly _proxy: MainThreadNotebookEditorsShape,
|
||||
readonly notebookData: ExtHostNotebookDocument,
|
||||
visibleRanges: vscode.NotebookRange[],
|
||||
selections: vscode.NotebookRange[],
|
||||
viewColumn: vscode.ViewColumn | undefined
|
||||
) {
|
||||
super();
|
||||
this._register(this._webComm.onDidReceiveMessage(e => {
|
||||
this._onDidReceiveMessage.fire(e);
|
||||
}));
|
||||
this._selections = selections;
|
||||
this._visibleRanges = visibleRanges;
|
||||
this._viewColumn = viewColumn;
|
||||
}
|
||||
|
||||
get viewColumn(): vscode.ViewColumn | undefined {
|
||||
return this._viewColumn;
|
||||
}
|
||||
|
||||
set viewColumn(_value) {
|
||||
throw readonly('viewColumn');
|
||||
}
|
||||
|
||||
get kernel() {
|
||||
return this._kernel;
|
||||
}
|
||||
|
||||
set kernel(_kernel: vscode.NotebookKernel | undefined) {
|
||||
throw readonly('kernel');
|
||||
}
|
||||
|
||||
_acceptKernel(kernel?: vscode.NotebookKernel) {
|
||||
this._kernel = kernel;
|
||||
get apiEditor(): vscode.NotebookEditor {
|
||||
if (!this._editor) {
|
||||
const that = this;
|
||||
this._editor = {
|
||||
get document() {
|
||||
return that.notebookData.apiNotebook;
|
||||
},
|
||||
get selections() {
|
||||
return that._selections;
|
||||
},
|
||||
get visibleRanges() {
|
||||
return that._visibleRanges;
|
||||
},
|
||||
revealRange(range, revealType) {
|
||||
that._proxy.$tryRevealRange(
|
||||
that.id,
|
||||
extHostConverter.NotebookRange.from(range),
|
||||
revealType ?? extHostTypes.NotebookEditorRevealType.Default
|
||||
);
|
||||
},
|
||||
get viewColumn() {
|
||||
return that._viewColumn;
|
||||
},
|
||||
edit(callback) {
|
||||
const edit = new NotebookEditorCellEditBuilder(this.document.version);
|
||||
callback(edit);
|
||||
return that._applyEdit(edit.finalize());
|
||||
},
|
||||
setDecorations(decorationType, range) {
|
||||
return that.setDecorations(decorationType, range);
|
||||
}
|
||||
};
|
||||
}
|
||||
return this._editor;
|
||||
}
|
||||
|
||||
get visible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
set visible(_state: boolean) {
|
||||
throw readonly('visible');
|
||||
}
|
||||
|
||||
_acceptVisibility(value: boolean) {
|
||||
this._visible = value;
|
||||
}
|
||||
|
||||
get visibleRanges() {
|
||||
return this._visibleRanges;
|
||||
}
|
||||
|
||||
set visibleRanges(_range: vscode.NotebookCellRange[]) {
|
||||
throw readonly('visibleRanges');
|
||||
}
|
||||
|
||||
_acceptVisibleRanges(value: vscode.NotebookCellRange[]): void {
|
||||
_acceptVisibleRanges(value: vscode.NotebookRange[]): void {
|
||||
this._visibleRanges = value;
|
||||
}
|
||||
|
||||
get active(): boolean {
|
||||
return this._active;
|
||||
_acceptSelections(selections: vscode.NotebookRange[]): void {
|
||||
this._selections = selections;
|
||||
}
|
||||
|
||||
set active(_state: boolean) {
|
||||
throw readonly('active');
|
||||
}
|
||||
|
||||
_acceptActive(value: boolean) {
|
||||
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());
|
||||
_acceptViewColumn(value: vscode.ViewColumn | undefined) {
|
||||
this._viewColumn = value;
|
||||
}
|
||||
|
||||
private _applyEdit(editData: INotebookEditData): Promise<boolean> {
|
||||
@@ -206,9 +171,9 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
const prevIndex = compressedEditsIndex;
|
||||
const prev = compressedEdits[prevIndex];
|
||||
|
||||
if (prev.editType === CellEditType.Replace && editData.cellEdits[i].editType === CellEditType.Replace) {
|
||||
const edit = editData.cellEdits[i];
|
||||
if ((edit.editType !== CellEditType.DocumentMetadata) && prev.index === edit.index) {
|
||||
const edit = editData.cellEdits[i];
|
||||
if (prev.editType === CellEditType.Replace && edit.editType === CellEditType.Replace) {
|
||||
if (prev.index === edit.index) {
|
||||
prev.cells.push(...(editData.cellEdits[i] as ICellReplaceEdit).cells);
|
||||
prev.count += (editData.cellEdits[i] as ICellReplaceEdit).count;
|
||||
continue;
|
||||
@@ -219,42 +184,24 @@ 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.id, editData.documentVersionId, compressedEdits);
|
||||
}
|
||||
|
||||
setDecorations(decorationType: vscode.NotebookEditorDecorationType, range: vscode.NotebookCellRange): void {
|
||||
const willBeEmpty = (range.start === range.end);
|
||||
if (willBeEmpty && !this._hasDecorationsForKey[decorationType.key]) {
|
||||
setDecorations(decorationType: vscode.NotebookEditorDecorationType, range: vscode.NotebookRange): void {
|
||||
if (range.isEmpty && !this._hasDecorationsForKey.has(decorationType.key)) {
|
||||
// avoid no-op call to the renderer
|
||||
return;
|
||||
}
|
||||
if (willBeEmpty) {
|
||||
delete this._hasDecorationsForKey[decorationType.key];
|
||||
if (range.isEmpty) {
|
||||
this._hasDecorationsForKey.delete(decorationType.key);
|
||||
} else {
|
||||
this._hasDecorationsForKey[decorationType.key] = true;
|
||||
this._hasDecorationsForKey.add(decorationType.key);
|
||||
}
|
||||
|
||||
return this._proxy.$trySetDecorations(
|
||||
this.id,
|
||||
range,
|
||||
extHostConverter.NotebookRange.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();
|
||||
}
|
||||
}
|
||||
|
||||
259
src/vs/workbench/api/common/extHostNotebookKernels.ts
Normal file
259
src/vs/workbench/api/common/extHostNotebookKernels.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ExtHostNotebookKernelsShape, IMainContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { asWebviewUri } from 'vs/workbench/api/common/shared/webview';
|
||||
|
||||
interface IKernelData {
|
||||
extensionId: ExtensionIdentifier,
|
||||
controller: vscode.NotebookController;
|
||||
onDidChangeSelection: Emitter<{ selected: boolean; notebook: vscode.NotebookDocument; }>;
|
||||
onDidReceiveMessage: Emitter<{ editor: vscode.NotebookEditor, message: any }>;
|
||||
}
|
||||
|
||||
export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
|
||||
|
||||
private readonly _proxy: MainThreadNotebookKernelsShape;
|
||||
|
||||
private readonly _kernelData = new Map<number, IKernelData>();
|
||||
private _handlePool: number = 0;
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
private readonly _initData: IExtHostInitDataService,
|
||||
private readonly _extHostNotebook: ExtHostNotebookController
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadNotebookKernels);
|
||||
}
|
||||
|
||||
createNotebookController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler?: vscode.NotebookExecuteHandler, preloads?: vscode.NotebookKernelPreload[]): vscode.NotebookController {
|
||||
|
||||
for (let data of this._kernelData.values()) {
|
||||
if (data.controller.id === id && ExtensionIdentifier.equals(extension.identifier, data.extensionId)) {
|
||||
throw new Error(`notebook controller with id '${id}' ALREADY exist`);
|
||||
}
|
||||
}
|
||||
|
||||
const handle = this._handlePool++;
|
||||
const that = this;
|
||||
|
||||
const _defaultExecutHandler = () => console.warn(`NO execute handler from notebook controller '${data.id}' of extension: '${extension.identifier}'`);
|
||||
|
||||
let isDisposed = false;
|
||||
const commandDisposables = new DisposableStore();
|
||||
|
||||
const onDidChangeSelection = new Emitter<{ selected: boolean, notebook: vscode.NotebookDocument }>();
|
||||
const onDidReceiveMessage = new Emitter<{ editor: vscode.NotebookEditor, message: any }>();
|
||||
|
||||
const data: INotebookKernelDto2 = {
|
||||
id: `${extension.identifier.value}/${id}`,
|
||||
viewType,
|
||||
extensionId: extension.identifier,
|
||||
extensionLocation: extension.extensionLocation,
|
||||
label: label || extension.identifier.value,
|
||||
preloads: preloads ? preloads.map(extHostTypeConverters.NotebookKernelPreload.from) : []
|
||||
};
|
||||
|
||||
//
|
||||
let _executeHandler: vscode.NotebookExecuteHandler = handler ?? _defaultExecutHandler;
|
||||
let _interruptHandler: vscode.NotebookInterruptHandler | undefined;
|
||||
|
||||
// todo@jrieken the selector needs to be massaged
|
||||
this._proxy.$addKernel(handle, data).catch(err => {
|
||||
// this can happen when a kernel with that ID is already registered
|
||||
console.log(err);
|
||||
isDisposed = true;
|
||||
});
|
||||
|
||||
// update: all setters write directly into the dto object
|
||||
// and trigger an update. the actual update will only happen
|
||||
// once per event loop execution
|
||||
let tokenPool = 0;
|
||||
const _update = () => {
|
||||
if (isDisposed) {
|
||||
return;
|
||||
}
|
||||
const myToken = ++tokenPool;
|
||||
Promise.resolve().then(() => {
|
||||
if (myToken === tokenPool) {
|
||||
this._proxy.$updateKernel(handle, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const controller: vscode.NotebookController = {
|
||||
get id() { return id; },
|
||||
get viewType() { return data.viewType; },
|
||||
onDidChangeNotebookAssociation: onDidChangeSelection.event,
|
||||
get label() {
|
||||
return data.label;
|
||||
},
|
||||
set label(value) {
|
||||
data.label = value ?? extension.displayName ?? extension.name;
|
||||
_update();
|
||||
},
|
||||
get detail() {
|
||||
return data.detail ?? '';
|
||||
},
|
||||
set detail(value) {
|
||||
data.detail = value;
|
||||
_update();
|
||||
},
|
||||
get description() {
|
||||
return data.description ?? '';
|
||||
},
|
||||
set description(value) {
|
||||
data.description = value;
|
||||
_update();
|
||||
},
|
||||
get supportedLanguages() {
|
||||
return data.supportedLanguages;
|
||||
},
|
||||
set supportedLanguages(value) {
|
||||
data.supportedLanguages = value;
|
||||
_update();
|
||||
},
|
||||
get hasExecutionOrder() {
|
||||
return data.hasExecutionOrder ?? false;
|
||||
},
|
||||
set hasExecutionOrder(value) {
|
||||
data.hasExecutionOrder = value;
|
||||
_update();
|
||||
},
|
||||
get preloads() {
|
||||
return data.preloads ? data.preloads.map(extHostTypeConverters.NotebookKernelPreload.to) : [];
|
||||
},
|
||||
get executeHandler() {
|
||||
return _executeHandler;
|
||||
},
|
||||
set executeHandler(value) {
|
||||
_executeHandler = value ?? _defaultExecutHandler;
|
||||
},
|
||||
get interruptHandler() {
|
||||
return _interruptHandler;
|
||||
},
|
||||
set interruptHandler(value) {
|
||||
_interruptHandler = value;
|
||||
data.supportsInterrupt = Boolean(value);
|
||||
_update();
|
||||
},
|
||||
createNotebookCellExecutionTask(cell) {
|
||||
if (isDisposed) {
|
||||
throw new Error('notebook controller is DISPOSED');
|
||||
}
|
||||
//todo@jrieken
|
||||
return that._extHostNotebook.createNotebookCellExecution(cell.notebook.uri, cell.index, data.id)!;
|
||||
},
|
||||
dispose: () => {
|
||||
if (!isDisposed) {
|
||||
isDisposed = true;
|
||||
this._kernelData.delete(handle);
|
||||
commandDisposables.dispose();
|
||||
onDidChangeSelection.dispose();
|
||||
onDidReceiveMessage.dispose();
|
||||
this._proxy.$removeKernel(handle);
|
||||
}
|
||||
},
|
||||
// --- ipc
|
||||
onDidReceiveMessage: onDidReceiveMessage.event,
|
||||
postMessage(message, editor) {
|
||||
return that._proxy.$postMessage(handle, editor && that._extHostNotebook.getIdByEditor(editor), message);
|
||||
},
|
||||
asWebviewUri(uri: URI) {
|
||||
return asWebviewUri(that._initData.environment, String(handle), uri);
|
||||
},
|
||||
// --- priority
|
||||
updateNotebookAffinity(notebook, priority) {
|
||||
that._proxy.$updateNotebookPriority(handle, notebook.uri, priority);
|
||||
}
|
||||
};
|
||||
|
||||
this._kernelData.set(handle, { extensionId: extension.identifier, controller, onDidChangeSelection, onDidReceiveMessage });
|
||||
return controller;
|
||||
}
|
||||
|
||||
$acceptSelection(handle: number, uri: UriComponents, value: boolean): void {
|
||||
const obj = this._kernelData.get(handle);
|
||||
if (obj) {
|
||||
obj.onDidChangeSelection.fire({
|
||||
selected: value,
|
||||
notebook: this._extHostNotebook.lookupNotebookDocument(URI.revive(uri))!.apiNotebook
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async $executeCells(handle: number, uri: UriComponents, handles: number[]): Promise<void> {
|
||||
const obj = this._kernelData.get(handle);
|
||||
if (!obj) {
|
||||
// extension can dispose kernels in the meantime
|
||||
return;
|
||||
}
|
||||
const document = this._extHostNotebook.lookupNotebookDocument(URI.revive(uri));
|
||||
if (!document) {
|
||||
throw new Error('MISSING notebook');
|
||||
}
|
||||
|
||||
const cells: vscode.NotebookCell[] = [];
|
||||
for (let cellHandle of handles) {
|
||||
const cell = document.getCell(cellHandle);
|
||||
if (cell) {
|
||||
cells.push(cell.apiCell);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await obj.controller.executeHandler.call(obj.controller, cells, document.apiNotebook, obj.controller);
|
||||
} catch (err) {
|
||||
//
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async $cancelCells(handle: number, uri: UriComponents, handles: number[]): Promise<void> {
|
||||
const obj = this._kernelData.get(handle);
|
||||
if (!obj) {
|
||||
// extension can dispose kernels in the meantime
|
||||
return;
|
||||
}
|
||||
const document = this._extHostNotebook.lookupNotebookDocument(URI.revive(uri));
|
||||
if (!document) {
|
||||
throw new Error('MISSING notebook');
|
||||
}
|
||||
if (obj.controller.interruptHandler) {
|
||||
await obj.controller.interruptHandler.call(obj.controller, document.apiNotebook);
|
||||
}
|
||||
|
||||
// we do both? interrupt and cancellation or should we be selective?
|
||||
for (let cellHandle of handles) {
|
||||
const cell = document.getCell(cellHandle);
|
||||
if (cell) {
|
||||
this._extHostNotebook.cancelOneNotebookCellExecution(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$acceptRendererMessage(handle: number, editorId: string, message: any): void {
|
||||
const obj = this._kernelData.get(handle);
|
||||
if (!obj) {
|
||||
// extension can dispose kernels in the meantime
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = this._extHostNotebook.getEditorById(editorId);
|
||||
if (!editor) {
|
||||
throw new Error(`send message for UNKNOWN editor: ${editorId}`);
|
||||
}
|
||||
|
||||
obj.onDidReceiveMessage.fire(Object.freeze({ editor: editor.apiEditor, message }));
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ export abstract class AbstractExtHostOutputChannel extends Disposable implements
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
if (!this._disposed) {
|
||||
@@ -90,7 +90,7 @@ export class ExtHostPushOutputChannel extends AbstractExtHostOutputChannel {
|
||||
super(name, false, undefined, proxy);
|
||||
}
|
||||
|
||||
append(value: string): void {
|
||||
override append(value: string): void {
|
||||
super.append(value);
|
||||
this._id.then(id => this._proxy.$append(id, value));
|
||||
this._onDidAppend.fire();
|
||||
@@ -103,7 +103,7 @@ class ExtHostLogFileOutputChannel extends AbstractExtHostOutputChannel {
|
||||
super(name, true, file, proxy);
|
||||
}
|
||||
|
||||
append(value: string): void {
|
||||
override append(value: string): void {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class ExtHostProgress implements ExtHostProgressShape {
|
||||
withProgress<R>(extension: IExtensionDescription, options: ProgressOptions, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Thenable<R> {
|
||||
const handle = this._handles++;
|
||||
const { title, location, cancellable } = options;
|
||||
const source = localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
|
||||
const source = { label: localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name), id: extension.identifier.value };
|
||||
|
||||
this._proxy.$startProgress(handle, { location: ProgressLocation.from(location), title, source, cancellable }, extension);
|
||||
return this._withProgress(handle, task, !!cancellable);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -259,6 +259,22 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
|
||||
// noop
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
checkProposedApiEnabled(this._extension);
|
||||
|
||||
if (!this._visible) {
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
this._proxy.$setInputBoxFocus(this._sourceControlHandle);
|
||||
}
|
||||
|
||||
showValidationMessage(message: string, type: vscode.SourceControlInputBoxValidationType) {
|
||||
checkProposedApiEnabled(this._extension);
|
||||
|
||||
this._proxy.$showValidationMessage(this._sourceControlHandle, message, type as any);
|
||||
}
|
||||
|
||||
$onInputBoxValueChange(value: string): void {
|
||||
this.updateValue(value);
|
||||
}
|
||||
|
||||
31
src/vs/workbench/api/common/extHostTelemetry.ts
Normal file
31
src/vs/workbench/api/common/extHostTelemetry.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostTelemetryShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
export class ExtHostTelemetry implements ExtHostTelemetryShape {
|
||||
private readonly _onDidChangeTelemetryEnabled = new Emitter<boolean>();
|
||||
readonly onDidChangeTelemetryEnabled: Event<boolean> = this._onDidChangeTelemetryEnabled.event;
|
||||
|
||||
private _enabled: boolean = false;
|
||||
|
||||
getTelemetryEnabled(): boolean {
|
||||
return this._enabled;
|
||||
}
|
||||
|
||||
$initializeTelemetryEnabled(enabled: boolean): void {
|
||||
this._enabled = enabled;
|
||||
}
|
||||
|
||||
$onDidChangeTelemetryEnabled(enabled: boolean): void {
|
||||
this._enabled = enabled;
|
||||
this._onDidChangeTelemetryEnabled.fire(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
export const IExtHostTelemetry = createDecorator<IExtHostTelemetry>('IExtHostTelemetry');
|
||||
export interface IExtHostTelemetry extends ExtHostTelemetry, ExtHostTelemetryShape { }
|
||||
@@ -5,22 +5,23 @@
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
|
||||
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 { URI } from 'vs/base/common/uri';
|
||||
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 { IShellLaunchConfigDto, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
|
||||
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
|
||||
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
|
||||
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
|
||||
|
||||
@@ -116,23 +117,26 @@ export class ExtHostTerminal {
|
||||
shellPath?: string,
|
||||
shellArgs?: string[] | string,
|
||||
cwd?: string | URI,
|
||||
env?: { [key: string]: string | null },
|
||||
env?: ITerminalEnvironment,
|
||||
icon?: string,
|
||||
initialText?: string,
|
||||
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, icon, initialText, waitOnExit, strictEnv, hideFromUser, isFeatureTerminal, isExtensionOwnedTerminal });
|
||||
}
|
||||
|
||||
public async createExtensionTerminal(): Promise<number> {
|
||||
if (typeof this._id !== 'string') {
|
||||
throw new Error('Terminal has already been created');
|
||||
}
|
||||
await this._proxy.$createTerminal(this._id, { name: this._name, isExtensionTerminal: true });
|
||||
await this._proxy.$createTerminal(this._id, { name: this._name, isExtensionCustomPtyTerminal: true });
|
||||
// At this point, the id has been set via `$acceptTerminalOpened`
|
||||
if (typeof this._id === 'string') {
|
||||
throw new Error('Terminal creation failed');
|
||||
@@ -184,6 +188,9 @@ export class ExtHostTerminal {
|
||||
}
|
||||
|
||||
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>();
|
||||
@@ -194,6 +201,9 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
|
||||
public readonly onProcessTitleChanged: Event<string> = this._onProcessTitleChanged.event;
|
||||
private readonly _onProcessOverrideDimensions = new Emitter<ITerminalDimensionsOverride | undefined>();
|
||||
public get onProcessOverrideDimensions(): Event<ITerminalDimensionsOverride | undefined> { return this._onProcessOverrideDimensions.event; }
|
||||
private readonly _onProcessShellTypeChanged = new Emitter<TerminalShellType>();
|
||||
public readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
|
||||
|
||||
|
||||
constructor(private readonly _pty: vscode.Pseudoterminal) { }
|
||||
|
||||
@@ -217,6 +227,10 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
|
||||
}
|
||||
}
|
||||
|
||||
async processBinary(data: string): Promise<void> {
|
||||
// No-op, processBinary is not supported in extextion owned terminals.
|
||||
}
|
||||
|
||||
acknowledgeDataEvent(charCount: number): void {
|
||||
// No-op, flow control is not supported in extension owned terminals. If this is ever
|
||||
// implemented it will need new pause and resume VS Code APIs.
|
||||
@@ -320,10 +334,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal;
|
||||
public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
|
||||
public abstract getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
|
||||
public abstract $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined>;
|
||||
public abstract $getAvailableShells(): Promise<IShellDefinitionDto[]>;
|
||||
public abstract $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]>;
|
||||
public abstract $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||
public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
|
||||
|
||||
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal {
|
||||
const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name);
|
||||
@@ -764,27 +776,18 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
}
|
||||
|
||||
public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string {
|
||||
// Return the empty string to avoid throwing
|
||||
return '';
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string {
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined> {
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public $getAvailableShells(): Promise<IShellDefinitionDto[]> {
|
||||
public $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]> {
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public async $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto> {
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void {
|
||||
// No-op for web worker ext host as workspace permissions aren't used
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
44
src/vs/workbench/api/common/extHostTestingPrivateApi.ts
Normal file
44
src/vs/workbench/api/common/extHostTestingPrivateApi.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { TestItemImpl } from 'vs/workbench/api/common/extHostTypes';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export const enum ExtHostTestItemEventType {
|
||||
NewChild,
|
||||
Disposed,
|
||||
Invalidated,
|
||||
SetProp,
|
||||
}
|
||||
|
||||
export type ExtHostTestItemEvent =
|
||||
| [evt: ExtHostTestItemEventType.NewChild, item: TestItemImpl]
|
||||
| [evt: ExtHostTestItemEventType.Disposed]
|
||||
| [evt: ExtHostTestItemEventType.Invalidated]
|
||||
| [evt: ExtHostTestItemEventType.SetProp, key: keyof vscode.TestItem<never>, value: any];
|
||||
|
||||
export interface IExtHostTestItemApi {
|
||||
children: Map<string, TestItemImpl>;
|
||||
parent?: TestItemImpl;
|
||||
bus: Emitter<ExtHostTestItemEvent>;
|
||||
}
|
||||
|
||||
const eventPrivateApis = new WeakMap<TestItemImpl, IExtHostTestItemApi>();
|
||||
|
||||
/**
|
||||
* Gets the private API for a test item implementation. This implementation
|
||||
* is a managed object, but we keep a weakmap to avoid exposing any of the
|
||||
* internals to extensions.
|
||||
*/
|
||||
export const getPrivateApiFor = (impl: TestItemImpl) => {
|
||||
let api = eventPrivateApis.get(impl);
|
||||
if (!api) {
|
||||
api = { children: new Map(), bus: new Emitter() };
|
||||
eventPrivateApis.set(impl, api);
|
||||
}
|
||||
|
||||
return api;
|
||||
};
|
||||
@@ -86,7 +86,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; },
|
||||
@@ -112,7 +112,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();
|
||||
}
|
||||
@@ -765,7 +767,7 @@ export class ExtHostTreeView<T> extends Disposable {
|
||||
this.nodes.clear();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override dispose() {
|
||||
this._refreshCancellationSource.dispose();
|
||||
|
||||
this.clearAll();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import * as vscode from 'vscode';
|
||||
import { RemoteTunnel, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { ProvidedPortAttributes, RemoteTunnel, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
@@ -46,6 +46,7 @@ export interface IExtHostTunnelService extends ExtHostTunnelServiceShape {
|
||||
getTunnels(): Promise<vscode.TunnelDescription[]>;
|
||||
onDidChangeTunnels: vscode.Event<void>;
|
||||
setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable>;
|
||||
registerPortsAttributesProvider(portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: vscode.PortAttributesProvider): IDisposable;
|
||||
}
|
||||
|
||||
export const IExtHostTunnelService = createDecorator<IExtHostTunnelService>('IExtHostTunnelService');
|
||||
@@ -71,6 +72,14 @@ export class ExtHostTunnelService implements IExtHostTunnelService {
|
||||
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
|
||||
return { dispose: () => { } };
|
||||
}
|
||||
registerPortsAttributesProvider(portSelector: { pid?: number, portRange?: [number, number] }, provider: vscode.PortAttributesProvider) {
|
||||
return { dispose: () => { } };
|
||||
}
|
||||
|
||||
async $providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: vscode.CancellationToken): Promise<ProvidedPortAttributes[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
async $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto | undefined> { return undefined; }
|
||||
async $closeTunnel(remote: { host: string, port: number }): Promise<void> { }
|
||||
async $onDidTunnelsChange(): Promise<void> { }
|
||||
|
||||
@@ -3,36 +3,37 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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 { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import type * as vscode from 'vscode';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import * as editorRange from 'vs/editor/common/core/range';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import * as htmlContent from 'vs/base/common/htmlContent';
|
||||
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
|
||||
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 { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import * as marked from 'vs/base/common/marked/marked';
|
||||
import { parse } from 'vs/base/common/marshalling';
|
||||
import { cloneAndChange } from 'vs/base/common/objects';
|
||||
import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log';
|
||||
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { isDefined, isNumber, isString } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import * as editorRange from 'vs/editor/common/core/range';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import { IContentDecorationRenderOptions, IDecorationOptions, IDecorationRenderOptions, IThemeDecorationRenderOptions } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
|
||||
import { EditorOverride, ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IMarkerData, IRelatedInformation, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers';
|
||||
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
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 { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
|
||||
import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { ISerializedTestResults, ITestItem, ITestMessage, SerializedTestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as types from './extHostTypes';
|
||||
|
||||
export interface PositionLike {
|
||||
line: number;
|
||||
@@ -509,7 +510,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 +545,60 @@ export namespace WorkspaceEdit {
|
||||
resource: entry.uri,
|
||||
edit: entry.edit,
|
||||
notebookMetadata: entry.notebookMetadata,
|
||||
notebookVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version
|
||||
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.apiNotebook.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.PartialMetadata,
|
||||
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)?.apiNotebook.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,
|
||||
outputId: entry.outputId,
|
||||
items: entry.newOutputItems?.map(NotebookCellOutputItem.from) || [],
|
||||
append: entry.append
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -736,8 +790,8 @@ export namespace location {
|
||||
};
|
||||
}
|
||||
|
||||
export function to(value: modes.Location): types.Location {
|
||||
return new types.Location(value.uri, Range.to(value.range));
|
||||
export function to(value: extHostProtocol.ILocationDto): types.Location {
|
||||
return new types.Location(URI.revive(value.uri), Range.to(value.range));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,9 +810,9 @@ export namespace DefinitionLink {
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
export function to(value: modes.LocationLink): vscode.LocationLink {
|
||||
export function to(value: extHostProtocol.IDefinitionLinkDto): vscode.LocationLink {
|
||||
return {
|
||||
targetUri: value.uri,
|
||||
targetUri: URI.revive(value.uri),
|
||||
targetRange: Range.to(value.range),
|
||||
targetSelectionRange: value.targetSelectionRange
|
||||
? Range.to(value.targetSelectionRange)
|
||||
@@ -796,6 +850,67 @@ 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>{
|
||||
frameId: inlineValueContext.frameId,
|
||||
stoppedLocation: Range.from(inlineValueContext.stoppedLocation)
|
||||
};
|
||||
}
|
||||
|
||||
export function to(inlineValueContext: extHostProtocol.IInlineValueContextDto): types.InlineValueContext {
|
||||
return new types.InlineValueContext(inlineValueContext.frameId, Range.to(inlineValueContext.stoppedLocation));
|
||||
}
|
||||
}
|
||||
|
||||
export namespace DocumentHighlight {
|
||||
export function from(documentHighlight: vscode.DocumentHighlight): modes.DocumentHighlight {
|
||||
return {
|
||||
@@ -1021,6 +1136,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 +1144,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 +1345,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 +1395,154 @@ 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 NotebookRange {
|
||||
|
||||
export function from(range: vscode.NotebookRange): ICellRange {
|
||||
return { start: range.start, end: range.end };
|
||||
}
|
||||
|
||||
export function to(range: ICellRange): types.NotebookRange {
|
||||
return new types.NotebookRange(range.start, range.end);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellMetadata {
|
||||
|
||||
export function to(data: notebooks.NotebookCellMetadata): types.NotebookCellMetadata {
|
||||
return new types.NotebookCellMetadata().with({
|
||||
...data,
|
||||
...{
|
||||
executionOrder: null,
|
||||
lastRunSuccess: null,
|
||||
runState: null,
|
||||
runStartTime: null,
|
||||
runStartTimeAdjustment: null,
|
||||
runEndTime: null
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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().with(data);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellPreviousExecutionResult {
|
||||
export function to(data: notebooks.NotebookCellMetadata): vscode.NotebookCellExecutionSummary {
|
||||
return {
|
||||
startTime: data.runStartTime,
|
||||
endTime: data.runEndTime,
|
||||
executionOrder: data.executionOrder,
|
||||
success: data.lastRunSuccess
|
||||
};
|
||||
}
|
||||
|
||||
export function from(data: vscode.NotebookCellExecutionSummary): Partial<notebooks.NotebookCellMetadata> {
|
||||
return {
|
||||
lastRunSuccess: data.success,
|
||||
runStartTime: data.startTime,
|
||||
runEndTime: data.endTime,
|
||||
executionOrder: data.executionOrder
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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.kind),
|
||||
language: data.language,
|
||||
source: data.source,
|
||||
metadata: {
|
||||
...data.metadata,
|
||||
...NotebookCellPreviousExecutionResult.from(data.latestExecutionSummary ?? {})
|
||||
},
|
||||
outputs: data.outputs ? data.outputs.map(NotebookCellOutput.from) : []
|
||||
};
|
||||
}
|
||||
|
||||
export function to(data: notebooks.ICellDto2): vscode.NotebookCellData {
|
||||
return new types.NotebookCellData(
|
||||
NotebookCellKind.to(data.cellKind),
|
||||
data.source,
|
||||
data.language,
|
||||
data.outputs ? data.outputs.map(NotebookCellOutput.to) : undefined,
|
||||
data.metadata ? NotebookCellMetadata.to(data.metadata) : undefined,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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 +1617,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,
|
||||
@@ -1377,65 +1626,170 @@ export namespace NotebookDecorationRenderOptions {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestState {
|
||||
export function from(item: vscode.TestState): ITestState {
|
||||
export namespace NotebookStatusBarItem {
|
||||
export function from(item: vscode.NotebookCellStatusBarItem, commandsConverter: CommandsConverter, disposables: DisposableStore): notebooks.INotebookCellStatusBarItem {
|
||||
const command = typeof item.command === 'string' ? { title: '', command: item.command } : item.command;
|
||||
return {
|
||||
runState: item.runState,
|
||||
duration: item.duration,
|
||||
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,
|
||||
})),
|
||||
alignment: item.alignment === types.NotebookCellStatusBarAlignment.Left ? notebooks.CellStatusbarAlignment.Left : notebooks.CellStatusbarAlignment.Right,
|
||||
command: commandsConverter.toInternal(command, disposables), // TODO@roblou
|
||||
text: item.text,
|
||||
tooltip: item.tooltip,
|
||||
accessibilityInformation: item.accessibilityInformation,
|
||||
priority: item.priority
|
||||
};
|
||||
}
|
||||
|
||||
export function to(item: ITestState): vscode.TestState {
|
||||
return new types.TestState(
|
||||
item.runState,
|
||||
item.messages.map(message => ({
|
||||
message: typeof message.message === 'string' ? message.message : MarkdownString.to(message.message),
|
||||
severity: message.severity,
|
||||
expectedOutput: message.expectedOutput,
|
||||
actualOutput: message.actualOutput,
|
||||
location: message.location && location.to({
|
||||
range: message.location.range,
|
||||
uri: URI.revive(message.location.uri)
|
||||
}),
|
||||
})),
|
||||
item.duration,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace TestItem {
|
||||
export function from(item: vscode.TestItem, parentExtId?: string): ITestItem {
|
||||
export namespace NotebookDocumentContentOptions {
|
||||
export function from(options: vscode.NotebookDocumentContentOptions | undefined): notebooks.TransientOptions {
|
||||
return {
|
||||
extId: item.id ?? (parentExtId ? `${parentExtId}\0${item.label}` : item.label),
|
||||
label: item.label,
|
||||
location: item.location ? location.from(item.location) : undefined,
|
||||
debuggable: item.debuggable ?? false,
|
||||
description: item.description,
|
||||
runnable: item.runnable ?? true,
|
||||
state: TestState.from(item.state),
|
||||
transientOutputs: options?.transientOutputs ?? false,
|
||||
transientCellMetadata: {
|
||||
...options?.transientCellMetadata,
|
||||
executionOrder: true,
|
||||
runState: true,
|
||||
runStartTime: true,
|
||||
runStartTimeAdjustment: true,
|
||||
runEndTime: true,
|
||||
lastRunSuccess: true
|
||||
},
|
||||
transientDocumentMetadata: options?.transientDocumentMetadata ?? {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookKernelPreload {
|
||||
export function from(preload: vscode.NotebookKernelPreload): { uri: UriComponents; provides: string[] } {
|
||||
return {
|
||||
uri: preload.uri,
|
||||
provides: typeof preload.provides === 'string'
|
||||
? [preload.provides]
|
||||
: preload.provides ?? []
|
||||
};
|
||||
}
|
||||
export function to(preload: { uri: UriComponents; provides: string[] }): vscode.NotebookKernelPreload {
|
||||
return {
|
||||
uri: URI.revive(preload.uri),
|
||||
provides: preload.provides
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestMessage {
|
||||
export function from(message: vscode.TestMessage): ITestMessage {
|
||||
return {
|
||||
message: MarkdownString.fromStrict(message.message) || '',
|
||||
severity: message.severity,
|
||||
expectedOutput: message.expectedOutput,
|
||||
actualOutput: message.actualOutput,
|
||||
location: message.location ? location.from(message.location) as any : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function toShallow(item: ITestItem): Omit<vscode.RequiredTestItem, 'children'> {
|
||||
export function to(item: ITestMessage): vscode.TestMessage {
|
||||
const message = new types.TestMessage(typeof item.message === 'string' ? item.message : MarkdownString.to(item.message));
|
||||
message.severity = item.severity;
|
||||
message.actualOutput = item.actualOutput;
|
||||
message.expectedOutput = item.expectedOutput;
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestItem {
|
||||
export type Raw<T = unknown> = vscode.TestItem<T>;
|
||||
|
||||
export function from(item: vscode.TestItem<unknown>): ITestItem {
|
||||
return {
|
||||
extId: item.id,
|
||||
label: item.label,
|
||||
uri: item.uri,
|
||||
range: Range.from(item.range) || null,
|
||||
debuggable: item.debuggable ?? false,
|
||||
description: item.description || null,
|
||||
runnable: item.runnable ?? true,
|
||||
error: item.error ? (MarkdownString.fromStrict(item.error) || null) : null,
|
||||
};
|
||||
}
|
||||
|
||||
export function fromResultSnapshot(item: vscode.TestResultSnapshot): ITestItem {
|
||||
return {
|
||||
extId: item.id,
|
||||
label: item.label,
|
||||
uri: item.uri,
|
||||
range: Range.from(item.range) || null,
|
||||
debuggable: false,
|
||||
description: item.description || null,
|
||||
error: null,
|
||||
runnable: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function toPlain(item: ITestItem): Omit<vscode.TestItem<never>, 'children' | 'invalidate' | 'discoverChildren'> {
|
||||
return {
|
||||
id: item.extId,
|
||||
label: item.label,
|
||||
location: item.location && location.to({
|
||||
range: item.location.range,
|
||||
uri: URI.revive(item.location.uri)
|
||||
}),
|
||||
uri: URI.revive(item.uri),
|
||||
range: Range.to(item.range || undefined),
|
||||
addChild: () => undefined,
|
||||
dispose: () => undefined,
|
||||
status: types.TestItemStatus.Pending,
|
||||
data: undefined as never,
|
||||
debuggable: item.debuggable,
|
||||
description: item.description,
|
||||
description: item.description || undefined,
|
||||
runnable: item.runnable,
|
||||
state: TestState.to(item.state),
|
||||
};
|
||||
}
|
||||
|
||||
export function to(item: ITestItem): types.TestItemImpl {
|
||||
const testItem = new types.TestItemImpl(item.extId, item.label, URI.revive(item.uri), undefined);
|
||||
testItem.range = Range.to(item.range || undefined);
|
||||
testItem.debuggable = item.debuggable;
|
||||
testItem.description = item.description || undefined;
|
||||
testItem.runnable = item.runnable;
|
||||
return testItem;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestResults {
|
||||
const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map<string, SerializedTestResultItem>): vscode.TestResultSnapshot => ({
|
||||
...TestItem.toPlain(item.item),
|
||||
taskStates: item.tasks.map(t => ({
|
||||
state: t.state,
|
||||
duration: t.duration,
|
||||
messages: t.messages.map(TestMessage.to),
|
||||
})),
|
||||
children: item.children
|
||||
.map(c => byInternalId.get(c))
|
||||
.filter(isDefined)
|
||||
.map(c => convertTestResultItem(c, byInternalId)),
|
||||
});
|
||||
|
||||
export function to(serialized: ISerializedTestResults): vscode.TestRunResult {
|
||||
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)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace CodeActionTriggerKind {
|
||||
|
||||
export function to(value: modes.CodeActionTriggerType): types.CodeActionTriggerKind {
|
||||
switch (value) {
|
||||
case modes.CodeActionTriggerType.Invoke:
|
||||
return types.CodeActionTriggerKind.Invoke;
|
||||
|
||||
case modes.CodeActionTriggerType.Auto:
|
||||
return types.CodeActionTriggerKind.Automatic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,15 @@ import { coalesceInPlace, equals } from 'vs/base/common/arrays';
|
||||
import { illegalArgument } from 'vs/base/common/errors';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { ReadonlyMapView, ResourceMap } from 'vs/base/common/map';
|
||||
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
|
||||
import { isStringArray } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
|
||||
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { addIdToOutput, CellEditType, ICellEditOperation, ICellOutputEdit, ITransformedDisplayOutputDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { getPrivateApiFor, ExtHostTestItemEventType, IExtHostTestItemApi } from 'vs/workbench/api/common/extHostTestingPrivateApi';
|
||||
import { CellEditType, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
function es5ClassCompat(target: Function): any {
|
||||
@@ -419,7 +421,7 @@ export class Selection extends Range {
|
||||
return this._anchor === this._end;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
override toJSON() {
|
||||
return {
|
||||
start: this.start,
|
||||
end: this.end,
|
||||
@@ -585,7 +587,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 +616,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;
|
||||
}
|
||||
|
||||
@@ -638,41 +675,40 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
|
||||
// --- notebook
|
||||
|
||||
replaceNotebookMetadata(uri: URI, value: vscode.NotebookDocumentMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.DocumentMetadata, metadata: { ...notebookDocumentMetadataDefaults, ...value } }, notebookMetadata: value });
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.DocumentMetadata, metadata: value }, notebookMetadata: value });
|
||||
}
|
||||
|
||||
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)) })) } });
|
||||
replaceNotebookCells(uri: URI, range: vscode.NotebookRange, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void;
|
||||
replaceNotebookCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void;
|
||||
replaceNotebookCells(uri: URI, startOrRange: number | vscode.NotebookRange, endOrCells: number | vscode.NotebookCellData[], cellsOrMetadata?: vscode.NotebookCellData[] | vscode.WorkspaceEditEntryMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
let start: number | undefined;
|
||||
let end: number | undefined;
|
||||
let cellData: vscode.NotebookCellData[] = [];
|
||||
let workspaceEditMetadata: vscode.WorkspaceEditEntryMetadata | undefined;
|
||||
|
||||
if (NotebookRange.isNotebookRange(startOrRange) && NotebookCellData.isNotebookCellDataArray(endOrCells) && !NotebookCellData.isNotebookCellDataArray(cellsOrMetadata)) {
|
||||
start = startOrRange.start;
|
||||
end = startOrRange.end;
|
||||
cellData = endOrCells;
|
||||
workspaceEditMetadata = cellsOrMetadata;
|
||||
} else if (typeof startOrRange === 'number' && typeof endOrCells === 'number' && NotebookCellData.isNotebookCellDataArray(cellsOrMetadata)) {
|
||||
start = startOrRange;
|
||||
end = endOrCells;
|
||||
cellData = cellsOrMetadata;
|
||||
workspaceEditMetadata = metadata;
|
||||
}
|
||||
|
||||
if (start === undefined || end === undefined) {
|
||||
throw new Error('Invalid arguments');
|
||||
}
|
||||
|
||||
if (start !== end || cellData.length > 0) {
|
||||
this._edits.push({ _type: FileEditType.CellReplace, uri, index: start, count: end - start, cells: cellData, metadata: workspaceEditMetadata });
|
||||
}
|
||||
}
|
||||
|
||||
replaceNotebookCellOutput(uri: URI, index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._editNotebookCellOutput(uri, index, false, outputs, metadata);
|
||||
}
|
||||
|
||||
appendNotebookCellOutput(uri: URI, index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._editNotebookCellOutput(uri, index, true, outputs, metadata);
|
||||
}
|
||||
|
||||
private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
|
||||
const edit: ICellOutputEdit = {
|
||||
editType: CellEditType.Output,
|
||||
index,
|
||||
append,
|
||||
outputs: outputs.map(output => {
|
||||
if (NotebookCellOutput.isNotebookCellOutput(output)) {
|
||||
return output.toJSON();
|
||||
} else {
|
||||
return addIdToOutput(output);
|
||||
}
|
||||
})
|
||||
};
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit });
|
||||
}
|
||||
|
||||
replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Metadata, index, metadata: cellMetadata } });
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.PartialMetadata, index, metadata: cellMetadata } });
|
||||
}
|
||||
|
||||
// --- text
|
||||
@@ -1143,9 +1179,9 @@ export class DocumentSymbol {
|
||||
}
|
||||
|
||||
|
||||
export enum CodeActionTrigger {
|
||||
Automatic = 1,
|
||||
Manual = 2,
|
||||
export enum CodeActionTriggerKind {
|
||||
Invoke = 1,
|
||||
Automatic = 2,
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
@@ -1383,20 +1419,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2397,6 +2439,53 @@ 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 frameId: number;
|
||||
readonly stoppedLocation: vscode.Range;
|
||||
|
||||
constructor(frameId: number, range: vscode.Range) {
|
||||
this.frameId = frameId;
|
||||
this.stoppedLocation = range;
|
||||
}
|
||||
}
|
||||
|
||||
//#region file api
|
||||
|
||||
export enum FileChangeType {
|
||||
@@ -2807,6 +2896,201 @@ export enum ColorThemeKind {
|
||||
|
||||
//#region Notebook
|
||||
|
||||
export class NotebookRange {
|
||||
static isNotebookRange(thing: any): thing is vscode.NotebookRange {
|
||||
if (thing instanceof NotebookRange) {
|
||||
return true;
|
||||
}
|
||||
if (!thing) {
|
||||
return false;
|
||||
}
|
||||
return typeof (<NotebookRange>thing).start === 'number'
|
||||
&& typeof (<NotebookRange>thing).end === 'number';
|
||||
}
|
||||
|
||||
private _start: number;
|
||||
private _end: number;
|
||||
|
||||
get start() {
|
||||
return this._start;
|
||||
}
|
||||
|
||||
get end() {
|
||||
return this._end;
|
||||
}
|
||||
|
||||
get isEmpty(): boolean {
|
||||
return this._start === this._end;
|
||||
}
|
||||
|
||||
constructor(start: number, end: number) {
|
||||
if (start < 0) {
|
||||
throw illegalArgument('start must be positive');
|
||||
}
|
||||
if (end < 0) {
|
||||
throw illegalArgument('end must be positive');
|
||||
}
|
||||
if (start <= end) {
|
||||
this._start = start;
|
||||
this._end = end;
|
||||
} else {
|
||||
this._start = end;
|
||||
this._end = start;
|
||||
}
|
||||
}
|
||||
|
||||
with(change: { start?: number, end?: number }): NotebookRange {
|
||||
let start = this._start;
|
||||
let end = this._end;
|
||||
|
||||
if (change.start !== undefined) {
|
||||
start = change.start;
|
||||
}
|
||||
if (change.end !== undefined) {
|
||||
end = change.end;
|
||||
}
|
||||
if (start === this._start && end === this._end) {
|
||||
return this;
|
||||
}
|
||||
return new NotebookRange(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellMetadata {
|
||||
readonly inputCollapsed?: boolean;
|
||||
readonly outputCollapsed?: boolean;
|
||||
readonly [key: string]: any;
|
||||
|
||||
constructor(inputCollapsed?: boolean, outputCollapsed?: boolean);
|
||||
constructor(data: Record<string, any>);
|
||||
constructor(inputCollapsedOrData: (boolean | undefined) | Record<string, any>, outputCollapsed?: boolean) {
|
||||
if (typeof inputCollapsedOrData === 'object') {
|
||||
Object.assign(this, inputCollapsedOrData);
|
||||
} else {
|
||||
this.inputCollapsed = inputCollapsedOrData;
|
||||
this.outputCollapsed = outputCollapsed;
|
||||
}
|
||||
}
|
||||
|
||||
with(change: {
|
||||
inputCollapsed?: boolean | null,
|
||||
outputCollapsed?: boolean | null,
|
||||
[key: string]: any
|
||||
}): NotebookCellMetadata {
|
||||
|
||||
let { inputCollapsed, outputCollapsed, ...remaining } = change;
|
||||
|
||||
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 (inputCollapsed === this.inputCollapsed &&
|
||||
outputCollapsed === this.outputCollapsed &&
|
||||
Object.keys(remaining).length === 0
|
||||
) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new NotebookCellMetadata(
|
||||
{
|
||||
inputCollapsed,
|
||||
outputCollapsed,
|
||||
...remaining
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookDocumentMetadata {
|
||||
readonly trusted: boolean;
|
||||
readonly [key: string]: any;
|
||||
|
||||
constructor(trusted?: boolean);
|
||||
constructor(data: Record<string, any>);
|
||||
constructor(trustedOrData: boolean | Record<string, any> = true) {
|
||||
if (typeof trustedOrData === 'object') {
|
||||
Object.assign(this, trustedOrData);
|
||||
this.trusted = trustedOrData.trusted ?? true;
|
||||
} else {
|
||||
this.trusted = trustedOrData;
|
||||
}
|
||||
}
|
||||
|
||||
with(change: {
|
||||
trusted?: boolean | null,
|
||||
[key: string]: any
|
||||
}): NotebookDocumentMetadata {
|
||||
|
||||
let { trusted, ...remaining } = change;
|
||||
|
||||
if (trusted === undefined) {
|
||||
trusted = this.trusted;
|
||||
} else if (trusted === null) {
|
||||
trusted = undefined;
|
||||
}
|
||||
|
||||
if (trusted === this.trusted &&
|
||||
Object.keys(remaining).length === 0
|
||||
) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new NotebookDocumentMetadata(
|
||||
{
|
||||
trusted,
|
||||
...remaining
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellData {
|
||||
|
||||
static isNotebookCellDataArray(value: unknown): value is vscode.NotebookCellData[] {
|
||||
return Array.isArray(value) && (<unknown[]>value).every(elem => NotebookCellData.isNotebookCellData(elem));
|
||||
}
|
||||
|
||||
static isNotebookCellData(value: unknown): value is vscode.NotebookCellData {
|
||||
// return value instanceof NotebookCellData;
|
||||
return true;
|
||||
}
|
||||
|
||||
kind: NotebookCellKind;
|
||||
source: string;
|
||||
language: string;
|
||||
outputs?: NotebookCellOutput[];
|
||||
metadata?: NotebookCellMetadata;
|
||||
latestExecutionSummary?: vscode.NotebookCellExecutionSummary;
|
||||
|
||||
constructor(kind: NotebookCellKind, source: string, language: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, latestExecutionSummary?: vscode.NotebookCellExecutionSummary) {
|
||||
this.kind = kind;
|
||||
this.source = source;
|
||||
this.language = language;
|
||||
this.outputs = outputs ?? [];
|
||||
this.metadata = metadata;
|
||||
this.latestExecutionSummary = latestExecutionSummary;
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookData {
|
||||
|
||||
cells: NotebookCellData[];
|
||||
metadata: NotebookDocumentMetadata;
|
||||
|
||||
constructor(cells: NotebookCellData[], metadata?: NotebookDocumentMetadata) {
|
||||
this.cells = cells;
|
||||
this.metadata = metadata ?? new NotebookDocumentMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class NotebookCellOutputItem {
|
||||
|
||||
static isNotebookCellOutputItem(obj: unknown): obj is vscode.NotebookCellOutputItem {
|
||||
@@ -2814,64 +3098,47 @@ export class NotebookCellOutputItem {
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly mime: string,
|
||||
readonly value: unknown, // JSON'able
|
||||
readonly metadata?: Record<string, string | number | boolean>
|
||||
) { }
|
||||
public mime: string,
|
||||
public value: unknown, // JSON'able
|
||||
public metadata?: Record<string, any>
|
||||
) {
|
||||
if (isFalsyOrWhitespace(this.mime)) {
|
||||
throw new Error('INVALID mime type, must not be empty or falsy');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellOutput {
|
||||
|
||||
static isNotebookCellOutput(obj: unknown): obj is vscode.NotebookCellOutput {
|
||||
return obj instanceof NotebookCellOutput;
|
||||
}
|
||||
id: string;
|
||||
outputs: NotebookCellOutputItem[];
|
||||
metadata?: Record<string, any>;
|
||||
|
||||
readonly id: string = generateUuid();
|
||||
|
||||
constructor(readonly outputs: NotebookCellOutputItem[]) { }
|
||||
|
||||
toJSON(): ITransformedDisplayOutputDto {
|
||||
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;
|
||||
}
|
||||
constructor(
|
||||
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 {
|
||||
outputId: this.id,
|
||||
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,
|
||||
Success = 3,
|
||||
Error = 4
|
||||
}
|
||||
|
||||
export enum NotebookRunState {
|
||||
Running = 1,
|
||||
Idle = 2
|
||||
export enum NotebookCellExecutionState {
|
||||
Idle = 1,
|
||||
Pending = 2,
|
||||
Executing = 3,
|
||||
}
|
||||
|
||||
export enum NotebookCellStatusBarAlignment {
|
||||
@@ -2886,6 +3153,21 @@ export enum NotebookEditorRevealType {
|
||||
AtTop = 3
|
||||
}
|
||||
|
||||
export class NotebookCellStatusBarItem {
|
||||
constructor(
|
||||
public text: string,
|
||||
public alignment: NotebookCellStatusBarAlignment,
|
||||
public command?: string | vscode.Command,
|
||||
public tooltip?: string,
|
||||
public priority?: number,
|
||||
public accessibilityInformation?: vscode.AccessibilityInformation) { }
|
||||
}
|
||||
|
||||
|
||||
export enum NotebookControllerAffinity {
|
||||
Default = 1,
|
||||
Preferred = 2
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -2947,7 +3229,7 @@ export class LinkedEditingRanges {
|
||||
}
|
||||
|
||||
//#region Testing
|
||||
export enum TestRunState {
|
||||
export enum TestResultState {
|
||||
Unset = 0,
|
||||
Queued = 1,
|
||||
Running = 2,
|
||||
@@ -2964,34 +3246,131 @@ export enum TestMessageSeverity {
|
||||
Hint = 3
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class TestState {
|
||||
#runState: TestRunState;
|
||||
#duration?: number;
|
||||
#messages: ReadonlyArray<Readonly<vscode.TestMessage>>;
|
||||
export enum TestItemStatus {
|
||||
Pending = 0,
|
||||
Resolved = 1,
|
||||
}
|
||||
|
||||
public get runState() {
|
||||
return this.#runState;
|
||||
const testItemPropAccessor = <K extends keyof vscode.TestItem<never>>(
|
||||
api: IExtHostTestItemApi,
|
||||
key: K,
|
||||
defaultValue: vscode.TestItem<never>[K],
|
||||
equals: (a: vscode.TestItem<never>[K], b: vscode.TestItem<never>[K]) => boolean
|
||||
) => {
|
||||
let value = defaultValue;
|
||||
return {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get() {
|
||||
return value;
|
||||
},
|
||||
set(newValue: vscode.TestItem<never>[K]) {
|
||||
if (!equals(value, newValue)) {
|
||||
value = newValue;
|
||||
api.bus.fire([ExtHostTestItemEventType.SetProp, key, newValue]);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const strictEqualComparator = <T>(a: T, b: T) => a === b;
|
||||
const rangeComparator = (a: vscode.Range | undefined, b: vscode.Range | undefined) => {
|
||||
if (a === b) { return true; }
|
||||
if (!a || !b) { return false; }
|
||||
return a.isEqual(b);
|
||||
};
|
||||
|
||||
export class TestItemImpl implements vscode.TestItem<unknown> {
|
||||
public readonly id!: string;
|
||||
public readonly uri!: vscode.Uri;
|
||||
public readonly children!: ReadonlyMap<string, TestItemImpl>;
|
||||
public readonly parent!: TestItemImpl | undefined;
|
||||
|
||||
public range!: vscode.Range | undefined;
|
||||
public description!: string | undefined;
|
||||
public runnable!: boolean;
|
||||
public debuggable!: boolean;
|
||||
public error!: string | vscode.MarkdownString;
|
||||
public status!: vscode.TestItemStatus;
|
||||
|
||||
/** Extension-owned resolve handler */
|
||||
public resolveHandler?: (token: vscode.CancellationToken) => void;
|
||||
|
||||
constructor(id: string, public label: string, uri: vscode.Uri, public data: unknown) {
|
||||
const api = getPrivateApiFor(this);
|
||||
|
||||
Object.defineProperties(this, {
|
||||
id: {
|
||||
value: id,
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
},
|
||||
uri: {
|
||||
value: uri,
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
},
|
||||
parent: {
|
||||
enumerable: false,
|
||||
get: () => api.parent,
|
||||
},
|
||||
children: {
|
||||
value: new ReadonlyMapView(api.children),
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
},
|
||||
range: testItemPropAccessor(api, 'range', undefined, rangeComparator),
|
||||
description: testItemPropAccessor(api, 'description', undefined, strictEqualComparator),
|
||||
runnable: testItemPropAccessor(api, 'runnable', true, strictEqualComparator),
|
||||
debuggable: testItemPropAccessor(api, 'debuggable', true, strictEqualComparator),
|
||||
status: testItemPropAccessor(api, 'status', TestItemStatus.Resolved, strictEqualComparator),
|
||||
error: testItemPropAccessor(api, 'error', undefined, strictEqualComparator),
|
||||
});
|
||||
}
|
||||
|
||||
public get duration() {
|
||||
return this.#duration;
|
||||
public invalidate() {
|
||||
getPrivateApiFor(this).bus.fire([ExtHostTestItemEventType.Invalidated]);
|
||||
}
|
||||
|
||||
public get messages() {
|
||||
return this.#messages;
|
||||
public dispose() {
|
||||
const api = getPrivateApiFor(this);
|
||||
if (api.parent) {
|
||||
getPrivateApiFor(api.parent).children.delete(this.id);
|
||||
}
|
||||
|
||||
api.bus.fire([ExtHostTestItemEventType.Disposed]);
|
||||
}
|
||||
|
||||
constructor(runState: TestRunState, messages: vscode.TestMessage[] = [], duration?: number) {
|
||||
this.#runState = runState;
|
||||
this.#messages = Object.freeze(messages.map(m => Object.freeze(m)));
|
||||
this.#duration = duration;
|
||||
public addChild(child: vscode.TestItem<unknown>) {
|
||||
if (!(child instanceof TestItemImpl)) {
|
||||
throw new Error('Test child must be created through vscode.test.createTestItem()');
|
||||
}
|
||||
|
||||
const api = getPrivateApiFor(this);
|
||||
if (api.children.has(child.id)) {
|
||||
throw new Error(`Attempted to insert a duplicate test item ID ${child.id}`);
|
||||
}
|
||||
|
||||
api.children.set(child.id, child);
|
||||
api.bus.fire([ExtHostTestItemEventType.NewChild, child]);
|
||||
}
|
||||
}
|
||||
|
||||
export type RequiredTestItem = vscode.RequiredTestItem;
|
||||
|
||||
export type TestItem = vscode.TestItem;
|
||||
export class TestMessage implements vscode.TestMessage {
|
||||
public severity = TestMessageSeverity.Error;
|
||||
public expectedOutput?: string;
|
||||
public actualOutput?: string;
|
||||
|
||||
public static diff(message: string | vscode.MarkdownString, expected: string, actual: string) {
|
||||
const msg = new TestMessage(message);
|
||||
msg.expectedOutput = expected;
|
||||
msg.actualOutput = actual;
|
||||
return msg;
|
||||
}
|
||||
|
||||
constructor(public message: string | vscode.MarkdownString) { }
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -3001,3 +3380,17 @@ export enum ExternalUriOpenerPriority {
|
||||
Default = 2,
|
||||
Preferred = 3,
|
||||
}
|
||||
|
||||
export enum WorkspaceTrustState {
|
||||
Untrusted = 0,
|
||||
Trusted = 1,
|
||||
Unspecified = 2
|
||||
}
|
||||
|
||||
export enum PortAutoForwardAction {
|
||||
Notify = 1,
|
||||
OpenBrowser = 2,
|
||||
OpenPreview = 3,
|
||||
Silent = 4,
|
||||
Ignore = 5
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { normalizeVersion, parseVersion } from 'vs/platform/extensions/common/extensionValidator';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { deserializeWebviewMessage } from 'vs/workbench/api/common/extHostWebviewMessaging';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -29,6 +31,8 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
#isDisposed: boolean = false;
|
||||
#hasCalledAsWebviewUri = false;
|
||||
|
||||
#serializeBuffersForPostMessage = false;
|
||||
|
||||
constructor(
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
proxy: extHostProtocol.MainThreadWebviewsShape,
|
||||
@@ -44,6 +48,7 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
this.#initData = initData;
|
||||
this.#workspace = workspace;
|
||||
this.#extension = extension;
|
||||
this.#serializeBuffersForPostMessage = shouldSerializeBuffersForPostMessage(extension);
|
||||
this.#deprecationService = deprecationService;
|
||||
}
|
||||
|
||||
@@ -97,7 +102,7 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
|
||||
public set options(newOptions: vscode.WebviewOptions) {
|
||||
this.assertNotDisposed();
|
||||
this.#proxy.$setOptions(this.#handle, convertWebviewOptions(this.#extension, this.#workspace, newOptions));
|
||||
this.#proxy.$setOptions(this.#handle, serializeWebviewOptions(this.#extension, this.#workspace, newOptions));
|
||||
this.#options = newOptions;
|
||||
}
|
||||
|
||||
@@ -105,7 +110,8 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
if (this.#isDisposed) {
|
||||
return false;
|
||||
}
|
||||
return this.#proxy.$postMessage(this.#handle, message);
|
||||
const serialized = serializeMessage(message, { serializeBuffersForPostMessage: this.#serializeBuffersForPostMessage });
|
||||
return this.#proxy.$postMessage(this.#handle, serialized.message, ...serialized.buffers);
|
||||
}
|
||||
|
||||
private assertNotDisposed() {
|
||||
@@ -115,6 +121,49 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
}
|
||||
}
|
||||
|
||||
export function shouldSerializeBuffersForPostMessage(extension: IExtensionDescription): boolean {
|
||||
if (!extension.enableProposedApi) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const version = normalizeVersion(parseVersion(extension.engines.vscode));
|
||||
return !!version && version.majorBase >= 1 && version.minorBase >= 56;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function serializeMessage(message: any, options: { serializeBuffersForPostMessage?: boolean }): { message: string, buffers: VSBuffer[] } {
|
||||
if (options.serializeBuffersForPostMessage) {
|
||||
// Extract all ArrayBuffers from the message and replace them with references.
|
||||
const vsBuffers: Array<{ original: ArrayBuffer, vsBuffer: VSBuffer }> = [];
|
||||
|
||||
const replacer = (_key: string, value: any) => {
|
||||
if (value && value instanceof ArrayBuffer) {
|
||||
let index = vsBuffers.findIndex(x => x.original === value);
|
||||
if (index === -1) {
|
||||
const bytes = new Uint8Array(value);
|
||||
const vsBuffer = VSBuffer.wrap(bytes);
|
||||
index = vsBuffers.length;
|
||||
vsBuffers.push({ original: value, vsBuffer });
|
||||
}
|
||||
|
||||
return <extHostProtocol.WebviewMessageArrayBufferReference>{
|
||||
$$vscode_array_buffer_reference$$: true,
|
||||
index,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const serializedMessage = JSON.stringify(message, replacer);
|
||||
return { message: serializedMessage, buffers: vsBuffers.map(x => x.vsBuffer) };
|
||||
} else {
|
||||
return { message: JSON.stringify(message), buffers: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
|
||||
private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
@@ -133,10 +182,12 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
|
||||
public $onMessage(
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
message: any
|
||||
jsonMessage: string,
|
||||
...buffers: VSBuffer[]
|
||||
): void {
|
||||
const webview = this.getWebview(handle);
|
||||
if (webview) {
|
||||
const { message } = deserializeWebviewMessage(jsonMessage, buffers);
|
||||
webview._onMessageEmitter.fire(message);
|
||||
}
|
||||
}
|
||||
@@ -148,7 +199,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
this._logService.warn(`${extensionId} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`);
|
||||
}
|
||||
|
||||
public createNewWebview(handle: string, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, extension: IExtensionDescription): ExtHostWebview {
|
||||
public createNewWebview(handle: string, options: extHostProtocol.IWebviewOptions, extension: IExtensionDescription): ExtHostWebview {
|
||||
const webview = new ExtHostWebview(handle, this._webviewProxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService);
|
||||
this._webviews.set(handle, webview);
|
||||
|
||||
@@ -170,22 +221,24 @@ export function toExtensionData(extension: IExtensionDescription): extHostProtoc
|
||||
return { id: extension.identifier, location: extension.extensionLocation };
|
||||
}
|
||||
|
||||
export function convertWebviewOptions(
|
||||
export function serializeWebviewOptions(
|
||||
extension: IExtensionDescription,
|
||||
workspace: IExtHostWorkspace | undefined,
|
||||
options: vscode.WebviewPanelOptions & vscode.WebviewOptions,
|
||||
): modes.IWebviewOptions {
|
||||
options: vscode.WebviewOptions,
|
||||
): extHostProtocol.IWebviewOptions {
|
||||
return {
|
||||
...options,
|
||||
enableCommandUris: options.enableCommandUris,
|
||||
enableScripts: options.enableScripts,
|
||||
portMapping: options.portMapping,
|
||||
localResourceRoots: options.localResourceRoots || getDefaultLocalResourceRoots(extension, workspace)
|
||||
};
|
||||
}
|
||||
|
||||
function reviveOptions(
|
||||
options: modes.IWebviewOptions & modes.IWebviewPanelOptions
|
||||
): vscode.WebviewOptions {
|
||||
export function reviveOptions(options: extHostProtocol.IWebviewOptions): vscode.WebviewOptions {
|
||||
return {
|
||||
...options,
|
||||
enableCommandUris: options.enableCommandUris,
|
||||
enableScripts: options.enableScripts,
|
||||
portMapping: options.portMapping,
|
||||
localResourceRoots: options.localResourceRoots?.map(components => URI.from(components)),
|
||||
};
|
||||
}
|
||||
|
||||
122
src/vs/workbench/api/common/extHostWebviewMessaging.ts
Normal file
122
src/vs/workbench/api/common/extHostWebviewMessaging.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import * as extHostProtocol from './extHost.protocol';
|
||||
|
||||
class ArrayBufferSet {
|
||||
public readonly buffers: ArrayBuffer[] = [];
|
||||
|
||||
public add(buffer: ArrayBuffer): number {
|
||||
let index = this.buffers.indexOf(buffer);
|
||||
if (index < 0) {
|
||||
index = this.buffers.length;
|
||||
this.buffers.push(buffer);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
export function serializeWebviewMessage(
|
||||
message: any,
|
||||
transfer?: readonly ArrayBuffer[]
|
||||
): { message: string, buffers: VSBuffer[] } {
|
||||
if (transfer) {
|
||||
// Extract all ArrayBuffers from the message and replace them with references.
|
||||
const arrayBuffers = new ArrayBufferSet();
|
||||
|
||||
const replacer = (_key: string, value: any) => {
|
||||
if (value instanceof ArrayBuffer) {
|
||||
const index = arrayBuffers.add(value);
|
||||
return <extHostProtocol.WebviewMessageArrayBufferReference>{
|
||||
$$vscode_array_buffer_reference$$: true,
|
||||
index,
|
||||
};
|
||||
} else if (ArrayBuffer.isView(value)) {
|
||||
const type = getTypedArrayType(value);
|
||||
if (type) {
|
||||
const index = arrayBuffers.add(value.buffer);
|
||||
return <extHostProtocol.WebviewMessageArrayBufferReference>{
|
||||
$$vscode_array_buffer_reference$$: true,
|
||||
index,
|
||||
view: {
|
||||
type: type,
|
||||
byteLength: value.byteLength,
|
||||
byteOffset: value.byteOffset,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
const serializedMessage = JSON.stringify(message, replacer);
|
||||
|
||||
const buffers = arrayBuffers.buffers.map(arrayBuffer => {
|
||||
const bytes = new Uint8Array(arrayBuffer);
|
||||
return VSBuffer.wrap(bytes);
|
||||
});
|
||||
|
||||
return { message: serializedMessage, buffers };
|
||||
} else {
|
||||
return { message: JSON.stringify(message), buffers: [] };
|
||||
}
|
||||
}
|
||||
|
||||
function getTypedArrayType(value: ArrayBufferView): extHostProtocol.WebviewMessageArrayBufferViewType | undefined {
|
||||
switch (value.constructor.name) {
|
||||
case 'Int8Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int8Array;
|
||||
case 'Uint8Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint8Array;
|
||||
case 'Uint8ClampedArray': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint8ClampedArray;
|
||||
case 'Int16Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int16Array;
|
||||
case 'Uint16Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint16Array;
|
||||
case 'Int32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int32Array;
|
||||
case 'Uint32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint32Array;
|
||||
case 'Float32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Float32Array;
|
||||
case 'Float64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Float64Array;
|
||||
case 'BigInt64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.BigInt64Array;
|
||||
case 'BigUint64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.BigUint64Array;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function deserializeWebviewMessage(jsonMessage: string, buffers: VSBuffer[]): { message: any, arrayBuffers: ArrayBuffer[] } {
|
||||
const arrayBuffers: ArrayBuffer[] = buffers.map(buffer => {
|
||||
const arrayBuffer = new ArrayBuffer(buffer.byteLength);
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
uint8Array.set(buffer.buffer);
|
||||
return arrayBuffer;
|
||||
});
|
||||
|
||||
const reviver = !buffers.length ? undefined : (_key: string, value: any) => {
|
||||
if (typeof value === 'object' && (value as extHostProtocol.WebviewMessageArrayBufferReference).$$vscode_array_buffer_reference$$) {
|
||||
const ref = value as extHostProtocol.WebviewMessageArrayBufferReference;
|
||||
const { index } = ref;
|
||||
const arrayBuffer = arrayBuffers[index];
|
||||
if (ref.view) {
|
||||
switch (ref.view.type) {
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Int8Array: return new Int8Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int8Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint8Array: return new Uint8Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint8Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint8ClampedArray: return new Uint8ClampedArray(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint8ClampedArray.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Int16Array: return new Int16Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int16Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint16Array: return new Uint16Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint16Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Int32Array: return new Int32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int32Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint32Array: return new Uint32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint32Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Float32Array: return new Float32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Float32Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Float64Array: return new Float64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Float64Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.BigInt64Array: return new BigInt64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / BigInt64Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.BigUint64Array: return new BigUint64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / BigUint64Array.BYTES_PER_ELEMENT);
|
||||
default: throw new Error('Unknown array buffer view type');
|
||||
}
|
||||
}
|
||||
return arrayBuffer;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const message = JSON.parse(jsonMessage, reviver);
|
||||
return { message, arrayBuffers };
|
||||
}
|
||||
@@ -7,10 +7,9 @@ import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { convertWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { serializeWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData, shouldSerializeBuffersForPostMessage } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { EditorGroupColumn } from 'vs/workbench/common/editor';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -48,20 +47,20 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel {
|
||||
viewType: string,
|
||||
title: string,
|
||||
viewColumn: vscode.ViewColumn | undefined,
|
||||
editorOptions: vscode.WebviewPanelOptions,
|
||||
panelOptions: vscode.WebviewPanelOptions,
|
||||
webview: ExtHostWebview
|
||||
) {
|
||||
super();
|
||||
this.#handle = handle;
|
||||
this.#proxy = proxy;
|
||||
this.#viewType = viewType;
|
||||
this.#options = editorOptions;
|
||||
this.#options = panelOptions;
|
||||
this.#viewColumn = viewColumn;
|
||||
this.#title = title;
|
||||
this.#webview = webview;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
public override dispose() {
|
||||
if (this.#isDisposed) {
|
||||
return;
|
||||
}
|
||||
@@ -200,8 +199,14 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
preserveFocus: typeof showOptions === 'object' && !!showOptions.preserveFocus
|
||||
};
|
||||
|
||||
const serializeBuffersForPostMessage = shouldSerializeBuffersForPostMessage(extension);
|
||||
const handle = ExtHostWebviewPanels.newHandle();
|
||||
this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options));
|
||||
this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, {
|
||||
title,
|
||||
panelOptions: serializeWebviewPanelOptions(options),
|
||||
webviewOptions: serializeWebviewOptions(extension, this.workspace, options),
|
||||
serializeBuffersForPostMessage,
|
||||
}, webviewShowOptions);
|
||||
|
||||
const webview = this.webviews.createNewWebview(handle, options, extension);
|
||||
const panel = this.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview);
|
||||
@@ -260,7 +265,9 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
}
|
||||
|
||||
this._serializers.set(viewType, { serializer, extension });
|
||||
this._proxy.$registerSerializer(viewType);
|
||||
this._proxy.$registerSerializer(viewType, {
|
||||
serializeBuffersForPostMessage: shouldSerializeBuffersForPostMessage(extension)
|
||||
});
|
||||
|
||||
return new extHostTypes.Disposable(() => {
|
||||
this._serializers.delete(viewType);
|
||||
@@ -271,10 +278,13 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
async $deserializeWebviewPanel(
|
||||
webviewHandle: extHostProtocol.WebviewHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
state: any,
|
||||
position: EditorGroupColumn,
|
||||
options: modes.IWebviewOptions & modes.IWebviewPanelOptions
|
||||
initData: {
|
||||
title: string;
|
||||
state: any;
|
||||
webviewOptions: extHostProtocol.IWebviewOptions;
|
||||
panelOptions: extHostProtocol.IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn
|
||||
): Promise<void> {
|
||||
const entry = this._serializers.get(viewType);
|
||||
if (!entry) {
|
||||
@@ -282,12 +292,12 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
}
|
||||
const { serializer, extension } = entry;
|
||||
|
||||
const webview = this.webviews.createNewWebview(webviewHandle, options, extension);
|
||||
const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview);
|
||||
await serializer.deserializeWebviewPanel(revivedPanel, state);
|
||||
const webview = this.webviews.createNewWebview(webviewHandle, initData.webviewOptions, extension);
|
||||
const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, initData.title, position, initData.panelOptions, webview);
|
||||
await serializer.deserializeWebviewPanel(revivedPanel, initData.state);
|
||||
}
|
||||
|
||||
public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: vscode.ViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) {
|
||||
public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: vscode.ViewColumn, options: extHostProtocol.IWebviewPanelOptions, webview: ExtHostWebview) {
|
||||
const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, position, options, webview);
|
||||
this._webviewPanels.set(webviewHandle, panel);
|
||||
return panel;
|
||||
@@ -297,3 +307,10 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
return this._webviewPanels.get(handle);
|
||||
}
|
||||
}
|
||||
|
||||
function serializeWebviewPanelOptions(options: vscode.WebviewPanelOptions): extHostProtocol.IWebviewPanelOptions {
|
||||
return {
|
||||
enableFindWidget: options.enableFindWidget,
|
||||
retainContextWhenHidden: options.retainContextWhenHidden,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class ExtHostWebviewView extends Disposable implements vscode.WebviewView {
|
||||
this.#isVisible = isVisible;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
public override dispose() {
|
||||
if (this.#isDisposed) {
|
||||
return;
|
||||
}
|
||||
@@ -146,7 +146,10 @@ export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsS
|
||||
}
|
||||
|
||||
this._viewProviders.set(viewType, { provider, extension });
|
||||
this._proxy.$registerWebviewViewProvider(toExtensionData(extension), viewType, webviewOptions);
|
||||
this._proxy.$registerWebviewViewProvider(toExtensionData(extension), viewType, {
|
||||
retainContextWhenHidden: webviewOptions?.retainContextWhenHidden,
|
||||
serializeBuffersForPostMessage: false,
|
||||
});
|
||||
|
||||
return new extHostTypes.Disposable(() => {
|
||||
this._viewProviders.delete(viewType);
|
||||
|
||||
@@ -168,6 +168,9 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
|
||||
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
|
||||
|
||||
private readonly _onDidGrantWorkspaceTrust = new Emitter<void>();
|
||||
readonly onDidGrantWorkspaceTrust: Event<void> = this._onDidGrantWorkspaceTrust.event;
|
||||
|
||||
private readonly _logService: ILogService;
|
||||
private readonly _requestIdProvider: Counter;
|
||||
private readonly _barrier: Barrier;
|
||||
@@ -181,6 +184,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
|
||||
private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = [];
|
||||
|
||||
private _trusted: boolean = false;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@@ -198,7 +203,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, trusted: boolean): void {
|
||||
this._trusted = trusted;
|
||||
this.$acceptWorkspaceData(data);
|
||||
this._barrier.open();
|
||||
}
|
||||
@@ -549,6 +555,24 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
resolveProxy(url: string): Promise<string | undefined> {
|
||||
return this._proxy.$resolveProxy(url);
|
||||
}
|
||||
|
||||
// --- trust ---
|
||||
|
||||
get trusted(): boolean {
|
||||
return this._trusted;
|
||||
}
|
||||
|
||||
requestWorkspaceTrust(options?: vscode.WorkspaceTrustRequestOptions): Promise<boolean | undefined> {
|
||||
const promise = this._proxy.$requestWorkspaceTrust(options);
|
||||
return options?.modal ? promise : Promise.resolve(this._trusted);
|
||||
}
|
||||
|
||||
$onDidGrantWorkspaceTrust(): void {
|
||||
if (!this._trusted) {
|
||||
this._trusted = true;
|
||||
this._onDidGrantWorkspaceTrust.fire();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const IExtHostWorkspace = createDecorator<IExtHostWorkspace>('IExtHostWorkspace');
|
||||
|
||||
@@ -43,11 +43,21 @@ 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,
|
||||
description: localize('menus.editorContext', "The editor context menu")
|
||||
},
|
||||
{
|
||||
key: 'editor/context/copy',
|
||||
id: MenuId.EditorContextCopy,
|
||||
description: localize('menus.editorContextCopyAs', "'Copy as' submenu in the editor context menu")
|
||||
},
|
||||
{
|
||||
key: 'explorer/context',
|
||||
id: MenuId.ExplorerContext,
|
||||
@@ -86,6 +96,11 @@ const apiMenus: IAPIMenu[] = [
|
||||
proposed: true,
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'menuBar/edit/copy',
|
||||
id: MenuId.MenubarCopy,
|
||||
description: localize('menus.opy', "'Copy as' submenu in the top level Edit menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/title',
|
||||
id: MenuId.SCMTitle,
|
||||
@@ -99,17 +114,17 @@ const apiMenus: IAPIMenu[] = [
|
||||
{
|
||||
key: 'scm/resourceState/context',
|
||||
id: MenuId.SCMResourceContext,
|
||||
description: localize('menus.resourceGroupContext', "The Source Control resource group context menu")
|
||||
description: localize('menus.resourceStateContext', "The Source Control resource state context menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/resourceFolder/context',
|
||||
id: MenuId.SCMResourceFolderContext,
|
||||
description: localize('menus.resourceStateContext', "The Source Control resource state context menu")
|
||||
description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/resourceGroup/context',
|
||||
id: MenuId.SCMResourceGroupContext,
|
||||
description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu")
|
||||
description: localize('menus.resourceGroupContext', "The Source Control resource group context menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/change/title',
|
||||
@@ -123,6 +138,12 @@ const apiMenus: IAPIMenu[] = [
|
||||
proposed: true,
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'statusBar/remoteIndicator',
|
||||
id: MenuId.StatusBarRemoteIndicatorMenu,
|
||||
description: localize('menus.statusBarRemoteIndicator', "The remote indicator menu in the status bar"),
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'view/title',
|
||||
id: MenuId.ViewTitle,
|
||||
@@ -155,12 +176,26 @@ const apiMenus: IAPIMenu[] = [
|
||||
description: localize('comment.actions', "The contributed comment context menu, rendered as buttons below the comment editor"),
|
||||
supportsSubmenus: false
|
||||
},
|
||||
/* {{SQL CARBON EDIT}} We use our own Notebook contributions
|
||||
{
|
||||
key: 'notebook/toolbar',
|
||||
id: MenuId.NotebookToolbar,
|
||||
description: localize('notebook.toolbar', "The contributed notebook toolbar menu"),
|
||||
proposed: true
|
||||
},
|
||||
*/
|
||||
{
|
||||
key: 'notebook/cell/title',
|
||||
id: MenuId.NotebookCellTitle,
|
||||
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 +211,21 @@ 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")
|
||||
},
|
||||
{
|
||||
key: 'ports/item/port/inline',
|
||||
id: MenuId.TunnelPortInline,
|
||||
description: localize('view.tunnelPortInline', "The Ports view item port inline menu")
|
||||
},
|
||||
// {{SQL CARBON EDIT}} start menu entries
|
||||
{
|
||||
key: 'dashboard/toolbar',
|
||||
@@ -221,7 +271,7 @@ const apiMenus: IAPIMenu[] = [
|
||||
key: 'dataGrid/item/context',
|
||||
id: MenuId.DataGridItemContext,
|
||||
description: localize('dataGrid.context', "The data grid item context menu")
|
||||
},
|
||||
}
|
||||
// {{SQL CARBON EDIT}} end menu entries
|
||||
];
|
||||
|
||||
@@ -384,7 +434,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'
|
||||
},
|
||||
@@ -512,7 +562,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'
|
||||
},
|
||||
@@ -565,7 +615,7 @@ commandsExtensionPoint.setHandler(extensions => {
|
||||
let absoluteIcon: { dark: URI; light?: URI; } | ThemeIcon | undefined;
|
||||
if (icon) {
|
||||
if (typeof icon === 'string') {
|
||||
absoluteIcon = ThemeIcon.fromString(icon) || { dark: resources.joinPath(extension.description.extensionLocation, icon) };
|
||||
absoluteIcon = ThemeIcon.fromString(icon) ?? { dark: resources.joinPath(extension.description.extensionLocation, icon), light: resources.joinPath(extension.description.extensionLocation, icon) };
|
||||
|
||||
} else {
|
||||
absoluteIcon = {
|
||||
|
||||
@@ -33,7 +33,6 @@ export interface StatusPipeArgs {
|
||||
type: 'status';
|
||||
}
|
||||
|
||||
|
||||
export interface ExtensionManagementPipeArgs {
|
||||
type: 'extensionManagement';
|
||||
list?: { showVersions?: boolean, category?: string; };
|
||||
@@ -42,7 +41,7 @@ export interface ExtensionManagementPipeArgs {
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
export type PipeCommand = OpenCommandPipeArgs | StatusPipeArgs | OpenExternalCommandPipeArgs;
|
||||
export type PipeCommand = OpenCommandPipeArgs | StatusPipeArgs | OpenExternalCommandPipeArgs | ExtensionManagementPipeArgs;
|
||||
|
||||
export interface ICommandsExecuter {
|
||||
executeCommand<T>(id: string, ...args: any[]): Promise<T>;
|
||||
@@ -147,15 +146,16 @@ 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();
|
||||
}
|
||||
|
||||
private async manageExtensions(data: ExtensionManagementPipeArgs, res: http.ServerResponse) {
|
||||
console.log('server: manageExtensions');
|
||||
try {
|
||||
const toExtOrVSIX = (inputs: string[] | undefined) => inputs?.map(input => /\.vsix$/i.test(input) ? URI.parse(input) : input);
|
||||
const commandArgs = {
|
||||
@@ -164,12 +164,16 @@ export class CLIServerBase {
|
||||
uninstall: toExtOrVSIX(data.uninstall),
|
||||
force: data.force
|
||||
};
|
||||
const output = await this._commands.executeCommand('_remoteCLI.manageExtensions', commandArgs, { allowTunneling: true });
|
||||
const output = await this._commands.executeCommand('_remoteCLI.manageExtensions', commandArgs);
|
||||
res.writeHead(200);
|
||||
res.write(output);
|
||||
} catch (e) {
|
||||
} catch (err) {
|
||||
res.writeHead(500);
|
||||
res.write(String(e));
|
||||
res.write(String(err), err => {
|
||||
if (err) {
|
||||
this.logService.error(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
res.end();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ExecutableDebugAdapter, SocketDebugAdapter, NamedPipeDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
@@ -27,7 +27,7 @@ import { createCancelablePromise, firstParallel } from 'vs/base/common/async';
|
||||
|
||||
export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
override readonly _serviceBrand: undefined;
|
||||
|
||||
private _integratedTerminalInstances = new DebugTerminalCollection();
|
||||
private _terminalDisposedListener: IDisposable | undefined;
|
||||
@@ -43,7 +43,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService);
|
||||
}
|
||||
|
||||
protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
|
||||
protected override createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
|
||||
switch (adapter.type) {
|
||||
case 'server':
|
||||
return new SocketDebugAdapter(adapter);
|
||||
@@ -55,7 +55,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
return super.createDebugAdapter(adapter, session);
|
||||
}
|
||||
|
||||
protected daExecutableFromPackage(session: ExtHostDebugSession, extensionRegistry: ExtensionDescriptionRegistry): DebugAdapterExecutable | undefined {
|
||||
protected override daExecutableFromPackage(session: ExtHostDebugSession, extensionRegistry: ExtensionDescriptionRegistry): DebugAdapterExecutable | undefined {
|
||||
const dae = ExecutableDebugAdapter.platformAdapterExecutable(extensionRegistry.getAllExtensionDescriptions(), session.type);
|
||||
if (dae) {
|
||||
return new DebugAdapterExecutable(dae.command, dae.args, dae.options);
|
||||
@@ -63,11 +63,11 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected createSignService(): ISignService | undefined {
|
||||
protected override createSignService(): ISignService | undefined {
|
||||
return new SignService();
|
||||
}
|
||||
|
||||
public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {
|
||||
public override async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {
|
||||
|
||||
if (args.kind === 'integrated') {
|
||||
|
||||
@@ -110,10 +110,23 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
if (giveShellTimeToInitialize) {
|
||||
// give a new terminal some time to initialize the shell
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
} else {
|
||||
if (configProvider.getConfiguration('debug.terminal').get<boolean>('clearBeforeReusing')) {
|
||||
// clear terminal before reusing it
|
||||
if (shell.indexOf('powershell') >= 0 || shell.indexOf('pwsh') >= 0 || shell.indexOf('cmd.exe') >= 0) {
|
||||
terminal.sendText('cls');
|
||||
} else if (shell.indexOf('bash') >= 0) {
|
||||
terminal.sendText('clear');
|
||||
} else if (platform.isWindows) {
|
||||
terminal.sendText('cls');
|
||||
} else {
|
||||
terminal.sendText('clear');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const command = prepareCommand(shell, args.args, cwdForPrepareCommand, args.env);
|
||||
terminal.sendText(command, true);
|
||||
terminal.sendText(command);
|
||||
|
||||
// Mark terminal as unused when its session ends, see #112055
|
||||
const sessionListener = this.onDidTerminateDebugSession(s => {
|
||||
@@ -133,7 +146,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
}
|
||||
|
||||
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
|
||||
return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment, this._workspaceService);
|
||||
return new ExtHostVariableResolverService(folders, editorService, configurationService, this._workspaceService);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,12 @@ 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';
|
||||
import { realpathSync } from 'vs/base/node/extpath';
|
||||
|
||||
class NodeModuleRequireInterceptor extends RequireInterceptor {
|
||||
|
||||
@@ -23,7 +24,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) {
|
||||
@@ -36,7 +37,7 @@ class NodeModuleRequireInterceptor extends RequireInterceptor {
|
||||
}
|
||||
return that._factories.get(request)!.load(
|
||||
request,
|
||||
URI.file(parent.filename),
|
||||
URI.file(realpathSync(parent.filename)),
|
||||
request => original.apply(this, [request, parent, isMain])
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,21 +3,20 @@
|
||||
* Licensed under the Source EULA. 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,7 +7,6 @@ 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 { SymlinkSupport } from 'vs/base/node/pfs';
|
||||
import { promises } from 'fs';
|
||||
@@ -16,6 +15,28 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData
|
||||
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 {
|
||||
|
||||
@@ -26,23 +47,23 @@ export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChann
|
||||
this._appender = appender;
|
||||
}
|
||||
|
||||
append(value: string): void {
|
||||
override append(value: string): void {
|
||||
super.append(value);
|
||||
this._appender.append(value);
|
||||
this._onDidAppend.fire();
|
||||
}
|
||||
|
||||
update(): void {
|
||||
override update(): void {
|
||||
this._appender.flush();
|
||||
super.update();
|
||||
}
|
||||
|
||||
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
|
||||
override show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
|
||||
this._appender.flush();
|
||||
super.show(columnOrPreserveFocus, preserveFocus);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
override clear(): void {
|
||||
this._appender.flush();
|
||||
super.clear();
|
||||
}
|
||||
@@ -64,7 +85,7 @@ export class ExtHostOutputService2 extends ExtHostOutputService {
|
||||
this._logsLocation = initData.logsLocation;
|
||||
}
|
||||
|
||||
$setVisibleChannel(channelId: string): void {
|
||||
override $setVisibleChannel(channelId: string): void {
|
||||
if (channelId) {
|
||||
const channel = this._channels.get(channelId);
|
||||
if (channel) {
|
||||
@@ -73,7 +94,7 @@ export class ExtHostOutputService2 extends ExtHostOutputService {
|
||||
}
|
||||
}
|
||||
|
||||
createOutputChannel(name: string): vscode.OutputChannel {
|
||||
override createOutputChannel(name: string): vscode.OutputChannel {
|
||||
name = name.trim();
|
||||
if (!name) {
|
||||
throw new Error('illegal argument `name`. must not be falsy');
|
||||
|
||||
@@ -35,13 +35,15 @@ export class NativeExtHostSearch extends ExtHostSearch {
|
||||
) {
|
||||
super(extHostRpc, _uriTransformer, _logService);
|
||||
|
||||
const outputChannel = new OutputChannel('RipgrepSearchUD', this._logService);
|
||||
this.registerTextSearchProvider(Schemas.userData, new RipgrepSearchProvider(outputChannel));
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this._registerEHSearchProviders();
|
||||
}
|
||||
}
|
||||
|
||||
private _registerEHSearchProviders(): void {
|
||||
const outputChannel = new OutputChannel(this._logService);
|
||||
const outputChannel = new OutputChannel('RipgrepSearchEH', this._logService);
|
||||
this.registerTextSearchProvider(Schemas.file, new RipgrepSearchProvider(outputChannel));
|
||||
this.registerInternalFileSearchProvider(Schemas.file, new SearchService());
|
||||
}
|
||||
@@ -57,7 +59,7 @@ export class NativeExtHostSearch extends ExtHostSearch {
|
||||
});
|
||||
}
|
||||
|
||||
$provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
|
||||
override $provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
|
||||
const query = reviveQuery(rawQuery);
|
||||
if (handle === this._internalFileSearchHandle) {
|
||||
return this.doInternalFileSearch(handle, session, query, token);
|
||||
@@ -89,7 +91,7 @@ export class NativeExtHostSearch extends ExtHostSearch {
|
||||
return <Promise<ISearchCompleteStats>>this._internalFileSearchProvider.doFileSearch(rawQuery, onResult, token);
|
||||
}
|
||||
|
||||
$clearCache(cacheKey: string): Promise<void> {
|
||||
override $clearCache(cacheKey: string): Promise<void> {
|
||||
if (this._internalFileSearchProvider) {
|
||||
this._internalFileSearchProvider.clearCache(cacheKey);
|
||||
}
|
||||
@@ -97,8 +99,7 @@ export class NativeExtHostSearch extends ExtHostSearch {
|
||||
return super.$clearCache(cacheKey);
|
||||
}
|
||||
|
||||
protected createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider): TextSearchManager {
|
||||
protected override createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider): TextSearchManager {
|
||||
return new NativeTextSearchManager(query, provider);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,12 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
authority: initData.remote.authority,
|
||||
platform: process.platform
|
||||
});
|
||||
} else {
|
||||
this.registerTaskSystem(Schemas.file, {
|
||||
scheme: Schemas.file,
|
||||
authority: '',
|
||||
platform: process.platform
|
||||
});
|
||||
}
|
||||
this._proxy.$registerSupportedExecutions(true, true, true);
|
||||
}
|
||||
@@ -117,13 +123,14 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
return resolvedTaskDTO;
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// private async getVariableResolver(workspaceFolders: vscode.WorkspaceFolder[]): Promise<ExtHostVariableResolverService> {
|
||||
// if (this._variableResolver === undefined) {
|
||||
// const configProvider = await this._configurationService.getConfigProvider();
|
||||
// this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider, process.env as IProcessEnvironment);
|
||||
// this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider, this.workspaceService);
|
||||
// }
|
||||
// return this._variableResolver;
|
||||
// } {{SQL CARBON EDIT}}
|
||||
// }
|
||||
|
||||
public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> {
|
||||
/*const uri: URI = URI.revive(uriComponents);
|
||||
@@ -146,19 +153,19 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
}
|
||||
};
|
||||
for (let variable of toResolve.variables) {
|
||||
result.variables[variable] = resolver.resolve(ws, variable);
|
||||
result.variables[variable] = await resolver.resolveAsync(ws, variable);
|
||||
}
|
||||
if (toResolve.process !== undefined) {
|
||||
let paths: string[] | undefined = undefined;
|
||||
if (toResolve.process.path !== undefined) {
|
||||
paths = toResolve.process.path.split(path.delimiter);
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
paths[i] = resolver.resolve(ws, paths[i]);
|
||||
paths[i] = await resolver.resolveAsync(ws, paths[i]);
|
||||
}
|
||||
}
|
||||
result.process = await win32.findExecutable(
|
||||
resolver.resolve(ws, toResolve.process.name),
|
||||
toResolve.process.cwd !== undefined ? resolver.resolve(ws, toResolve.process.cwd) : undefined,
|
||||
await resolver.resolveAsync(ws, toResolve.process.name),
|
||||
toResolve.process.cwd !== undefined ? await resolver.resolveAsync(ws, toResolve.process.cwd) : undefined,
|
||||
paths
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,37 +3,31 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import * as os from 'os';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostConfiguration, ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { getSystemShell, getSystemShellSync } from 'vs/base/node/shell';
|
||||
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 { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { SafeConfigProvider } from 'vs/platform/terminal/common/terminal';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostConfigProvider, ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal';
|
||||
import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment';
|
||||
import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { getSystemShell, getSystemShellSync } from 'vs/base/node/shell';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { detectAvailableProfiles } from 'vs/workbench/contrib/terminal/node/terminalProfiles';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
|
||||
private _variableResolver: ExtHostVariableResolverService | undefined;
|
||||
private _variableResolverPromise: Promise<ExtHostVariableResolverService>;
|
||||
private _lastActiveWorkspace: IWorkspaceFolder | undefined;
|
||||
|
||||
// TODO: Pull this from main side
|
||||
private _isWorkspaceShellAllowed: boolean = false;
|
||||
private _defaultShell: string | undefined;
|
||||
|
||||
constructor(
|
||||
@@ -41,18 +35,17 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
@IExtHostConfiguration private _extHostConfiguration: ExtHostConfiguration,
|
||||
@IExtHostWorkspace private _extHostWorkspace: ExtHostWorkspace,
|
||||
@IExtHostDocumentsAndEditors private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
@ILogService private _logService: ILogService,
|
||||
@IExtHostInitDataService private _extHostInitDataService: IExtHostInitDataService
|
||||
@ILogService private _logService: ILogService
|
||||
) {
|
||||
super(true, extHostRpc);
|
||||
|
||||
// Getting the SystemShell is an async operation, however, the ExtHost terminal service is mostly synchronous
|
||||
// and the API `vscode.env.shell` is also synchronous. The default shell _should_ be set when extensions are
|
||||
// starting up but if not, we run getSystemShellSync below which gets a sane default.
|
||||
getSystemShell(platform.platform).then(s => this._defaultShell = s);
|
||||
getSystemShell(platform.OS, process.env as platform.IProcessEnvironment).then(s => this._defaultShell = s);
|
||||
|
||||
this._updateLastActiveWorkspace();
|
||||
this._updateVariableResolver();
|
||||
this._variableResolverPromise = this._updateVariableResolver();
|
||||
this._registerListeners();
|
||||
}
|
||||
|
||||
@@ -71,63 +64,43 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
withNullAsUndefined(options.shellArgs),
|
||||
withNullAsUndefined(options.cwd),
|
||||
withNullAsUndefined(options.env),
|
||||
withNullAsUndefined(options.icon),
|
||||
withNullAsUndefined(options.message),
|
||||
/*options.waitOnExit*/ undefined,
|
||||
withNullAsUndefined(options.strictEnv),
|
||||
withNullAsUndefined(options.hideFromUser),
|
||||
withNullAsUndefined(isFeatureTerminal));
|
||||
withNullAsUndefined(isFeatureTerminal),
|
||||
true
|
||||
);
|
||||
return terminal.value;
|
||||
}
|
||||
|
||||
public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string {
|
||||
const fetchSetting = (key: string): { userValue: string | string[] | undefined, value: string | string[] | undefined, defaultValue: string | string[] | undefined } => {
|
||||
const setting = configProvider
|
||||
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
|
||||
.inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
|
||||
return this._apiInspectConfigToPlain<string | string[]>(setting);
|
||||
};
|
||||
|
||||
return terminalEnvironment.getDefaultShell(
|
||||
fetchSetting,
|
||||
this._isWorkspaceShellAllowed,
|
||||
this._defaultShell ?? getSystemShellSync(platform.platform),
|
||||
this._buildSafeConfigProvider(configProvider),
|
||||
this._defaultShell ?? getSystemShellSync(platform.OS, process.env as platform.IProcessEnvironment),
|
||||
process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'),
|
||||
process.env.windir,
|
||||
terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, this._variableResolver),
|
||||
terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, process.env, this._variableResolver),
|
||||
this._logService,
|
||||
useAutomationShell
|
||||
);
|
||||
}
|
||||
|
||||
public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string {
|
||||
const fetchSetting = (key: string): { userValue: string | string[] | undefined, value: string | string[] | undefined, defaultValue: string | string[] | undefined } => {
|
||||
const setting = configProvider
|
||||
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
|
||||
.inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
|
||||
return this._apiInspectConfigToPlain<string | string[]>(setting);
|
||||
};
|
||||
|
||||
return terminalEnvironment.getDefaultShellArgs(fetchSetting, this._isWorkspaceShellAllowed, useAutomationShell, terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, this._variableResolver), this._logService);
|
||||
}
|
||||
|
||||
private _apiInspectConfigToPlain<T>(
|
||||
config: { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, workspaceFolderValue?: T } | undefined
|
||||
): { userValue: T | undefined, value: T | undefined, defaultValue: T | undefined } {
|
||||
return {
|
||||
userValue: config ? config.globalValue : undefined,
|
||||
value: config ? config.workspaceValue : undefined,
|
||||
defaultValue: config ? config.defaultValue : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
private async _getNonInheritedEnv(): Promise<platform.IProcessEnvironment> {
|
||||
const env = await getMainProcessParentEnv();
|
||||
env.VSCODE_IPC_HOOK_CLI = process.env['VSCODE_IPC_HOOK_CLI']!;
|
||||
return env;
|
||||
return terminalEnvironment.getDefaultShellArgs(
|
||||
this._buildSafeConfigProvider(configProvider),
|
||||
useAutomationShell,
|
||||
terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, process.env, this._variableResolver),
|
||||
this._logService
|
||||
);
|
||||
}
|
||||
|
||||
private _registerListeners(): void {
|
||||
this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(() => this._updateLastActiveWorkspace());
|
||||
this._extHostWorkspace.onDidChangeWorkspace(() => this._updateVariableResolver());
|
||||
this._extHostWorkspace.onDidChangeWorkspace(() => {
|
||||
this._variableResolverPromise = this._updateVariableResolver();
|
||||
});
|
||||
}
|
||||
|
||||
private _updateLastActiveWorkspace(): void {
|
||||
@@ -137,105 +110,16 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
}
|
||||
}
|
||||
|
||||
private async _updateVariableResolver(): Promise<void> {
|
||||
private async _updateVariableResolver(): Promise<ExtHostVariableResolverService> {
|
||||
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
||||
const workspaceFolders = await this._extHostWorkspace.getWorkspaceFolders2();
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider, process.env as platform.IProcessEnvironment);
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider);
|
||||
return this._variableResolver;
|
||||
}
|
||||
|
||||
public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined> {
|
||||
const shellLaunchConfig: IShellLaunchConfig = {
|
||||
name: shellLaunchConfigDto.name,
|
||||
executable: shellLaunchConfigDto.executable,
|
||||
args: shellLaunchConfigDto.args,
|
||||
cwd: typeof shellLaunchConfigDto.cwd === 'string' ? shellLaunchConfigDto.cwd : URI.revive(shellLaunchConfigDto.cwd),
|
||||
env: shellLaunchConfigDto.env,
|
||||
flowControl: shellLaunchConfigDto.flowControl
|
||||
};
|
||||
|
||||
// Merge in shell and args from settings
|
||||
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
|
||||
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
||||
if (!shellLaunchConfig.executable) {
|
||||
shellLaunchConfig.executable = this.getDefaultShell(false, configProvider);
|
||||
shellLaunchConfig.args = this.getDefaultShellArgs(false, configProvider);
|
||||
} else {
|
||||
if (this._variableResolver) {
|
||||
shellLaunchConfig.executable = this._variableResolver.resolve(this._lastActiveWorkspace, shellLaunchConfig.executable);
|
||||
if (shellLaunchConfig.args) {
|
||||
if (Array.isArray(shellLaunchConfig.args)) {
|
||||
const resolvedArgs: string[] = [];
|
||||
for (const arg of shellLaunchConfig.args) {
|
||||
resolvedArgs.push(this._variableResolver.resolve(this._lastActiveWorkspace, arg));
|
||||
}
|
||||
shellLaunchConfig.args = resolvedArgs;
|
||||
} else {
|
||||
shellLaunchConfig.args = this._variableResolver.resolve(this._lastActiveWorkspace, shellLaunchConfig.args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents);
|
||||
let lastActiveWorkspace: IWorkspaceFolder | undefined;
|
||||
if (activeWorkspaceRootUriComponents && activeWorkspaceRootUri) {
|
||||
// Get the environment
|
||||
const apiLastActiveWorkspace = await this._extHostWorkspace.getWorkspaceFolder(activeWorkspaceRootUri);
|
||||
if (apiLastActiveWorkspace) {
|
||||
lastActiveWorkspace = {
|
||||
uri: apiLastActiveWorkspace.uri,
|
||||
name: apiLastActiveWorkspace.name,
|
||||
index: apiLastActiveWorkspace.index,
|
||||
toResource: () => {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Get the initial cwd
|
||||
const terminalConfig = configProvider.getConfiguration('terminal.integrated');
|
||||
|
||||
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._variableResolver), activeWorkspaceRootUri, terminalConfig.cwd, this._logService);
|
||||
shellLaunchConfig.cwd = initialCwd;
|
||||
|
||||
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 env = terminalEnvironment.createTerminalEnvironment(
|
||||
shellLaunchConfig,
|
||||
envFromConfig,
|
||||
terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._variableResolver),
|
||||
isWorkspaceShellAllowed,
|
||||
this._extHostInitDataService.version,
|
||||
terminalConfig.get<'auto' | 'off' | 'on'>('detectLocale', 'auto'),
|
||||
baseEnv
|
||||
);
|
||||
|
||||
// Apply extension environment variable collections to the environment
|
||||
if (!shellLaunchConfig.strictEnv) {
|
||||
const mergedCollection = new MergedEnvironmentVariableCollection(this._environmentVariableCollections);
|
||||
mergedCollection.applyToProcessEnvironment(env);
|
||||
}
|
||||
|
||||
this._proxy.$sendResolvedLaunchConfig(id, shellLaunchConfig);
|
||||
// Fork the process and listen for messages
|
||||
this._logService.debug(`Terminal process launching on ext host`, { shellLaunchConfig, initialCwd, cols, rows, env });
|
||||
// TODO: Support conpty on remote, it doesn't seem to work for some reason?
|
||||
// TODO: When conpty is enabled, only enable it when accessibilityMode is off
|
||||
const enableConpty = false; //terminalConfig.get('windowsEnableConpty') as boolean;
|
||||
|
||||
const terminalProcess = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, process.env as platform.IProcessEnvironment, enableConpty, this._logService);
|
||||
this._setupExtHostProcessListeners(id, terminalProcess);
|
||||
const error = await terminalProcess.start();
|
||||
if (error) {
|
||||
// TODO: Teardown?
|
||||
return error;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public $getAvailableShells(): Promise<IShellDefinitionDto[]> {
|
||||
return detectAvailableShells();
|
||||
public async $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]> {
|
||||
const safeConfigProvider = this._buildSafeConfigProvider(await this._extHostConfiguration.getConfigProvider());
|
||||
return detectAvailableProfiles(configuredProfilesOnly, safeConfigProvider, undefined, this._logService, await this._variableResolverPromise, this._lastActiveWorkspace);
|
||||
}
|
||||
|
||||
public async $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto> {
|
||||
@@ -246,7 +130,16 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
};
|
||||
}
|
||||
|
||||
public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void {
|
||||
this._isWorkspaceShellAllowed = isAllowed;
|
||||
// TODO: Remove when workspace trust is enabled
|
||||
private _buildSafeConfigProvider(configProvider: ExtHostConfigProvider): SafeConfigProvider {
|
||||
const config = configProvider.getConfiguration();
|
||||
return (key: string) => {
|
||||
const isWorkspaceConfigAllowed = config.get('terminal.integrated.allowWorkspaceConfiguration');
|
||||
if (isWorkspaceConfigAllowed) {
|
||||
return config.get(key) as any;
|
||||
}
|
||||
const inspected = config.inspect(key);
|
||||
return inspected?.globalValue || inspected?.defaultValue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { MainThreadTunnelServiceShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { MainThreadTunnelServiceShape, MainContext, PortAttributesProviderSelector } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import type * as vscode from 'vscode';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -13,10 +13,11 @@ import { exec } from 'child_process';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as fs from 'fs';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { IExtHostTunnelService, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { TunnelOptions, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { TunnelOptions, TunnelCreationOptions, ProvidedPortAttributes, ProvidedOnAutoForward } from 'vs/platform/remote/common/tunnel';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { MovingAverage } from 'vs/base/common/numbers';
|
||||
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
@@ -37,7 +38,7 @@ class ExtensionTunnel implements vscode.Tunnel {
|
||||
}
|
||||
}
|
||||
|
||||
export function getSockets(stdout: string): { pid: number, socket: number }[] {
|
||||
export function getSockets(stdout: string): Record<string, { pid: number; socket: number; }> {
|
||||
const lines = stdout.trim().split('\n');
|
||||
const mapped: { pid: number, socket: number }[] = [];
|
||||
lines.forEach(line => {
|
||||
@@ -49,7 +50,11 @@ export function getSockets(stdout: string): { pid: number, socket: number }[] {
|
||||
});
|
||||
}
|
||||
});
|
||||
return mapped;
|
||||
const socketMap = mapped.reduce((m, socket) => {
|
||||
m[socket.socket] = socket;
|
||||
return m;
|
||||
}, {} as Record<string, typeof mapped[0]>);
|
||||
return socketMap;
|
||||
}
|
||||
|
||||
export function loadListeningPorts(...stdouts: string[]): { socket: number, ip: string, port: number }[] {
|
||||
@@ -106,29 +111,70 @@ function knownExcludeCmdline(command: string): boolean {
|
||||
|| (command.indexOf('_productName=VSCode') !== -1);
|
||||
}
|
||||
|
||||
export async function findPorts(tcp: string, tcp6: string, procSockets: string, processes: { pid: number, cwd: string, cmd: string }[]): Promise<CandidatePort[]> {
|
||||
const connections: { socket: number, ip: string, port: number }[] = loadListeningPorts(tcp, tcp6);
|
||||
const sockets = getSockets(procSockets);
|
||||
export function getRootProcesses(stdout: string) {
|
||||
const lines = stdout.trim().split('\n');
|
||||
const mapped: { pid: number, cmd: string, ppid: number }[] = [];
|
||||
lines.forEach(line => {
|
||||
const match = /^\d+\s+\D+\s+root\s+(\d+)\s+(\d+).+\d+\:\d+\:\d+\s+(.+)$/.exec(line)!;
|
||||
if (match && match.length >= 4) {
|
||||
mapped.push({
|
||||
pid: parseInt(match[1], 10),
|
||||
ppid: parseInt(match[2]),
|
||||
cmd: match[3]
|
||||
});
|
||||
}
|
||||
});
|
||||
return mapped;
|
||||
}
|
||||
|
||||
const socketMap = sockets.reduce((m, socket) => {
|
||||
m[socket.socket] = socket;
|
||||
return m;
|
||||
}, {} as Record<string, typeof sockets[0]>);
|
||||
export async function findPorts(connections: { socket: number, ip: string, port: number }[], socketMap: Record<string, { pid: number, socket: number }>, processes: { pid: number, cwd: string, cmd: string }[]): Promise<CandidatePort[]> {
|
||||
const processMap = processes.reduce((m, process) => {
|
||||
m[process.pid] = process;
|
||||
return m;
|
||||
}, {} as Record<string, typeof processes[0]>);
|
||||
|
||||
const ports: CandidatePort[] = [];
|
||||
connections.filter((connection => socketMap[connection.socket])).forEach(({ socket, ip, port }) => {
|
||||
const command = processMap[socketMap[socket].pid].cmd;
|
||||
if (!knownExcludeCmdline(command)) {
|
||||
ports.push({ host: ip, port, detail: processMap[socketMap[socket].pid].cmd, pid: socketMap[socket].pid });
|
||||
connections.forEach(({ socket, ip, port }) => {
|
||||
const pid = socketMap[socket] ? socketMap[socket].pid : undefined;
|
||||
const command: string | undefined = pid ? processMap[pid]?.cmd : undefined;
|
||||
if (pid && command && !knownExcludeCmdline(command)) {
|
||||
ports.push({ host: ip, port, detail: command, pid });
|
||||
}
|
||||
});
|
||||
return ports;
|
||||
}
|
||||
|
||||
export function tryFindRootPorts(connections: { socket: number, ip: string, port: number }[], rootProcessesStdout: string, previousPorts: Map<number, CandidatePort & { ppid: number }>): Map<number, CandidatePort & { ppid: number }> {
|
||||
const ports: Map<number, CandidatePort & { ppid: number }> = new Map();
|
||||
const rootProcesses = getRootProcesses(rootProcessesStdout);
|
||||
|
||||
for (const connection of connections) {
|
||||
const previousPort = previousPorts.get(connection.port);
|
||||
if (previousPort) {
|
||||
ports.set(connection.port, previousPort);
|
||||
continue;
|
||||
}
|
||||
const rootProcessMatch = rootProcesses.find((value) => value.cmd.includes(`${connection.port}`));
|
||||
if (rootProcessMatch) {
|
||||
let bestMatch = rootProcessMatch;
|
||||
// There are often several processes that "look" like they could match the port.
|
||||
// The one we want is usually the child of the other. Find the most child process.
|
||||
let mostChild: { pid: number, cmd: string, ppid: number } | undefined;
|
||||
do {
|
||||
mostChild = rootProcesses.find(value => value.ppid === bestMatch.pid);
|
||||
if (mostChild) {
|
||||
bestMatch = mostChild;
|
||||
}
|
||||
} while (mostChild);
|
||||
ports.set(connection.port, { host: connection.ip, port: connection.port, pid: bestMatch.pid, detail: bestMatch.cmd, ppid: bestMatch.ppid });
|
||||
} else {
|
||||
ports.set(connection.port, { host: connection.ip, port: connection.port, ppid: Number.MAX_VALUE });
|
||||
}
|
||||
}
|
||||
|
||||
return ports;
|
||||
}
|
||||
|
||||
export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService {
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly _proxy: MainThreadTunnelServiceShape;
|
||||
@@ -138,6 +184,10 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
private _onDidChangeTunnels: Emitter<void> = new Emitter<void>();
|
||||
onDidChangeTunnels: vscode.Event<void> = this._onDidChangeTunnels.event;
|
||||
private _candidateFindingEnabled: boolean = false;
|
||||
private _foundRootPorts: Map<number, CandidatePort & { ppid: number }> = new Map();
|
||||
|
||||
private _providerHandleCounter: number = 0;
|
||||
private _portAttributesProviders: Map<number, { provider: vscode.PortAttributesProvider, selector: PortAttributesProviderSelector }> = new Map();
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@@ -147,11 +197,12 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) ${extension.identifier.value} called openTunnel API for ${forward.remoteAddress.host}:${forward.remoteAddress.port}.`);
|
||||
const tunnel = await this._proxy.$openTunnel(forward, extension.displayName);
|
||||
if (tunnel) {
|
||||
const disposableTunnel: vscode.Tunnel = new ExtensionTunnel(tunnel.remoteAddress, tunnel.localAddress, () => {
|
||||
@@ -172,6 +223,43 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
return Math.max(movingAverage * 20, 2000);
|
||||
}
|
||||
|
||||
private nextPortAttributesProviderHandle(): number {
|
||||
return this._providerHandleCounter++;
|
||||
}
|
||||
|
||||
registerPortsAttributesProvider(portSelector: PortAttributesProviderSelector, provider: vscode.PortAttributesProvider): vscode.Disposable {
|
||||
const providerHandle = this.nextPortAttributesProviderHandle();
|
||||
this._portAttributesProviders.set(providerHandle, { selector: portSelector, provider });
|
||||
|
||||
this._proxy.$registerPortsAttributesProvider(portSelector, providerHandle);
|
||||
return new types.Disposable(() => {
|
||||
this._portAttributesProviders.delete(providerHandle);
|
||||
this._proxy.$unregisterPortsAttributesProvider(providerHandle);
|
||||
});
|
||||
}
|
||||
|
||||
async $providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: vscode.CancellationToken): Promise<ProvidedPortAttributes[]> {
|
||||
const providedAttributes: vscode.ProviderResult<vscode.PortAttributes>[] = [];
|
||||
for (const handle of handles) {
|
||||
const provider = this._portAttributesProviders.get(handle);
|
||||
if (!provider) {
|
||||
return [];
|
||||
}
|
||||
providedAttributes.push(...(await Promise.all(ports.map(async (port) => {
|
||||
return provider.provider.providePortAttributes(port, pid, commandline, cancellationToken);
|
||||
}))));
|
||||
}
|
||||
|
||||
const allAttributes = <vscode.PortAttributes[]>providedAttributes.filter(attribute => !!attribute);
|
||||
|
||||
return (allAttributes.length > 0) ? allAttributes.map(attributes => {
|
||||
return {
|
||||
autoForwardAction: <ProvidedOnAutoForward><unknown>attributes.autoForwardAction,
|
||||
port: attributes.port
|
||||
};
|
||||
}) : [];
|
||||
}
|
||||
|
||||
async $registerCandidateFinder(enable: boolean): Promise<void> {
|
||||
if (enable && this._candidateFindingEnabled) {
|
||||
// already enabled
|
||||
@@ -180,10 +268,11 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
this._candidateFindingEnabled = enable;
|
||||
// Regularly scan to see if the candidate ports have changed.
|
||||
let movingAverage = new MovingAverage();
|
||||
let oldPorts: { host: string, port: number, detail: string }[] | undefined = undefined;
|
||||
let oldPorts: { host: string, port: number, detail?: string }[] | undefined = undefined;
|
||||
while (this._candidateFindingEnabled) {
|
||||
const startTime = new Date().getTime();
|
||||
const newPorts = await this.findCandidatePorts();
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) found candidate ports ${newPorts.map(port => port.port).join(', ')}`);
|
||||
const timeTaken = new Date().getTime() - startTime;
|
||||
movingAverage.update(timeTaken);
|
||||
if (!oldPorts || (JSON.stringify(oldPorts) !== JSON.stringify(newPorts))) {
|
||||
@@ -195,14 +284,19 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
}
|
||||
|
||||
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
|
||||
// Do not wait for any of the proxy promises here.
|
||||
// It will delay startup and there is nothing that needs to be waited for.
|
||||
if (provider) {
|
||||
if (provider.candidatePortSource !== undefined) {
|
||||
this._proxy.$setCandidatePortSource(provider.candidatePortSource);
|
||||
}
|
||||
if (provider.showCandidatePort) {
|
||||
this._showCandidatePort = provider.showCandidatePort;
|
||||
await this._proxy.$setCandidateFilter();
|
||||
this._proxy.$setCandidateFilter();
|
||||
}
|
||||
if (provider.tunnelFactory) {
|
||||
this._forwardPortProvider = provider.tunnelFactory;
|
||||
await this._proxy.$setTunnelProvider(provider.tunnelFeatures ?? {
|
||||
this._proxy.$setTunnelProvider(provider.tunnelFeatures ?? {
|
||||
elevation: false,
|
||||
public: false
|
||||
});
|
||||
@@ -235,31 +329,36 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
async $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto | undefined> {
|
||||
if (this._forwardPortProvider) {
|
||||
try {
|
||||
this.logService.trace('$forwardPort: Getting tunnel from provider.');
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Getting tunnel from provider.');
|
||||
const providedPort = this._forwardPortProvider(tunnelOptions, tunnelCreationOptions);
|
||||
this.logService.trace('$forwardPort: Got tunnel promise from provider.');
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Got tunnel promise from provider.');
|
||||
if (providedPort !== undefined) {
|
||||
const tunnel = await providedPort;
|
||||
this.logService.trace('$forwardPort: Successfully awaited tunnel from provider.');
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Successfully awaited tunnel from provider.');
|
||||
if (!this._extensionTunnels.has(tunnelOptions.remoteAddress.host)) {
|
||||
this._extensionTunnels.set(tunnelOptions.remoteAddress.host, new Map());
|
||||
}
|
||||
const disposeListener = this._register(tunnel.onDidDispose(() => this._proxy.$closeTunnel(tunnel.remoteAddress)));
|
||||
const disposeListener = this._register(tunnel.onDidDispose(() => {
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Extension fired tunnel\'s onDidDispose.');
|
||||
return this._proxy.$closeTunnel(tunnel.remoteAddress);
|
||||
}));
|
||||
this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, { tunnel, disposeListener });
|
||||
return TunnelDto.fromApiTunnel(tunnel);
|
||||
} else {
|
||||
this.logService.trace('$forwardPort: Tunnel is undefined');
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Tunnel is undefined');
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.trace('$forwardPort: tunnel provider error');
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) tunnel provider error');
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async $applyCandidateFilter(candidates: CandidatePort[]): Promise<CandidatePort[]> {
|
||||
const filter = await Promise.all(candidates.map(candidate => this._showCandidatePort(candidate.host, candidate.port, candidate.detail)));
|
||||
return candidates.filter((candidate, index) => filter[index]);
|
||||
const filter = await Promise.all(candidates.map(candidate => this._showCandidatePort(candidate.host, candidate.port, candidate.detail ?? '')));
|
||||
const result = candidates.filter((candidate, index) => filter[index]);
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) filtered from ${candidates.map(port => port.port).join(', ')} to ${result.map(port => port.port).join(', ')}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
async findCandidatePorts(): Promise<CandidatePort[]> {
|
||||
@@ -271,11 +370,14 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
} catch (e) {
|
||||
// File reading error. No additional handling needed.
|
||||
}
|
||||
const connections: { socket: number, ip: string, port: number }[] = loadListeningPorts(tcp, tcp6);
|
||||
|
||||
const procSockets: string = await (new Promise(resolve => {
|
||||
exec('ls -l /proc/[0-9]*/fd/[0-9]* | grep socket:', (error, stdout, stderr) => {
|
||||
resolve(stdout);
|
||||
});
|
||||
}));
|
||||
const socketMap = getSockets(procSockets);
|
||||
|
||||
const procChildren = await pfs.readdir('/proc');
|
||||
const processes: {
|
||||
@@ -295,6 +397,36 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
//
|
||||
}
|
||||
}
|
||||
return findPorts(tcp, tcp6, procSockets, processes);
|
||||
|
||||
const unFoundConnections: { socket: number, ip: string, port: number }[] = [];
|
||||
const filteredConnections = connections.filter((connection => {
|
||||
const foundConnection = socketMap[connection.socket];
|
||||
if (!foundConnection) {
|
||||
unFoundConnections.push(connection);
|
||||
}
|
||||
return foundConnection;
|
||||
}));
|
||||
|
||||
const foundPorts = findPorts(filteredConnections, socketMap, processes);
|
||||
let heuristicPorts: CandidatePort[] | undefined;
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) number of possible root ports ${unFoundConnections.length}`);
|
||||
if (unFoundConnections.length > 0) {
|
||||
const rootProcesses: string = await (new Promise(resolve => {
|
||||
exec('ps -F -A -l | grep root', (error, stdout, stderr) => {
|
||||
resolve(stdout);
|
||||
});
|
||||
}));
|
||||
this._foundRootPorts = tryFindRootPorts(unFoundConnections, rootProcesses, this._foundRootPorts);
|
||||
heuristicPorts = Array.from(this._foundRootPorts.values());
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) heuristic ports ${heuristicPorts.join(', ')}`);
|
||||
|
||||
}
|
||||
return foundPorts.then(foundCandidates => {
|
||||
if (heuristicPorts) {
|
||||
return foundCandidates.concat(heuristicPorts);
|
||||
} else {
|
||||
return foundCandidates;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterc
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { MainContext, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
namespace TrustedFunction {
|
||||
|
||||
@@ -68,6 +69,9 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||
private _fakeModules?: WorkerRequireInterceptor;
|
||||
|
||||
protected async _beforeAlmostReadyToRunExtensions(): Promise<void> {
|
||||
const mainThreadConsole = this._extHostContext.getProxy(MainContext.MainThreadConsole);
|
||||
wrapConsoleMethods(mainThreadConsole, this._initData.environment.isExtensionDevelopmentDebug);
|
||||
|
||||
// initialize API and register actors
|
||||
const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors);
|
||||
this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._registry);
|
||||
@@ -167,3 +171,78 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||
function ensureSuffix(path: string, suffix: string): string {
|
||||
return path.endsWith(suffix) ? path : path + suffix;
|
||||
}
|
||||
|
||||
// copied from bootstrap-fork.js
|
||||
function wrapConsoleMethods(service: MainThreadConsoleShape, callToNative: boolean) {
|
||||
wrap('info', 'log');
|
||||
wrap('log', 'log');
|
||||
wrap('warn', 'warn');
|
||||
wrap('error', 'error');
|
||||
|
||||
function wrap(method: 'error' | 'warn' | 'info' | 'log', severity: 'error' | 'warn' | 'log') {
|
||||
const original = console[method];
|
||||
console[method] = function () {
|
||||
service.$logExtensionHostMessage({ type: '__$console', severity, arguments: safeToArray(arguments) });
|
||||
if (callToNative) {
|
||||
original.apply(console, arguments as any);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const MAX_LENGTH = 100000;
|
||||
|
||||
function safeToArray(args: IArguments) {
|
||||
const seen: any[] = [];
|
||||
const argsArray = [];
|
||||
|
||||
// Massage some arguments with special treatment
|
||||
if (args.length) {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
|
||||
// Any argument of type 'undefined' needs to be specially treated because
|
||||
// JSON.stringify will simply ignore those. We replace them with the string
|
||||
// 'undefined' which is not 100% right, but good enough to be logged to console
|
||||
if (typeof args[i] === 'undefined') {
|
||||
args[i] = 'undefined';
|
||||
}
|
||||
|
||||
// Any argument that is an Error will be changed to be just the error stack/message
|
||||
// itself because currently cannot serialize the error over entirely.
|
||||
else if (args[i] instanceof Error) {
|
||||
const errorObj = args[i];
|
||||
if (errorObj.stack) {
|
||||
args[i] = errorObj.stack;
|
||||
} else {
|
||||
args[i] = errorObj.toString();
|
||||
}
|
||||
}
|
||||
|
||||
argsArray.push(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const res = JSON.stringify(argsArray, function (key, value) {
|
||||
|
||||
// Objects get special treatment to prevent circles
|
||||
if (value && typeof value === 'object') {
|
||||
if (seen.indexOf(value) !== -1) {
|
||||
return '[Circular]';
|
||||
}
|
||||
|
||||
seen.push(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
if (res.length > MAX_LENGTH) {
|
||||
return 'Output omitted for a large object that exceeds the limits';
|
||||
}
|
||||
|
||||
return res;
|
||||
} catch (error) {
|
||||
return `Output omitted for an object that cannot be inspected ('${error.toString()}')`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* Licensed under the Source EULA. 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;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import 'vs/css!./media/actions';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
@@ -28,13 +28,14 @@ import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup';
|
||||
|
||||
class InspectContextKeysAction extends Action2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.inspectContextKeys',
|
||||
title: { value: nls.localize('inspect context keys', "Inspect Context Keys"), original: 'Inspect Context Keys' },
|
||||
title: { value: localize('inspect context keys', "Inspect Context Keys"), original: 'Inspect Context Keys' },
|
||||
category: CATEGORIES.Developer,
|
||||
f1: true
|
||||
});
|
||||
@@ -96,7 +97,7 @@ class ToggleScreencastModeAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.toggleScreencastMode',
|
||||
title: { value: nls.localize('toggle screencast mode', "Toggle Screencast Mode"), original: 'Toggle Screencast Mode' },
|
||||
title: { value: localize('toggle screencast mode', "Toggle Screencast Mode"), original: 'Toggle Screencast Mode' },
|
||||
category: CATEGORIES.Developer,
|
||||
f1: true
|
||||
});
|
||||
@@ -241,7 +242,7 @@ class LogStorageAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.logStorage',
|
||||
title: { value: nls.localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"), original: 'Log Storage Database Contents' },
|
||||
title: { value: localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"), original: 'Log Storage Database Contents' },
|
||||
category: CATEGORIES.Developer,
|
||||
f1: true
|
||||
});
|
||||
@@ -257,21 +258,30 @@ class LogWorkingCopiesAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.logWorkingCopies',
|
||||
title: { value: nls.localize({ key: 'logWorkingCopies', comment: ['A developer only action to log the working copies that exist.'] }, "Log Working Copies"), original: 'Log Working Copies' },
|
||||
title: { value: localize({ key: 'logWorkingCopies', comment: ['A developer only action to log the working copies that exist.'] }, "Log Working Copies"), original: 'Log Working Copies' },
|
||||
category: CATEGORIES.Developer,
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor): void {
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const workingCopyService = accessor.get(IWorkingCopyService);
|
||||
const workingCopyBackupService = accessor.get(IWorkingCopyBackupService);
|
||||
const logService = accessor.get(ILogService);
|
||||
|
||||
const backups = await workingCopyBackupService.getBackups();
|
||||
|
||||
const msg = [
|
||||
`Dirty Working Copies:`,
|
||||
...workingCopyService.dirtyWorkingCopies.map(workingCopy => workingCopy.resource.toString(true)),
|
||||
``,
|
||||
`All Working Copies:`,
|
||||
...workingCopyService.workingCopies.map(workingCopy => workingCopy.resource.toString(true)),
|
||||
`[Working Copies]`,
|
||||
...(workingCopyService.workingCopies.length > 0) ?
|
||||
workingCopyService.workingCopies.map(workingCopy => `${workingCopy.isDirty() ? '● ' : ''}${workingCopy.resource.toString(true)} (typeId: ${workingCopy.typeId || '<no typeId>'})`) :
|
||||
['<none>'],
|
||||
``,
|
||||
`[Backups]`,
|
||||
...(backups.length > 0) ?
|
||||
backups.map(backup => `${backup.resource.toString(true)} (typeId: ${backup.typeId || '<no typeId>'})`) :
|
||||
['<none>'],
|
||||
];
|
||||
|
||||
logService.info(msg.join('\n'));
|
||||
@@ -291,7 +301,7 @@ const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationE
|
||||
configurationRegistry.registerConfiguration({
|
||||
id: 'screencastMode',
|
||||
order: 9,
|
||||
title: nls.localize('screencastModeConfigurationTitle', "Screencast Mode"),
|
||||
title: localize('screencastModeConfigurationTitle', "Screencast Mode"),
|
||||
type: 'object',
|
||||
properties: {
|
||||
'screencastMode.verticalOffset': {
|
||||
@@ -299,18 +309,18 @@ configurationRegistry.registerConfiguration({
|
||||
default: 20,
|
||||
minimum: 0,
|
||||
maximum: 90,
|
||||
description: nls.localize('screencastMode.location.verticalPosition', "Controls the vertical offset of the screencast mode overlay from the bottom as a percentage of the workbench height.")
|
||||
description: localize('screencastMode.location.verticalPosition', "Controls the vertical offset of the screencast mode overlay from the bottom as a percentage of the workbench height.")
|
||||
},
|
||||
'screencastMode.fontSize': {
|
||||
type: 'number',
|
||||
default: 56,
|
||||
minimum: 20,
|
||||
maximum: 100,
|
||||
description: nls.localize('screencastMode.fontSize', "Controls the font size (in pixels) of the screencast mode keyboard.")
|
||||
description: localize('screencastMode.fontSize', "Controls the font size (in pixels) of the screencast mode keyboard.")
|
||||
},
|
||||
'screencastMode.onlyKeyboardShortcuts': {
|
||||
type: 'boolean',
|
||||
description: nls.localize('screencastMode.onlyKeyboardShortcuts', "Only show keyboard shortcuts in screencast mode."),
|
||||
description: localize('screencastMode.onlyKeyboardShortcuts', "Only show keyboard shortcuts in screencast mode."),
|
||||
default: false
|
||||
},
|
||||
'screencastMode.keyboardOverlayTimeout': {
|
||||
@@ -318,20 +328,20 @@ configurationRegistry.registerConfiguration({
|
||||
default: 800,
|
||||
minimum: 500,
|
||||
maximum: 5000,
|
||||
description: nls.localize('screencastMode.keyboardOverlayTimeout', "Controls how long (in milliseconds) the keyboard overlay is shown in screencast mode.")
|
||||
description: localize('screencastMode.keyboardOverlayTimeout', "Controls how long (in milliseconds) the keyboard overlay is shown in screencast mode.")
|
||||
},
|
||||
'screencastMode.mouseIndicatorColor': {
|
||||
type: 'string',
|
||||
format: 'color-hex',
|
||||
default: '#FF0000',
|
||||
description: nls.localize('screencastMode.mouseIndicatorColor', "Controls the color in hex (#RGB, #RGBA, #RRGGBB or #RRGGBBAA) of the mouse indicator in screencast mode.")
|
||||
description: localize('screencastMode.mouseIndicatorColor', "Controls the color in hex (#RGB, #RGBA, #RRGGBB or #RRGGBBAA) of the mouse indicator in screencast mode.")
|
||||
},
|
||||
'screencastMode.mouseIndicatorSize': {
|
||||
type: 'number',
|
||||
default: 20,
|
||||
minimum: 20,
|
||||
maximum: 100,
|
||||
description: nls.localize('screencastMode.mouseIndicatorSize', "Controls the size (in pixels) of the mouse indicator in screencast mode.")
|
||||
description: localize('screencastMode.mouseIndicatorSize', "Controls the size (in pixels) of the mouse indicator in screencast mode.")
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { localize } from 'vs/nls';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { isMacintosh, isLinux, language } from 'vs/base/common/platform';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
@@ -24,7 +24,7 @@ class KeybindingsReferenceAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: KeybindingsReferenceAction.ID,
|
||||
title: { value: nls.localize('keybindingsReference', "Keyboard Shortcuts Reference"), original: 'Keyboard Shortcuts Reference' },
|
||||
title: { value: localize('keybindingsReference', "Keyboard Shortcuts Reference"), original: 'Keyboard Shortcuts Reference' },
|
||||
category: CATEGORIES.Help,
|
||||
f1: true,
|
||||
keybinding: {
|
||||
@@ -54,7 +54,7 @@ class OpenDocumentationUrlAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: OpenDocumentationUrlAction.ID,
|
||||
title: { value: nls.localize('openDocumentationUrl', "Documentation"), original: 'Documentation' },
|
||||
title: { value: localize('openDocumentationUrl', "Documentation"), original: 'Documentation' },
|
||||
category: CATEGORIES.Help,
|
||||
f1: true
|
||||
});
|
||||
@@ -78,7 +78,7 @@ class OpenIntroductoryVideosUrlAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: OpenIntroductoryVideosUrlAction.ID,
|
||||
title: { value: nls.localize('openIntroductoryVideosUrl', "Introductory Videos"), original: 'Introductory Videos' },
|
||||
title: { value: localize('openIntroductoryVideosUrl', "Introductory Videos"), original: 'Introductory Videos' },
|
||||
category: CATEGORIES.Help,
|
||||
f1: true
|
||||
});
|
||||
@@ -102,7 +102,7 @@ class OpenTipsAndTricksUrlAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: OpenTipsAndTricksUrlAction.ID,
|
||||
title: { value: nls.localize('openTipsAndTricksUrl', "Tips and Tricks"), original: 'Tips and Tricks' },
|
||||
title: { value: localize('openTipsAndTricksUrl', "Tips and Tricks"), original: 'Tips and Tricks' },
|
||||
category: CATEGORIES.Help,
|
||||
f1: true
|
||||
});
|
||||
@@ -126,7 +126,7 @@ class OpenNewsletterSignupUrlAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: OpenNewsletterSignupUrlAction.ID,
|
||||
title: { value: nls.localize('newsletterSignup', "Signup for the VS Code Newsletter"), original: 'Signup for the VS Code Newsletter' },
|
||||
title: { value: localize('newsletterSignup', "Signup for the VS Code Newsletter"), original: 'Signup for the VS Code Newsletter' },
|
||||
category: CATEGORIES.Help,
|
||||
f1: true
|
||||
});
|
||||
@@ -151,7 +151,7 @@ class OpenTwitterUrlAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: OpenTwitterUrlAction.ID,
|
||||
title: { value: nls.localize('openTwitterUrl', "Join Us on Twitter"), original: 'Join Us on Twitter' },
|
||||
title: { value: localize('openTwitterUrl', "Join Us on Twitter"), original: 'Join Us on Twitter' },
|
||||
category: CATEGORIES.Help,
|
||||
f1: true
|
||||
});
|
||||
@@ -175,7 +175,7 @@ class OpenRequestFeatureUrlAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: OpenRequestFeatureUrlAction.ID,
|
||||
title: { value: nls.localize('openUserVoiceUrl', "Search Feature Requests"), original: 'Search Feature Requests' },
|
||||
title: { value: localize('openUserVoiceUrl', "Search Feature Requests"), original: 'Search Feature Requests' },
|
||||
category: CATEGORIES.Help,
|
||||
f1: true
|
||||
});
|
||||
@@ -199,7 +199,7 @@ class OpenLicenseUrlAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: OpenLicenseUrlAction.ID,
|
||||
title: { value: nls.localize('openLicenseUrl', "View License"), original: 'View License' },
|
||||
title: { value: localize('openLicenseUrl', "View License"), original: 'View License' },
|
||||
category: CATEGORIES.Help,
|
||||
f1: true
|
||||
});
|
||||
@@ -228,7 +228,7 @@ class OpenPrivacyStatementUrlAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: OpenPrivacyStatementUrlAction.ID,
|
||||
title: { value: nls.localize('openPrivacyStatement', "Privacy Statement"), original: 'Privacy Statement' },
|
||||
title: { value: localize('openPrivacyStatement', "Privacy Statement"), original: 'Privacy Statement' },
|
||||
category: CATEGORIES.Help,
|
||||
f1: true
|
||||
});
|
||||
@@ -296,7 +296,7 @@ if (OpenDocumentationUrlAction.AVAILABLE) {
|
||||
group: '1_welcome',
|
||||
command: {
|
||||
id: OpenDocumentationUrlAction.ID,
|
||||
title: nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation")
|
||||
title: localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
@@ -308,7 +308,7 @@ if (KeybindingsReferenceAction.AVAILABLE) {
|
||||
group: '2_reference',
|
||||
command: {
|
||||
id: KeybindingsReferenceAction.ID,
|
||||
title: nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference")
|
||||
title: localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
@@ -319,7 +319,7 @@ if (OpenIntroductoryVideosUrlAction.AVAILABLE) {
|
||||
group: '2_reference',
|
||||
command: {
|
||||
id: OpenIntroductoryVideosUrlAction.ID,
|
||||
title: nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos")
|
||||
title: localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
@@ -330,7 +330,7 @@ if (OpenTipsAndTricksUrlAction.AVAILABLE) {
|
||||
group: '2_reference',
|
||||
command: {
|
||||
id: OpenTipsAndTricksUrlAction.ID,
|
||||
title: nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "Tips and Tri&&cks")
|
||||
title: localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "Tips and Tri&&cks")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
@@ -342,7 +342,7 @@ if (OpenTwitterUrlAction.AVAILABLE) {
|
||||
group: '3_feedback',
|
||||
command: {
|
||||
id: OpenTwitterUrlAction.ID,
|
||||
title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter")
|
||||
title: localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
@@ -353,7 +353,7 @@ if (OpenRequestFeatureUrlAction.AVAILABLE) {
|
||||
group: '3_feedback',
|
||||
command: {
|
||||
id: OpenRequestFeatureUrlAction.ID,
|
||||
title: nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests")
|
||||
title: localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
@@ -365,7 +365,7 @@ if (OpenLicenseUrlAction.AVAILABLE) {
|
||||
group: '4_legal',
|
||||
command: {
|
||||
id: OpenLicenseUrlAction.ID,
|
||||
title: nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License")
|
||||
title: localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
@@ -376,7 +376,7 @@ if (OpenPrivacyStatementUrlAction.AVAILABE) {
|
||||
group: '4_legal',
|
||||
command: {
|
||||
id: OpenPrivacyStatementUrlAction.ID,
|
||||
title: nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "Privac&&y Statement")
|
||||
title: localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "Privac&&y Statement")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { SyncActionDescriptor, MenuId, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
|
||||
@@ -33,7 +33,7 @@ class CloseSidebarAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.closeSidebar',
|
||||
title: { value: nls.localize('closeSidebar', "Close Side Bar"), original: 'Close Side Bar' },
|
||||
title: { value: localize('closeSidebar', "Close Side Bar"), original: 'Close Side Bar' },
|
||||
category: CATEGORIES.View,
|
||||
f1: true
|
||||
});
|
||||
@@ -51,7 +51,7 @@ registerAction2(CloseSidebarAction);
|
||||
export class ToggleActivityBarVisibilityAction extends Action2 {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleActivityBarVisibility';
|
||||
static readonly LABEL = nls.localize('toggleActivityBar', "Toggle Activity Bar Visibility");
|
||||
static readonly LABEL = localize('toggleActivityBar', "Toggle Activity Bar Visibility");
|
||||
|
||||
private static readonly activityBarVisibleKey = 'workbench.activityBar.visible';
|
||||
|
||||
@@ -81,7 +81,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '2_workbench_layout',
|
||||
command: {
|
||||
id: ToggleActivityBarVisibilityAction.ID,
|
||||
title: nls.localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar"),
|
||||
title: localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar"),
|
||||
toggled: ContextKeyExpr.equals('config.workbench.activityBar.visible', true)
|
||||
},
|
||||
order: 4
|
||||
@@ -96,7 +96,7 @@ class ToggleCenteredLayout extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: ToggleCenteredLayout.ID,
|
||||
title: { value: nls.localize('toggleCenteredLayout', "Toggle Centered Layout"), original: 'Toggle Centered Layout' },
|
||||
title: { value: localize('toggleCenteredLayout', "Toggle Centered Layout"), original: 'Toggle Centered Layout' },
|
||||
category: CATEGORIES.View,
|
||||
f1: true
|
||||
});
|
||||
@@ -115,7 +115,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '1_toggle_view',
|
||||
command: {
|
||||
id: ToggleCenteredLayout.ID,
|
||||
title: nls.localize({ key: 'miToggleCenteredLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered Layout"),
|
||||
title: localize({ key: 'miToggleCenteredLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered Layout"),
|
||||
toggled: IsCenteredLayoutContext
|
||||
},
|
||||
order: 3
|
||||
@@ -126,7 +126,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
export class ToggleSidebarPositionAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleSidebarPosition';
|
||||
static readonly LABEL = nls.localize('toggleSidebarPosition', "Toggle Side Bar Position");
|
||||
static readonly LABEL = localize('toggleSidebarPosition', "Toggle Side Bar Position");
|
||||
|
||||
private static readonly sidebarPositionConfigurationKey = 'workbench.sideBar.location';
|
||||
|
||||
@@ -139,7 +139,7 @@ export class ToggleSidebarPositionAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
override run(): Promise<void> {
|
||||
const position = this.layoutService.getSideBarPosition();
|
||||
const newPositionValue = (position === Position.LEFT) ? 'right' : 'left';
|
||||
|
||||
@@ -147,7 +147,7 @@ export class ToggleSidebarPositionAction extends Action {
|
||||
}
|
||||
|
||||
static getLabel(layoutService: IWorkbenchLayoutService): string {
|
||||
return layoutService.getSideBarPosition() === Position.LEFT ? nls.localize('moveSidebarRight', "Move Side Bar Right") : nls.localize('moveSidebarLeft', "Move Side Bar Left");
|
||||
return layoutService.getSideBarPosition() === Position.LEFT ? localize('moveSidebarRight', "Move Side Bar Right") : localize('moveSidebarLeft', "Move Side Bar Left");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: ToggleSidebarPositionAction.ID,
|
||||
title: { value: nls.localize('toggleSidebarPosition', "Toggle Side Bar Position"), original: 'Toggle Side Bar Position' },
|
||||
title: { value: localize('toggleSidebarPosition', "Toggle Side Bar Position"), original: 'Toggle Side Bar Position' },
|
||||
category: CATEGORIES.View,
|
||||
f1: true
|
||||
});
|
||||
@@ -170,7 +170,7 @@ MenuRegistry.appendMenuItems([{
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: ToggleSidebarPositionAction.ID,
|
||||
title: nls.localize('move sidebar right', "Move Side Bar Right")
|
||||
title: localize('move sidebar right', "Move Side Bar Right")
|
||||
},
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.notEquals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))),
|
||||
order: 1
|
||||
@@ -181,7 +181,7 @@ MenuRegistry.appendMenuItems([{
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: ToggleSidebarPositionAction.ID,
|
||||
title: nls.localize('move sidebar right', "Move Side Bar Right")
|
||||
title: localize('move sidebar right', "Move Side Bar Right")
|
||||
},
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.notEquals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))),
|
||||
order: 1
|
||||
@@ -192,7 +192,7 @@ MenuRegistry.appendMenuItems([{
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: ToggleSidebarPositionAction.ID,
|
||||
title: nls.localize('move sidebar left', "Move Side Bar Left")
|
||||
title: localize('move sidebar left', "Move Side Bar Left")
|
||||
},
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.equals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))),
|
||||
order: 1
|
||||
@@ -203,7 +203,7 @@ MenuRegistry.appendMenuItems([{
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: ToggleSidebarPositionAction.ID,
|
||||
title: nls.localize('move sidebar left', "Move Side Bar Left")
|
||||
title: localize('move sidebar left', "Move Side Bar Left")
|
||||
},
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.equals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))),
|
||||
order: 1
|
||||
@@ -214,7 +214,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: ToggleSidebarPositionAction.ID,
|
||||
title: nls.localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right")
|
||||
title: localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right")
|
||||
},
|
||||
when: ContextKeyExpr.notEquals('config.workbench.sideBar.location', 'right'),
|
||||
order: 2
|
||||
@@ -224,7 +224,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: ToggleSidebarPositionAction.ID,
|
||||
title: nls.localize({ key: 'miMoveSidebarLeft', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left")
|
||||
title: localize({ key: 'miMoveSidebarLeft', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left")
|
||||
},
|
||||
when: ContextKeyExpr.equals('config.workbench.sideBar.location', 'right'),
|
||||
order: 2
|
||||
@@ -234,7 +234,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
|
||||
export class ToggleEditorVisibilityAction extends Action {
|
||||
static readonly ID = 'workbench.action.toggleEditorVisibility';
|
||||
static readonly LABEL = nls.localize('toggleEditor', "Toggle Editor Area Visibility");
|
||||
static readonly LABEL = localize('toggleEditor', "Toggle Editor Area Visibility");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -244,7 +244,7 @@ export class ToggleEditorVisibilityAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
this.layoutService.toggleMaximizedPanel();
|
||||
}
|
||||
}
|
||||
@@ -255,7 +255,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '2_workbench_layout',
|
||||
command: {
|
||||
id: ToggleEditorVisibilityAction.ID,
|
||||
title: nls.localize({ key: 'miShowEditorArea', comment: ['&& denotes a mnemonic'] }, "Show &&Editor Area"),
|
||||
title: localize({ key: 'miShowEditorArea', comment: ['&& denotes a mnemonic'] }, "Show &&Editor Area"),
|
||||
toggled: EditorAreaVisibleContext
|
||||
},
|
||||
order: 5
|
||||
@@ -263,7 +263,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
group: '2_appearance',
|
||||
title: nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"),
|
||||
title: localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"),
|
||||
submenu: MenuId.MenubarAppearanceMenu,
|
||||
order: 1
|
||||
});
|
||||
@@ -273,7 +273,7 @@ registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: TOGGLE_SIDEBAR_VISIBILITY_ACTION_ID,
|
||||
title: { value: nls.localize('toggleSidebar', "Toggle Side Bar Visibility"), original: 'Toggle Side Bar Visibility' },
|
||||
title: { value: localize('toggleSidebar', "Toggle Side Bar Visibility"), original: 'Toggle Side Bar Visibility' },
|
||||
category: CATEGORIES.View,
|
||||
f1: true,
|
||||
keybinding: {
|
||||
@@ -293,7 +293,7 @@ MenuRegistry.appendMenuItems([{
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: TOGGLE_SIDEBAR_VISIBILITY_ACTION_ID,
|
||||
title: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
},
|
||||
when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))),
|
||||
order: 2
|
||||
@@ -304,7 +304,7 @@ MenuRegistry.appendMenuItems([{
|
||||
group: '3_workbench_layout_move',
|
||||
command: {
|
||||
id: TOGGLE_SIDEBAR_VISIBILITY_ACTION_ID,
|
||||
title: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
},
|
||||
when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))),
|
||||
order: 2
|
||||
@@ -315,7 +315,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '2_workbench_layout',
|
||||
command: {
|
||||
id: TOGGLE_SIDEBAR_VISIBILITY_ACTION_ID,
|
||||
title: nls.localize({ key: 'miShowSidebar', comment: ['&& denotes a mnemonic'] }, "Show &&Side Bar"),
|
||||
title: localize({ key: 'miShowSidebar', comment: ['&& denotes a mnemonic'] }, "Show &&Side Bar"),
|
||||
toggled: SideBarVisibleContext
|
||||
},
|
||||
order: 1
|
||||
@@ -326,7 +326,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
export class ToggleStatusbarVisibilityAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleStatusbarVisibility';
|
||||
static readonly LABEL = nls.localize('toggleStatusbar', "Toggle Status Bar Visibility");
|
||||
static readonly LABEL = localize('toggleStatusbar', "Toggle Status Bar Visibility");
|
||||
|
||||
private static readonly statusbarVisibleKey = 'workbench.statusBar.visible';
|
||||
|
||||
@@ -339,7 +339,7 @@ export class ToggleStatusbarVisibilityAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
override run(): Promise<void> {
|
||||
const visibility = this.layoutService.isVisible(Parts.STATUSBAR_PART);
|
||||
const newVisibilityValue = !visibility;
|
||||
|
||||
@@ -353,7 +353,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '2_workbench_layout',
|
||||
command: {
|
||||
id: ToggleStatusbarVisibilityAction.ID,
|
||||
title: nls.localize({ key: 'miShowStatusbar', comment: ['&& denotes a mnemonic'] }, "Show S&&tatus Bar"),
|
||||
title: localize({ key: 'miShowStatusbar', comment: ['&& denotes a mnemonic'] }, "Show S&&tatus Bar"),
|
||||
toggled: ContextKeyExpr.equals('config.workbench.statusBar.visible', true)
|
||||
},
|
||||
order: 3
|
||||
@@ -364,7 +364,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
class ToggleTabsVisibilityAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleTabsVisibility';
|
||||
static readonly LABEL = nls.localize('toggleTabs', "Toggle Tab Visibility");
|
||||
static readonly LABEL = localize('toggleTabs', "Toggle Tab Visibility");
|
||||
|
||||
private static readonly tabsVisibleKey = 'workbench.editor.showTabs';
|
||||
|
||||
@@ -376,7 +376,7 @@ class ToggleTabsVisibilityAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
override run(): Promise<void> {
|
||||
const visibility = this.configurationService.getValue<string>(ToggleTabsVisibilityAction.tabsVisibleKey);
|
||||
const newVisibilityValue = !visibility;
|
||||
|
||||
@@ -395,7 +395,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleTabsVisibilityA
|
||||
class ToggleZenMode extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleZenMode';
|
||||
static readonly LABEL = nls.localize('toggleZenMode', "Toggle Zen Mode");
|
||||
static readonly LABEL = localize('toggleZenMode', "Toggle Zen Mode");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -405,7 +405,7 @@ class ToggleZenMode extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
this.layoutService.toggleZenMode();
|
||||
}
|
||||
}
|
||||
@@ -416,7 +416,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '1_toggle_view',
|
||||
command: {
|
||||
id: ToggleZenMode.ID,
|
||||
title: nls.localize('miToggleZenMode', "Zen Mode"),
|
||||
title: localize('miToggleZenMode', "Zen Mode"),
|
||||
toggled: InEditorZenModeContext
|
||||
},
|
||||
order: 2
|
||||
@@ -438,7 +438,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
export class ToggleMenuBarAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleMenuBar';
|
||||
static readonly LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar");
|
||||
static readonly LABEL = localize('toggleMenuBar', "Toggle Menu Bar");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -448,7 +448,7 @@ export class ToggleMenuBarAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
this.layoutService.toggleMenuBar();
|
||||
}
|
||||
}
|
||||
@@ -461,8 +461,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '2_workbench_layout',
|
||||
command: {
|
||||
id: ToggleMenuBarAction.ID,
|
||||
title: nls.localize({ key: 'miShowMenuBar', comment: ['&& denotes a mnemonic'] }, "Show Menu &&Bar"),
|
||||
toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'))
|
||||
title: localize({ key: 'miShowMenuBar', comment: ['&& denotes a mnemonic'] }, "Show Menu &&Bar"),
|
||||
toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact'))
|
||||
},
|
||||
when: IsMacNativeContext.toNegated(),
|
||||
order: 0
|
||||
@@ -472,7 +472,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
|
||||
export class ResetViewLocationsAction extends Action {
|
||||
static readonly ID = 'workbench.action.resetViewLocations';
|
||||
static readonly LABEL = nls.localize('resetViewLocations', "Reset View Locations");
|
||||
static readonly LABEL = localize('resetViewLocations', "Reset View Locations");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -482,7 +482,7 @@ export class ResetViewLocationsAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
this.viewDescriptorService.reset();
|
||||
}
|
||||
}
|
||||
@@ -492,7 +492,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(ResetViewLocationsAct
|
||||
// --- Move View with Command
|
||||
export class MoveViewAction extends Action {
|
||||
static readonly ID = 'workbench.action.moveView';
|
||||
static readonly LABEL = nls.localize('moveView', "Move View");
|
||||
static readonly LABEL = localize('moveView', "Move View");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -521,7 +521,7 @@ export class MoveViewAction extends Action {
|
||||
if (!hasAddedView) {
|
||||
results.push({
|
||||
type: 'separator',
|
||||
label: nls.localize('sidebarContainer', "Side Bar / {0}", containerModel.title)
|
||||
label: localize('sidebarContainer', "Side Bar / {0}", containerModel.title)
|
||||
});
|
||||
hasAddedView = true;
|
||||
}
|
||||
@@ -545,7 +545,7 @@ export class MoveViewAction extends Action {
|
||||
if (!hasAddedView) {
|
||||
results.push({
|
||||
type: 'separator',
|
||||
label: nls.localize('panelContainer', "Panel / {0}", containerModel.title)
|
||||
label: localize('panelContainer', "Panel / {0}", containerModel.title)
|
||||
});
|
||||
hasAddedView = true;
|
||||
}
|
||||
@@ -563,7 +563,7 @@ export class MoveViewAction extends Action {
|
||||
|
||||
private async getView(viewId?: string): Promise<string> {
|
||||
const quickPick = this.quickInputService.createQuickPick();
|
||||
quickPick.placeholder = nls.localize('moveFocusedView.selectView', "Select a View to Move");
|
||||
quickPick.placeholder = localize('moveFocusedView.selectView', "Select a View to Move");
|
||||
quickPick.items = this.getViewItems();
|
||||
quickPick.selectedItems = quickPick.items.filter(item => (item as IQuickPickItem).id === viewId) as IQuickPickItem[];
|
||||
|
||||
@@ -585,7 +585,7 @@ export class MoveViewAction extends Action {
|
||||
});
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
const focusedViewId = FocusedViewContext.getValue(this.contextKeyService);
|
||||
let viewId: string;
|
||||
|
||||
@@ -608,7 +608,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveViewAction), 'Vie
|
||||
// --- Move Focused View with Command
|
||||
export class MoveFocusedViewAction extends Action {
|
||||
static readonly ID = 'workbench.action.moveFocusedView';
|
||||
static readonly LABEL = nls.localize('moveFocusedView', "Move Focused View");
|
||||
static readonly LABEL = localize('moveFocusedView', "Move Focused View");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -624,23 +624,23 @@ export class MoveFocusedViewAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(viewId: string): Promise<void> {
|
||||
override async run(viewId: string): Promise<void> {
|
||||
const focusedViewId = viewId || FocusedViewContext.getValue(this.contextKeyService);
|
||||
|
||||
if (focusedViewId === undefined || focusedViewId.trim() === '') {
|
||||
this.notificationService.error(nls.localize('moveFocusedView.error.noFocusedView', "There is no view currently focused."));
|
||||
this.notificationService.error(localize('moveFocusedView.error.noFocusedView', "There is no view currently focused."));
|
||||
return;
|
||||
}
|
||||
|
||||
const viewDescriptor = this.viewDescriptorService.getViewDescriptorById(focusedViewId);
|
||||
if (!viewDescriptor || !viewDescriptor.canMoveView) {
|
||||
this.notificationService.error(nls.localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable."));
|
||||
this.notificationService.error(localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable."));
|
||||
return;
|
||||
}
|
||||
|
||||
const quickPick = this.quickInputService.createQuickPick();
|
||||
quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a Destination for the View");
|
||||
quickPick.title = nls.localize({ key: 'moveFocusedView.title', comment: ['{0} indicates the title of the view the user has selected to move.'] }, "View: Move {0}", viewDescriptor.name);
|
||||
quickPick.placeholder = localize('moveFocusedView.selectDestination', "Select a Destination for the View");
|
||||
quickPick.title = localize({ key: 'moveFocusedView.title', comment: ['{0} indicates the title of the view the user has selected to move.'] }, "View: Move {0}", viewDescriptor.name);
|
||||
|
||||
const items: Array<IQuickPickItem | IQuickPickSeparator> = [];
|
||||
const currentContainer = this.viewDescriptorService.getViewContainerByViewId(focusedViewId)!;
|
||||
@@ -650,20 +650,20 @@ export class MoveFocusedViewAction extends Action {
|
||||
if (!(isViewSolo && currentLocation === ViewContainerLocation.Panel)) {
|
||||
items.push({
|
||||
id: '_.panel.newcontainer',
|
||||
label: nls.localize({ key: 'moveFocusedView.newContainerInPanel', comment: ['Creates a new top-level tab in the panel.'] }, "New Panel Entry"),
|
||||
label: localize({ key: 'moveFocusedView.newContainerInPanel', comment: ['Creates a new top-level tab in the panel.'] }, "New Panel Entry"),
|
||||
});
|
||||
}
|
||||
|
||||
if (!(isViewSolo && currentLocation === ViewContainerLocation.Sidebar)) {
|
||||
items.push({
|
||||
id: '_.sidebar.newcontainer',
|
||||
label: nls.localize('moveFocusedView.newContainerInSidebar', "New Side Bar Entry")
|
||||
label: localize('moveFocusedView.newContainerInSidebar', "New Side Bar Entry")
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
type: 'separator',
|
||||
label: nls.localize('sidebar', "Side Bar")
|
||||
label: localize('sidebar', "Side Bar")
|
||||
});
|
||||
|
||||
const pinnedViewlets = this.activityBarService.getVisibleViewContainerIds();
|
||||
@@ -678,13 +678,13 @@ export class MoveFocusedViewAction extends Action {
|
||||
.map(viewletId => {
|
||||
return {
|
||||
id: viewletId,
|
||||
label: this.viewDescriptorService.getViewContainerById(viewletId)!.title
|
||||
label: this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerById(viewletId)!)!.title
|
||||
};
|
||||
}));
|
||||
|
||||
items.push({
|
||||
type: 'separator',
|
||||
label: nls.localize('panel', "Panel")
|
||||
label: localize('panel', "Panel")
|
||||
});
|
||||
|
||||
const pinnedPanels = this.panelService.getPinnedPanels();
|
||||
@@ -699,7 +699,7 @@ export class MoveFocusedViewAction extends Action {
|
||||
.map(panel => {
|
||||
return {
|
||||
id: panel.id,
|
||||
label: this.viewDescriptorService.getViewContainerById(panel.id)!.title
|
||||
label: this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerById(panel.id)!)!.title
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -731,7 +731,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveFocusedViewAction
|
||||
// --- Reset View Location with Command
|
||||
export class ResetFocusedViewLocationAction extends Action {
|
||||
static readonly ID = 'workbench.action.resetFocusedViewLocation';
|
||||
static readonly LABEL = nls.localize('resetFocusedViewLocation', "Reset Focused View Location");
|
||||
static readonly LABEL = localize('resetFocusedViewLocation', "Reset Focused View Location");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -744,7 +744,7 @@ export class ResetFocusedViewLocationAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
const focusedViewId = FocusedViewContext.getValue(this.contextKeyService);
|
||||
|
||||
let viewDescriptor: IViewDescriptor | null = null;
|
||||
@@ -753,7 +753,7 @@ export class ResetFocusedViewLocationAction extends Action {
|
||||
}
|
||||
|
||||
if (!viewDescriptor) {
|
||||
this.notificationService.error(nls.localize('resetFocusedView.error.noFocusedView', "There is no view currently focused."));
|
||||
this.notificationService.error(localize('resetFocusedView.error.noFocusedView', "There is no view currently focused."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -807,7 +807,7 @@ export class IncreaseViewSizeAction extends BaseResizeViewAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.increaseViewSize',
|
||||
title: { value: nls.localize('increaseViewSize', "Increase Current View Size"), original: 'Increase Current View Size' },
|
||||
title: { value: localize('increaseViewSize', "Increase Current View Size"), original: 'Increase Current View Size' },
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
@@ -822,7 +822,7 @@ export class IncreaseViewWidthAction extends BaseResizeViewAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.increaseViewWidth',
|
||||
title: { value: nls.localize('increaseEditorWidth', "Increase Editor Width"), original: 'Increase Editor Width' },
|
||||
title: { value: localize('increaseEditorWidth', "Increase Editor Width"), original: 'Increase Editor Width' },
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
@@ -837,7 +837,7 @@ export class IncreaseViewHeightAction extends BaseResizeViewAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.increaseViewHeight',
|
||||
title: { value: nls.localize('increaseEditorHeight', "Increase Editor Height"), original: 'Increase Editor Height' },
|
||||
title: { value: localize('increaseEditorHeight', "Increase Editor Height"), original: 'Increase Editor Height' },
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
@@ -851,7 +851,7 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.decreaseViewSize',
|
||||
title: { value: nls.localize('decreaseViewSize', "Decrease Current View Size"), original: 'Decrease Current View Size' },
|
||||
title: { value: localize('decreaseViewSize', "Decrease Current View Size"), original: 'Decrease Current View Size' },
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
@@ -865,7 +865,7 @@ export class DecreaseViewWidthAction extends BaseResizeViewAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.decreaseViewWidth',
|
||||
title: { value: nls.localize('decreaseEditorWidth', "Decrease Editor Width"), original: 'Decrease Editor Width' },
|
||||
title: { value: localize('decreaseEditorWidth', "Decrease Editor Width"), original: 'Decrease Editor Width' },
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
@@ -881,7 +881,7 @@ export class DecreaseViewHeightAction extends BaseResizeViewAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.decreaseViewHeight',
|
||||
title: { value: nls.localize('decreaseEditorHeight', "Decrease Editor Height"), original: 'Decrease Editor Height' },
|
||||
title: { value: localize('decreaseEditorHeight', "Decrease Editor Height"), original: 'Decrease Editor Height' },
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,15 +7,16 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus, getSelectionKeyboardEvent } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus, getSelectionKeyboardEvent, WorkbenchListWidget, WorkbenchListSelectionNavigation } from 'vs/platform/list/browser/listService';
|
||||
import { PagedList } from 'vs/base/browser/ui/list/listPaging';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
import { equals, range } from 'vs/base/common/arrays';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
|
||||
import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { DataTree } from 'vs/base/browser/ui/tree/dataTree';
|
||||
import { ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { Table } from 'vs/base/browser/ui/table/tableWidget';
|
||||
|
||||
function ensureDOMFocus(widget: ListWidget | undefined): void {
|
||||
// it can happen that one of the commands is executed while
|
||||
@@ -27,36 +28,40 @@ function ensureDOMFocus(widget: ListWidget | undefined): void {
|
||||
}
|
||||
}
|
||||
|
||||
function focusDown(accessor: ServicesAccessor, arg2?: number, loop: boolean = false): void {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
const count = typeof arg2 === 'number' ? arg2 : 1;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
const list = focused;
|
||||
|
||||
list.focusNext(count);
|
||||
const listFocus = list.getFocus();
|
||||
if (listFocus.length) {
|
||||
list.reveal(listFocus[0]);
|
||||
}
|
||||
async function updateFocus(widget: WorkbenchListWidget, updateFocusFn: (widget: WorkbenchListWidget) => void | Promise<void>): Promise<void> {
|
||||
if (!WorkbenchListSelectionNavigation.getValue(widget.contextKeyService)) {
|
||||
return updateFocusFn(widget);
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const tree = focused;
|
||||
const focus = widget.getFocus();
|
||||
const selection = widget.getSelection();
|
||||
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
tree.focusNext(count, loop, fakeKeyboardEvent);
|
||||
await updateFocusFn(widget);
|
||||
|
||||
const listFocus = tree.getFocus();
|
||||
if (listFocus.length) {
|
||||
tree.reveal(listFocus[0]);
|
||||
}
|
||||
const newFocus = widget.getFocus();
|
||||
|
||||
if (selection.length > 1 || !equals(focus, selection) || equals(focus, newFocus)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure DOM Focus
|
||||
ensureDOMFocus(focused);
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
widget.setSelection(newFocus, fakeKeyboardEvent);
|
||||
}
|
||||
|
||||
async function navigate(widget: WorkbenchListWidget | undefined, updateFocusFn: (widget: WorkbenchListWidget) => void | Promise<void>): Promise<void> {
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
await updateFocus(widget, updateFocusFn);
|
||||
|
||||
const listFocus = widget.getFocus();
|
||||
|
||||
if (listFocus.length) {
|
||||
widget.reveal(listFocus[0]);
|
||||
}
|
||||
|
||||
ensureDOMFocus(widget);
|
||||
}
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
@@ -68,13 +73,87 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
primary: KeyCode.DownArrow,
|
||||
secondary: [KeyMod.WinCtrl | KeyCode.KEY_N]
|
||||
},
|
||||
handler: (accessor, arg2) => focusDown(accessor, arg2)
|
||||
handler: (accessor, arg2) => {
|
||||
navigate(accessor.get(IListService).lastFocusedList, async widget => {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
await widget.focusNext(typeof arg2 === 'number' ? arg2 : 1, false, fakeKeyboardEvent);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function expandMultiSelection(focused: List<unknown> | PagedList<unknown> | ObjectTree<unknown, unknown> | DataTree<unknown, unknown, unknown> | AsyncDataTree<unknown, unknown, unknown>, previousFocus: unknown): void {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusUp',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.UpArrow,
|
||||
mac: {
|
||||
primary: KeyCode.UpArrow,
|
||||
secondary: [KeyMod.WinCtrl | KeyCode.KEY_P]
|
||||
},
|
||||
handler: (accessor, arg2) => {
|
||||
navigate(accessor.get(IListService).lastFocusedList, async widget => {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
await widget.focusPrevious(typeof arg2 === 'number' ? arg2 : 1, false, fakeKeyboardEvent);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusPageDown',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.PageDown,
|
||||
handler: (accessor) => {
|
||||
navigate(accessor.get(IListService).lastFocusedList, async widget => {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
await widget.focusNextPage(fakeKeyboardEvent);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusPageUp',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.PageUp,
|
||||
handler: (accessor) => {
|
||||
navigate(accessor.get(IListService).lastFocusedList, async widget => {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
await widget.focusPreviousPage(fakeKeyboardEvent);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusFirst',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.Home,
|
||||
handler: (accessor) => {
|
||||
navigate(accessor.get(IListService).lastFocusedList, async widget => {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
await widget.focusFirst(fakeKeyboardEvent);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusLast',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.End,
|
||||
handler: (accessor) => {
|
||||
navigate(accessor.get(IListService).lastFocusedList, async widget => {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
await widget.focusLast(fakeKeyboardEvent);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function expandMultiSelection(focused: WorkbenchListWidget, previousFocus: unknown): void {
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
|
||||
const list = focused;
|
||||
|
||||
const focus = list.getFocus() ? list.getFocus()[0] : undefined;
|
||||
@@ -115,64 +194,28 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey),
|
||||
primary: KeyMod.Shift | KeyCode.DownArrow,
|
||||
handler: (accessor, arg2) => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List / Tree
|
||||
if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const list = focused;
|
||||
|
||||
// Focus down first
|
||||
const previousFocus = list.getFocus() ? list.getFocus()[0] : undefined;
|
||||
focusDown(accessor, arg2, false);
|
||||
|
||||
// Then adjust selection
|
||||
expandMultiSelection(focused, previousFocus);
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function focusUp(accessor: ServicesAccessor, arg2?: number, loop: boolean = false): void {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
const count = typeof arg2 === 'number' ? arg2 : 1;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
const list = focused;
|
||||
|
||||
list.focusPrevious(count);
|
||||
const listFocus = list.getFocus();
|
||||
if (listFocus.length) {
|
||||
list.reveal(listFocus[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const tree = focused;
|
||||
|
||||
// Focus down first
|
||||
const previousFocus = widget.getFocus() ? widget.getFocus()[0] : undefined;
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
tree.focusPrevious(count, loop, fakeKeyboardEvent);
|
||||
widget.focusNext(typeof arg2 === 'number' ? arg2 : 1, false, fakeKeyboardEvent);
|
||||
|
||||
const listFocus = tree.getFocus();
|
||||
if (listFocus.length) {
|
||||
tree.reveal(listFocus[0]);
|
||||
// Then adjust selection
|
||||
expandMultiSelection(widget, previousFocus);
|
||||
|
||||
const focus = widget.getFocus();
|
||||
|
||||
if (focus.length) {
|
||||
widget.reveal(focus[0]);
|
||||
}
|
||||
|
||||
ensureDOMFocus(widget);
|
||||
}
|
||||
|
||||
// Ensure DOM Focus
|
||||
ensureDOMFocus(focused);
|
||||
}
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusUp',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.UpArrow,
|
||||
mac: {
|
||||
primary: KeyCode.UpArrow,
|
||||
secondary: [KeyMod.WinCtrl | KeyCode.KEY_P]
|
||||
},
|
||||
handler: (accessor, arg2) => focusUp(accessor, arg2)
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
@@ -181,19 +224,27 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey),
|
||||
primary: KeyMod.Shift | KeyCode.UpArrow,
|
||||
handler: (accessor, arg2) => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List / Tree
|
||||
if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const list = focused;
|
||||
|
||||
// Focus up first
|
||||
const previousFocus = list.getFocus() ? list.getFocus()[0] : undefined;
|
||||
focusUp(accessor, arg2, false);
|
||||
|
||||
// Then adjust selection
|
||||
expandMultiSelection(focused, previousFocus);
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Focus up first
|
||||
const previousFocus = widget.getFocus() ? widget.getFocus()[0] : undefined;
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
widget.focusPrevious(typeof arg2 === 'number' ? arg2 : 1, false, fakeKeyboardEvent);
|
||||
|
||||
// Then adjust selection
|
||||
expandMultiSelection(widget, previousFocus);
|
||||
|
||||
const focus = widget.getFocus();
|
||||
|
||||
if (focus.length) {
|
||||
widget.reveal(focus[0]);
|
||||
}
|
||||
|
||||
ensureDOMFocus(widget);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -207,29 +258,29 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow]
|
||||
},
|
||||
handler: (accessor) => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// Tree only
|
||||
if (focused && !(focused instanceof List || focused instanceof PagedList)) {
|
||||
if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const tree = focused;
|
||||
const focusedElements = tree.getFocus();
|
||||
if (!widget || !(widget instanceof ObjectTree || widget instanceof DataTree || widget instanceof AsyncDataTree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (focusedElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
const tree = widget;
|
||||
const focusedElements = tree.getFocus();
|
||||
|
||||
const focus = focusedElements[0];
|
||||
if (focusedElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tree.collapse(focus)) {
|
||||
const parent = tree.getParentElement(focus);
|
||||
const focus = focusedElements[0];
|
||||
|
||||
if (parent) {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
tree.setFocus([parent], fakeKeyboardEvent);
|
||||
tree.reveal(parent);
|
||||
}
|
||||
}
|
||||
if (!tree.collapse(focus)) {
|
||||
const parent = tree.getParentElement(focus);
|
||||
|
||||
if (parent) {
|
||||
navigate(widget, widget => {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
widget.setFocus([parent], fakeKeyboardEvent);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,10 +296,10 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow]
|
||||
},
|
||||
handler: (accessor) => {
|
||||
const focusedTree = accessor.get(IListService).lastFocusedList;
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
if (focusedTree && !(focusedTree instanceof List || focusedTree instanceof PagedList)) {
|
||||
focusedTree.collapseAll();
|
||||
if (focused && !(focused instanceof List || focused instanceof PagedList || focused instanceof Table)) {
|
||||
focused.collapseAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -259,25 +310,24 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
handler: (accessor) => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
if (!focused || focused instanceof List || focused instanceof PagedList) {
|
||||
if (!widget || !(widget instanceof ObjectTree || widget instanceof DataTree || widget instanceof AsyncDataTree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const tree = focused;
|
||||
const focusedElements = tree.getFocus();
|
||||
if (focusedElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
const focus = focusedElements[0];
|
||||
const parent = tree.getParentElement(focus);
|
||||
if (parent) {
|
||||
const tree = widget;
|
||||
const focusedElements = tree.getFocus();
|
||||
if (focusedElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
const focus = focusedElements[0];
|
||||
const parent = tree.getParentElement(focus);
|
||||
if (parent) {
|
||||
navigate(widget, widget => {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
tree.setFocus([parent], fakeKeyboardEvent);
|
||||
tree.reveal(parent);
|
||||
}
|
||||
widget.setFocus([parent], fakeKeyboardEvent);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -288,206 +338,70 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.RightArrow,
|
||||
handler: (accessor) => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// Tree only
|
||||
if (focused && !(focused instanceof List || focused instanceof PagedList)) {
|
||||
if (focused instanceof ObjectTree || focused instanceof DataTree) {
|
||||
// TODO@Joao: instead of doing this here, just delegate to a tree method
|
||||
const tree = focused;
|
||||
const focusedElements = tree.getFocus();
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (focusedElements.length === 0) {
|
||||
return;
|
||||
if (widget instanceof ObjectTree || widget instanceof DataTree) {
|
||||
// TODO@Joao: instead of doing this here, just delegate to a tree method
|
||||
const focusedElements = widget.getFocus();
|
||||
|
||||
if (focusedElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const focus = focusedElements[0];
|
||||
|
||||
if (!widget.expand(focus)) {
|
||||
const child = widget.getFirstElementChild(focus);
|
||||
|
||||
if (child) {
|
||||
const node = widget.getNode(child);
|
||||
|
||||
if (node.visible) {
|
||||
navigate(widget, widget => {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
widget.setFocus([child], fakeKeyboardEvent);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (widget instanceof AsyncDataTree) {
|
||||
// TODO@Joao: instead of doing this here, just delegate to a tree method
|
||||
const focusedElements = widget.getFocus();
|
||||
|
||||
const focus = focusedElements[0];
|
||||
if (focusedElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tree.expand(focus)) {
|
||||
const child = tree.getFirstElementChild(focus);
|
||||
const focus = focusedElements[0];
|
||||
widget.expand(focus).then(didExpand => {
|
||||
if (focus && !didExpand) {
|
||||
const child = widget.getFirstElementChild(focus);
|
||||
|
||||
if (child) {
|
||||
const node = tree.getNode(child);
|
||||
const node = widget.getNode(child);
|
||||
|
||||
if (node.visible) {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
tree.setFocus([child], fakeKeyboardEvent);
|
||||
tree.reveal(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (focused instanceof AsyncDataTree) {
|
||||
// TODO@Joao: instead of doing this here, just delegate to a tree method
|
||||
const tree = focused;
|
||||
const focusedElements = tree.getFocus();
|
||||
|
||||
if (focusedElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const focus = focusedElements[0];
|
||||
tree.expand(focus).then(didExpand => {
|
||||
if (focus && !didExpand) {
|
||||
const child = tree.getFirstElementChild(focus);
|
||||
|
||||
if (child) {
|
||||
const node = tree.getNode(child);
|
||||
|
||||
if (node.visible) {
|
||||
navigate(widget, widget => {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
tree.setFocus([child], fakeKeyboardEvent);
|
||||
tree.reveal(child);
|
||||
}
|
||||
widget.setFocus([child], fakeKeyboardEvent);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusPageUp',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.PageUp,
|
||||
handler: (accessor) => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
focused.focusPreviousPage();
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
focused.focusPreviousPage(fakeKeyboardEvent);
|
||||
}
|
||||
|
||||
// Ensure DOM Focus
|
||||
ensureDOMFocus(focused);
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusPageDown',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.PageDown,
|
||||
handler: (accessor) => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
focused.focusNextPage();
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
focused.focusNextPage(fakeKeyboardEvent);
|
||||
}
|
||||
|
||||
// Ensure DOM Focus
|
||||
ensureDOMFocus(focused);
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusFirst',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.Home,
|
||||
handler: accessor => listFocusFirst(accessor)
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusFirstChild',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: 0,
|
||||
handler: accessor => listFocusFirst(accessor, { fromFocused: true })
|
||||
});
|
||||
|
||||
function listFocusFirst(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
const list = focused;
|
||||
|
||||
list.setFocus([0]);
|
||||
list.reveal(0);
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const tree = focused;
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
tree.focusFirst(fakeKeyboardEvent);
|
||||
|
||||
const focus = tree.getFocus();
|
||||
|
||||
if (focus.length > 0) {
|
||||
tree.reveal(focus[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure DOM Focus
|
||||
ensureDOMFocus(focused);
|
||||
}
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusLast',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyCode.End,
|
||||
handler: accessor => listFocusLast(accessor)
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.focusLastChild',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: 0,
|
||||
handler: accessor => listFocusLast(accessor, { fromFocused: true })
|
||||
});
|
||||
|
||||
function listFocusLast(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
const list = focused;
|
||||
|
||||
list.setFocus([list.length - 1]);
|
||||
list.reveal(list.length - 1);
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const tree = focused;
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
tree.focusLast(fakeKeyboardEvent);
|
||||
|
||||
const focus = tree.getFocus();
|
||||
|
||||
if (focus.length > 0) {
|
||||
tree.reveal(focus[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure DOM Focus
|
||||
ensureDOMFocus(focused);
|
||||
}
|
||||
|
||||
|
||||
function focusElement(accessor: ServicesAccessor, retainCurrentFocus: boolean): void {
|
||||
function selectElement(accessor: ServicesAccessor, retainCurrentFocus: boolean): void {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
const fakeKeyboardEvent = getSelectionKeyboardEvent('keydown', retainCurrentFocus);
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
|
||||
const list = focused;
|
||||
list.setSelection(list.getFocus(), fakeKeyboardEvent);
|
||||
}
|
||||
@@ -524,7 +438,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow]
|
||||
},
|
||||
handler: (accessor) => {
|
||||
focusElement(accessor, false);
|
||||
selectElement(accessor, false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -533,7 +447,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
handler: accessor => {
|
||||
focusElement(accessor, true);
|
||||
selectElement(accessor, true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -546,7 +460,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
|
||||
const list = focused;
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
list.setSelection(range(list.length), fakeKeyboardEvent);
|
||||
@@ -653,7 +567,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
}
|
||||
}
|
||||
|
||||
focusElement(accessor, true);
|
||||
selectElement(accessor, true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -663,43 +577,24 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListHasSelectionOrFocus),
|
||||
primary: KeyCode.Escape,
|
||||
handler: (accessor) => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
const list = focused;
|
||||
|
||||
list.setSelection([]);
|
||||
list.setFocus([]);
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const list = focused;
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
|
||||
list.setSelection([], fakeKeyboardEvent);
|
||||
list.setFocus([], fakeKeyboardEvent);
|
||||
}
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
widget.setSelection([], fakeKeyboardEvent);
|
||||
widget.setFocus([], fakeKeyboardEvent);
|
||||
widget.setAnchor(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: 'list.toggleKeyboardNavigation',
|
||||
handler: (accessor) => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
const list = focused;
|
||||
list.toggleKeyboardNavigation();
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const tree = focused;
|
||||
tree.toggleKeyboardNavigation();
|
||||
}
|
||||
const widget = accessor.get(IListService).lastFocusedList;
|
||||
widget?.toggleKeyboardNavigation();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -709,7 +604,7 @@ CommandsRegistry.registerCommand({
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
if (focused instanceof List || focused instanceof PagedList || focused instanceof Table) {
|
||||
// TODO@joao
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
@@ -32,7 +32,7 @@ abstract class BaseNavigationAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<boolean | IViewlet | IPanel> {
|
||||
override async run(): Promise<void> {
|
||||
const isEditorFocus = this.layoutService.hasFocus(Parts.EDITOR_PART);
|
||||
const isPanelFocus = this.layoutService.hasFocus(Parts.PANEL_PART);
|
||||
const isSidebarFocus = this.layoutService.hasFocus(Parts.SIDEBAR_PART);
|
||||
@@ -41,7 +41,7 @@ abstract class BaseNavigationAction extends Action {
|
||||
if (isEditorFocus) {
|
||||
const didNavigate = this.navigateAcrossEditorGroup(this.toGroupDirection(this.direction));
|
||||
if (didNavigate) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
neighborPart = this.layoutService.getVisibleNeighborPart(Parts.EDITOR_PART, this.direction);
|
||||
@@ -56,18 +56,12 @@ abstract class BaseNavigationAction extends Action {
|
||||
}
|
||||
|
||||
if (neighborPart === Parts.EDITOR_PART) {
|
||||
return this.navigateToEditorGroup(this.direction === Direction.Right ? GroupLocation.FIRST : GroupLocation.LAST);
|
||||
this.navigateToEditorGroup(this.direction === Direction.Right ? GroupLocation.FIRST : GroupLocation.LAST);
|
||||
} else if (neighborPart === Parts.SIDEBAR_PART) {
|
||||
this.navigateToSidebar();
|
||||
} else if (neighborPart === Parts.PANEL_PART) {
|
||||
this.navigateToPanel();
|
||||
}
|
||||
|
||||
if (neighborPart === Parts.SIDEBAR_PART) {
|
||||
return this.navigateToSidebar();
|
||||
}
|
||||
|
||||
if (neighborPart === Parts.PANEL_PART) {
|
||||
return this.navigateToPanel();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async navigateToPanel(): Promise<IPanel | boolean> {
|
||||
@@ -137,7 +131,7 @@ abstract class BaseNavigationAction extends Action {
|
||||
class NavigateLeftAction extends BaseNavigationAction {
|
||||
|
||||
static readonly ID = 'workbench.action.navigateLeft';
|
||||
static readonly LABEL = nls.localize('navigateLeft', "Navigate to the View on the Left");
|
||||
static readonly LABEL = localize('navigateLeft', "Navigate to the View on the Left");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -154,7 +148,7 @@ class NavigateLeftAction extends BaseNavigationAction {
|
||||
class NavigateRightAction extends BaseNavigationAction {
|
||||
|
||||
static readonly ID = 'workbench.action.navigateRight';
|
||||
static readonly LABEL = nls.localize('navigateRight', "Navigate to the View on the Right");
|
||||
static readonly LABEL = localize('navigateRight', "Navigate to the View on the Right");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -171,7 +165,7 @@ class NavigateRightAction extends BaseNavigationAction {
|
||||
class NavigateUpAction extends BaseNavigationAction {
|
||||
|
||||
static readonly ID = 'workbench.action.navigateUp';
|
||||
static readonly LABEL = nls.localize('navigateUp', "Navigate to the View Above");
|
||||
static readonly LABEL = localize('navigateUp', "Navigate to the View Above");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -188,7 +182,7 @@ class NavigateUpAction extends BaseNavigationAction {
|
||||
class NavigateDownAction extends BaseNavigationAction {
|
||||
|
||||
static readonly ID = 'workbench.action.navigateDown';
|
||||
static readonly LABEL = nls.localize('navigateDown', "Navigate to the View Below");
|
||||
static readonly LABEL = localize('navigateDown', "Navigate to the View Below");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -229,7 +223,7 @@ function focusNextOrPreviousPart(layoutService: IWorkbenchLayoutService, editorS
|
||||
|
||||
export class FocusNextPart extends Action {
|
||||
static readonly ID = 'workbench.action.focusNextPart';
|
||||
static readonly LABEL = nls.localize('focusNextPart', "Focus Next Part");
|
||||
static readonly LABEL = localize('focusNextPart', "Focus Next Part");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -240,14 +234,14 @@ export class FocusNextPart extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
focusNextOrPreviousPart(this.layoutService, this.editorService, true);
|
||||
}
|
||||
}
|
||||
|
||||
export class FocusPreviousPart extends Action {
|
||||
static readonly ID = 'workbench.action.focusPreviousPart';
|
||||
static readonly LABEL = nls.localize('focusPreviousPart', "Focus Previous Part");
|
||||
static readonly LABEL = localize('focusPreviousPart', "Focus Previous Part");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -258,7 +252,7 @@ export class FocusPreviousPart extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
focusNextOrPreviousPart(this.layoutService, this.editorService, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export class TextInputActionsProvider extends Disposable implements IWorkbenchCo
|
||||
// Cut / Copy / Paste
|
||||
new Action('editor.action.clipboardCutAction', localize('cut', "Cut"), undefined, true, async () => document.execCommand('cut')),
|
||||
new Action('editor.action.clipboardCopyAction', localize('copy', "Copy"), undefined, true, async () => document.execCommand('copy')),
|
||||
new Action('editor.action.clipboardPasteAction', localize('paste', "Paste"), undefined, true, async (element: HTMLInputElement | HTMLTextAreaElement) => {
|
||||
new Action('editor.action.clipboardPasteAction', localize('paste', "Paste"), undefined, true, async element => {
|
||||
|
||||
// Native: paste is supported
|
||||
if (isNative) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
@@ -46,18 +46,18 @@ abstract class BaseOpenRecentAction extends Action {
|
||||
|
||||
private readonly removeFromRecentlyOpened: IQuickInputButton = {
|
||||
iconClass: Codicon.removeClose.classNames,
|
||||
tooltip: nls.localize('remove', "Remove from Recently Opened")
|
||||
tooltip: localize('remove', "Remove from Recently Opened")
|
||||
};
|
||||
|
||||
private readonly dirtyRecentlyOpenedFolder: IQuickInputButton = {
|
||||
iconClass: 'dirty-workspace ' + Codicon.closeDirty.classNames,
|
||||
tooltip: nls.localize('dirtyRecentlyOpenedFolder', "Folder With Unsaved Files"),
|
||||
tooltip: localize('dirtyRecentlyOpenedFolder', "Folder With Unsaved Files"),
|
||||
alwaysVisible: true
|
||||
};
|
||||
|
||||
private readonly dirtyRecentlyOpenedWorkspace: IQuickInputButton = {
|
||||
...this.dirtyRecentlyOpenedFolder,
|
||||
tooltip: nls.localize('dirtyRecentlyOpenedWorkspace', "Workspace With Unsaved Files"),
|
||||
tooltip: localize('dirtyRecentlyOpenedWorkspace', "Workspace With Unsaved Files"),
|
||||
};
|
||||
|
||||
constructor(
|
||||
@@ -78,7 +78,7 @@ abstract class BaseOpenRecentAction extends Action {
|
||||
|
||||
protected abstract isQuickNavigate(): boolean;
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
const recentlyOpened = await this.workspacesService.getRecentlyOpened();
|
||||
const dirtyWorkspacesAndFolders = await this.workspacesService.getDirtyWorkspaces();
|
||||
|
||||
@@ -133,14 +133,14 @@ abstract class BaseOpenRecentAction extends Action {
|
||||
|
||||
let keyMods: IKeyMods | undefined;
|
||||
|
||||
const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: hasWorkspaces ? nls.localize('workspacesAndFolders', "folders & workspaces") : nls.localize('folders', "folders") };
|
||||
const fileSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('files', "files") };
|
||||
const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: hasWorkspaces ? localize('workspacesAndFolders', "folders & workspaces") : localize('folders', "folders") };
|
||||
const fileSeparator: IQuickPickSeparator = { type: 'separator', label: localize('files', "files") };
|
||||
const picks = [workspaceSeparator, ...workspacePicks, fileSeparator, ...filePicks];
|
||||
|
||||
const pick = await this.quickInputService.pick(picks, {
|
||||
contextKey: inRecentFilesPickerContextKey,
|
||||
activeItem: [...workspacePicks, ...filePicks][autoFocusSecondEntry ? 1 : 0],
|
||||
placeHolder: isMacintosh ? nls.localize('openRecentPlaceholderMac', "Select to open (hold Cmd-key to force new window or Alt-key for same window)") : nls.localize('openRecentPlaceholder', "Select to open (hold Ctrl-key to force new window or Alt-key for same window)"),
|
||||
placeHolder: isMacintosh ? localize('openRecentPlaceholderMac', "Select to open (hold Cmd-key to force new window or Alt-key for same window)") : localize('openRecentPlaceholder', "Select to open (hold Ctrl-key to force new window or Alt-key for same window)"),
|
||||
matchOnDescription: true,
|
||||
onKeyMods: mods => keyMods = mods,
|
||||
quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined,
|
||||
@@ -157,9 +157,9 @@ abstract class BaseOpenRecentAction extends Action {
|
||||
const isDirtyWorkspace = context.button === this.dirtyRecentlyOpenedWorkspace;
|
||||
const result = await this.dialogService.confirm({
|
||||
type: 'question',
|
||||
title: isDirtyWorkspace ? nls.localize('dirtyWorkspace', "Workspace with Unsaved Files") : nls.localize('dirtyFolder', "Folder with Unsaved Files"),
|
||||
message: isDirtyWorkspace ? nls.localize('dirtyWorkspaceConfirm', "Do you want to open the workspace to review the unsaved files?") : nls.localize('dirtyFolderConfirm', "Do you want to open the folder to review the unsaved files?"),
|
||||
detail: isDirtyWorkspace ? nls.localize('dirtyWorkspaceConfirmDetail', "Workspaces with unsaved files cannot be removed until all unsaved files have been saved or reverted.") : nls.localize('dirtyFolderConfirmDetail', "Folders with unsaved files cannot be removed until all unsaved files have been saved or reverted.")
|
||||
title: isDirtyWorkspace ? localize('dirtyWorkspace', "Workspace with Unsaved Files") : localize('dirtyFolder', "Folder with Unsaved Files"),
|
||||
message: isDirtyWorkspace ? localize('dirtyWorkspaceConfirm', "Do you want to open the workspace to review the unsaved files?") : localize('dirtyFolderConfirm', "Do you want to open the folder to review the unsaved files?"),
|
||||
detail: isDirtyWorkspace ? localize('dirtyWorkspaceConfirmDetail', "Workspaces with unsaved files cannot be removed until all unsaved files have been saved or reverted.") : localize('dirtyFolderConfirmDetail', "Folders with unsaved files cannot be removed until all unsaved files have been saved or reverted.")
|
||||
});
|
||||
|
||||
if (result.confirmed) {
|
||||
@@ -212,7 +212,7 @@ abstract class BaseOpenRecentAction extends Action {
|
||||
return {
|
||||
iconClasses,
|
||||
label: name,
|
||||
ariaLabel: isDirty ? isWorkspace ? nls.localize('recentDirtyWorkspaceAriaLabel', "{0}, workspace with unsaved changes", name) : nls.localize('recentDirtyFolderAriaLabel', "{0}, folder with unsaved changes", name) : name,
|
||||
ariaLabel: isDirty ? isWorkspace ? localize('recentDirtyWorkspaceAriaLabel', "{0}, workspace with unsaved changes", name) : localize('recentDirtyFolderAriaLabel', "{0}, folder with unsaved changes", name) : name,
|
||||
description: parentPath,
|
||||
buttons: isDirty ? [isWorkspace ? this.dirtyRecentlyOpenedWorkspace : this.dirtyRecentlyOpenedFolder] : [this.removeFromRecentlyOpened],
|
||||
openable,
|
||||
@@ -224,7 +224,7 @@ abstract class BaseOpenRecentAction extends Action {
|
||||
export class OpenRecentAction extends BaseOpenRecentAction {
|
||||
|
||||
static readonly ID = 'workbench.action.openRecent';
|
||||
static readonly LABEL = nls.localize('openRecent', "Open Recent...");
|
||||
static readonly LABEL = localize('openRecent', "Open Recent...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -250,7 +250,7 @@ export class OpenRecentAction extends BaseOpenRecentAction {
|
||||
class QuickPickRecentAction extends BaseOpenRecentAction {
|
||||
|
||||
static readonly ID = 'workbench.action.quickOpenRecent';
|
||||
static readonly LABEL = nls.localize('quickOpenRecent', "Quick Open Recent...");
|
||||
static readonly LABEL = localize('quickOpenRecent', "Quick Open Recent...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -276,7 +276,7 @@ class QuickPickRecentAction extends BaseOpenRecentAction {
|
||||
class ToggleFullScreenAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleFullScreen';
|
||||
static readonly LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen");
|
||||
static readonly LABEL = localize('toggleFullScreen', "Toggle Full Screen");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -286,7 +286,7 @@ class ToggleFullScreenAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
override run(): Promise<void> {
|
||||
return this.hostService.toggleFullScreen();
|
||||
}
|
||||
}
|
||||
@@ -294,7 +294,7 @@ class ToggleFullScreenAction extends Action {
|
||||
export class ReloadWindowAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.reloadWindow';
|
||||
static readonly LABEL = nls.localize('reloadWindow', "Reload Window");
|
||||
static readonly LABEL = localize('reloadWindow', "Reload Window");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -304,17 +304,15 @@ export class ReloadWindowAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<boolean> {
|
||||
override async run(): Promise<void> {
|
||||
await this.hostService.reload();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class ShowAboutDialogAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.showAboutDialog';
|
||||
static readonly LABEL = nls.localize('about', "About");
|
||||
static readonly LABEL = localize('about', "About");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -324,7 +322,7 @@ class ShowAboutDialogAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
override run(): Promise<void> {
|
||||
return this.dialogService.about();
|
||||
}
|
||||
}
|
||||
@@ -332,7 +330,7 @@ class ShowAboutDialogAction extends Action {
|
||||
export class NewWindowAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.newWindow';
|
||||
static readonly LABEL = nls.localize('newWindow', "New Window");
|
||||
static readonly LABEL = localize('newWindow', "New Window");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -342,8 +340,8 @@ export class NewWindowAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
return this.hostService.openWindow();
|
||||
override run(): Promise<void> {
|
||||
return this.hostService.openWindow({ remoteAuthority: null });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +350,7 @@ class BlurAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.blur',
|
||||
title: nls.localize('blur', "Remove keyboard focus from focused element")
|
||||
title: localize('blur', "Remove keyboard focus from focused element")
|
||||
});
|
||||
}
|
||||
|
||||
@@ -369,7 +367,7 @@ const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActio
|
||||
|
||||
// --- Actions Registration
|
||||
|
||||
const fileCategory = nls.localize('file', "File");
|
||||
const fileCategory = localize('file', "File");
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(NewWindowAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_N }), 'New Window');
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickPickRecentAction), 'File: Quick Open Recent...', fileCategory);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenRecentAction, { primary: KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', fileCategory);
|
||||
@@ -426,7 +424,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
group: 'z_ConfirmClose',
|
||||
command: {
|
||||
id: 'workbench.action.toggleConfirmBeforeClose',
|
||||
title: nls.localize('miConfirmClose', "Confirm Before Close"),
|
||||
title: localize('miConfirmClose', "Confirm Before Close"),
|
||||
toggled: ContextKeyExpr.notEquals('config.window.confirmBeforeClose', 'never')
|
||||
},
|
||||
order: 1,
|
||||
@@ -437,13 +435,13 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
group: '1_new',
|
||||
command: {
|
||||
id: NewWindowAction.ID,
|
||||
title: nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")
|
||||
title: localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
title: nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"),
|
||||
title: localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"),
|
||||
submenu: MenuId.MenubarRecentMenu,
|
||||
group: '2_open',
|
||||
order: 4
|
||||
@@ -453,7 +451,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, {
|
||||
group: 'y_more',
|
||||
command: {
|
||||
id: OpenRecentAction.ID,
|
||||
title: nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...")
|
||||
title: localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
@@ -462,7 +460,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '1_toggle_view',
|
||||
command: {
|
||||
id: ToggleFullScreenAction.ID,
|
||||
title: nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "&&Full Screen"),
|
||||
title: localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "&&Full Screen"),
|
||||
toggled: IsFullscreenContext
|
||||
},
|
||||
order: 1
|
||||
@@ -472,7 +470,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: 'z_about',
|
||||
command: {
|
||||
id: ShowAboutDialogAction.ID,
|
||||
title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About")
|
||||
title: localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About")
|
||||
},
|
||||
order: 1,
|
||||
when: IsMacNativeContext.toNegated()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import * as nls from 'vs/nls';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing';
|
||||
@@ -12,9 +12,9 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL, PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { MenuRegistry, MenuId, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { MenuRegistry, MenuId, SyncActionDescriptor, Action2, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { EmptyWorkspaceSupportContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
@@ -23,11 +23,13 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { WORKSPACE_TRUST_ENABLED } from 'vs/workbench/services/workspaces/common/workspaceTrust';
|
||||
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
|
||||
export class OpenFileAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openFile';
|
||||
static readonly LABEL = nls.localize('openFile', "Open File...");
|
||||
static readonly LABEL = localize('openFile', "Open File...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -37,7 +39,7 @@ export class OpenFileAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(event?: unknown, data?: ITelemetryData): Promise<void> {
|
||||
override run(event?: unknown, data?: ITelemetryData): Promise<void> {
|
||||
return this.dialogService.pickFileAndOpen({ forceNewWindow: false, telemetryExtraData: data });
|
||||
}
|
||||
}
|
||||
@@ -45,7 +47,7 @@ export class OpenFileAction extends Action {
|
||||
export class OpenFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openFolder';
|
||||
static readonly LABEL = nls.localize('openFolder', "Open Folder...");
|
||||
static readonly LABEL = localize('openFolder', "Open Folder...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -55,7 +57,7 @@ export class OpenFolderAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(event?: unknown, data?: ITelemetryData): Promise<void> {
|
||||
override run(event?: unknown, data?: ITelemetryData): Promise<void> {
|
||||
return this.dialogService.pickFolderAndOpen({ forceNewWindow: false, telemetryExtraData: data });
|
||||
}
|
||||
}
|
||||
@@ -63,7 +65,7 @@ export class OpenFolderAction extends Action {
|
||||
export class OpenFileFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openFileFolder';
|
||||
static readonly LABEL = nls.localize('openFileFolder', "Open...");
|
||||
static readonly LABEL = localize('openFileFolder', "Open...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -73,7 +75,7 @@ export class OpenFileFolderAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(event?: unknown, data?: ITelemetryData): Promise<void> {
|
||||
override run(event?: unknown, data?: ITelemetryData): Promise<void> {
|
||||
return this.dialogService.pickFileFolderAndOpen({ forceNewWindow: false, telemetryExtraData: data });
|
||||
}
|
||||
}
|
||||
@@ -81,7 +83,7 @@ export class OpenFileFolderAction extends Action {
|
||||
export class OpenWorkspaceAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openWorkspace';
|
||||
static readonly LABEL = nls.localize('openWorkspaceAction', "Open Workspace...");
|
||||
static readonly LABEL = localize('openWorkspaceAction', "Open Workspace...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -91,7 +93,7 @@ export class OpenWorkspaceAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(event?: unknown, data?: ITelemetryData): Promise<void> {
|
||||
override run(event?: unknown, data?: ITelemetryData): Promise<void> {
|
||||
return this.dialogService.pickWorkspaceAndOpen({ telemetryExtraData: data });
|
||||
}
|
||||
}
|
||||
@@ -99,7 +101,7 @@ export class OpenWorkspaceAction extends Action {
|
||||
export class CloseWorkspaceAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.closeFolder';
|
||||
static readonly LABEL = nls.localize('closeWorkspace', "Close Workspace");
|
||||
static readonly LABEL = localize('closeWorkspace', "Close Workspace");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -112,9 +114,9 @@ export class CloseWorkspaceAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
this.notificationService.info(nls.localize('noWorkspaceOrFolderOpened', "There is currently no workspace or folder opened in this instance to close."));
|
||||
this.notificationService.info(localize('noWorkspaceOrFolderOpened', "There is currently no workspace or folder opened in this instance to close."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,7 +127,7 @@ export class CloseWorkspaceAction extends Action {
|
||||
export class OpenWorkspaceConfigFileAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openWorkspaceConfigFile';
|
||||
static readonly LABEL = nls.localize('openWorkspaceConfigFile', "Open Workspace Configuration File");
|
||||
static readonly LABEL = localize('openWorkspaceConfigFile', "Open Workspace Configuration File");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -138,7 +140,7 @@ export class OpenWorkspaceConfigFileAction extends Action {
|
||||
this.enabled = !!this.workspaceContextService.getWorkspace().configuration;
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
const configuration = this.workspaceContextService.getWorkspace().configuration;
|
||||
if (configuration) {
|
||||
await this.editorService.openEditor({ resource: configuration, options: { pinned: true } });
|
||||
@@ -159,7 +161,7 @@ export class AddRootFolderAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
override run(): Promise<void> {
|
||||
return this.commandService.executeCommand(ADD_ROOT_FOLDER_COMMAND_ID);
|
||||
}
|
||||
}
|
||||
@@ -167,7 +169,7 @@ export class AddRootFolderAction extends Action {
|
||||
export class GlobalRemoveRootFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.removeRootFolder';
|
||||
static readonly LABEL = nls.localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace...");
|
||||
static readonly LABEL = localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -179,7 +181,7 @@ export class GlobalRemoveRootFolderAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
const state = this.contextService.getWorkbenchState();
|
||||
|
||||
// Workspace / Folder
|
||||
@@ -195,7 +197,7 @@ export class GlobalRemoveRootFolderAction extends Action {
|
||||
export class SaveWorkspaceAsAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.saveWorkspaceAs';
|
||||
static readonly LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As...");
|
||||
static readonly LABEL = localize('saveWorkspaceAsAction', "Save Workspace As...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -207,7 +209,7 @@ export class SaveWorkspaceAsAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
const configPathUri = await this.workspaceEditingService.pickNewWorkspacePath();
|
||||
if (configPathUri && hasWorkspaceFileExtension(configPathUri)) {
|
||||
switch (this.contextService.getWorkbenchState()) {
|
||||
@@ -225,7 +227,7 @@ export class SaveWorkspaceAsAction extends Action {
|
||||
export class DuplicateWorkspaceInNewWindowAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.duplicateWorkspaceInNewWindow';
|
||||
static readonly LABEL = nls.localize('duplicateWorkspaceInNewWindow', "Duplicate As Workspace in New Window");
|
||||
static readonly LABEL = localize('duplicateWorkspaceInNewWindow', "Duplicate As Workspace in New Window");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -239,7 +241,7 @@ export class DuplicateWorkspaceInNewWindowAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
override async run(): Promise<void> {
|
||||
const folders = this.workspaceContextService.getWorkspace().folders;
|
||||
const remoteAuthority = this.environmentService.remoteAuthority;
|
||||
|
||||
@@ -250,10 +252,29 @@ export class DuplicateWorkspaceInNewWindowAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
class WorkspaceTrustManageAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.manageTrust',
|
||||
title: { value: localize('manageTrustAction', "Manage Workspace Trust"), original: 'Manage Workspace Trust' },
|
||||
precondition: ContextKeyExpr.and(IsWebContext.negate(), ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true)),
|
||||
category: localize('workspacesCategory', "Workspaces"),
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor) {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
await commandService.executeCommand('workbench.trust.manage');
|
||||
}
|
||||
}
|
||||
|
||||
registerAction2(WorkspaceTrustManageAction);
|
||||
|
||||
// --- Actions Registration
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
const workspacesCategory = nls.localize('workspaces', "Workspaces");
|
||||
const workspacesCategory = localize('workspaces', "Workspaces");
|
||||
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(AddRootFolderAction), 'Workspaces: Add Folder to Workspace...', workspacesCategory);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(GlobalRemoveRootFolderAction), 'Workspaces: Remove Folder from Workspace...', workspacesCategory);
|
||||
@@ -271,7 +292,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
group: '3_workspace',
|
||||
command: {
|
||||
id: ADD_ROOT_FOLDER_COMMAND_ID,
|
||||
title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...")
|
||||
title: localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
@@ -280,7 +301,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
group: '3_workspace',
|
||||
command: {
|
||||
id: SaveWorkspaceAsAction.ID,
|
||||
title: nls.localize('miSaveWorkspaceAs', "Save Workspace As...")
|
||||
title: localize('miSaveWorkspaceAs', "Save Workspace As...")
|
||||
},
|
||||
order: 2,
|
||||
when: EmptyWorkspaceSupportContext
|
||||
@@ -298,7 +319,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
group: '6_close',
|
||||
command: {
|
||||
id: CloseWorkspaceAction.ID,
|
||||
title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"),
|
||||
title: localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"),
|
||||
precondition: WorkspaceFolderCountContext.notEqualsTo('0')
|
||||
},
|
||||
order: 3,
|
||||
@@ -309,7 +330,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
group: '6_close',
|
||||
command: {
|
||||
id: CloseWorkspaceAction.ID,
|
||||
title: nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace")
|
||||
title: localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace")
|
||||
},
|
||||
order: 3,
|
||||
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), EmptyWorkspaceSupportContext)
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { dirname, removeTrailingPathSeparator } from 'vs/base/common/resources';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
@@ -17,14 +17,15 @@ import { IQuickInputService, IPickOptions, IQuickPickItem } from 'vs/platform/qu
|
||||
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IFileDialogService, IPickAndOpenOptions } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IOpenWindowOptions, IWindowOpenable } from 'vs/platform/windows/common/windows';
|
||||
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
|
||||
export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder';
|
||||
export const ADD_ROOT_FOLDER_LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace...");
|
||||
export const ADD_ROOT_FOLDER_LABEL = localize('addFolderToWorkspace', "Add Folder to Workspace...");
|
||||
|
||||
export const PICK_WORKSPACE_FOLDER_COMMAND_ID = '_workbench.pickWorkspaceFolder';
|
||||
|
||||
@@ -60,19 +61,21 @@ CommandsRegistry.registerCommand({
|
||||
handler: async (accessor) => {
|
||||
const workspaceEditingService = accessor.get(IWorkspaceEditingService);
|
||||
const dialogsService = accessor.get(IFileDialogService);
|
||||
const pathService = accessor.get(IPathService);
|
||||
const folders = await dialogsService.showOpenDialog({
|
||||
openLabel: mnemonicButtonLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")),
|
||||
title: nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"),
|
||||
openLabel: mnemonicButtonLabel(localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")),
|
||||
title: localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"),
|
||||
canSelectFolders: true,
|
||||
canSelectMany: true,
|
||||
defaultUri: await dialogsService.defaultFolderPath()
|
||||
defaultUri: await dialogsService.defaultFolderPath(),
|
||||
availableFileSystems: [pathService.defaultUriScheme]
|
||||
});
|
||||
|
||||
if (!folders || !folders.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await workspaceEditingService.addFolders(folders.map(folder => ({ uri: resources.removeTrailingPathSeparator(folder) })));
|
||||
await workspaceEditingService.addFolders(folders.map(folder => ({ uri: removeTrailingPathSeparator(folder) })));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -91,7 +94,7 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, async functio
|
||||
const folderPicks: IQuickPickItem[] = folders.map(folder => {
|
||||
return {
|
||||
label: folder.name,
|
||||
description: labelService.getUriLabel(resources.dirname(folder.uri), { relative: true }),
|
||||
description: labelService.getUriLabel(dirname(folder.uri), { relative: true }),
|
||||
folder,
|
||||
iconClasses: getIconClasses(modelService, modeService, folder.uri, FileKind.ROOT_FOLDER)
|
||||
};
|
||||
@@ -104,7 +107,7 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, async functio
|
||||
}
|
||||
|
||||
if (!options.placeHolder) {
|
||||
options.placeHolder = nls.localize('workspaceFolderPickerPlaceholder', "Select workspace folder");
|
||||
options.placeHolder = localize('workspaceFolderPickerPlaceholder', "Select workspace folder");
|
||||
}
|
||||
|
||||
if (typeof options.matchOnDescription !== 'boolean') {
|
||||
@@ -127,21 +130,28 @@ interface IOpenFolderAPICommandOptions {
|
||||
forceNewWindow?: boolean;
|
||||
forceReuseWindow?: boolean;
|
||||
noRecentEntry?: boolean;
|
||||
forceLocalWindow?: boolean;
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: 'vscode.openFolder',
|
||||
handler: (accessor: ServicesAccessor, uri?: URI, arg?: boolean | IOpenFolderAPICommandOptions) => {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
||||
// Be compatible to previous args by converting to options
|
||||
if (typeof arg === 'boolean') {
|
||||
arg = { forceNewWindow: arg };
|
||||
}
|
||||
|
||||
// Without URI, ask to pick a folder or workpsace to open
|
||||
// Without URI, ask to pick a folder or workspace to open
|
||||
if (!uri) {
|
||||
return commandService.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg?.forceNewWindow });
|
||||
const options: IPickAndOpenOptions = {
|
||||
forceNewWindow: arg?.forceNewWindow
|
||||
};
|
||||
if (arg?.forceLocalWindow) {
|
||||
options.remoteAuthority = null;
|
||||
options.availableFileSystems = ['file'];
|
||||
}
|
||||
return commandService.executeCommand('_files.pickFolderAndOpen', options);
|
||||
}
|
||||
|
||||
uri = URI.revive(uri);
|
||||
@@ -149,17 +159,28 @@ CommandsRegistry.registerCommand({
|
||||
const options: IOpenWindowOptions = {
|
||||
forceNewWindow: arg?.forceNewWindow,
|
||||
forceReuseWindow: arg?.forceReuseWindow,
|
||||
noRecentEntry: arg?.noRecentEntry
|
||||
noRecentEntry: arg?.noRecentEntry,
|
||||
remoteAuthority: arg?.forceLocalWindow ? null : undefined
|
||||
};
|
||||
|
||||
const uriToOpen: IWindowOpenable = (hasWorkspaceFileExtension(uri) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri };
|
||||
return commandService.executeCommand('_files.windowOpen', [uriToOpen], options);
|
||||
},
|
||||
description: {
|
||||
description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.',
|
||||
args: [
|
||||
{ name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || value instanceof URI },
|
||||
{ name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Wheter the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' }
|
||||
{
|
||||
name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder',
|
||||
constraint: (value: any) => value === undefined || value === null || value instanceof URI
|
||||
},
|
||||
{
|
||||
name: 'options',
|
||||
description: '(optional) Options. Object with the following properties: ' +
|
||||
'`forceNewWindow`: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. ' +
|
||||
'`forceReuseWindow`: Whether to force opening the folder/workspace in the same window. Defaults to false. ' +
|
||||
'`noRecentEntry`: Whether the opened URI will appear in the \'Open Recent\' list. Defaults to false. ' +
|
||||
'Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.',
|
||||
constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user