mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 18:48:33 -05:00
Merge from vscode 1ec43773e37997841c5af42b33ddb180e9735bf2
This commit is contained in:
@@ -32,6 +32,23 @@ const BUILT_IN_AUTH_DEPENDENTS: AuthDependent[] = [
|
||||
}
|
||||
];
|
||||
|
||||
interface AllowedExtension {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function readAllowedExtensions(storageService: IStorageService, providerId: string, accountName: string): AllowedExtension[] {
|
||||
let trustedExtensions: AllowedExtension[] = [];
|
||||
try {
|
||||
const trustedExtensionSrc = storageService.get(`${providerId}-${accountName}`, StorageScope.GLOBAL);
|
||||
if (trustedExtensionSrc) {
|
||||
trustedExtensions = JSON.parse(trustedExtensionSrc);
|
||||
}
|
||||
} catch (err) { }
|
||||
|
||||
return trustedExtensions;
|
||||
}
|
||||
|
||||
export class MainThreadAuthenticationProvider extends Disposable {
|
||||
private _sessionMenuItems = new Map<string, IDisposable[]>();
|
||||
private _accounts = new Map<string, string[]>(); // Map account name to session ids
|
||||
@@ -48,30 +65,25 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
this.registerCommandsAndContextMenuItems();
|
||||
}
|
||||
|
||||
private setPermissionsForAccount(quickInputService: IQuickInputService, doLogin?: boolean) {
|
||||
const quickPick = quickInputService.createQuickPick();
|
||||
private manageTrustedExtensions(quickInputService: IQuickInputService, storageService: IStorageService, accountName: string) {
|
||||
const quickPick = quickInputService.createQuickPick<{ label: string, extension: AllowedExtension }>();
|
||||
quickPick.canSelectMany = true;
|
||||
const items = this.dependents.map(dependent => {
|
||||
const allowedExtensions = readAllowedExtensions(storageService, this.id, accountName);
|
||||
const items = allowedExtensions.map(extension => {
|
||||
return {
|
||||
label: dependent.label,
|
||||
description: dependent.scopeDescriptions,
|
||||
picked: true,
|
||||
scopes: dependent.scopes
|
||||
label: extension.name,
|
||||
extension
|
||||
};
|
||||
});
|
||||
|
||||
quickPick.items = items;
|
||||
// TODO read from storage and filter is not doLogin
|
||||
quickPick.selectedItems = items;
|
||||
quickPick.title = nls.localize('signInTo', "Sign in to {0}", this.displayName);
|
||||
quickPick.placeholder = nls.localize('accountPermissions', "Choose what features and extensions to authorize to use this account");
|
||||
quickPick.title = nls.localize('manageTrustedExtensions', "Manage Trusted Extensions");
|
||||
quickPick.placeholder = nls.localize('manageExensions', "Choose which extensions can access this account");
|
||||
|
||||
quickPick.onDidAccept(() => {
|
||||
const scopes = quickPick.selectedItems.reduce((previous, current) => previous.concat((current as any).scopes), []);
|
||||
if (scopes.length && doLogin) {
|
||||
this.login(scopes);
|
||||
}
|
||||
|
||||
const updatedAllowedList = quickPick.selectedItems.map(item => item.extension);
|
||||
storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL);
|
||||
quickPick.dispose();
|
||||
});
|
||||
|
||||
@@ -87,7 +99,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
this._register(CommandsRegistry.registerCommand({
|
||||
id: `signIn${this.id}`,
|
||||
handler: (accessor, args) => {
|
||||
this.setPermissionsForAccount(accessor.get(IQuickInputService), true);
|
||||
this.login(this.dependents.reduce((previous: string[], current) => previous.concat(current.scopes), []));
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -130,19 +142,26 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
id: `configureSessions${session.id}`,
|
||||
handler: (accessor, args) => {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
const storageService = accessor.get(IStorageService);
|
||||
|
||||
const quickPick = quickInputService.createQuickPick();
|
||||
const items = [{ label: 'Sign Out' }];
|
||||
const manage = nls.localize('manageTrustedExtensions', "Manage Trusted Extensions");
|
||||
const signOut = nls.localize('signOut', "Sign Out");
|
||||
const items = ([{ label: manage }, { label: signOut }]);
|
||||
|
||||
quickPick.items = items;
|
||||
|
||||
quickPick.onDidAccept(e => {
|
||||
const selected = quickPick.selectedItems[0];
|
||||
if (selected.label === 'Sign Out') {
|
||||
if (selected.label === signOut) {
|
||||
const sessionsForAccount = this._accounts.get(session.accountName);
|
||||
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
|
||||
}
|
||||
|
||||
if (selected.label === manage) {
|
||||
this.manageTrustedExtensions(quickInputService, storageService, session.accountName);
|
||||
}
|
||||
|
||||
quickPick.dispose();
|
||||
});
|
||||
|
||||
@@ -185,6 +204,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
disposeables.forEach(disposeable => disposeable.dispose());
|
||||
this._sessionMenuItems.delete(accountName);
|
||||
}
|
||||
this._accounts.delete(accountName);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -242,55 +262,45 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
this.authenticationService.sessionsUpdate(id, event);
|
||||
}
|
||||
|
||||
async $getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
|
||||
if (alwaysAllow) {
|
||||
return alwaysAllow === 'true';
|
||||
async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
let allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
if (allowList.some(extension => extension.id === extensionId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { choice, checkboxChecked } = await this.dialogService.show(
|
||||
const { choice } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information from {1}.", extensionName, providerName),
|
||||
nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information for the {1} account '{2}'.", extensionName, providerName, accountName),
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
|
||||
{
|
||||
cancelId: 0,
|
||||
checkbox: {
|
||||
label: nls.localize('neverAgain', "Don't Show Again")
|
||||
}
|
||||
cancelId: 0
|
||||
}
|
||||
);
|
||||
|
||||
const allow = choice === 1;
|
||||
if (checkboxChecked) {
|
||||
this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL);
|
||||
if (allow) {
|
||||
allowList = allowList.concat({ id: extensionId, name: extensionName });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
return allow;
|
||||
}
|
||||
|
||||
async $loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
|
||||
if (alwaysAllow) {
|
||||
return alwaysAllow === 'true';
|
||||
}
|
||||
|
||||
const { choice, checkboxChecked } = await this.dialogService.show(
|
||||
async $loginPrompt(providerName: string, extensionName: string): Promise<boolean> {
|
||||
const { choice } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName),
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue")],
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
|
||||
{
|
||||
cancelId: 0,
|
||||
checkbox: {
|
||||
label: nls.localize('neverAgain', "Don't Show Again")
|
||||
}
|
||||
cancelId: 0
|
||||
}
|
||||
);
|
||||
|
||||
const allow = choice === 1;
|
||||
if (checkboxChecked) {
|
||||
this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL);
|
||||
}
|
||||
return choice === 1;
|
||||
}
|
||||
|
||||
return allow;
|
||||
async $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void> {
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName).concat({ id: extensionId, name: extensionName });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,8 @@ import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IEx
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/browser/notebookService';
|
||||
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellsSplice, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
@@ -30,16 +29,17 @@ export class MainThreadNotebookDocument extends Disposable {
|
||||
) {
|
||||
super();
|
||||
this._textModel = new NotebookTextModel(handle, viewType, uri);
|
||||
this._register(this._textModel.onDidModelChange(e => {
|
||||
this._proxy.$acceptModelChanged(this.uri, e);
|
||||
}));
|
||||
}
|
||||
|
||||
async deleteCell(uri: URI, index: number): Promise<boolean> {
|
||||
let deleteExtHostCell = await this._proxy.$deleteCell(this.viewType, uri, index);
|
||||
if (deleteExtHostCell) {
|
||||
this._textModel.removeCell(index);
|
||||
return true;
|
||||
}
|
||||
applyEdit(modelVersionId: number, edits: ICellEditOperation[]): boolean {
|
||||
return this._textModel.applyEdit(modelVersionId, edits);
|
||||
}
|
||||
|
||||
return false;
|
||||
updateRenderers(renderers: number[]) {
|
||||
this._textModel.updateRenderers(renderers);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -65,6 +65,16 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
async $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean> {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
|
||||
if (controller) {
|
||||
return controller.tryApplyEdits(resource, modelVersionId, edits, renderers);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
registerListeners() {
|
||||
this._register(this._notebookService.onDidChangeActiveEditor(e => {
|
||||
this._proxy.$updateActiveEditor(e.viewType, e.uri);
|
||||
@@ -148,11 +158,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
return handle;
|
||||
}
|
||||
|
||||
async $spliceNotebookCells(viewType: string, resource: UriComponents, splices: NotebookCellsSplice[], renderers: number[]): Promise<void> {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
controller?.spliceNotebookCells(resource, splices, renderers);
|
||||
}
|
||||
|
||||
async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
controller?.spliceNotebookCellOutputs(resource, cellHandle, splices, renderers);
|
||||
@@ -201,11 +206,8 @@ export class MainThreadNotebookController implements IMainNotebookController {
|
||||
mainthreadNotebook = this._mapping.get(URI.from(uri).toString());
|
||||
if (mainthreadNotebook && mainthreadNotebook.textModel.cells.length === 0) {
|
||||
// it's empty, we should create an empty template one
|
||||
const templateCell = await this._proxy.$createEmptyCell(this._viewType, uri, 0, mainthreadNotebook.textModel.languages.length ? mainthreadNotebook.textModel.languages[0] : '', CellKind.Code);
|
||||
if (templateCell) {
|
||||
let mainCell = new NotebookCellTextModel(URI.revive(templateCell.uri), templateCell.handle, templateCell.source, templateCell.language, templateCell.cellKind, templateCell.outputs, templateCell.metadata);
|
||||
mainthreadNotebook.textModel.insertTemplateCell(mainCell);
|
||||
}
|
||||
const mainCell = mainthreadNotebook.textModel.createCellTextModel([''], mainthreadNotebook.textModel.languages.length ? mainthreadNotebook.textModel.languages[0] : '', CellKind.Code, [], undefined);
|
||||
mainthreadNotebook.textModel.insertTemplateCell(mainCell);
|
||||
}
|
||||
return mainthreadNotebook?.textModel;
|
||||
}
|
||||
@@ -213,10 +215,15 @@ export class MainThreadNotebookController implements IMainNotebookController {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
spliceNotebookCells(resource: UriComponents, splices: NotebookCellsSplice[], renderers: number[]): void {
|
||||
async tryApplyEdits(resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean> {
|
||||
let mainthreadNotebook = this._mapping.get(URI.from(resource).toString());
|
||||
mainthreadNotebook?.textModel.updateRenderers(renderers);
|
||||
mainthreadNotebook?.textModel.$spliceNotebookCells(splices);
|
||||
|
||||
if (mainthreadNotebook) {
|
||||
mainthreadNotebook.updateRenderers(renderers);
|
||||
return mainthreadNotebook.applyEdit(modelVersionId, edits);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
spliceNotebookCellOutputs(resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): void {
|
||||
@@ -259,26 +266,6 @@ export class MainThreadNotebookController implements IMainNotebookController {
|
||||
document?.textModel.updateRenderers(renderers);
|
||||
}
|
||||
|
||||
async createRawCell(uri: URI, index: number, language: string, type: CellKind): Promise<NotebookCellTextModel | undefined> {
|
||||
let cell = await this._proxy.$createEmptyCell(this._viewType, uri, index, language, type);
|
||||
if (cell) {
|
||||
let mainCell = new NotebookCellTextModel(URI.revive(cell.uri), cell.handle, cell.source, cell.language, cell.cellKind, cell.outputs, cell.metadata);
|
||||
return mainCell;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async deleteCell(uri: URI, index: number): Promise<boolean> {
|
||||
let mainthreadNotebook = this._mapping.get(URI.from(uri).toString());
|
||||
|
||||
if (mainthreadNotebook) {
|
||||
return mainthreadNotebook.deleteCell(uri, index);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async executeNotebookCell(uri: URI, handle: number): Promise<void> {
|
||||
return this._proxy.$executeNotebook(this._viewType, uri, handle);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ITextFileSaveParticipant, IResolvedTextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileSaveParticipant, ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
@@ -23,9 +23,9 @@ class ExtHostSaveParticipant implements ITextFileSaveParticipant {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentSaveParticipant);
|
||||
}
|
||||
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, _progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason; }, _progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
|
||||
if (!shouldSynchronizeModel(editorModel.textEditorModel)) {
|
||||
if (!editorModel.textEditorModel || !shouldSynchronizeModel(editorModel.textEditorModel)) {
|
||||
// the model never made it to the extension
|
||||
// host meaning we cannot participate in its save
|
||||
return undefined;
|
||||
|
||||
@@ -13,6 +13,8 @@ import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITermina
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
||||
import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
|
||||
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
|
||||
@@ -29,8 +31,9 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
extHostContext: IExtHostContext,
|
||||
@ITerminalService private readonly _terminalService: ITerminalService,
|
||||
@ITerminalInstanceService readonly terminalInstanceService: ITerminalInstanceService,
|
||||
@IRemoteAgentService readonly _remoteAgentService: IRemoteAgentService,
|
||||
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService,
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
|
||||
this._remoteAuthority = extHostContext.remoteAuthority;
|
||||
@@ -71,6 +74,13 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
if (activeInstance) {
|
||||
this._proxy.$acceptActiveTerminalChanged(activeInstance.id);
|
||||
}
|
||||
if (this._environmentVariableService.collections.size > 0) {
|
||||
const collectionAsArray = [...this._environmentVariableService.collections.entries()];
|
||||
const serializedCollections: [string, ISerializableEnvironmentVariableCollection][] = collectionAsArray.map(e => {
|
||||
return [e[0], serializeEnvironmentVariableCollection(e[1].map)];
|
||||
});
|
||||
this._proxy.$initEnvironmentVariableCollections(serializedCollections);
|
||||
}
|
||||
|
||||
this._terminalService.extHostReady(extHostContext.remoteAuthority);
|
||||
}
|
||||
@@ -346,6 +356,18 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
}
|
||||
return terminal;
|
||||
}
|
||||
|
||||
$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void {
|
||||
if (collection) {
|
||||
const translatedCollection = {
|
||||
persistent,
|
||||
map: deserializeEnvironmentVariableCollection(collection)
|
||||
};
|
||||
this._environmentVariableService.set(extensionIdentifier, translatedCollection);
|
||||
} else {
|
||||
this._environmentVariableService.delete(extensionIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Disposable, DisposableStore, dispose, IDisposable, IReference } from 'v
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { isEqual, isEqualOrParent } from 'vs/base/common/resources';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
@@ -35,9 +35,11 @@ import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/
|
||||
import { WebviewExtensionDescription, WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
|
||||
import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
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';
|
||||
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
|
||||
import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { extHostNamedCustomer } from '../common/extHostCustomers';
|
||||
|
||||
@@ -121,6 +123,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
constructor(
|
||||
context: extHostProtocol.IExtHostContext,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService,
|
||||
@ICustomEditorService private readonly _customEditorService: ICustomEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@@ -164,6 +168,20 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
},
|
||||
resolveWebview: () => { throw new Error('not implemented'); }
|
||||
}));
|
||||
|
||||
workingCopyFileService.registerWorkingCopyProvider((editorResource) => {
|
||||
const matchedWorkingCopies: IWorkingCopy[] = [];
|
||||
|
||||
for (const workingCopy of workingCopyService.workingCopies) {
|
||||
if (workingCopy instanceof MainThreadCustomEditorModel) {
|
||||
if (isEqualOrParent(editorResource, workingCopy.editorResource)) {
|
||||
matchedWorkingCopies.push(workingCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchedWorkingCopies;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public $createWebviewPanel(
|
||||
@@ -564,6 +582,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
private _currentEditIndex: number = -1;
|
||||
private _savePoint: number = -1;
|
||||
private readonly _edits: Array<number> = [];
|
||||
private _fromBackup: boolean = false;
|
||||
|
||||
public static async create(
|
||||
instantiationService: IInstantiationService,
|
||||
@@ -574,7 +593,9 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
cancellation: CancellationToken,
|
||||
) {
|
||||
const { editable } = await proxy.$createWebviewCustomEditorDocument(resource, viewType, cancellation);
|
||||
return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, editable, getEditors);
|
||||
const model = instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, editable, getEditors);
|
||||
await model.init();
|
||||
return model;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -587,6 +608,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IUndoRedoService private readonly _undoService: IUndoRedoService,
|
||||
@IBackupFileService private readonly _backupFileService: IBackupFileService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -595,6 +617,15 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
}
|
||||
}
|
||||
|
||||
get editorResource() {
|
||||
return this._editorResource;
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
const backup = await this._backupFileService.resolve<CustomDocumentBackupData>(this.resource);
|
||||
this._fromBackup = !!backup;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
if (this._editable) {
|
||||
this._undoService.removeElements(this._editorResource);
|
||||
@@ -624,7 +655,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
return this._edits.length > 0 && this._savePoint !== this._currentEditIndex;
|
||||
if (this._edits.length > 0) {
|
||||
return this._savePoint !== this._currentEditIndex;
|
||||
}
|
||||
return this._fromBackup;
|
||||
}
|
||||
|
||||
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
|
||||
|
||||
@@ -490,6 +490,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTerminalService.onDidWriteTerminalData(listener, thisArg, disposables);
|
||||
},
|
||||
getEnvironmentVariableCollection(persistent?: boolean): vscode.EnvironmentVariableCollection {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTerminalService.getEnvironmentVariableCollection(extension, persistent);
|
||||
},
|
||||
get state() {
|
||||
return extHostWindow.state;
|
||||
},
|
||||
@@ -600,15 +604,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
createInputBox(): vscode.InputBox {
|
||||
return extHostQuickOpen.createInputBox(extension.identifier);
|
||||
},
|
||||
registerNotebookProvider: (viewType: string, provider: vscode.NotebookProvider) => {
|
||||
return extHostNotebook.registerNotebookProvider(extension, viewType, provider);
|
||||
},
|
||||
registerNotebookOutputRenderer: (type: string, outputFilter: vscode.NotebookOutputSelector, renderer: vscode.NotebookOutputRenderer) => {
|
||||
return extHostNotebook.registerNotebookOutputRenderer(type, extension, outputFilter, renderer);
|
||||
},
|
||||
get activeNotebookDocument(): vscode.NotebookDocument | undefined {
|
||||
return extHostNotebook.activeNotebookDocument;
|
||||
},
|
||||
get activeColorTheme(): vscode.ColorTheme {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTheming.activeColorTheme;
|
||||
@@ -925,6 +920,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
}
|
||||
};
|
||||
|
||||
// namespace: notebook
|
||||
const notebook: typeof vscode.notebook = {
|
||||
registerNotebookProvider: (viewType: string, provider: vscode.NotebookProvider) => {
|
||||
return extHostNotebook.registerNotebookProvider(extension, viewType, provider);
|
||||
},
|
||||
registerNotebookOutputRenderer: (type: string, outputFilter: vscode.NotebookOutputSelector, renderer: vscode.NotebookOutputRenderer) => {
|
||||
return extHostNotebook.registerNotebookOutputRenderer(type, extension, outputFilter, renderer);
|
||||
},
|
||||
get activeNotebookDocument(): vscode.NotebookDocument | undefined {
|
||||
return extHostNotebook.activeNotebookDocument;
|
||||
}
|
||||
};
|
||||
|
||||
return <typeof vscode>{
|
||||
// {{SQL CARBON EDIT}} - Expose the VS Code version here for extensions that rely on it
|
||||
version: initData.vscodeVersion,
|
||||
@@ -939,6 +947,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
comment,
|
||||
comments,
|
||||
tasks,
|
||||
notebook,
|
||||
window,
|
||||
workspace,
|
||||
// types
|
||||
@@ -974,6 +983,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
DocumentLink: extHostTypes.DocumentLink,
|
||||
DocumentSymbol: extHostTypes.DocumentSymbol,
|
||||
EndOfLine: extHostTypes.EndOfLine,
|
||||
EnvironmentVariableMutatorType: extHostTypes.EnvironmentVariableMutatorType,
|
||||
EvaluatableExpression: extHostTypes.EvaluatableExpression,
|
||||
EventEmitter: Emitter,
|
||||
ExtensionKind: extHostTypes.ExtensionKind,
|
||||
|
||||
@@ -51,9 +51,10 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { Dto } from 'vs/base/common/types';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
|
||||
@@ -161,8 +162,9 @@ export interface MainThreadAuthenticationShape extends IDisposable {
|
||||
$registerAuthenticationProvider(id: string, displayName: string): void;
|
||||
$unregisterAuthenticationProvider(id: string): void;
|
||||
$onDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
|
||||
$getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$loginPrompt(providerName: string, extensionName: string): Promise<boolean>;
|
||||
$setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadConfigurationShape extends IDisposable {
|
||||
@@ -440,6 +442,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
|
||||
$stopSendingDataEvents(): void;
|
||||
$startHandlingLinks(): void;
|
||||
$stopHandlingLinks(): void;
|
||||
$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void;
|
||||
|
||||
// Process
|
||||
$sendProcessTitle(terminalId: number, title: string): void;
|
||||
@@ -696,10 +699,10 @@ export interface MainThreadNotebookShape extends IDisposable {
|
||||
$registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, handle: number, preloads: UriComponents[]): Promise<void>;
|
||||
$unregisterNotebookRenderer(handle: number): Promise<void>;
|
||||
$createNotebookDocument(handle: number, viewType: string, resource: UriComponents): Promise<void>;
|
||||
$tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean>;
|
||||
$updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise<void>;
|
||||
$updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise<void>;
|
||||
$updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise<void>;
|
||||
$spliceNotebookCells(viewType: string, resource: UriComponents, splices: NotebookCellsSplice[], renderers: number[]): Promise<void>;
|
||||
$spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void>;
|
||||
$postMessage(handle: number, value: any): Promise<boolean>;
|
||||
}
|
||||
@@ -1391,6 +1394,7 @@ export interface ExtHostTerminalServiceShape {
|
||||
$getAvailableShells(): Promise<IShellDefinitionDto[]>;
|
||||
$getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||
$handleLink(id: number, link: string): Promise<boolean>;
|
||||
$initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void;
|
||||
}
|
||||
|
||||
export interface ExtHostSCMShape {
|
||||
@@ -1535,13 +1539,12 @@ export interface ExtHostCommentsShape {
|
||||
export interface ExtHostNotebookShape {
|
||||
$resolveNotebook(viewType: string, uri: UriComponents): Promise<number | undefined>;
|
||||
$executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void>;
|
||||
$createEmptyCell(viewType: string, uri: UriComponents, index: number, language: string, type: CellKind): Promise<ICellDto | undefined>;
|
||||
$deleteCell(viewType: string, uri: UriComponents, index: number): Promise<boolean>;
|
||||
$saveNotebook(viewType: string, uri: UriComponents): Promise<boolean>;
|
||||
$updateActiveEditor(viewType: string, uri: UriComponents): Promise<void>;
|
||||
$destoryNotebookDocument(viewType: string, uri: UriComponents): Promise<boolean>;
|
||||
$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void;
|
||||
$onDidReceiveMessage(uri: UriComponents, message: any): void;
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void;
|
||||
}
|
||||
|
||||
export interface ExtHostStorageShape {
|
||||
|
||||
@@ -50,6 +50,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
getAccessToken: async () => {
|
||||
const isAllowed = await this._proxy.$getSessionsPrompt(
|
||||
provider.id,
|
||||
session.accountName,
|
||||
provider.displayName,
|
||||
ExtensionIdentifier.toKey(requestingExtension.identifier),
|
||||
requestingExtension.displayName || requestingExtension.name);
|
||||
@@ -70,12 +71,15 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
throw new Error(`No authentication provider with id '${providerId}' is currently registered.`);
|
||||
}
|
||||
|
||||
const isAllowed = await this._proxy.$loginPrompt(provider.id, provider.displayName, ExtensionIdentifier.toKey(requestingExtension.identifier), requestingExtension.displayName || requestingExtension.name);
|
||||
const extensionName = requestingExtension.displayName || requestingExtension.name;
|
||||
const isAllowed = await this._proxy.$loginPrompt(provider.displayName, extensionName);
|
||||
if (!isAllowed) {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
|
||||
return provider.login(scopes);
|
||||
const newSession = await provider.login(scopes);
|
||||
await this._proxy.$setTrustedExtension(provider.id, newSession.accountName, ExtensionIdentifier.toKey(requestingExtension.identifier), extensionName);
|
||||
return newSession;
|
||||
}
|
||||
|
||||
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { readonly } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ISplice } from 'vs/base/common/sequence';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { CellKind, CellOutputKind, ExtHostNotebookShape, ICellDto, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, NotebookCellsSplice } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { CellUri, diff, IErrorOutput, INotebookDisplayOrder, IOrderedMimeType, IOutput, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { Disposable as VSCodeDisposable } from './extHostTypes';
|
||||
|
||||
interface IObservable<T> {
|
||||
@@ -44,7 +44,8 @@ const notebookDocumentMetadataDefaults: vscode.NotebookDocumentMetadata = {
|
||||
};
|
||||
|
||||
export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
public source: string[];
|
||||
|
||||
private originalSource: string[];
|
||||
private _outputs: any[];
|
||||
private _onDidChangeOutputs = new Emitter<ISplice<vscode.CellOutput>[]>();
|
||||
onDidChangeOutputs: Event<ISplice<vscode.CellOutput>[]> = this._onDidChangeOutputs.event;
|
||||
@@ -55,21 +56,28 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
|
||||
private _metadataChangeListener: IDisposable;
|
||||
|
||||
get source() {
|
||||
if (this._textDocument && this._initalVersion !== this._textDocument?.version) {
|
||||
return this._textDocument.getText();
|
||||
} else {
|
||||
return this.originalSource.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private viewType: string,
|
||||
private documentUri: URI,
|
||||
readonly handle: number,
|
||||
readonly uri: URI,
|
||||
private _content: string,
|
||||
public cellKind: CellKind,
|
||||
public readonly cellKind: CellKind,
|
||||
public language: string,
|
||||
outputs: any[],
|
||||
_metadata: vscode.NotebookCellMetadata | undefined,
|
||||
private _proxy: MainThreadNotebookShape
|
||||
) {
|
||||
super();
|
||||
|
||||
this.source = this._content.split(/\r|\n|\r\n/g);
|
||||
this.originalSource = this._content.split(/\r|\n|\r\n/g);
|
||||
this._outputs = outputs;
|
||||
|
||||
const observableMetadata = getObservable(_metadata || {} as any);
|
||||
@@ -106,7 +114,7 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
return this._metadata;
|
||||
}
|
||||
|
||||
set metadata(newMetadata: vscode.NotebookCellMetadata | undefined) {
|
||||
set metadata(newMetadata: vscode.NotebookCellMetadata) {
|
||||
this._metadataChangeListener.dispose();
|
||||
const observableMetadata = getObservable(newMetadata || {} as any); // TODO defaults
|
||||
this._metadata = observableMetadata.proxy;
|
||||
@@ -125,7 +133,7 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
if (this._textDocument && this._initalVersion !== this._textDocument?.version) {
|
||||
return this._textDocument.getText();
|
||||
} else {
|
||||
return this.source.join('\n');
|
||||
return this.originalSource.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,9 +142,9 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
this._initalVersion = this._textDocument.version;
|
||||
}
|
||||
|
||||
detachTextDocument(document: vscode.TextDocument) {
|
||||
detachTextDocument() {
|
||||
if (this._textDocument && this._textDocument.version !== this._initalVersion) {
|
||||
this.source = this._textDocument.getText().split(/\r|\n|\r\n/g);
|
||||
this.originalSource = this._textDocument.getText().split(/\r|\n|\r\n/g);
|
||||
}
|
||||
|
||||
this._textDocument = undefined;
|
||||
@@ -156,29 +164,6 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
return this._cells;
|
||||
}
|
||||
|
||||
set cells(newCells: ExtHostCell[]) {
|
||||
let diffs = diff<ExtHostCell>(this._cells, newCells, (a) => {
|
||||
return this._cellDisposableMapping.has(a.handle);
|
||||
});
|
||||
|
||||
diffs.forEach(diff => {
|
||||
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
|
||||
this._cellDisposableMapping.get(this._cells[i].handle)?.clear();
|
||||
this._cellDisposableMapping.delete(this._cells[i].handle);
|
||||
}
|
||||
|
||||
diff.toInsert.forEach(cell => {
|
||||
this._cellDisposableMapping.set(cell.handle, new DisposableStore());
|
||||
this._cellDisposableMapping.get(cell.handle)?.add(cell.onDidChangeOutputs((outputDiffs) => {
|
||||
this.eventuallyUpdateCellOutputs(cell, outputDiffs);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
this._cells = newCells;
|
||||
this.eventuallyUpdateCells(diffs);
|
||||
}
|
||||
|
||||
private _languages: string[] = [];
|
||||
|
||||
get languages() {
|
||||
@@ -211,8 +196,15 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
this._displayOrder = newOrder;
|
||||
}
|
||||
|
||||
private _versionId = 0;
|
||||
|
||||
get versionId() {
|
||||
return this._versionId;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
private _documentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
public viewType: string,
|
||||
public uri: URI,
|
||||
public renderingHandler: ExtHostNotebookOutputRenderingHandler
|
||||
@@ -229,51 +221,47 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
|
||||
get isDirty() { return false; }
|
||||
|
||||
eventuallyUpdateCells(diffs: ISplice<ExtHostCell>[]) {
|
||||
let renderers = new Set<number>();
|
||||
let diffDtos: NotebookCellsSplice[] = [];
|
||||
accpetModelChanged(event: NotebookCellsChangedEvent) {
|
||||
this.$spliceNotebookCells(event.changes);
|
||||
this._versionId = event.versionId;
|
||||
}
|
||||
|
||||
diffDtos = diffs.map(diff => {
|
||||
let inserts = diff.toInsert;
|
||||
private $spliceNotebookCells(splices: NotebookCellsSplice2[]): void {
|
||||
if (!splices.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cellDtos = inserts.map(cell => {
|
||||
let outputs: IOutput[] = [];
|
||||
if (cell.outputs.length) {
|
||||
outputs = cell.outputs.map(output => {
|
||||
if (output.outputKind === CellOutputKind.Rich) {
|
||||
const ret = this.transformMimeTypes(cell, output);
|
||||
splices.reverse().forEach(splice => {
|
||||
let cellDtos = splice[2];
|
||||
let newCells = cellDtos.map(cell => {
|
||||
const extCell = new ExtHostCell(this.viewType, this.uri, cell.handle, URI.revive(cell.uri), cell.source.join('\n'), cell.cellKind, cell.language, cell.outputs, cell.metadata, this._proxy);
|
||||
const document = this._documentsAndEditors.getDocument(URI.revive(cell.uri));
|
||||
|
||||
if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
|
||||
renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return output as IStreamOutput | IErrorOutput;
|
||||
}
|
||||
});
|
||||
if (document) {
|
||||
extCell.attachTextDocument(document.document);
|
||||
}
|
||||
|
||||
return {
|
||||
uri: cell.uri,
|
||||
handle: cell.handle,
|
||||
source: cell.source,
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: outputs,
|
||||
metadata: cell.metadata,
|
||||
isDirty: false
|
||||
};
|
||||
if (!this._cellDisposableMapping.has(extCell.handle)) {
|
||||
this._cellDisposableMapping.set(extCell.handle, new DisposableStore());
|
||||
}
|
||||
|
||||
let store = this._cellDisposableMapping.get(extCell.handle)!;
|
||||
|
||||
store.add(extCell.onDidChangeOutputs((diffs) => {
|
||||
this.eventuallyUpdateCellOutputs(extCell, diffs);
|
||||
}));
|
||||
|
||||
return extCell;
|
||||
});
|
||||
|
||||
return [diff.start, diff.deleteCount, cellDtos];
|
||||
});
|
||||
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);
|
||||
|
||||
this._proxy.$spliceNotebookCells(
|
||||
this.viewType,
|
||||
this.uri,
|
||||
diffDtos,
|
||||
Array.from(renderers)
|
||||
);
|
||||
}
|
||||
|
||||
this.cells.splice(splice[0], splice[1], ...newCells);
|
||||
});
|
||||
}
|
||||
|
||||
eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<vscode.CellOutput>[]) {
|
||||
@@ -283,7 +271,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
|
||||
let transformedOutputs = outputs.map(output => {
|
||||
if (output.outputKind === CellOutputKind.Rich) {
|
||||
const ret = this.transformMimeTypes(cell, output);
|
||||
const ret = this.transformMimeTypes(output);
|
||||
|
||||
if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
|
||||
renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
|
||||
@@ -300,35 +288,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
this._proxy.$spliceNotebookCellOutputs(this.viewType, this.uri, cell.handle, outputDtos, Array.from(renderers));
|
||||
}
|
||||
|
||||
insertCell(index: number, cell: ExtHostCell) {
|
||||
this.cells.splice(index, 0, cell);
|
||||
|
||||
if (!this._cellDisposableMapping.has(cell.handle)) {
|
||||
this._cellDisposableMapping.set(cell.handle, new DisposableStore());
|
||||
}
|
||||
|
||||
let store = this._cellDisposableMapping.get(cell.handle)!;
|
||||
|
||||
store.add(cell.onDidChangeOutputs((diffs) => {
|
||||
this.eventuallyUpdateCellOutputs(cell, diffs);
|
||||
}));
|
||||
}
|
||||
|
||||
deleteCell(index: number): boolean {
|
||||
if (index >= this.cells.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let cell = this.cells[index];
|
||||
this._cellDisposableMapping.get(cell.handle)?.dispose();
|
||||
this._cellDisposableMapping.delete(cell.handle);
|
||||
|
||||
this.cells.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
transformMimeTypes(cell: ExtHostCell, output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto {
|
||||
transformMimeTypes(output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto {
|
||||
let mimeTypes = Object.keys(output.data);
|
||||
|
||||
// TODO@rebornix, the document display order might be assigned a bit later. We need to postpone sending the outputs to the core side.
|
||||
@@ -341,7 +301,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
let handlers = this.renderingHandler.findBestMatchedRenderer(mimeType);
|
||||
|
||||
if (handlers.length) {
|
||||
let renderedOutput = handlers[0].render(this, cell, output, mimeType);
|
||||
let renderedOutput = handlers[0].render(this, output, mimeType);
|
||||
|
||||
orderMimeTypes.push({
|
||||
mimeType: mimeType,
|
||||
@@ -395,14 +355,84 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
detachCellTextDocument(textDocument: vscode.TextDocument) {
|
||||
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.uri.toString());
|
||||
if (cell) {
|
||||
cell.detachTextDocument(textDocument);
|
||||
cell.detachTextDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookEditorCellEdit {
|
||||
private _finalized: boolean = false;
|
||||
private readonly _documentVersionId: number;
|
||||
private _collectedEdits: ICellEditOperation[] = [];
|
||||
private _renderers = new Set<number>();
|
||||
|
||||
constructor(
|
||||
readonly editor: ExtHostNotebookEditor
|
||||
) {
|
||||
this._documentVersionId = editor.document.versionId;
|
||||
}
|
||||
|
||||
finalize(): INotebookEditData {
|
||||
this._finalized = true;
|
||||
return {
|
||||
documentVersionId: this._documentVersionId,
|
||||
edits: this._collectedEdits,
|
||||
renderers: Array.from(this._renderers)
|
||||
};
|
||||
}
|
||||
|
||||
private _throwIfFinalized() {
|
||||
if (this._finalized) {
|
||||
throw new Error('Edit is only valid while callback runs');
|
||||
}
|
||||
}
|
||||
|
||||
insert(index: number, content: string, language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void {
|
||||
this._throwIfFinalized();
|
||||
|
||||
let cell = {
|
||||
source: [content],
|
||||
language,
|
||||
cellKind: type,
|
||||
outputs: (outputs as any[]), // TODO@rebornix
|
||||
metadata
|
||||
};
|
||||
|
||||
const transformedOutputs = outputs.map(output => {
|
||||
if (output.outputKind === CellOutputKind.Rich) {
|
||||
const ret = this.editor.document.transformMimeTypes(output);
|
||||
|
||||
if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
|
||||
this._renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return output as IStreamOutput | IErrorOutput;
|
||||
}
|
||||
});
|
||||
|
||||
cell.outputs = transformedOutputs;
|
||||
|
||||
this._collectedEdits.push({
|
||||
editType: CellEditType.Insert,
|
||||
index,
|
||||
cells: [cell]
|
||||
});
|
||||
}
|
||||
|
||||
delete(index: number): void {
|
||||
this._throwIfFinalized();
|
||||
|
||||
this._collectedEdits.push({
|
||||
editType: CellEditType.Delete,
|
||||
index,
|
||||
count: 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor {
|
||||
private _viewColumn: vscode.ViewColumn | undefined;
|
||||
private static _cellhandlePool: number = 0;
|
||||
onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
|
||||
|
||||
constructor(
|
||||
@@ -438,11 +468,52 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
}));
|
||||
}
|
||||
|
||||
createCell(content: string, language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): vscode.NotebookCell {
|
||||
const handle = ExtHostNotebookEditor._cellhandlePool++;
|
||||
const uri = CellUri.generate(this.document.uri, handle);
|
||||
const cell = new ExtHostCell(this.viewType, this.uri, handle, uri, content, type, language, outputs, metadata, this._proxy);
|
||||
return cell;
|
||||
edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable<boolean> {
|
||||
const edit = new NotebookEditorCellEdit(this);
|
||||
callback(edit);
|
||||
return this._applyEdit(edit);
|
||||
}
|
||||
|
||||
private _applyEdit(editBuilder: NotebookEditorCellEdit): Promise<boolean> {
|
||||
const editData = editBuilder.finalize();
|
||||
|
||||
// return when there is nothing to do
|
||||
if (editData.edits.length === 0) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
let compressedEdits: ICellEditOperation[] = [];
|
||||
let compressedEditsIndex = -1;
|
||||
|
||||
for (let i = 0; i < editData.edits.length; i++) {
|
||||
if (compressedEditsIndex < 0) {
|
||||
compressedEdits.push(editData.edits[i]);
|
||||
compressedEditsIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
let prevIndex = compressedEditsIndex;
|
||||
let prev = compressedEdits[prevIndex];
|
||||
|
||||
if (prev.editType === CellEditType.Insert && editData.edits[i].editType === CellEditType.Insert) {
|
||||
if (prev.index === editData.edits[i].index) {
|
||||
prev.cells.push(...(editData.edits[i] as ICellInsertEdit).cells);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (prev.editType === CellEditType.Delete && editData.edits[i].editType === CellEditType.Delete) {
|
||||
if (prev.index === editData.edits[i].index) {
|
||||
prev.count += (editData.edits[i] as ICellDeleteEdit).count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
compressedEdits.push(editData.edits[i]);
|
||||
compressedEditsIndex++;
|
||||
}
|
||||
|
||||
return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.renderers);
|
||||
}
|
||||
|
||||
get viewColumn(): vscode.ViewColumn | undefined {
|
||||
@@ -480,8 +551,8 @@ export class ExtHostNotebookOutputRenderer {
|
||||
return false;
|
||||
}
|
||||
|
||||
render(document: ExtHostNotebookDocument, cell: ExtHostCell, output: vscode.CellOutput, mimeType: string): string {
|
||||
let html = this.renderer.render(document, cell, output, mimeType);
|
||||
render(document: ExtHostNotebookDocument, output: vscode.CellOutput, mimeType: string): string {
|
||||
let html = this.renderer.render(document, output, mimeType);
|
||||
|
||||
return html;
|
||||
}
|
||||
@@ -584,7 +655,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
|
||||
if (provider) {
|
||||
if (!this._documents.has(URI.revive(uri).toString())) {
|
||||
let document = new ExtHostNotebookDocument(this._proxy, viewType, URI.revive(uri), this);
|
||||
let document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, viewType, URI.revive(uri), this);
|
||||
await this._proxy.$createNotebookDocument(
|
||||
document.handle,
|
||||
viewType,
|
||||
@@ -632,55 +703,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
return provider.provider.executeCell(document!, cell);
|
||||
}
|
||||
|
||||
async $createEmptyCell(viewType: string, uri: URI, index: number, language: string, type: CellKind): Promise<ICellDto | undefined> {
|
||||
let provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (provider) {
|
||||
let editor = this._editors.get(URI.revive(uri).toString());
|
||||
let document = this._documents.get(URI.revive(uri).toString());
|
||||
|
||||
let rawCell = editor?.editor.createCell('', language, type, [], { editable: true, runnable: true }) as ExtHostCell;
|
||||
document?.insertCell(index, rawCell!);
|
||||
|
||||
let allDocuments = this._documentsAndEditors.allDocuments();
|
||||
for (let { document: textDocument } of allDocuments) {
|
||||
let data = CellUri.parse(textDocument.uri);
|
||||
if (data) {
|
||||
if (uri.toString() === data.notebook.toString() && textDocument.uri.toString() === rawCell.uri.toString()) {
|
||||
rawCell.attachTextDocument(textDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
uri: rawCell.uri,
|
||||
handle: rawCell.handle,
|
||||
source: rawCell.source,
|
||||
language: rawCell.language,
|
||||
cellKind: rawCell.cellKind,
|
||||
metadata: rawCell.metadata,
|
||||
outputs: []
|
||||
};
|
||||
}
|
||||
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
async $deleteCell(viewType: string, uri: UriComponents, index: number): Promise<boolean> {
|
||||
let provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (!provider) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let document = this._documents.get(URI.revive(uri).toString());
|
||||
|
||||
if (document) {
|
||||
return document.deleteCell(index);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async $saveNotebook(viewType: string, uri: UriComponents): Promise<boolean> {
|
||||
let provider = this._notebookProviders.get(viewType);
|
||||
let document = this._documents.get(URI.revive(uri).toString());
|
||||
@@ -732,4 +754,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
editor.onDidReceiveMessage.fire(message);
|
||||
}
|
||||
}
|
||||
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void {
|
||||
let editor = this._editors.get(URI.revive(uriComponents).toString());
|
||||
|
||||
if (editor) {
|
||||
editor.editor.document.accpetModelChanged(event);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ import { timeout } from 'vs/base/common/async';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
||||
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Disposable as VSCodeDisposable } from './extHostTypes';
|
||||
import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType } from './extHostTypes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
|
||||
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
|
||||
|
||||
@@ -35,7 +37,8 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
|
||||
attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void;
|
||||
getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
|
||||
getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
|
||||
registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable
|
||||
registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable;
|
||||
getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection;
|
||||
}
|
||||
|
||||
export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');
|
||||
@@ -333,6 +336,8 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
||||
public abstract $getAvailableShells(): Promise<IShellDefinitionDto[]>;
|
||||
public abstract $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||
public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
|
||||
public abstract getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection;
|
||||
public abstract $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void;
|
||||
|
||||
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal {
|
||||
const terminal = new ExtHostTerminal(this._proxy, options, options.name);
|
||||
@@ -637,6 +642,67 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
||||
}
|
||||
}
|
||||
|
||||
export class EnvironmentVariableCollection implements vscode.EnvironmentVariableCollection {
|
||||
readonly map: Map<string, vscode.EnvironmentVariableMutator> = new Map();
|
||||
|
||||
protected readonly _onDidChangeCollection: Emitter<void> = new Emitter<void>();
|
||||
get onDidChangeCollection(): Event<void> { return this._onDidChangeCollection && this._onDidChangeCollection.event; }
|
||||
|
||||
constructor(
|
||||
readonly persistent: boolean,
|
||||
serialized?: ISerializableEnvironmentVariableCollection
|
||||
) {
|
||||
this.map = new Map(serialized);
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this.map.size;
|
||||
}
|
||||
|
||||
replace(variable: string, value: string): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace });
|
||||
}
|
||||
|
||||
append(variable: string, value: string): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append });
|
||||
}
|
||||
|
||||
prepend(variable: string, value: string): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend });
|
||||
}
|
||||
|
||||
private _setIfDiffers(variable: string, mutator: vscode.EnvironmentVariableMutator): void {
|
||||
const current = this.map.get(variable);
|
||||
if (!current || current.value !== mutator.value || current.type !== mutator.type) {
|
||||
this.map.set(variable, mutator);
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
}
|
||||
|
||||
get(variable: string): vscode.EnvironmentVariableMutator | undefined {
|
||||
return this.map.get(variable);
|
||||
}
|
||||
|
||||
forEach(callback: (variable: string, mutator: vscode.EnvironmentVariableMutator, collection: vscode.EnvironmentVariableCollection) => any, thisArg?: any): void {
|
||||
this.map.forEach((value, key) => callback(key, value, this));
|
||||
}
|
||||
|
||||
delete(variable: string): void {
|
||||
this.map.delete(variable);
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.map.clear();
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.map.clear();
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
|
||||
throw new Error('Not implemented');
|
||||
@@ -669,4 +735,13 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void {
|
||||
// No-op for web worker ext host as workspace permissions aren't used
|
||||
}
|
||||
|
||||
public getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection {
|
||||
// This is not implemented so worker ext host extensions cannot influence terminal envs
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void {
|
||||
// No-op for web worker ext host as collections aren't used
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remo
|
||||
import type * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
function es5ClassCompat(target: Function): any {
|
||||
///@ts-ignore
|
||||
@@ -483,6 +484,12 @@ export enum EndOfLine {
|
||||
CRLF = 2
|
||||
}
|
||||
|
||||
export enum EnvironmentVariableMutatorType {
|
||||
Replace = 1,
|
||||
Append = 2,
|
||||
Prepend = 3
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class TextEdit {
|
||||
|
||||
@@ -2591,22 +2598,22 @@ interface EditState {
|
||||
|
||||
export class CustomDocument<EditType = unknown> implements vscode.CustomDocument<EditType> {
|
||||
|
||||
|
||||
readonly #edits = new Cache<EditType>('edits');
|
||||
|
||||
#editState: EditState;
|
||||
|
||||
readonly #viewType: string;
|
||||
readonly #uri: vscode.Uri;
|
||||
|
||||
#editState: EditState = {
|
||||
allEdits: [],
|
||||
currentIndex: -1,
|
||||
saveIndex: -1,
|
||||
};
|
||||
#isDisposed = false;
|
||||
#version = 1;
|
||||
|
||||
constructor(viewType: string, uri: vscode.Uri) {
|
||||
this.#viewType = viewType;
|
||||
this.#uri = uri;
|
||||
this.#editState = {
|
||||
allEdits: [],
|
||||
currentIndex: 0,
|
||||
saveIndex: 0
|
||||
};
|
||||
}
|
||||
|
||||
//#region Public API
|
||||
@@ -2615,15 +2622,27 @@ export class CustomDocument<EditType = unknown> implements vscode.CustomDocument
|
||||
|
||||
public get uri(): vscode.Uri { return this.#uri; }
|
||||
|
||||
public get fileName(): string { return this.uri.fsPath; }
|
||||
|
||||
public get isUntitled() { return this.uri.scheme === Schemas.untitled; }
|
||||
|
||||
#onDidDispose = new Emitter<void>();
|
||||
public readonly onDidDispose = this.#onDidDispose.event;
|
||||
|
||||
get appliedEdits() {
|
||||
public get isClosed() { return this.#isDisposed; }
|
||||
|
||||
public get version() { return this.#version; }
|
||||
|
||||
public get isDirty() {
|
||||
return this.#editState.currentIndex !== this.#editState.saveIndex;
|
||||
}
|
||||
|
||||
public get appliedEdits() {
|
||||
return this.#editState.allEdits.slice(0, this.#editState.currentIndex + 1)
|
||||
.map(id => this._getEdit(id));
|
||||
}
|
||||
|
||||
get savedEdits() {
|
||||
public get savedEdits() {
|
||||
return this.#editState.allEdits.slice(0, this.#editState.saveIndex + 1)
|
||||
.map(id => this._getEdit(id));
|
||||
}
|
||||
@@ -2631,11 +2650,13 @@ export class CustomDocument<EditType = unknown> implements vscode.CustomDocument
|
||||
//#endregion
|
||||
|
||||
/** @internal */ _dispose(): void {
|
||||
this.#isDisposed = true;
|
||||
this.#onDidDispose.fire();
|
||||
this.#onDidDispose.dispose();
|
||||
}
|
||||
|
||||
/** @internal */ _updateEditState(state: EditState) {
|
||||
++this.#version;
|
||||
this.#editState = state;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,73 +24,91 @@ import * as extHostTypes from './extHostTypes';
|
||||
type IconPath = URI | { light: URI, dark: URI };
|
||||
|
||||
export class ExtHostWebview implements vscode.Webview {
|
||||
private _html: string = '';
|
||||
private _isDisposed: boolean = false;
|
||||
private _hasCalledAsWebviewUri = false;
|
||||
|
||||
public readonly _onMessageEmitter = new Emitter<any>();
|
||||
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
|
||||
readonly #handle: extHostProtocol.WebviewPanelHandle;
|
||||
readonly #proxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
readonly #deprecationService: IExtHostApiDeprecationService;
|
||||
|
||||
readonly #initData: WebviewInitData;
|
||||
readonly #workspace: IExtHostWorkspace | undefined;
|
||||
readonly #extension: IExtensionDescription;
|
||||
|
||||
#html: string = '';
|
||||
#options: vscode.WebviewOptions;
|
||||
#isDisposed: boolean = false;
|
||||
#hasCalledAsWebviewUri = false;
|
||||
|
||||
constructor(
|
||||
private readonly _handle: extHostProtocol.WebviewPanelHandle,
|
||||
private readonly _proxy: extHostProtocol.MainThreadWebviewsShape,
|
||||
private _options: vscode.WebviewOptions,
|
||||
private readonly _initData: WebviewInitData,
|
||||
private readonly _workspace: IExtHostWorkspace | undefined,
|
||||
private readonly _extension: IExtensionDescription,
|
||||
private readonly _deprecationService: IExtHostApiDeprecationService,
|
||||
) { }
|
||||
handle: extHostProtocol.WebviewPanelHandle,
|
||||
proxy: extHostProtocol.MainThreadWebviewsShape,
|
||||
options: vscode.WebviewOptions,
|
||||
initData: WebviewInitData,
|
||||
workspace: IExtHostWorkspace | undefined,
|
||||
extension: IExtensionDescription,
|
||||
deprecationService: IExtHostApiDeprecationService,
|
||||
) {
|
||||
this.#handle = handle;
|
||||
this.#proxy = proxy;
|
||||
this.#options = options;
|
||||
this.#initData = initData;
|
||||
this.#workspace = workspace;
|
||||
this.#extension = extension;
|
||||
this.#deprecationService = deprecationService;
|
||||
}
|
||||
|
||||
/* internal */ readonly _onMessageEmitter = new Emitter<any>();
|
||||
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
|
||||
|
||||
public dispose() {
|
||||
this._onMessageEmitter.dispose();
|
||||
}
|
||||
|
||||
public asWebviewUri(resource: vscode.Uri): vscode.Uri {
|
||||
this._hasCalledAsWebviewUri = true;
|
||||
return asWebviewUri(this._initData, this._handle, resource);
|
||||
this.#hasCalledAsWebviewUri = true;
|
||||
return asWebviewUri(this.#initData, this.#handle, resource);
|
||||
}
|
||||
|
||||
public get cspSource(): string {
|
||||
return this._initData.webviewCspSource
|
||||
.replace('{{uuid}}', this._handle);
|
||||
return this.#initData.webviewCspSource
|
||||
.replace('{{uuid}}', this.#handle);
|
||||
}
|
||||
|
||||
public get html(): string {
|
||||
this.assertNotDisposed();
|
||||
return this._html;
|
||||
return this.#html;
|
||||
}
|
||||
|
||||
public set html(value: string) {
|
||||
this.assertNotDisposed();
|
||||
if (this._html !== value) {
|
||||
this._html = value;
|
||||
if (!this._hasCalledAsWebviewUri && /(["'])vscode-resource:([^\s'"]+?)(["'])/i.test(value)) {
|
||||
this._hasCalledAsWebviewUri = true;
|
||||
this._deprecationService.report('Webview vscode-resource: uris', this._extension,
|
||||
if (this.#html !== value) {
|
||||
this.#html = value;
|
||||
if (!this.#hasCalledAsWebviewUri && /(["'])vscode-resource:([^\s'"]+?)(["'])/i.test(value)) {
|
||||
this.#hasCalledAsWebviewUri = true;
|
||||
this.#deprecationService.report('Webview vscode-resource: uris', this.#extension,
|
||||
`Please migrate to use the 'webview.asWebviewUri' api instead: https://aka.ms/vscode-webview-use-aswebviewuri`);
|
||||
}
|
||||
this._proxy.$setHtml(this._handle, value);
|
||||
this.#proxy.$setHtml(this.#handle, value);
|
||||
}
|
||||
}
|
||||
|
||||
public get options(): vscode.WebviewOptions {
|
||||
this.assertNotDisposed();
|
||||
return this._options;
|
||||
return this.#options;
|
||||
}
|
||||
|
||||
public set options(newOptions: vscode.WebviewOptions) {
|
||||
this.assertNotDisposed();
|
||||
this._proxy.$setOptions(this._handle, convertWebviewOptions(this._extension, this._workspace, newOptions));
|
||||
this._options = newOptions;
|
||||
this.#proxy.$setOptions(this.#handle, convertWebviewOptions(this.#extension, this.#workspace, newOptions));
|
||||
this.#options = newOptions;
|
||||
}
|
||||
|
||||
public postMessage(message: any): Promise<boolean> {
|
||||
this.assertNotDisposed();
|
||||
return this._proxy.$postMessage(this._handle, message);
|
||||
return this.#proxy.$postMessage(this.#handle, message);
|
||||
}
|
||||
|
||||
private assertNotDisposed() {
|
||||
if (this._isDisposed) {
|
||||
if (this.#isDisposed) {
|
||||
throw new Error('Webview is disposed');
|
||||
}
|
||||
}
|
||||
@@ -98,19 +116,18 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
|
||||
export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPanel {
|
||||
|
||||
private readonly _handle: extHostProtocol.WebviewPanelHandle;
|
||||
private readonly _proxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
private readonly _viewType: string;
|
||||
private _title: string;
|
||||
private _iconPath?: IconPath;
|
||||
readonly #handle: extHostProtocol.WebviewPanelHandle;
|
||||
readonly #proxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
readonly #viewType: string;
|
||||
|
||||
readonly #options: vscode.WebviewPanelOptions;
|
||||
readonly #webview: ExtHostWebview;
|
||||
readonly #options: vscode.WebviewPanelOptions;
|
||||
|
||||
#title: string;
|
||||
#iconPath?: IconPath;
|
||||
#viewColumn: vscode.ViewColumn | undefined = undefined;
|
||||
#visible: boolean = true;
|
||||
#active: boolean = true;
|
||||
|
||||
#isDisposed: boolean = false;
|
||||
|
||||
readonly #onDidDispose = this._register(new Emitter<void>());
|
||||
@@ -129,12 +146,12 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
webview: ExtHostWebview
|
||||
) {
|
||||
super();
|
||||
this._handle = handle;
|
||||
this._proxy = proxy;
|
||||
this._viewType = viewType;
|
||||
this.#handle = handle;
|
||||
this.#proxy = proxy;
|
||||
this.#viewType = viewType;
|
||||
this.#options = editorOptions;
|
||||
this.#viewColumn = viewColumn;
|
||||
this._title = title;
|
||||
this.#title = title;
|
||||
this.#webview = webview;
|
||||
}
|
||||
|
||||
@@ -145,7 +162,7 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
|
||||
this.#isDisposed = true;
|
||||
this.#onDidDispose.fire();
|
||||
this._proxy.$disposeWebview(this._handle);
|
||||
this.#proxy.$disposeWebview(this.#handle);
|
||||
this.#webview.dispose();
|
||||
|
||||
super.dispose();
|
||||
@@ -158,33 +175,33 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
|
||||
get viewType(): string {
|
||||
this.assertNotDisposed();
|
||||
return this._viewType;
|
||||
return this.#viewType;
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
this.assertNotDisposed();
|
||||
return this._title;
|
||||
return this.#title;
|
||||
}
|
||||
|
||||
set title(value: string) {
|
||||
this.assertNotDisposed();
|
||||
if (this._title !== value) {
|
||||
this._title = value;
|
||||
this._proxy.$setTitle(this._handle, value);
|
||||
if (this.#title !== value) {
|
||||
this.#title = value;
|
||||
this.#proxy.$setTitle(this.#handle, value);
|
||||
}
|
||||
}
|
||||
|
||||
get iconPath(): IconPath | undefined {
|
||||
this.assertNotDisposed();
|
||||
return this._iconPath;
|
||||
return this.#iconPath;
|
||||
}
|
||||
|
||||
set iconPath(value: IconPath | undefined) {
|
||||
this.assertNotDisposed();
|
||||
if (this._iconPath !== value) {
|
||||
this._iconPath = value;
|
||||
if (this.#iconPath !== value) {
|
||||
this.#iconPath = value;
|
||||
|
||||
this._proxy.$setIconPath(this._handle, URI.isUri(value) ? { light: value, dark: value } : value);
|
||||
this.#proxy.$setIconPath(this.#handle, URI.isUri(value) ? { light: value, dark: value } : value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,12 +244,12 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
|
||||
public postMessage(message: any): Promise<boolean> {
|
||||
this.assertNotDisposed();
|
||||
return this._proxy.$postMessage(this._handle, message);
|
||||
return this.#proxy.$postMessage(this.#handle, message);
|
||||
}
|
||||
|
||||
public reveal(viewColumn?: vscode.ViewColumn, preserveFocus?: boolean): void {
|
||||
this.assertNotDisposed();
|
||||
this._proxy.$reveal(this._handle, {
|
||||
this.#proxy.$reveal(this.#handle, {
|
||||
viewColumn: viewColumn ? typeConverters.ViewColumn.from(viewColumn) : undefined,
|
||||
preserveFocus: !!preserveFocus
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace schema {
|
||||
case 'debug/toolbar': return MenuId.DebugToolBar;
|
||||
case 'debug/toolBar': return MenuId.DebugToolBar;
|
||||
case 'menuBar/file': return MenuId.MenubarFileMenu;
|
||||
case 'menuBar/webNavigation': return MenuId.MenubarWebNavigationMenu;
|
||||
case 'scm/title': return MenuId.SCMTitle;
|
||||
case 'scm/sourceControl': return MenuId.SCMSourceControl;
|
||||
case 'scm/resourceState/context': return MenuId.SCMResourceContext;
|
||||
@@ -70,6 +71,7 @@ namespace schema {
|
||||
switch (menuId) {
|
||||
case MenuId.StatusBarWindowIndicatorMenu:
|
||||
case MenuId.MenubarFileMenu:
|
||||
case MenuId.MenubarWebNavigationMenu:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -20,14 +20,21 @@ import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostD
|
||||
import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { getSystemShell, 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 { BaseExtHostTerminalService, ExtHostTerminal, EnvironmentVariableCollection } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection';
|
||||
|
||||
export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
|
||||
private _variableResolver: ExtHostVariableResolverService | undefined;
|
||||
private _lastActiveWorkspace: IWorkspaceFolder | undefined;
|
||||
|
||||
private _environmentVariableCollections: Map<string, EnvironmentVariableCollection> = new Map();
|
||||
|
||||
// TODO: Pull this from main side
|
||||
private _isWorkspaceShellAllowed: boolean = false;
|
||||
|
||||
@@ -191,6 +198,10 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
baseEnv
|
||||
);
|
||||
|
||||
// Apply extension environment variable collections to the environment
|
||||
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);
|
||||
@@ -215,4 +226,50 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void {
|
||||
this._isWorkspaceShellAllowed = isAllowed;
|
||||
}
|
||||
|
||||
public getEnvironmentVariableCollection(extension: IExtensionDescription, persistent: boolean = false): vscode.EnvironmentVariableCollection {
|
||||
let collection: EnvironmentVariableCollection | undefined;
|
||||
if (persistent) {
|
||||
// If persistent is specified, return the current collection if it exists
|
||||
collection = this._environmentVariableCollections.get(extension.identifier.value);
|
||||
|
||||
// If persistence changed then create a new collection
|
||||
if (collection && !collection.persistent) {
|
||||
collection = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (!collection) {
|
||||
// If not persistent, clear out the current collection and create a new one
|
||||
dispose(this._environmentVariableCollections.get(extension.identifier.value));
|
||||
collection = new EnvironmentVariableCollection(persistent);
|
||||
this._setEnvironmentVariableCollection(extension.identifier.value, collection);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
private _syncEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void {
|
||||
const serialized = serializeEnvironmentVariableCollection(collection.map);
|
||||
this._proxy.$setEnvironmentVariableCollection(extensionIdentifier, collection.persistent, serialized.length === 0 ? undefined : serialized);
|
||||
}
|
||||
|
||||
public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void {
|
||||
collections.forEach(entry => {
|
||||
const extensionIdentifier = entry[0];
|
||||
const collection = new EnvironmentVariableCollection(true, entry[1]);
|
||||
this._setEnvironmentVariableCollection(extensionIdentifier, collection);
|
||||
});
|
||||
}
|
||||
|
||||
private _setEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void {
|
||||
this._environmentVariableCollections.set(extensionIdentifier, collection);
|
||||
collection.onDidChangeCollection(() => {
|
||||
// When any collection value changes send this immediately, this is done to ensure
|
||||
// following calls to createTerminal will be created with the new environment. It will
|
||||
// result in more noise by sending multiple updates when called but collections are
|
||||
// expected to be small.
|
||||
this._syncEnvironmentVariableCollection(extensionIdentifier, collection!);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user