mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
Merge branch 'notebook/dev' into main
This commit is contained in:
commit
0eff78fc42
|
@ -140,6 +140,7 @@ export class MenuId {
|
|||
static readonly CommentThreadActions = new MenuId('CommentThreadActions');
|
||||
static readonly CommentTitle = new MenuId('CommentTitle');
|
||||
static readonly CommentActions = new MenuId('CommentActions');
|
||||
static readonly NotebookToolbar = new MenuId('NotebookToolbar');
|
||||
static readonly NotebookCellTitle = new MenuId('NotebookCellTitle');
|
||||
static readonly NotebookCellInsert = new MenuId('NotebookCellInsert');
|
||||
static readonly NotebookCellBetween = new MenuId('NotebookCellBetween');
|
||||
|
|
17
src/vs/vscode.proposed.d.ts
vendored
17
src/vs/vscode.proposed.d.ts
vendored
|
@ -1402,8 +1402,25 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/106744, NotebookSerializer
|
||||
|
||||
export interface NotebookSerializer {
|
||||
dataToNotebook(data: Uint8Array): NotebookData | Thenable<NotebookData>;
|
||||
notebookToData(data: NotebookData): Uint8Array | Thenable<Uint8Array>;
|
||||
}
|
||||
|
||||
export namespace notebook {
|
||||
|
||||
// TODO@api use NotebookDocumentFilter instead of just notebookType:string?
|
||||
// TODO@API options duplicates the more powerful variant on NotebookContentProvider
|
||||
export function registerNotebookSerializer(notebookType: string, provider: NotebookSerializer, options?: NotebookDocumentContentOptions): Disposable;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/106744, NotebookContentProvider
|
||||
|
||||
|
||||
interface NotebookDocumentBackup {
|
||||
/**
|
||||
* Unique identifier for the backup.
|
||||
|
|
|
@ -24,7 +24,7 @@ import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/no
|
|||
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 { ICellEditOperation, ICellRange, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookCellsChangeType, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellEditOperation, ICellRange, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookCellsChangeType, NotebookDataDto, TransientMetadata, TransientOptions } 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 { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
@ -107,6 +107,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
|||
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
private readonly _notebookProviders = new Map<string, { controller: IMainNotebookController, disposable: IDisposable }>();
|
||||
private readonly _notebookSerializer = new Map<number, IDisposable>();
|
||||
private readonly _notebookKernelProviders = new Map<number, { extension: NotebookExtensionDescription, emitter: Emitter<URI | undefined>, provider: IDisposable }>();
|
||||
private readonly _editorEventListenersMapping = new Map<string, DisposableStore>();
|
||||
private readonly _documentEventListenersMapping = new ResourceMap<DisposableStore>();
|
||||
|
@ -124,7 +125,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
|||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@INotebookCellStatusBarService private readonly _cellStatusBarService: INotebookCellStatusBarService,
|
||||
@INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService,
|
||||
@INotebookEditorModelResolverService private readonly _notebookEditorModelResolverService: INotebookEditorModelResolverService,
|
||||
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
|
@ -147,6 +148,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
|||
item.emitter.dispose();
|
||||
item.provider.dispose();
|
||||
}
|
||||
dispose(this._notebookSerializer.values());
|
||||
dispose(this._editorEventListenersMapping.values());
|
||||
dispose(this._documentEventListenersMapping.values());
|
||||
dispose(this._cellStatusBarEntries.values());
|
||||
|
@ -323,7 +325,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
|||
this._proxy.$acceptNotebookActiveKernelChange(e);
|
||||
}));
|
||||
|
||||
this._disposables.add(this._notebookService.onNotebookDocumentSaved(e => {
|
||||
this._disposables.add(this._notebookEditorModelResolverService.onDidSaveNotebook(e => {
|
||||
this._proxy.$acceptModelSaved(e);
|
||||
}));
|
||||
|
||||
|
@ -403,8 +405,8 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
|||
contentOptions.transientOutputs = newOptions.transientOutputs;
|
||||
},
|
||||
viewOptions: options.viewOptions,
|
||||
openNotebook: async (viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer) => {
|
||||
const data = await this._proxy.$openNotebook(viewType, uri, backupId, token, untitledDocumentData);
|
||||
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
|
||||
|
@ -452,6 +454,24 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
|||
}
|
||||
}
|
||||
|
||||
$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);
|
||||
},
|
||||
notebookToData: (data: NotebookDataDto): Promise<VSBuffer> => {
|
||||
return this._proxy.$notebookToData(handle, data);
|
||||
}
|
||||
});
|
||||
this._notebookSerializer.set(handle, registration);
|
||||
}
|
||||
|
||||
$unregisterNotebookSerializer(handle: number): void {
|
||||
this._notebookSerializer.get(handle)?.dispose();
|
||||
this._notebookSerializer.delete(handle);
|
||||
}
|
||||
|
||||
async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void> {
|
||||
const emitter = new Emitter<URI | undefined>();
|
||||
const that = this;
|
||||
|
@ -585,7 +605,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
|||
|
||||
async $tryOpenDocument(uriComponents: UriComponents): Promise<URI> {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const ref = await this._notebookModelResolverService.resolve(uri, undefined);
|
||||
const ref = await this._notebookEditorModelResolverService.resolve(uri, undefined);
|
||||
this._modelReferenceCollection.add(uri, ref);
|
||||
return uri;
|
||||
}
|
||||
|
@ -593,7 +613,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
|||
async $trySaveDocument(uriComponents: UriComponents) {
|
||||
const uri = URI.revive(uriComponents);
|
||||
|
||||
const ref = await this._notebookModelResolverService.resolve(uri);
|
||||
const ref = await this._notebookEditorModelResolverService.resolve(uri);
|
||||
const saveResult = await ref.object.save();
|
||||
ref.dispose();
|
||||
return saveResult;
|
||||
|
|
|
@ -1051,6 +1051,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeActiveNotebookKernel;
|
||||
},
|
||||
registerNotebookSerializer(viewType, serializer, options) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.registerNotebookSerializer(extension, viewType, serializer, options);
|
||||
},
|
||||
registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider, options?: {
|
||||
transientOutputs: boolean;
|
||||
transientMetadata: { [K in keyof vscode.NotebookCellMetadata]?: boolean }
|
||||
|
|
|
@ -50,7 +50,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
|||
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 { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { Dto } from 'vs/base/common/types';
|
||||
import { DebugConfigurationProviderTriggerKind, WorkspaceTrustState } from 'vs/workbench/api/common/extHostTypes';
|
||||
|
@ -840,6 +840,10 @@ export interface MainThreadNotebookShape extends IDisposable {
|
|||
}): Promise<void>;
|
||||
$updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise<void>;
|
||||
$unregisterNotebookProvider(viewType: string): Promise<void>;
|
||||
|
||||
$registerNotebookSerializer(handle: number, extension: NotebookExtensionDescription, viewType: string, options: TransientOptions): void;
|
||||
$unregisterNotebookSerializer(handle: number): void;
|
||||
|
||||
$registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void>;
|
||||
$unregisterNotebookKernelProvider(handle: number): Promise<void>;
|
||||
$onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void;
|
||||
|
@ -1863,10 +1867,15 @@ export interface ExtHostNotebookShape {
|
|||
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
|
||||
$cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
|
||||
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
|
||||
$openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer): Promise<NotebookDataDto>;
|
||||
|
||||
$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>;
|
||||
$backupNotebook(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string>;
|
||||
|
||||
$dataToNotebook(handle: number, data: VSBuffer): Promise<NotebookDataDto>;
|
||||
$notebookToData(handle: number, data: NotebookDataDto): Promise<VSBuffer>;
|
||||
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void;
|
||||
$acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void;
|
||||
$acceptModelSaved(uriComponents: UriComponents): void;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
@ -17,7 +17,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa
|
|||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
|
||||
import { CellStatusbarAlignment, CellUri, INotebookCellStatusBarEntry, INotebookExclusiveDocumentFilter, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellStatusbarAlignment, CellUri, INotebookCellStatusBarEntry, INotebookExclusiveDocumentFilter, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument';
|
||||
|
@ -209,6 +209,7 @@ export class NotebookEditorDecorationType {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
type NotebookContentProviderData = {
|
||||
readonly provider: vscode.NotebookContentProvider;
|
||||
readonly extension: IExtensionDescription;
|
||||
|
@ -496,9 +497,52 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
|||
});
|
||||
}
|
||||
|
||||
// --- serialize/deserialize
|
||||
|
||||
private _handlePool = 0;
|
||||
private readonly _notebookSerializer = new Map<number, vscode.NotebookSerializer>();
|
||||
|
||||
registerNotebookSerializer(extension: IExtensionDescription, viewType: string, serializer: vscode.NotebookSerializer, options?: TransientOptions): vscode.Disposable {
|
||||
const handle = this._handlePool++;
|
||||
this._notebookSerializer.set(handle, serializer);
|
||||
this._proxy.$registerNotebookSerializer(
|
||||
handle,
|
||||
{ id: extension.identifier, location: extension.extensionLocation, description: extension.description },
|
||||
viewType,
|
||||
options ?? { transientOutputs: false, transientMetadata: {} }
|
||||
);
|
||||
return toDisposable(() => {
|
||||
this._proxy.$unregisterNotebookSerializer(handle);
|
||||
});
|
||||
}
|
||||
|
||||
async $dataToNotebook(handle: number, bytes: VSBuffer): Promise<NotebookDataDto> {
|
||||
const serializer = this._notebookSerializer.get(handle);
|
||||
if (!serializer) {
|
||||
throw new Error('NO serializer found');
|
||||
}
|
||||
const data = await serializer.dataToNotebook(bytes.buffer);
|
||||
return {
|
||||
metadata: typeConverters.NotebookDocumentMetadata.from(data.metadata),
|
||||
cells: data.cells.map(typeConverters.NotebookCellData.from),
|
||||
};
|
||||
}
|
||||
|
||||
async $notebookToData(handle: number, data: NotebookDataDto): Promise<VSBuffer> {
|
||||
const serializer = this._notebookSerializer.get(handle);
|
||||
if (!serializer) {
|
||||
throw new Error('NO serializer found');
|
||||
}
|
||||
const bytes = await serializer.notebookToData({
|
||||
metadata: typeConverters.NotebookDocumentMetadata.to(data.metadata),
|
||||
cells: data.cells.map(typeConverters.NotebookCellData.to)
|
||||
});
|
||||
return VSBuffer.wrap(bytes);
|
||||
}
|
||||
|
||||
// --- open, save, saveAs, backup
|
||||
|
||||
async $openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer): Promise<NotebookDataDto> {
|
||||
async $openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): Promise<NotebookDataDto> {
|
||||
const { provider } = this._getProviderData(viewType);
|
||||
const data = await provider.openNotebook(URI.revive(uri), { backupId, untitledDocumentData: untitledDocumentData?.buffer }, token);
|
||||
return {
|
||||
|
|
|
@ -1469,6 +1469,16 @@ export namespace NotebookCellData {
|
|||
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 {
|
||||
|
|
|
@ -170,6 +170,12 @@ const apiMenus: IAPIMenu[] = [
|
|||
description: localize('comment.actions', "The contributed comment context menu, rendered as buttons below the comment editor"),
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'notebook/toolbar',
|
||||
id: MenuId.NotebookToolbar,
|
||||
description: localize('notebook.toolbar', "The contributed notebook toolbar menu"),
|
||||
proposed: true
|
||||
},
|
||||
{
|
||||
key: 'notebook/cell/title',
|
||||
id: MenuId.NotebookCellTitle,
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
|
||||
// Scrollable Element
|
||||
|
||||
export const SCROLLABLE_ELEMENT_PADDING_TOP = 22;
|
||||
export const SCROLLABLE_ELEMENT_PADDING_TOP = 20;
|
||||
// export const SCROLLABLE_ELEMENT_PADDING_TOP_WITH_TOOLBAR = 8;
|
||||
|
||||
// Cell sizing related
|
||||
export const CELL_MARGIN = 8;
|
||||
|
|
|
@ -173,6 +173,12 @@ registerAction2(class extends NotebookCellAction {
|
|||
primary: KeyMod.Alt | KeyMod.Shift | KeyCode.DownArrow,
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
menu: {
|
||||
id: MenuId.NotebookCellTitle,
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),
|
||||
group: CellOverflowToolbarGroups.Edit,
|
||||
order: 12
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -610,7 +610,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorContext, {
|
|||
when: NOTEBOOK_EDITOR_FOCUSED
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {
|
||||
command: {
|
||||
id: EXECUTE_NOTEBOOK_COMMAND_ID,
|
||||
title: localize('notebookActions.menu.executeNotebook', "Execute Notebook (Run all cells)"),
|
||||
|
@ -618,10 +618,10 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
|||
},
|
||||
order: -1,
|
||||
group: 'navigation',
|
||||
when: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.toNegated(), executeNotebookCondition)
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.toNegated(), executeNotebookCondition)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {
|
||||
command: {
|
||||
id: CANCEL_NOTEBOOK_COMMAND_ID,
|
||||
title: localize('notebookActions.menu.cancelNotebook', "Stop Notebook Execution"),
|
||||
|
@ -629,7 +629,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
|||
},
|
||||
order: -1,
|
||||
group: 'navigation',
|
||||
when: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK)
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK)
|
||||
});
|
||||
|
||||
registerAction2(class extends NotebookCellAction {
|
||||
|
@ -1494,8 +1494,8 @@ registerAction2(class extends NotebookAction {
|
|||
id: CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID,
|
||||
title: localize('clearAllCellsOutputs', 'Clear All Cells Outputs'),
|
||||
menu: {
|
||||
id: MenuId.EditorTitle,
|
||||
when: NOTEBOOK_IS_ACTIVE_EDITOR,
|
||||
id: MenuId.NotebookToolbar,
|
||||
// when: NOTEBOOK_IS_ACTIVE_EDITOR,
|
||||
group: 'navigation',
|
||||
order: 0
|
||||
},
|
||||
|
|
|
@ -25,14 +25,11 @@ import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
|
|||
import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser';
|
||||
import { CellEditState, ICellOutputViewModel, IDisplayOutputLayoutUpdateRequest, IGenericCellViewModel, IInsetRenderOutput, NotebookLayoutInfo, NOTEBOOK_DIFF_EDITOR_ID } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { DiffSide, DIFF_CELL_MARGIN, IDiffCellInfo, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
|
||||
import { CellUri, INotebookDiffEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IDiffChange } from 'vs/base/common/diff/diff';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
|
||||
|
@ -91,8 +88,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
@IContextKeyService readonly contextKeyService: IContextKeyService,
|
||||
@INotebookEditorWorkerService readonly notebookEditorWorkerService: INotebookEditorWorkerService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IFileService private readonly _fileService: FileService,
|
||||
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
) {
|
||||
|
@ -304,42 +299,9 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
|
||||
this._revealFirst = true;
|
||||
|
||||
this._modifiedResourceDisposableStore.add(this._fileService.watch(this._model.modified.resource));
|
||||
this._modifiedResourceDisposableStore.add(this._fileService.onDidFilesChange(async e => {
|
||||
if (this._model === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.contains(this._model.modified.resource)) {
|
||||
if (this._model.modified.isDirty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modified = this._model.modified;
|
||||
const lastResolvedFileStat = modified.lastResolvedFileStat;
|
||||
const currFileStat = await this._resolveStats(modified.resource);
|
||||
|
||||
if (lastResolvedFileStat && currFileStat && currFileStat.mtime > lastResolvedFileStat.mtime) {
|
||||
await this._model.resolveModifiedFromDisk();
|
||||
await this.updateLayout();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.contains(this._model.original.resource)) {
|
||||
if (this._model.original.isDirty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const original = this._model.original;
|
||||
const lastResolvedFileStat = original.lastResolvedFileStat;
|
||||
const currFileStat = await this._resolveStats(original.resource);
|
||||
|
||||
if (lastResolvedFileStat && currFileStat && currFileStat.mtime > lastResolvedFileStat.mtime) {
|
||||
await this._model.resolveOriginalFromDisk();
|
||||
await this.updateLayout();
|
||||
return;
|
||||
}
|
||||
this._modifiedResourceDisposableStore.add(Event.any(this._model.original.notebook.onDidChangeContent, this._model.modified.notebook.onDidChangeContent)(e => {
|
||||
if (this._model !== null) {
|
||||
this.updateLayout();
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -419,19 +381,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
this._originalWebview.element.style.left = `16px`;
|
||||
}
|
||||
|
||||
private async _resolveStats(resource: URI) {
|
||||
if (resource.scheme === Schemas.untitled) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const newStats = await this._fileService.resolve(resource, { resolveMetadata: true });
|
||||
return newStats;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async updateLayout() {
|
||||
if (!this._model) {
|
||||
return;
|
||||
|
|
|
@ -11,6 +11,25 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .notebook-top-toolbar {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
padding-left: 8px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .notebook-top-toolbar .monaco-action-bar .action-item {
|
||||
width: 24px;
|
||||
height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .notebook-top-toolbar .monaco-action-bar .action-item .action-label {
|
||||
background-size: 16px;
|
||||
margin: 4px 4px 0 4px;
|
||||
}
|
||||
|
||||
.monaco-workbench .cell.markdown {
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
|
@ -855,7 +874,7 @@
|
|||
.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator .codicon {
|
||||
visibility: visible;
|
||||
height: 16px;
|
||||
padding: 4px;
|
||||
padding: 4px 4px 4px 6px;
|
||||
}
|
||||
|
||||
/** Theming */
|
||||
|
|
|
@ -34,11 +34,11 @@ class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditor
|
|||
}
|
||||
|
||||
async resolveOriginalFromDisk() {
|
||||
await this.original.load({ forceReadFromDisk: true });
|
||||
await this.original.load({ forceReadFromFile: true });
|
||||
}
|
||||
|
||||
async resolveModifiedFromDisk() {
|
||||
await this.modified.load({ forceReadFromDisk: true });
|
||||
await this.modified.load({ forceReadFromFile: true });
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
@ -53,7 +53,7 @@ export class NotebookDiffEditorInput extends EditorInput {
|
|||
|
||||
static readonly ID: string = 'workbench.input.diffNotebookInput';
|
||||
|
||||
private _textModel: IReference<IResolvedNotebookEditorModel> | null = null;
|
||||
private _modifiedTextModel: IReference<IResolvedNotebookEditorModel> | null = null;
|
||||
private _originalTextModel: IReference<IResolvedNotebookEditorModel> | null = null;
|
||||
private _defaultDirtyState: boolean = false;
|
||||
|
||||
|
@ -82,14 +82,14 @@ export class NotebookDiffEditorInput extends EditorInput {
|
|||
}
|
||||
|
||||
isDirty() {
|
||||
if (!this._textModel) {
|
||||
return !!this._defaultDirtyState;
|
||||
if (!this._modifiedTextModel) {
|
||||
return this._defaultDirtyState;
|
||||
}
|
||||
return this._textModel.object.isDirty();
|
||||
return this._modifiedTextModel.object.isDirty();
|
||||
}
|
||||
|
||||
isUntitled(): boolean {
|
||||
return this._textModel?.object.isUntitled() || false;
|
||||
return this._modifiedTextModel?.object.isUntitled() || false;
|
||||
}
|
||||
|
||||
isReadonly() {
|
||||
|
@ -97,12 +97,12 @@ export class NotebookDiffEditorInput extends EditorInput {
|
|||
}
|
||||
|
||||
async save(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
|
||||
if (this._textModel) {
|
||||
if (this._modifiedTextModel) {
|
||||
|
||||
if (this.isUntitled()) {
|
||||
return this.saveAs(group, options);
|
||||
} else {
|
||||
await this._textModel.object.save();
|
||||
await this._modifiedTextModel.object.save();
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -112,7 +112,7 @@ export class NotebookDiffEditorInput extends EditorInput {
|
|||
}
|
||||
|
||||
async saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
|
||||
if (!this._textModel || !this.viewType) {
|
||||
if (!this._modifiedTextModel || !this.viewType) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ export class NotebookDiffEditorInput extends EditorInput {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const dialogPath = this._textModel.object.resource;
|
||||
const dialogPath = this._modifiedTextModel.object.resource;
|
||||
const target = await this._fileDialogService.pickFileToSave(dialogPath, options?.availableFileSystems);
|
||||
if (!target) {
|
||||
return undefined; // save cancelled
|
||||
|
@ -147,7 +147,7 @@ ${patterns}
|
|||
`);
|
||||
}
|
||||
|
||||
if (!await this._textModel.object.saveAs(target)) {
|
||||
if (!await this._modifiedTextModel.object.saveAs(target)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -156,10 +156,10 @@ ${patterns}
|
|||
|
||||
// called when users rename a notebook document
|
||||
rename(group: GroupIdentifier, target: URI): IMoveResult | undefined {
|
||||
if (this._textModel) {
|
||||
if (this._modifiedTextModel) {
|
||||
const contributedNotebookProviders = this._notebookService.getContributedNotebookProviders(target);
|
||||
|
||||
if (contributedNotebookProviders.find(provider => provider.id === this._textModel!.object.viewType)) {
|
||||
if (contributedNotebookProviders.find(provider => provider.id === this._modifiedTextModel!.object.viewType)) {
|
||||
return this._move(group, target);
|
||||
}
|
||||
}
|
||||
|
@ -171,8 +171,8 @@ ${patterns}
|
|||
}
|
||||
|
||||
async revert(group: GroupIdentifier, options?: IRevertOptions): Promise<void> {
|
||||
if (this._textModel && this._textModel.object.isDirty()) {
|
||||
await this._textModel.object.revert(options);
|
||||
if (this._modifiedTextModel && this._modifiedTextModel.object.isDirty()) {
|
||||
await this._modifiedTextModel.object.revert(options);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -183,14 +183,19 @@ ${patterns}
|
|||
return null;
|
||||
}
|
||||
|
||||
if (!this._textModel) {
|
||||
this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!);
|
||||
if (!this._modifiedTextModel) {
|
||||
this._modifiedTextModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!);
|
||||
this._register(this._modifiedTextModel.object.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
|
||||
if (this._modifiedTextModel.object.isDirty() !== this._defaultDirtyState) {
|
||||
this._onDidChangeDirty.fire();
|
||||
}
|
||||
|
||||
}
|
||||
if (!this._originalTextModel) {
|
||||
this._originalTextModel = await this._notebookModelResolverService.resolve(this.originalResource, this.viewType!);
|
||||
}
|
||||
|
||||
return new NotebookDiffEditorModel(this._originalTextModel.object, this._textModel.object);
|
||||
return new NotebookDiffEditorModel(this._originalTextModel.object, this._modifiedTextModel.object);
|
||||
}
|
||||
|
||||
matches(otherInput: unknown): boolean {
|
||||
|
@ -205,8 +210,8 @@ ${patterns}
|
|||
}
|
||||
|
||||
dispose() {
|
||||
this._textModel?.dispose();
|
||||
this._textModel = null;
|
||||
this._modifiedTextModel?.dispose();
|
||||
this._modifiedTextModel = null;
|
||||
this._originalTextModel?.dispose();
|
||||
this._originalTextModel = null;
|
||||
super.dispose();
|
||||
|
|
|
@ -29,7 +29,7 @@ export class NotebookEditorInput extends EditorInput {
|
|||
|
||||
private readonly _name: string;
|
||||
|
||||
private _textModel: IReference<IResolvedNotebookEditorModel> | null = null;
|
||||
private _editorModelReference: IReference<IResolvedNotebookEditorModel> | null = null;
|
||||
private _defaultDirtyState: boolean = false;
|
||||
|
||||
constructor(
|
||||
|
@ -56,14 +56,14 @@ export class NotebookEditorInput extends EditorInput {
|
|||
}
|
||||
|
||||
isDirty() {
|
||||
if (!this._textModel) {
|
||||
return !!this._defaultDirtyState;
|
||||
if (!this._editorModelReference) {
|
||||
return this._defaultDirtyState;
|
||||
}
|
||||
return this._textModel.object.isDirty();
|
||||
return this._editorModelReference.object.isDirty();
|
||||
}
|
||||
|
||||
isUntitled(): boolean {
|
||||
return this._textModel?.object.isUntitled() || false;
|
||||
return this._editorModelReference?.object.isUntitled() || false;
|
||||
}
|
||||
|
||||
isReadonly() {
|
||||
|
@ -71,12 +71,12 @@ export class NotebookEditorInput extends EditorInput {
|
|||
}
|
||||
|
||||
async save(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
|
||||
if (this._textModel) {
|
||||
if (this._editorModelReference) {
|
||||
|
||||
if (this.isUntitled()) {
|
||||
return this.saveAs(group, options);
|
||||
} else {
|
||||
await this._textModel.object.save();
|
||||
await this._editorModelReference.object.save(options);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -86,7 +86,7 @@ export class NotebookEditorInput extends EditorInput {
|
|||
}
|
||||
|
||||
async saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
|
||||
if (!this._textModel) {
|
||||
if (!this._editorModelReference) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ export class NotebookEditorInput extends EditorInput {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const dialogPath = this.isUntitled() ? await this._suggestName(this._name) : this._textModel.object.resource;
|
||||
const dialogPath = this.isUntitled() ? await this._suggestName(this._name) : this._editorModelReference.object.resource;
|
||||
|
||||
const target = await this._fileDialogService.pickFileToSave(dialogPath, options?.availableFileSystems);
|
||||
if (!target) {
|
||||
|
@ -122,7 +122,7 @@ ${patterns}
|
|||
`);
|
||||
}
|
||||
|
||||
if (!await this._textModel.object.saveAs(target)) {
|
||||
if (!await this._editorModelReference.object.saveAs(target)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -135,27 +135,25 @@ ${patterns}
|
|||
|
||||
// called when users rename a notebook document
|
||||
rename(group: GroupIdentifier, target: URI): IMoveResult | undefined {
|
||||
if (this._textModel) {
|
||||
if (this._editorModelReference) {
|
||||
const contributedNotebookProviders = this._notebookService.getContributedNotebookProviders(target);
|
||||
|
||||
if (contributedNotebookProviders.find(provider => provider.id === this._textModel!.object.viewType)) {
|
||||
if (contributedNotebookProviders.find(provider => provider.id === this._editorModelReference!.object.viewType)) {
|
||||
return this._move(group, target);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _move(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined {
|
||||
private _move(_group: GroupIdentifier, newResource: URI): { editor: IEditorInput } {
|
||||
const editorInput = NotebookEditorInput.create(this._instantiationService, newResource, this.viewType);
|
||||
return { editor: editorInput };
|
||||
}
|
||||
|
||||
async revert(group: GroupIdentifier, options?: IRevertOptions): Promise<void> {
|
||||
if (this._textModel && this._textModel.object.isDirty()) {
|
||||
await this._textModel.object.revert(options);
|
||||
async revert(_group: GroupIdentifier, options?: IRevertOptions): Promise<void> {
|
||||
if (this._editorModelReference && this._editorModelReference.object.isDirty()) {
|
||||
await this._editorModelReference.object.revert(options);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async resolve(): Promise<IResolvedNotebookEditorModel | null> {
|
||||
|
@ -163,20 +161,20 @@ ${patterns}
|
|||
return null;
|
||||
}
|
||||
|
||||
if (!this._textModel) {
|
||||
this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType);
|
||||
if (!this._editorModelReference) {
|
||||
this._editorModelReference = await this._notebookModelResolverService.resolve(this.resource, this.viewType);
|
||||
if (this.isDisposed()) {
|
||||
this._textModel.dispose();
|
||||
this._textModel = null;
|
||||
this._editorModelReference.dispose();
|
||||
this._editorModelReference = null;
|
||||
return null;
|
||||
}
|
||||
this._register(this._textModel.object.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
|
||||
if (this._textModel.object.isDirty()) {
|
||||
this._register(this._editorModelReference.object.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
|
||||
if (this._editorModelReference.object.isDirty()) {
|
||||
this._onDidChangeDirty.fire();
|
||||
}
|
||||
}
|
||||
|
||||
return this._textModel.object;
|
||||
return this._editorModelReference.object;
|
||||
}
|
||||
|
||||
matches(otherInput: unknown): boolean {
|
||||
|
@ -190,8 +188,8 @@ ${patterns}
|
|||
}
|
||||
|
||||
dispose() {
|
||||
this._textModel?.dispose();
|
||||
this._textModel = null;
|
||||
this._editorModelReference?.dispose();
|
||||
this._editorModelReference = null;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,12 +26,12 @@ import { Range } from 'vs/editor/common/core/range';
|
|||
import { IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import * as nls from 'vs/nls';
|
||||
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IMenu, IMenuService, MenuId, MenuItemAction, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
|
@ -69,6 +69,10 @@ import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
|
|||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus';
|
||||
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService';
|
||||
|
||||
const $ = DOM.$;
|
||||
|
||||
|
@ -187,6 +191,7 @@ export class ListViewInfoAccessor extends Disposable {
|
|||
export class NotebookEditorWidget extends Disposable implements INotebookEditor {
|
||||
private static readonly EDITOR_MEMENTOS = new Map<string, EditorMemento<unknown>>();
|
||||
private _overlayContainer!: HTMLElement;
|
||||
private _notebookTopToolbarContainer!: HTMLElement;
|
||||
private _body!: HTMLElement;
|
||||
private _overflowContainer!: HTMLElement;
|
||||
private _webview: BackLayerWebView<ICommonCellInfo> | null = null;
|
||||
|
@ -214,6 +219,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
private readonly _memento: Memento;
|
||||
private readonly _onDidFocusEmitter = this._register(new Emitter<void>());
|
||||
public readonly onDidFocus = this._onDidFocusEmitter.event;
|
||||
private readonly _onDidBlurEmitter = this._register(new Emitter<void>());
|
||||
public readonly onDidBlur = this._onDidBlurEmitter.event;
|
||||
private readonly _insetModifyQueueByOutputId = new SequencerByKey<string>();
|
||||
private _kernelManger: NotebookEditorKernelManager;
|
||||
private _cellContextKeyManager: CellContextKeyManager | null = null;
|
||||
|
@ -328,6 +335,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
@IThemeService private readonly themeService: IThemeService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@optional(ITASExperimentService) private readonly experimentService: ITASExperimentService
|
||||
) {
|
||||
super();
|
||||
this.isEmbedded = creationOptions.isEmbedded || false;
|
||||
|
@ -509,6 +518,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
}
|
||||
|
||||
private _createBody(parent: HTMLElement): void {
|
||||
this._notebookTopToolbarContainer = document.createElement('div');
|
||||
this._notebookTopToolbarContainer.classList.add('notebook-top-toolbar');
|
||||
DOM.append(parent, this._notebookTopToolbarContainer);
|
||||
this._body = document.createElement('div');
|
||||
this._body.classList.add('cell-list-container');
|
||||
this._createCellList();
|
||||
|
@ -653,6 +665,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
const widgetFocusTracker = DOM.trackFocus(this.getDomNode());
|
||||
this._register(widgetFocusTracker);
|
||||
this._register(widgetFocusTracker.onDidFocus(() => this._onDidFocusEmitter.fire()));
|
||||
this._register(widgetFocusTracker.onDidBlur(() => this._onDidBlurEmitter.fire()));
|
||||
|
||||
this._reigsterNotebookActionsToolbar();
|
||||
this._register(this.onDidFocus(() => {
|
||||
this._showNotebookActionsinEditorToolbar();
|
||||
}));
|
||||
this._register(this.onDidBlur(() => {
|
||||
this._editorToolbarDisposable?.dispose();
|
||||
this._toolbarActionDisposable.clear();
|
||||
}));
|
||||
}
|
||||
|
||||
private showListContextMenu(e: IListContextMenuEvent<CellViewModel>) {
|
||||
|
@ -668,6 +690,111 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
});
|
||||
}
|
||||
|
||||
private _notebookGlobalActionsMenu!: IMenu;
|
||||
private _toolbarActionDisposable = this._register(new DisposableStore());
|
||||
private _topToolbar!: ToolBar;
|
||||
private _useGlobalToolbar: boolean = false;
|
||||
private _editorToolbarDisposable: IDisposable | null = null;
|
||||
private _reigsterNotebookActionsToolbar() {
|
||||
const cellMenu = this.instantiationService.createInstance(CellMenus);
|
||||
this._notebookGlobalActionsMenu = this._register(cellMenu.getNotebookToolbar(this.scopedContextKeyService));
|
||||
this._register(this._notebookGlobalActionsMenu);
|
||||
|
||||
this._useGlobalToolbar = this.configurationService.getValue<boolean | undefined>('notebook.experimental.globalToolbar') ?? false;
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('notebook.experimental.globalToolbar')) {
|
||||
this._useGlobalToolbar = this.configurationService.getValue<boolean>('notebook.experimental.globalToolbar');
|
||||
this._showNotebookActionsinEditorToolbar();
|
||||
}
|
||||
}));
|
||||
|
||||
this._topToolbar = new ToolBar(this._notebookTopToolbarContainer, this.contextMenuService, {
|
||||
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
|
||||
actionViewItemProvider: action => {
|
||||
return createActionViewItem(this.instantiationService, action);
|
||||
},
|
||||
renderDropdownAsChildElement: true
|
||||
});
|
||||
this._register(this._topToolbar);
|
||||
|
||||
this._showNotebookActionsinEditorToolbar();
|
||||
this._register(this._notebookGlobalActionsMenu.onDidChange(() => {
|
||||
this._showNotebookActionsinEditorToolbar();
|
||||
}));
|
||||
|
||||
if (this.experimentService) {
|
||||
this.experimentService.getTreatment<boolean>('nbtoolbarineditor').then(treatment => {
|
||||
if (treatment === undefined) {
|
||||
return;
|
||||
}
|
||||
if (this._useGlobalToolbar !== treatment) {
|
||||
this._useGlobalToolbar = treatment;
|
||||
this._showNotebookActionsinEditorToolbar();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _showNotebookActionsinEditorToolbar() {
|
||||
if (!this._useGlobalToolbar) {
|
||||
// schedule actions registration in next frame, otherwise we are seeing duplicated notbebook actions temporarily
|
||||
this._editorToolbarDisposable?.dispose();
|
||||
this._editorToolbarDisposable = DOM.scheduleAtNextAnimationFrame(() => {
|
||||
const groups = this._notebookGlobalActionsMenu.getActions({ shouldForwardArgs: true });
|
||||
this._toolbarActionDisposable.clear();
|
||||
this._topToolbar.setActions([], []);
|
||||
if (!this.viewModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._isVisible || !this.hasFocus()) {
|
||||
return;
|
||||
}
|
||||
|
||||
groups.forEach(group => {
|
||||
const groupName = group[0];
|
||||
const actions = group[1];
|
||||
|
||||
let order = groupName === 'navigation' ? -10 : 0;
|
||||
for (let i = 0; i < actions.length; i++) {
|
||||
const menuItemAction = actions[i] as MenuItemAction;
|
||||
this._toolbarActionDisposable.add(MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: menuItemAction.item.id,
|
||||
title: menuItemAction.item.title + ' ' + this.viewModel?.uri.scheme,
|
||||
category: menuItemAction.item.category,
|
||||
tooltip: menuItemAction.item.tooltip,
|
||||
icon: menuItemAction.item.icon,
|
||||
precondition: menuItemAction.item.precondition,
|
||||
toggled: menuItemAction.item.toggled,
|
||||
},
|
||||
title: menuItemAction.item.title + ' ' + this.viewModel?.uri.scheme,
|
||||
group: groupName,
|
||||
order: order
|
||||
}));
|
||||
order++;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this._notebookTopToolbarContainer.style.display = 'none';
|
||||
} else {
|
||||
this._toolbarActionDisposable.clear();
|
||||
this._topToolbar.setActions([], []);
|
||||
const groups = this._notebookGlobalActionsMenu.getActions({ shouldForwardArgs: true });
|
||||
this._notebookTopToolbarContainer.style.display = 'flex';
|
||||
const primaryGroup = groups.find(group => group[0] === 'navigation');
|
||||
const primaryActions = primaryGroup ? primaryGroup[1] : [];
|
||||
const secondaryActions = groups.filter(group => group[0] !== 'navigation').reduce((prev: (MenuItemAction | SubmenuItemAction)[], curr) => { prev.push(...curr[1]); return prev; }, []);
|
||||
|
||||
this._topToolbar.setActions(primaryActions, secondaryActions);
|
||||
}
|
||||
|
||||
if (this._dimension && this._isVisible) {
|
||||
this.layout(this._dimension);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateForCursorNavigationMode(applyFocusChange: () => void): void {
|
||||
if (this._cursorNavigationMode) {
|
||||
// Will fire onDidChangeFocus, resetting the state to Container
|
||||
|
@ -1252,7 +1379,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
}
|
||||
|
||||
this._dimension = new DOM.Dimension(dimension.width, dimension.height);
|
||||
DOM.size(this._body, dimension.width, dimension.height);
|
||||
DOM.size(this._body, dimension.width, dimension.height - (this._useGlobalToolbar ? /** Toolbar height */ 26 : 0));
|
||||
this._list.updateOptions({ additionalScrollHeight: this._scrollBeyondLastLine ? dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP : 0 });
|
||||
this._list.layout(dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, dimension.width);
|
||||
|
||||
|
@ -1314,6 +1441,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
const focused = DOM.isAncestor(document.activeElement, this._overlayContainer);
|
||||
this._editorFocus.set(focused);
|
||||
this.viewModel?.setFocus(focused);
|
||||
|
||||
if (!focused) {
|
||||
this._editorToolbarDisposable?.dispose();
|
||||
this._toolbarActionDisposable.clear();
|
||||
}
|
||||
}
|
||||
|
||||
hasFocus() {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
import { localize } from 'vs/nls';
|
||||
import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
|
@ -31,7 +30,7 @@ import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, DisplayOrderKey
|
|||
import { NotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookMarkdownRenderer';
|
||||
import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer';
|
||||
import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
|
||||
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { ComplexNotebookProviderInfo, IMainNotebookController, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { Extensions as EditorExtensions, IEditorTypesHandler, IEditorType, IEditorAssociationsRegistry } from 'vs/workbench/browser/editor';
|
||||
|
@ -233,16 +232,13 @@ class ModelData implements IDisposable {
|
|||
}
|
||||
}
|
||||
|
||||
interface INotebookProviderData {
|
||||
controller: IMainNotebookController;
|
||||
extensionData: NotebookExtensionDescription;
|
||||
}
|
||||
|
||||
|
||||
export class NotebookService extends Disposable implements INotebookService, IEditorTypesHandler {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _notebookProviders = new Map<string, INotebookProviderData>();
|
||||
private readonly _notebookProviders = new Map<string, ComplexNotebookProviderInfo | SimpleNotebookProviderInfo>();
|
||||
private readonly _notebookProviderInfoStore: NotebookProviderInfoStore;
|
||||
private readonly _notebookRenderersInfoStore: NotebookOutputRendererInfoStore = new NotebookOutputRendererInfoStore();
|
||||
private readonly _markdownRenderersInfos = new Set<INotebookMarkdownRendererInfo>();
|
||||
|
@ -254,9 +250,6 @@ export class NotebookService extends Disposable implements INotebookService, IEd
|
|||
readonly onDidAddNotebookDocument = this._onDidAddNotebookDocument.event;
|
||||
readonly onDidRemoveNotebookDocument = this._onDidRemoveNotebookDocument.event;
|
||||
|
||||
private readonly _onNotebookDocumentSaved: Emitter<URI> = this._register(new Emitter<URI>());
|
||||
readonly onNotebookDocumentSaved: Event<URI> = this._onNotebookDocumentSaved.event;
|
||||
|
||||
private readonly _onDidChangeEditorTypes = this._register(new Emitter<void>());
|
||||
onDidChangeEditorTypes: Event<void> = this._onDidChangeEditorTypes.event;
|
||||
|
||||
|
@ -430,11 +423,15 @@ export class NotebookService extends Disposable implements INotebookService, IEd
|
|||
return this._notebookProviders.has(viewType);
|
||||
}
|
||||
|
||||
registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable {
|
||||
private _registerProviderData(viewType: string, data: SimpleNotebookProviderInfo | ComplexNotebookProviderInfo): void {
|
||||
if (this._notebookProviders.has(viewType)) {
|
||||
throw new Error(`notebook controller for viewtype '${viewType}' already exists`);
|
||||
}
|
||||
this._notebookProviders.set(viewType, { extensionData, controller });
|
||||
this._notebookProviders.set(viewType, data);
|
||||
}
|
||||
|
||||
registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable {
|
||||
this._registerProviderData(viewType, new ComplexNotebookProviderInfo(viewType, controller, extensionData));
|
||||
|
||||
if (controller.viewOptions && !this._notebookProviderInfoStore.get(viewType)) {
|
||||
// register this content provider to the static contribution, if it does not exist
|
||||
|
@ -465,6 +462,26 @@ export class NotebookService extends Disposable implements INotebookService, IEd
|
|||
});
|
||||
}
|
||||
|
||||
registerNotebookSerializer(viewType: string, extensionData: NotebookExtensionDescription, serializer: INotebookSerializer): IDisposable {
|
||||
this._registerProviderData(viewType, new SimpleNotebookProviderInfo(viewType, serializer, extensionData));
|
||||
return toDisposable(() => {
|
||||
this._notebookProviders.delete(viewType);
|
||||
});
|
||||
}
|
||||
|
||||
async withNotebookDataProvider(resource: URI): Promise<ComplexNotebookProviderInfo | SimpleNotebookProviderInfo> {
|
||||
const [first] = this._notebookProviderInfoStore.getContributedNotebook(resource);
|
||||
if (!first) {
|
||||
throw new Error(`NO contribution for resource: '${resource.toString()}'`);
|
||||
}
|
||||
await this.canResolve(first.id);
|
||||
const result = this._notebookProviders.get(first.id);
|
||||
if (!result) {
|
||||
throw new Error(`NO provider registered for view type: '${first.id}'`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable {
|
||||
const d = this._notebookKernelProviderInfoStore.add(provider);
|
||||
const kernelChangeEventListener = provider.onDidChangeKernels((e) => {
|
||||
|
@ -503,47 +520,6 @@ export class NotebookService extends Disposable implements INotebookService, IEd
|
|||
return Array.from(this._markdownRenderersInfos);
|
||||
}
|
||||
|
||||
// --- notebook documents: IO
|
||||
|
||||
private _withProvider(viewType: string): INotebookProviderData {
|
||||
const result = this._notebookProviders.get(viewType);
|
||||
if (!result) {
|
||||
throw new Error(`having NO provider for ${viewType}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchNotebookRawData(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions }> {
|
||||
if (!await this.canResolve(viewType)) {
|
||||
throw new Error(`CANNOT fetch notebook data, there is NO provider for '${viewType}'`);
|
||||
}
|
||||
const provider = this._withProvider(viewType)!;
|
||||
return await provider.controller.openNotebook(viewType, uri, backupId, token, untitledDocumentData);
|
||||
}
|
||||
|
||||
async save(viewType: string, resource: URI, token: CancellationToken): Promise<boolean> {
|
||||
const provider = this._withProvider(viewType);
|
||||
const ret = await provider.controller.save(resource, token);
|
||||
if (ret) {
|
||||
this._onNotebookDocumentSaved.fire(resource);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
async saveAs(viewType: string, resource: URI, target: URI, token: CancellationToken): Promise<boolean> {
|
||||
const provider = this._withProvider(viewType);
|
||||
const ret = await provider.controller.saveAs(resource, target, token);
|
||||
if (ret) {
|
||||
this._onNotebookDocumentSaved.fire(resource);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
async backup(viewType: string, uri: URI, token: CancellationToken): Promise<string | undefined> {
|
||||
const provider = this._withProvider(viewType);
|
||||
return provider.controller.backup(uri, token);
|
||||
}
|
||||
|
||||
// --- notebook documents: create, destory, retrieve, enumerate
|
||||
|
||||
createNotebookTextModel(viewType: string, uri: URI, data: NotebookDataDto, transientOptions: TransientOptions): NotebookTextModel {
|
||||
|
@ -568,17 +544,10 @@ export class NotebookService extends Disposable implements INotebookService, IEd
|
|||
return [...this._models].map(e => e[1].model);
|
||||
}
|
||||
|
||||
destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void {
|
||||
this._onWillDisposeDocument(notebook);
|
||||
}
|
||||
|
||||
private _onWillDisposeDocument(model: INotebookTextModel): void {
|
||||
|
||||
const modelData = this._models.get(model.uri);
|
||||
this._models.delete(model.uri);
|
||||
|
||||
if (modelData) {
|
||||
modelData.model.dispose();
|
||||
this._models.delete(model.uri);
|
||||
modelData.dispose();
|
||||
this._onDidRemoveNotebookDocument.fire(modelData.model.uri);
|
||||
}
|
||||
|
@ -675,17 +644,18 @@ export class NotebookService extends Disposable implements INotebookService, IEd
|
|||
return ret;
|
||||
}
|
||||
|
||||
// --- data provider IPC (deprecated)
|
||||
|
||||
async resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise<void> {
|
||||
const entry = this._notebookProviders.get(viewType);
|
||||
if (entry) {
|
||||
if (entry instanceof ComplexNotebookProviderInfo) {
|
||||
entry.controller.resolveNotebookEditor(viewType, uri, editorId);
|
||||
}
|
||||
}
|
||||
|
||||
onDidReceiveMessage(viewType: string, editorId: string, rendererType: string | undefined, message: any): void {
|
||||
const provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (provider) {
|
||||
if (provider instanceof ComplexNotebookProviderInfo) {
|
||||
return provider.controller.onDidReceiveMessage(editorId, rendererType, message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,10 @@ export class CellMenus {
|
|||
@IMenuService private readonly menuService: IMenuService,
|
||||
) { }
|
||||
|
||||
getNotebookToolbar(contextKeyService: IContextKeyService): IMenu {
|
||||
return this.getMenu(MenuId.NotebookToolbar, contextKeyService);
|
||||
}
|
||||
|
||||
getCellTitleMenu(contextKeyService: IContextKeyService): IMenu {
|
||||
return this.getMenu(MenuId.NotebookCellTitle, contextKeyService);
|
||||
}
|
||||
|
|
|
@ -67,7 +67,9 @@ export class CellOutputElement extends Disposable {
|
|||
}
|
||||
|
||||
detach() {
|
||||
this.domNode.parentElement?.removeChild(this.domNode);
|
||||
if (this.domNode) {
|
||||
this.domNode.parentElement?.removeChild(this.domNode);
|
||||
}
|
||||
}
|
||||
|
||||
updateDOMTop(top: number) {
|
||||
|
|
|
@ -18,10 +18,8 @@ import { IAccessibilityInformation } from 'vs/platform/accessibility/common/acce
|
|||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IEditorModel } from 'vs/platform/editor/common/editor';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IRevertOptions } from 'vs/workbench/common/editor';
|
||||
import { IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IFileStatWithMetadata } from 'vs/platform/files/common/files';
|
||||
import { ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export enum CellKind {
|
||||
|
@ -179,7 +177,7 @@ export interface INotebookTextModel {
|
|||
readonly versionId: number;
|
||||
|
||||
readonly cells: readonly ICell[];
|
||||
onWillDispose(listener: () => void): IDisposable;
|
||||
onWillDispose: Event<void>;
|
||||
}
|
||||
|
||||
export type NotebookCellTextModelSplice<T> = [
|
||||
|
@ -604,7 +602,7 @@ export interface INotebookLoadOptions {
|
|||
/**
|
||||
* Go to disk bypassing any cache of the model if any.
|
||||
*/
|
||||
forceReadFromDisk?: boolean;
|
||||
forceReadFromFile?: boolean;
|
||||
}
|
||||
|
||||
export interface IResolvedNotebookEditorModel extends INotebookEditorModel {
|
||||
|
@ -613,17 +611,17 @@ export interface IResolvedNotebookEditorModel extends INotebookEditorModel {
|
|||
|
||||
export interface INotebookEditorModel extends IEditorModel {
|
||||
readonly onDidChangeDirty: Event<void>;
|
||||
readonly onDidSave: Event<void>;
|
||||
readonly resource: URI;
|
||||
readonly viewType: string;
|
||||
readonly notebook: NotebookTextModel | undefined;
|
||||
readonly lastResolvedFileStat: IFileStatWithMetadata | undefined;
|
||||
isResolved(): this is IResolvedNotebookEditorModel;
|
||||
isDirty(): boolean;
|
||||
isUntitled(): boolean;
|
||||
load(options?: INotebookLoadOptions): Promise<IResolvedNotebookEditorModel>;
|
||||
save(): Promise<boolean>;
|
||||
save(options?: ISaveOptions): Promise<boolean>;
|
||||
saveAs(target: URI): Promise<boolean>;
|
||||
revert(options?: IRevertOptions | undefined): Promise<void>;
|
||||
revert(options?: IRevertOptions): Promise<void>;
|
||||
}
|
||||
|
||||
export interface INotebookDiffEditorModel extends IEditorModel {
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { EditorModel, IRevertOptions } from 'vs/workbench/common/editor';
|
||||
import { EditorModel, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { CellEditType, CellKind, ICellEditOperation, INotebookEditorModel, INotebookLoadOptions, IResolvedNotebookEditorModel, NotebookCellsChangeType, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellEditType, CellKind, ICellEditOperation, INotebookEditorModel, INotebookLoadOptions, IResolvedNotebookEditorModel, NotebookCellsChangeType, NotebookDataDto, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IMainNotebookController, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWorkingCopyService, IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
@ -19,17 +19,23 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/
|
|||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { TaskSequentializer } from 'vs/base/common/async';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { bufferToStream, streamToBuffer, VSBuffer, VSBufferReadableStream } from 'vs/base/common/buffer';
|
||||
import { assertType } from 'vs/base/common/types';
|
||||
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
import { IFileWorkingCopyModel, IFileWorkingCopyModelContentChangedEvent, IFileWorkingCopyModelFactory, IResolvedFileWorkingCopy } from 'vs/workbench/services/workingCopy/common/fileWorkingCopy';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
|
||||
export class NotebookEditorModel extends EditorModel implements INotebookEditorModel {
|
||||
//#region --- complex content provider
|
||||
|
||||
export class ComplexNotebookEditorModel extends EditorModel implements INotebookEditorModel {
|
||||
|
||||
private readonly _onDidSave = this._register(new Emitter<void>());
|
||||
private readonly _onDidChangeDirty = this._register(new Emitter<void>());
|
||||
private readonly _onDidChangeContent = this._register(new Emitter<void>());
|
||||
|
||||
readonly onDidSave = this._onDidSave.event;
|
||||
readonly onDidChangeDirty = this._onDidChangeDirty.event;
|
||||
readonly onDidChangeContent = this._onDidChangeContent.event;
|
||||
|
||||
private _lastResolvedFileStat?: IFileStatWithMetadata;
|
||||
|
||||
|
@ -42,6 +48,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
constructor(
|
||||
readonly resource: URI,
|
||||
readonly viewType: string,
|
||||
private readonly _contentProvider: IMainNotebookController,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService,
|
||||
@IBackupFileService private readonly _backupFileService: IBackupFileService,
|
||||
|
@ -62,7 +69,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
get name() { return that._name; }
|
||||
readonly capabilities = that.isUntitled() ? WorkingCopyCapabilities.Untitled : WorkingCopyCapabilities.None;
|
||||
readonly onDidChangeDirty = that.onDidChangeDirty;
|
||||
readonly onDidChangeContent = that.onDidChangeContent;
|
||||
readonly onDidChangeContent = that._onDidChangeContent.event;
|
||||
isDirty(): boolean { return that.isDirty(); }
|
||||
backup(token: CancellationToken): Promise<IWorkingCopyBackup> { return that.backup(token); }
|
||||
save(): Promise<boolean> { return that.save(); }
|
||||
|
@ -82,7 +89,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
const stats = await this._resolveStats(this.resource);
|
||||
if (stats && this._lastResolvedFileStat && stats.etag !== this._lastResolvedFileStat.etag) {
|
||||
this._logService.debug('[notebook editor model] trigger load after file event');
|
||||
this.load({ forceReadFromDisk: true });
|
||||
this.load({ forceReadFromFile: true });
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -99,10 +106,6 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
return this.resource.scheme === Schemas.untitled;
|
||||
}
|
||||
|
||||
get lastResolvedFileStat(): IFileStatWithMetadata | undefined {
|
||||
return this._lastResolvedFileStat;
|
||||
}
|
||||
|
||||
get notebook(): NotebookTextModel | undefined {
|
||||
const candidate = this._notebookService.getNotebookTextModel(this.resource);
|
||||
return candidate && candidate.viewType === this.viewType ? candidate : undefined;
|
||||
|
@ -121,7 +124,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
return {};
|
||||
}
|
||||
|
||||
const backupId = await this._notebookService.backup(this.viewType, this.resource, token);
|
||||
const backupId = await this._contentProvider.backup(this.resource, token);
|
||||
if (token.isCancellationRequested) {
|
||||
return {};
|
||||
}
|
||||
|
@ -131,7 +134,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
meta: {
|
||||
mtime: stats?.mtime ?? Date.now(),
|
||||
viewType: this.notebook.viewType,
|
||||
backupId: backupId
|
||||
backupId
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -142,7 +145,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
return;
|
||||
}
|
||||
|
||||
await this.load({ forceReadFromDisk: true });
|
||||
await this.load({ forceReadFromFile: true });
|
||||
const newStats = await this._resolveStats(this.resource);
|
||||
this._lastResolvedFileStat = newStats;
|
||||
|
||||
|
@ -150,8 +153,8 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
this._onDidChangeDirty.fire();
|
||||
}
|
||||
|
||||
async load(options?: INotebookLoadOptions): Promise<NotebookEditorModel & IResolvedNotebookEditorModel> {
|
||||
if (options?.forceReadFromDisk) {
|
||||
async load(options?: INotebookLoadOptions): Promise<IResolvedNotebookEditorModel> {
|
||||
if (options?.forceReadFromFile) {
|
||||
this._logService.debug('[notebook editor model] load from provider (forceRead)', this.resource.toString());
|
||||
this._loadFromProvider(undefined);
|
||||
assertType(this.isResolved());
|
||||
|
@ -188,7 +191,9 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
|
||||
private async _loadFromProvider(backupId: string | undefined): Promise<void> {
|
||||
|
||||
const data = await this._notebookService.fetchNotebookRawData(this.viewType, this.resource, backupId, CancellationToken.None, (await this.getUntitledDocumentData(this.resource)));
|
||||
const untitledData = await this.getUntitledDocumentData(this.resource);
|
||||
const data = await this._contentProvider.open(this.resource, backupId, untitledData, CancellationToken.None);
|
||||
|
||||
this._lastResolvedFileStat = await this._resolveStats(this.resource);
|
||||
|
||||
if (this.isDisposed()) {
|
||||
|
@ -326,10 +331,13 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
if (!this.isResolved()) {
|
||||
return;
|
||||
}
|
||||
await this._notebookService.save(this.notebook.viewType, this.notebook.uri, CancellationToken.None);
|
||||
this._logService.debug(`[notebook editor model] save(${versionId}) - document saved saved, start updating file stats`, this.resource.toString(true));
|
||||
const success = await this._contentProvider.save(this.notebook.uri, CancellationToken.None);
|
||||
this._logService.debug(`[notebook editor model] save(${versionId}) - document saved saved, start updating file stats`, this.resource.toString(true), success);
|
||||
this._lastResolvedFileStat = await this._resolveStats(this.resource);
|
||||
this.setDirty(false);
|
||||
if (success) {
|
||||
this.setDirty(false);
|
||||
this._onDidSave.fire();
|
||||
}
|
||||
})()).then(() => {
|
||||
return true;
|
||||
});
|
||||
|
@ -353,10 +361,13 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
return true;
|
||||
}
|
||||
|
||||
await this._notebookService.saveAs(this.notebook.viewType, this.notebook.uri, targetResource, CancellationToken.None);
|
||||
this._logService.debug(`[notebook editor model] saveAs - document saved, start updating file stats`, this.resource.toString(true));
|
||||
const success = await this._contentProvider.saveAs(this.notebook.uri, targetResource, CancellationToken.None);
|
||||
this._logService.debug(`[notebook editor model] saveAs - document saved, start updating file stats`, this.resource.toString(true), success);
|
||||
this._lastResolvedFileStat = await this._resolveStats(this.resource);
|
||||
this.setDirty(false);
|
||||
if (success) {
|
||||
this.setDirty(false);
|
||||
this._onDidSave.fire();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -375,3 +386,177 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region --- simple content provider
|
||||
|
||||
export class SimpleNotebookEditorModel extends EditorModel implements INotebookEditorModel {
|
||||
|
||||
readonly onDidChangeDirty: Event<void>;
|
||||
readonly onDidSave: Event<void>;
|
||||
|
||||
readonly resource: URI;
|
||||
readonly viewType: string;
|
||||
readonly notebook: NotebookTextModel;
|
||||
|
||||
constructor(
|
||||
private readonly _workingCopy: IResolvedFileWorkingCopy<NotebookFileWorkingCopyModel>
|
||||
) {
|
||||
super();
|
||||
this.resource = _workingCopy.resource;
|
||||
this.viewType = _workingCopy.model.notebookModel.viewType;
|
||||
this.notebook = _workingCopy.model.notebookModel;
|
||||
|
||||
this.onDidChangeDirty = Event.signal(_workingCopy.onDidChangeDirty);
|
||||
this.onDidSave = Event.signal(_workingCopy.onDidSave);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._workingCopy.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
isDirty(): boolean {
|
||||
return this._workingCopy.isDirty();
|
||||
}
|
||||
|
||||
revert(options?: IRevertOptions): Promise<void> {
|
||||
return this._workingCopy.revert(options);
|
||||
}
|
||||
|
||||
save(options?: ISaveOptions): Promise<boolean> {
|
||||
return this._workingCopy.save(options);
|
||||
}
|
||||
|
||||
isUntitled(): boolean {
|
||||
return this.resource.scheme === Schemas.untitled;
|
||||
}
|
||||
|
||||
async load(options?: INotebookLoadOptions): Promise<IResolvedNotebookEditorModel> {
|
||||
// todo@bpasero,jrieken is this right?
|
||||
// resolve return a working copy but I believe it is always THIS
|
||||
await this._workingCopy.resolve(options);
|
||||
return this;
|
||||
}
|
||||
|
||||
saveAs(target: URI): Promise<boolean> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookFileWorkingCopyModel implements IFileWorkingCopyModel {
|
||||
|
||||
private readonly _onDidChangeContent = new Emitter<IFileWorkingCopyModelContentChangedEvent>();
|
||||
private readonly _changeListener: IDisposable;
|
||||
|
||||
readonly onDidChangeContent = this._onDidChangeContent.event;
|
||||
readonly onWillDispose: Event<void>;
|
||||
|
||||
constructor(
|
||||
private readonly _notebookModel: NotebookTextModel,
|
||||
private readonly _notebookSerializer: INotebookSerializer
|
||||
) {
|
||||
this.onWillDispose = _notebookModel.onWillDispose.bind(_notebookModel);
|
||||
|
||||
this._changeListener = _notebookModel.onDidChangeContent(e => {
|
||||
for (const rawEvent of e.rawEvents) {
|
||||
if (rawEvent.kind === NotebookCellsChangeType.Initialize) {
|
||||
continue;
|
||||
}
|
||||
if (rawEvent.transient) {
|
||||
continue;
|
||||
}
|
||||
//todo@jrieken,@rebornix forward this information from notebook model
|
||||
this._onDidChangeContent.fire({
|
||||
isRedoing: false,
|
||||
isUndoing: false
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._changeListener.dispose();
|
||||
this._onDidChangeContent.dispose();
|
||||
this._notebookModel.dispose();
|
||||
}
|
||||
|
||||
get notebookModel() {
|
||||
return this._notebookModel;
|
||||
}
|
||||
|
||||
async snapshot(token: CancellationToken): Promise<VSBufferReadableStream> {
|
||||
|
||||
const data: NotebookDataDto = {
|
||||
metadata: this._notebookModel.metadata,
|
||||
cells: [],
|
||||
};
|
||||
|
||||
for (const cell of this._notebookModel.cells) {
|
||||
data.cells.push({
|
||||
cellKind: cell.cellKind,
|
||||
language: cell.language,
|
||||
source: cell.getValue(),
|
||||
outputs: cell.outputs
|
||||
});
|
||||
}
|
||||
|
||||
const bytes = await this._notebookSerializer.notebookToData(data);
|
||||
if (token.isCancellationRequested) {
|
||||
throw canceled();
|
||||
}
|
||||
return bufferToStream(bytes);
|
||||
}
|
||||
|
||||
async update(stream: VSBufferReadableStream, token: CancellationToken): Promise<void> {
|
||||
|
||||
const bytes = await streamToBuffer(stream);
|
||||
const data = await this._notebookSerializer.dataToNotebook(bytes);
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._notebookModel.metadata = data.metadata;
|
||||
this._notebookModel.transientOptions = this._notebookSerializer.options;
|
||||
const edits: ICellEditOperation[] = [{ editType: CellEditType.Replace, index: 0, count: this._notebookModel.cells.length, cells: data.cells }];
|
||||
this._notebookModel.applyEdits(edits, true, undefined, () => undefined, undefined, false);
|
||||
}
|
||||
|
||||
getAlternativeVersionId(): number {
|
||||
//todo@jrieken,@rebornix -> add alternative version id
|
||||
return this._notebookModel.versionId;
|
||||
}
|
||||
|
||||
pushStackElement(): void {
|
||||
this._notebookModel.pushStackElement(nls.localize('save', 'Save Notebook'), undefined, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookFileWorkingCopyModelFactory implements IFileWorkingCopyModelFactory<NotebookFileWorkingCopyModel>{
|
||||
|
||||
constructor(
|
||||
@INotebookService private readonly _notebookService: INotebookService
|
||||
) { }
|
||||
|
||||
async createModel(resource: URI, stream: VSBufferReadableStream, token: CancellationToken): Promise<NotebookFileWorkingCopyModel> {
|
||||
|
||||
const info = await this._notebookService.withNotebookDataProvider(resource);
|
||||
if (!(info instanceof SimpleNotebookProviderInfo)) {
|
||||
throw new Error('CANNOT open file notebook with this provider');
|
||||
}
|
||||
|
||||
const data = await info.serializer.dataToNotebook(await streamToBuffer(stream));
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
throw canceled();
|
||||
}
|
||||
|
||||
const notebookModel = this._notebookService.createNotebookTextModel(info.viewType, resource, data, info.serializer.options);
|
||||
return new NotebookFileWorkingCopyModel(notebookModel, info.serializer);
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -6,21 +6,31 @@
|
|||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CellUri, IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
|
||||
import { ComplexNotebookEditorModel, NotebookFileWorkingCopyModel, NotebookFileWorkingCopyModelFactory, SimpleNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
|
||||
import { combinedDisposable, DisposableStore, IDisposable, IReference, ReferenceCollection } from 'vs/base/common/lifecycle';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { ComplexNotebookProviderInfo, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { FileWorkingCopyManager, IFileWorkingCopyManager } from 'vs/workbench/services/workingCopy/common/fileWorkingCopyManager';
|
||||
import { IResolvedFileWorkingCopy } from 'vs/workbench/services/workingCopy/common/fileWorkingCopy';
|
||||
|
||||
export const INotebookEditorModelResolverService = createDecorator<INotebookEditorModelResolverService>('INotebookModelResolverService');
|
||||
|
||||
export interface INotebookEditorModelResolverService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
onDidSaveNotebook: Event<URI>;
|
||||
|
||||
resolve(resource: URI, viewType?: string): Promise<IReference<IResolvedNotebookEditorModel>>;
|
||||
}
|
||||
|
||||
class NotebookModelReferenceCollection extends ReferenceCollection<Promise<IResolvedNotebookEditorModel>> {
|
||||
|
||||
export class NotebookModelReferenceCollection extends ReferenceCollection<Promise<IResolvedNotebookEditorModel>> {
|
||||
private readonly _workingCopyManager: IFileWorkingCopyManager<NotebookFileWorkingCopyModel>;
|
||||
private readonly _modelListener = new Map<IResolvedNotebookEditorModel, IDisposable>();
|
||||
|
||||
private readonly _onDidSaveNotebook = new Emitter<URI>();
|
||||
readonly onDidSaveNotebook: Event<URI> = this._onDidSaveNotebook.event;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService readonly _instantiationService: IInstantiationService,
|
||||
|
@ -28,18 +38,39 @@ export class NotebookModelReferenceCollection extends ReferenceCollection<Promis
|
|||
@ILogService private readonly _logService: ILogService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._workingCopyManager = <any>_instantiationService.createInstance(
|
||||
FileWorkingCopyManager,
|
||||
new NotebookFileWorkingCopyModelFactory(_notebookService)
|
||||
);
|
||||
}
|
||||
|
||||
protected createReferencedObject(key: string, viewType: string): Promise<IResolvedNotebookEditorModel> {
|
||||
protected async createReferencedObject(key: string, viewType: string): Promise<IResolvedNotebookEditorModel> {
|
||||
const uri = URI.parse(key);
|
||||
const model = this._instantiationService.createInstance(NotebookEditorModel, uri, viewType);
|
||||
const promise = model.load();
|
||||
return promise;
|
||||
const info = await this._notebookService.withNotebookDataProvider(uri);
|
||||
|
||||
let result: IResolvedNotebookEditorModel;
|
||||
|
||||
if (info instanceof ComplexNotebookProviderInfo) {
|
||||
const model = this._instantiationService.createInstance(ComplexNotebookEditorModel, uri, viewType, info.controller);
|
||||
result = await model.load();
|
||||
|
||||
} else if (info instanceof SimpleNotebookProviderInfo) {
|
||||
const workingCopy = await this._workingCopyManager.resolve(uri);
|
||||
result = new SimpleNotebookEditorModel(<IResolvedFileWorkingCopy<NotebookFileWorkingCopyModel>>workingCopy);
|
||||
|
||||
} else {
|
||||
throw new Error(`CANNOT open ${key}, no provider found`);
|
||||
}
|
||||
|
||||
this._modelListener.set(result, result.onDidSave(() => this._onDidSaveNotebook.fire(result.resource)));
|
||||
return result;
|
||||
}
|
||||
|
||||
protected destroyReferencedObject(_key: string, object: Promise<IResolvedNotebookEditorModel>): void {
|
||||
object.then(model => {
|
||||
this._notebookService.destoryNotebookDocument(model.viewType, model.notebook);
|
||||
this._modelListener.get(model)?.dispose();
|
||||
this._modelListener.delete(model);
|
||||
model.dispose();
|
||||
}).catch(err => {
|
||||
this._logService.critical('FAILED to destory notebook', err);
|
||||
|
@ -53,11 +84,14 @@ export class NotebookModelResolverService implements INotebookEditorModelResolve
|
|||
|
||||
private readonly _data: NotebookModelReferenceCollection;
|
||||
|
||||
readonly onDidSaveNotebook: Event<URI>;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@INotebookService private readonly _notebookService: INotebookService
|
||||
) {
|
||||
this._data = instantiationService.createInstance(NotebookModelReferenceCollection);
|
||||
this.onDidSaveNotebook = this._data.onDidSaveNotebook;
|
||||
}
|
||||
|
||||
async resolve(resource: URI, viewType?: string): Promise<IReference<IResolvedNotebookEditorModel>> {
|
||||
|
|
|
@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
|
||||
import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { INotebookTextModel, INotebookRendererInfo, INotebookKernelProvider, INotebookKernel, TransientMetadata, NotebookDataDto, TransientOptions, INotebookExclusiveDocumentFilter, IOrderedMimeType, IOutputDto, INotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookRendererInfo, INotebookKernelProvider, INotebookKernel, NotebookDataDto, TransientOptions, INotebookExclusiveDocumentFilter, IOrderedMimeType, IOutputDto, INotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
|
@ -22,32 +22,56 @@ export const INotebookService = createDecorator<INotebookService>('notebookServi
|
|||
|
||||
export interface IMainNotebookController {
|
||||
viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; };
|
||||
options: { transientOutputs: boolean; transientMetadata: TransientMetadata; };
|
||||
options: TransientOptions;
|
||||
resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise<void>;
|
||||
onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: any): void;
|
||||
|
||||
openNotebook(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>;
|
||||
open(uri: URI, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>;
|
||||
save(uri: URI, token: CancellationToken): Promise<boolean>;
|
||||
saveAs(uri: URI, target: URI, token: CancellationToken): Promise<boolean>;
|
||||
backup(uri: URI, token: CancellationToken): Promise<string>;
|
||||
}
|
||||
|
||||
export interface INotebookSerializer {
|
||||
options: TransientOptions;
|
||||
dataToNotebook(data: VSBuffer): Promise<NotebookDataDto>
|
||||
notebookToData(data: NotebookDataDto): Promise<VSBuffer>;
|
||||
}
|
||||
|
||||
export interface INotebookRawData {
|
||||
data: NotebookDataDto;
|
||||
transientOptions: TransientOptions;
|
||||
}
|
||||
|
||||
export class ComplexNotebookProviderInfo {
|
||||
constructor(
|
||||
readonly viewType: string,
|
||||
readonly controller: IMainNotebookController,
|
||||
readonly extensionData: NotebookExtensionDescription
|
||||
) { }
|
||||
}
|
||||
|
||||
export class SimpleNotebookProviderInfo {
|
||||
constructor(
|
||||
readonly viewType: string,
|
||||
readonly serializer: INotebookSerializer,
|
||||
readonly extensionData: NotebookExtensionDescription
|
||||
) { }
|
||||
}
|
||||
|
||||
export interface INotebookService {
|
||||
readonly _serviceBrand: undefined;
|
||||
canResolve(viewType: string): Promise<boolean>;
|
||||
|
||||
onDidRemoveNotebookDocument: Event<URI>;
|
||||
onDidAddNotebookDocument: Event<NotebookTextModel>;
|
||||
onNotebookDocumentSaved: Event<URI>;
|
||||
|
||||
onDidChangeKernels: Event<URI | undefined>;
|
||||
onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelFriendlyId: string | undefined; }>;
|
||||
|
||||
registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable;
|
||||
registerNotebookSerializer(viewType: string, extensionData: NotebookExtensionDescription, serializer: INotebookSerializer): IDisposable;
|
||||
withNotebookDataProvider(resource: URI): Promise<ComplexNotebookProviderInfo | SimpleNotebookProviderInfo>;
|
||||
|
||||
getMimeTypeInfo(textModel: NotebookTextModel, output: IOutputDto): readonly IOrderedMimeType[];
|
||||
|
||||
|
@ -66,12 +90,6 @@ export interface INotebookService {
|
|||
getContributedNotebookProviders(resource?: URI): readonly NotebookProviderInfo[];
|
||||
getContributedNotebookProvider(viewType: string): NotebookProviderInfo | undefined;
|
||||
getNotebookProviderResourceRoots(): URI[];
|
||||
destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void;
|
||||
|
||||
fetchNotebookRawData(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken, untitledDocumentData?: VSBuffer): Promise<INotebookRawData>;
|
||||
save(viewType: string, resource: URI, token: CancellationToken): Promise<boolean>;
|
||||
saveAs(viewType: string, resource: URI, target: URI, token: CancellationToken): Promise<boolean>;
|
||||
backup(viewType: string, uri: URI, token: CancellationToken): Promise<string | undefined>;
|
||||
|
||||
onDidReceiveMessage(viewType: string, editorId: string, rendererType: string | undefined, message: unknown): void;
|
||||
setToCopy(items: NotebookCellTextModel[], isCopy: boolean): void;
|
||||
|
|
|
@ -13,8 +13,8 @@ import { IFileService } from 'vs/platform/files/common/files';
|
|||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { ComplexNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
|
||||
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
import { IWorkingCopy, IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
|
@ -32,6 +32,8 @@ suite('NotebookEditorModel', function () {
|
|||
getUriBasenameLabel(uri: URI) { return uri.toString(); }
|
||||
};
|
||||
|
||||
const notebookDataProvider = new class extends mock<IMainNotebookController>() { };
|
||||
|
||||
test('working copy uri', function () {
|
||||
if (1) {
|
||||
this.skip();
|
||||
|
@ -47,8 +49,8 @@ suite('NotebookEditorModel', function () {
|
|||
}
|
||||
};
|
||||
|
||||
new NotebookEditorModel(r1, 'fff', notebokService, workingCopyService, backupService, fileService, notificationService, new NullLogService(), untitledTextEditorService, labelService);
|
||||
new NotebookEditorModel(r2, 'fff', notebokService, workingCopyService, backupService, fileService, notificationService, new NullLogService(), untitledTextEditorService, labelService);
|
||||
new ComplexNotebookEditorModel(r1, 'fff', notebookDataProvider, notebokService, workingCopyService, backupService, fileService, notificationService, new NullLogService(), untitledTextEditorService, labelService);
|
||||
new ComplexNotebookEditorModel(r2, 'fff', notebookDataProvider, notebokService, workingCopyService, backupService, fileService, notificationService, new NullLogService(), untitledTextEditorService, labelService);
|
||||
|
||||
assert.strictEqual(copies.length, 2);
|
||||
assert.strictEqual(!isEqual(copies[0].resource, copies[1].resource), true);
|
||||
|
|
|
@ -30,7 +30,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
|||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
import { IFileStatWithMetadata } from 'vs/platform/files/common/files';
|
||||
import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
|
||||
import { ListViewInfoAccessor } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget';
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
|
@ -53,6 +52,9 @@ export class TestCell extends NotebookCellTextModel {
|
|||
export class NotebookEditorTestModel extends EditorModel implements INotebookEditorModel {
|
||||
private _dirty = false;
|
||||
|
||||
protected readonly _onDidSave = this._register(new Emitter<void>());
|
||||
readonly onDidSave = this._onDidSave.event;
|
||||
|
||||
protected readonly _onDidChangeDirty = this._register(new Emitter<void>());
|
||||
readonly onDidChangeDirty = this._onDidChangeDirty.event;
|
||||
|
||||
|
@ -85,7 +87,6 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi
|
|||
}));
|
||||
}
|
||||
}
|
||||
lastResolvedFileStat: IFileStatWithMetadata | undefined;
|
||||
|
||||
isDirty() {
|
||||
return this._dirty;
|
||||
|
@ -107,6 +108,7 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi
|
|||
if (this._notebook) {
|
||||
this._dirty = false;
|
||||
this._onDidChangeDirty.fire();
|
||||
this._onDidSave.fire();
|
||||
// todo, flush all states
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue