Path: blob/main/src/vs/workbench/contrib/notebook/common/notebookCommon.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { VSBuffer } from '../../../../base/common/buffer.js';6import { CancellationToken } from '../../../../base/common/cancellation.js';7import { IDiffResult } from '../../../../base/common/diff/diff.js';8import { Event } from '../../../../base/common/event.js';9import * as glob from '../../../../base/common/glob.js';10import { IMarkdownString } from '../../../../base/common/htmlContent.js';11import { Iterable } from '../../../../base/common/iterator.js';12import { IDisposable } from '../../../../base/common/lifecycle.js';13import { Mimes } from '../../../../base/common/mime.js';14import { Schemas } from '../../../../base/common/network.js';15import { basename } from '../../../../base/common/path.js';16import { isWindows } from '../../../../base/common/platform.js';17import { ISplice } from '../../../../base/common/sequence.js';18import { ThemeColor } from '../../../../base/common/themables.js';19import { URI, UriComponents } from '../../../../base/common/uri.js';20import { Range } from '../../../../editor/common/core/range.js';21import * as editorCommon from '../../../../editor/common/editorCommon.js';22import { Command, WorkspaceEditMetadata } from '../../../../editor/common/languages.js';23import { IReadonlyTextBuffer, ITextModel } from '../../../../editor/common/model.js';24import { IAccessibilityInformation } from '../../../../platform/accessibility/common/accessibility.js';25import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';26import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';27import { IFileReadLimits } from '../../../../platform/files/common/files.js';28import { UndoRedoGroup } from '../../../../platform/undoRedo/common/undoRedo.js';29import { IRevertOptions, ISaveOptions, IUntypedEditorInput } from '../../../common/editor.js';30import { NotebookTextModel } from './model/notebookTextModel.js';31import { ICellExecutionError } from './notebookExecutionStateService.js';32import { INotebookTextModelLike } from './notebookKernelService.js';33import { ICellRange } from './notebookRange.js';34import { RegisteredEditorPriority } from '../../../services/editor/common/editorResolverService.js';35import { generateMetadataUri, generate as generateUri, extractCellOutputDetails, parseMetadataUri, parse as parseUri } from '../../../services/notebook/common/notebookDocumentService.js';36import { IWorkingCopyBackupMeta, IWorkingCopySaveEvent } from '../../../services/workingCopy/common/workingCopy.js';37import { SnapshotContext } from '../../../services/workingCopy/common/fileWorkingCopy.js';3839export const NOTEBOOK_EDITOR_ID = 'workbench.editor.notebook';40export const NOTEBOOK_DIFF_EDITOR_ID = 'workbench.editor.notebookTextDiffEditor';41export const NOTEBOOK_MULTI_DIFF_EDITOR_ID = 'workbench.editor.notebookMultiTextDiffEditor';42export const INTERACTIVE_WINDOW_EDITOR_ID = 'workbench.editor.interactive';43export const REPL_EDITOR_ID = 'workbench.editor.repl';44export const NOTEBOOK_OUTPUT_EDITOR_ID = 'workbench.editor.notebookOutputEditor';4546export const EXECUTE_REPL_COMMAND_ID = 'replNotebook.input.execute';4748export enum CellKind {49Markup = 1,50Code = 251}5253export const NOTEBOOK_DISPLAY_ORDER: readonly string[] = [54'application/json',55'application/javascript',56'text/html',57'image/svg+xml',58Mimes.latex,59Mimes.markdown,60'image/png',61'image/jpeg',62Mimes.text63];6465export const ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER: readonly string[] = [66Mimes.latex,67Mimes.markdown,68'application/json',69'text/html',70'image/svg+xml',71'image/png',72'image/jpeg',73Mimes.text,74];7576/**77* A mapping of extension IDs who contain renderers, to notebook ids who they78* should be treated as the same in the renderer selection logic. This is used79* to prefer the 1st party Jupyter renderers even though they're in a separate80* extension, for instance. See #136247.81*/82export const RENDERER_EQUIVALENT_EXTENSIONS: ReadonlyMap<string, ReadonlySet<string>> = new Map([83['ms-toolsai.jupyter', new Set(['jupyter-notebook', 'interactive'])],84['ms-toolsai.jupyter-renderers', new Set(['jupyter-notebook', 'interactive'])],85]);8687export const RENDERER_NOT_AVAILABLE = '_notAvailable';8889export type ContributedNotebookRendererEntrypoint = string | { readonly extends: string; readonly path: string };9091export enum NotebookRunState {92Running = 1,93Idle = 294}9596export type NotebookDocumentMetadata = Record<string, unknown>;9798export enum NotebookCellExecutionState {99Unconfirmed = 1,100Pending = 2,101Executing = 3102}103export enum NotebookExecutionState {104Unconfirmed = 1,105Pending = 2,106Executing = 3107}108109export interface INotebookCellPreviousExecutionResult {110executionOrder?: number;111success?: boolean;112duration?: number;113}114115export interface NotebookCellMetadata {116/**117* custom metadata118*/119[key: string]: unknown;120}121122export interface NotebookCellInternalMetadata {123/**124* Used only for diffing of Notebooks.125* This is not persisted and generally useful only when diffing two notebooks.126* Useful only after we've manually matched a few cells together so we know which cells are matching.127*/128internalId?: string;129executionId?: string;130executionOrder?: number;131lastRunSuccess?: boolean;132runStartTime?: number;133runStartTimeAdjustment?: number;134runEndTime?: number;135renderDuration?: { [key: string]: number };136error?: ICellExecutionError;137}138139export interface NotebookCellCollapseState {140inputCollapsed?: boolean;141outputCollapsed?: boolean;142}143144export interface NotebookCellDefaultCollapseConfig {145codeCell?: NotebookCellCollapseState;146markupCell?: NotebookCellCollapseState;147}148149export type InteractiveWindowCollapseCodeCells = 'always' | 'never' | 'fromEditor';150151export type TransientCellMetadata = { readonly [K in keyof NotebookCellMetadata]?: boolean };152export type CellContentMetadata = { readonly [K in keyof NotebookCellMetadata]?: boolean };153export type TransientDocumentMetadata = { readonly [K in keyof NotebookDocumentMetadata]?: boolean };154155export interface TransientOptions {156readonly transientOutputs: boolean;157readonly transientCellMetadata: TransientCellMetadata;158readonly transientDocumentMetadata: TransientDocumentMetadata;159readonly cellContentMetadata: CellContentMetadata;160}161162/** Note: enum values are used for sorting */163export const enum NotebookRendererMatch {164/** Renderer has a hard dependency on an available kernel */165WithHardKernelDependency = 0,166/** Renderer works better with an available kernel */167WithOptionalKernelDependency = 1,168/** Renderer is kernel-agnostic */169Pure = 2,170/** Renderer is for a different mimeType or has a hard dependency which is unsatisfied */171Never = 3,172}173174/**175* Renderer messaging requirement. While this allows for 'optional' messaging,176* VS Code effectively treats it the same as true right now. "Partial177* activation" of extensions is a very tricky problem, which could allow178* solving this. But for now, optional is mostly only honored for aznb.179*/180export const enum RendererMessagingSpec {181Always = 'always',182Never = 'never',183Optional = 'optional',184}185186export type NotebookRendererEntrypoint = { readonly extends: string | undefined; readonly path: URI };187188export interface INotebookRendererInfo {189readonly id: string;190readonly displayName: string;191readonly entrypoint: NotebookRendererEntrypoint;192readonly extensionLocation: URI;193readonly extensionId: ExtensionIdentifier;194readonly messaging: RendererMessagingSpec;195196readonly mimeTypes: readonly string[];197198readonly isBuiltin: boolean;199200matchesWithoutKernel(mimeType: string): NotebookRendererMatch;201matches(mimeType: string, kernelProvides: ReadonlyArray<string>): NotebookRendererMatch;202}203204export interface INotebookStaticPreloadInfo {205readonly type: string;206readonly entrypoint: URI;207readonly extensionLocation: URI;208readonly localResourceRoots: readonly URI[];209}210211export interface IOrderedMimeType {212mimeType: string;213rendererId: string;214isTrusted: boolean;215}216217export interface IOutputItemDto {218readonly mime: string;219readonly data: VSBuffer;220}221222export interface IOutputDto {223outputs: IOutputItemDto[];224outputId: string;225metadata?: Record<string, any>;226}227228export interface ICellOutput {229readonly versionId: number;230outputs: IOutputItemDto[];231metadata?: Record<string, any>;232outputId: string;233/**234* Alternative output id that's reused when the output is updated.235*/236alternativeOutputId: string;237onDidChangeData: Event<void>;238replaceData(items: IOutputDto): void;239appendData(items: IOutputItemDto[]): void;240appendedSinceVersion(versionId: number, mime: string): VSBuffer | undefined;241asDto(): IOutputDto;242bumpVersion(): void;243dispose(): void;244}245246export interface CellInternalMetadataChangedEvent {247readonly lastRunSuccessChanged?: boolean;248}249250export interface INotebookDocumentMetadataTextModel {251/**252* Notebook Metadata Uri.253*/254readonly uri: URI;255/**256* Triggered when the Notebook Metadata changes.257*/258readonly onDidChange: Event<void>;259readonly metadata: Readonly<NotebookDocumentMetadata>;260readonly textBuffer: IReadonlyTextBuffer;261/**262* Text representation of the Notebook Metadata263*/264getValue(): string;265getHash(): string;266}267268export interface ICell {269readonly uri: URI;270handle: number;271language: string;272cellKind: CellKind;273outputs: ICellOutput[];274metadata: NotebookCellMetadata;275internalMetadata: NotebookCellInternalMetadata;276getHashValue(): number;277textBuffer: IReadonlyTextBuffer;278textModel?: ITextModel;279onDidChangeTextModel: Event<void>;280getValue(): string;281onDidChangeOutputs?: Event<NotebookCellOutputsSplice>;282onDidChangeOutputItems?: Event<void>;283onDidChangeLanguage: Event<string>;284onDidChangeMetadata: Event<void>;285onDidChangeInternalMetadata: Event<CellInternalMetadataChangedEvent>;286}287288export interface INotebookSnapshotOptions {289context: SnapshotContext;290outputSizeLimit: number;291transientOptions?: TransientOptions;292}293294export interface INotebookTextModel extends INotebookTextModelLike {295readonly notebookType: string;296readonly viewType: string;297metadata: NotebookDocumentMetadata;298readonly transientOptions: TransientOptions;299readonly uri: URI;300readonly versionId: number;301readonly length: number;302readonly cells: readonly ICell[];303reset(cells: ICellDto2[], metadata: NotebookDocumentMetadata, transientOptions: TransientOptions): void;304createSnapshot(options: INotebookSnapshotOptions): NotebookData;305restoreSnapshot(snapshot: NotebookData, transientOptions?: TransientOptions): void;306applyEdits(rawEdits: ICellEditOperation[], synchronous: boolean, beginSelectionState: ISelectionState | undefined, endSelectionsComputer: () => ISelectionState | undefined, undoRedoGroup: UndoRedoGroup | undefined, computeUndoRedo?: boolean): boolean;307onDidChangeContent: Event<NotebookTextModelChangedEvent>;308onWillDispose: Event<void>;309}310311export type NotebookCellTextModelSplice<T> = [312start: number,313deleteCount: number,314newItems: T[]315];316317export type NotebookCellOutputsSplice = {318start: number /* start */;319deleteCount: number /* delete count */;320newOutputs: ICellOutput[];321};322323export interface IMainCellDto {324handle: number;325url: string;326source: string[];327eol: string;328versionId: number;329language: string;330cellKind: CellKind;331outputs: IOutputDto[];332metadata?: NotebookCellMetadata;333internalMetadata?: NotebookCellInternalMetadata;334}335336export enum NotebookCellsChangeType {337ModelChange = 1,338Move = 2,339ChangeCellLanguage = 5,340Initialize = 6,341ChangeCellMetadata = 7,342Output = 8,343OutputItem = 9,344ChangeCellContent = 10,345ChangeDocumentMetadata = 11,346ChangeCellInternalMetadata = 12,347ChangeCellMime = 13,348Unknown = 100349}350351export interface NotebookCellsInitializeEvent<T> {352readonly kind: NotebookCellsChangeType.Initialize;353readonly changes: NotebookCellTextModelSplice<T>[];354}355356export interface NotebookCellContentChangeEvent {357readonly kind: NotebookCellsChangeType.ChangeCellContent;358readonly index: number;359}360361export interface NotebookCellsModelChangedEvent<T> {362readonly kind: NotebookCellsChangeType.ModelChange;363readonly changes: NotebookCellTextModelSplice<T>[];364}365366export interface NotebookCellsModelMoveEvent<T> {367readonly kind: NotebookCellsChangeType.Move;368readonly index: number;369readonly length: number;370readonly newIdx: number;371readonly cells: T[];372}373374export interface NotebookOutputChangedEvent {375readonly kind: NotebookCellsChangeType.Output;376readonly index: number;377readonly outputs: IOutputDto[];378readonly append: boolean;379}380381export interface NotebookOutputItemChangedEvent {382readonly kind: NotebookCellsChangeType.OutputItem;383readonly index: number;384readonly outputId: string;385readonly outputItems: IOutputItemDto[];386readonly append: boolean;387}388389export interface NotebookCellsChangeLanguageEvent {390readonly kind: NotebookCellsChangeType.ChangeCellLanguage;391readonly index: number;392readonly language: string;393}394395export interface NotebookCellsChangeMimeEvent {396readonly kind: NotebookCellsChangeType.ChangeCellMime;397readonly index: number;398readonly mime: string | undefined;399}400401export interface NotebookCellsChangeMetadataEvent {402readonly kind: NotebookCellsChangeType.ChangeCellMetadata;403readonly index: number;404readonly metadata: NotebookCellMetadata;405}406407export interface NotebookCellsChangeInternalMetadataEvent {408readonly kind: NotebookCellsChangeType.ChangeCellInternalMetadata;409readonly index: number;410readonly internalMetadata: NotebookCellInternalMetadata;411}412413export interface NotebookDocumentChangeMetadataEvent {414readonly kind: NotebookCellsChangeType.ChangeDocumentMetadata;415readonly metadata: NotebookDocumentMetadata;416}417418export interface NotebookDocumentUnknownChangeEvent {419readonly kind: NotebookCellsChangeType.Unknown;420}421422export type NotebookRawContentEventDto = NotebookCellsInitializeEvent<IMainCellDto> | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent<IMainCellDto> | NotebookCellsModelMoveEvent<IMainCellDto> | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMimeEvent | NotebookCellsChangeMetadataEvent | NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent;423424export type NotebookCellsChangedEventDto = {425readonly rawEvents: NotebookRawContentEventDto[];426readonly versionId: number;427};428429export type NotebookRawContentEvent = (NotebookCellsInitializeEvent<ICell> | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent<ICell> | NotebookCellsModelMoveEvent<ICell> | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMimeEvent | NotebookCellsChangeMetadataEvent | NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent) & { transient: boolean };430431export enum SelectionStateType {432Handle = 0,433Index = 1434}435436export interface ISelectionHandleState {437kind: SelectionStateType.Handle;438primary: number | null;439selections: number[];440}441442export interface ISelectionIndexState {443kind: SelectionStateType.Index;444focus: ICellRange;445selections: ICellRange[];446}447448export type ISelectionState = ISelectionHandleState | ISelectionIndexState;449450export type NotebookTextModelChangedEvent = {451readonly rawEvents: NotebookRawContentEvent[];452readonly versionId: number;453readonly synchronous: boolean | undefined;454readonly endSelectionState: ISelectionState | undefined;455};456457export type NotebookTextModelWillAddRemoveEvent = {458readonly rawEvent: NotebookCellsModelChangedEvent<ICell>;459};460461export const enum CellEditType {462Replace = 1,463Output = 2,464Metadata = 3,465CellLanguage = 4,466DocumentMetadata = 5,467Move = 6,468OutputItems = 7,469PartialMetadata = 8,470PartialInternalMetadata = 9,471}472473export interface ICellDto2 {474source: string;475language: string;476mime: string | undefined;477cellKind: CellKind;478outputs: IOutputDto[];479metadata?: NotebookCellMetadata;480internalMetadata?: NotebookCellInternalMetadata;481collapseState?: NotebookCellCollapseState;482}483484export interface ICellReplaceEdit {485editType: CellEditType.Replace;486index: number;487count: number;488cells: ICellDto2[];489}490491export interface ICellOutputEdit {492editType: CellEditType.Output;493index: number;494outputs: IOutputDto[];495append?: boolean;496}497498export interface ICellOutputEditByHandle {499editType: CellEditType.Output;500handle: number;501outputs: IOutputDto[];502append?: boolean;503}504505export interface ICellOutputItemEdit {506editType: CellEditType.OutputItems;507outputId: string;508items: IOutputItemDto[];509append?: boolean;510}511512export interface ICellMetadataEdit {513editType: CellEditType.Metadata;514index: number;515metadata: NotebookCellMetadata;516}517518// These types are nullable because we need to use 'null' on the EH side so it is JSON-stringified519export type NullablePartialNotebookCellMetadata = {520[Key in keyof Partial<NotebookCellMetadata>]: NotebookCellMetadata[Key] | null521};522523export interface ICellPartialMetadataEdit {524editType: CellEditType.PartialMetadata;525index: number;526metadata: NullablePartialNotebookCellMetadata;527}528529export interface ICellPartialMetadataEditByHandle {530editType: CellEditType.PartialMetadata;531handle: number;532metadata: NullablePartialNotebookCellMetadata;533}534535export type NullablePartialNotebookCellInternalMetadata = {536[Key in keyof Partial<NotebookCellInternalMetadata>]: NotebookCellInternalMetadata[Key] | null537};538export interface ICellPartialInternalMetadataEdit {539editType: CellEditType.PartialInternalMetadata;540index: number;541internalMetadata: NullablePartialNotebookCellInternalMetadata;542}543544export interface ICellPartialInternalMetadataEditByHandle {545editType: CellEditType.PartialInternalMetadata;546handle: number;547internalMetadata: NullablePartialNotebookCellInternalMetadata;548}549550export interface ICellLanguageEdit {551editType: CellEditType.CellLanguage;552index: number;553language: string;554}555556export interface IDocumentMetadataEdit {557editType: CellEditType.DocumentMetadata;558metadata: NotebookDocumentMetadata;559}560561export interface ICellMoveEdit {562editType: CellEditType.Move;563index: number;564length: number;565newIdx: number;566}567568export type IImmediateCellEditOperation = ICellOutputEditByHandle | ICellPartialMetadataEditByHandle | ICellOutputItemEdit | ICellPartialInternalMetadataEdit | ICellPartialInternalMetadataEditByHandle | ICellPartialMetadataEdit;569export type ICellEditOperation = IImmediateCellEditOperation | ICellReplaceEdit | ICellOutputEdit | ICellMetadataEdit | ICellPartialMetadataEdit | ICellPartialInternalMetadataEdit | IDocumentMetadataEdit | ICellMoveEdit | ICellOutputItemEdit | ICellLanguageEdit;570571572export interface IWorkspaceNotebookCellEdit {573metadata?: WorkspaceEditMetadata;574resource: URI;575notebookVersionId: number | undefined;576cellEdit: ICellPartialMetadataEdit | IDocumentMetadataEdit | ICellReplaceEdit;577}578579export interface IWorkspaceNotebookCellEditDto {580metadata?: WorkspaceEditMetadata;581resource: URI;582notebookVersionId: number | undefined;583cellEdit: ICellPartialMetadataEdit | IDocumentMetadataEdit | ICellReplaceEdit;584}585586export interface NotebookData {587readonly cells: ICellDto2[];588readonly metadata: NotebookDocumentMetadata;589}590591592export interface INotebookContributionData {593extension?: ExtensionIdentifier;594providerDisplayName: string;595displayName: string;596filenamePattern: (string | glob.IRelativePattern | INotebookExclusiveDocumentFilter)[];597priority?: RegisteredEditorPriority;598}599600export namespace NotebookMetadataUri {601export const scheme = Schemas.vscodeNotebookMetadata;602export function generate(notebook: URI): URI {603return generateMetadataUri(notebook);604}605export function parse(metadata: URI): URI | undefined {606return parseMetadataUri(metadata);607}608}609610export namespace CellUri {611export const scheme = Schemas.vscodeNotebookCell;612export function generate(notebook: URI, handle: number): URI {613return generateUri(notebook, handle);614}615616export function parse(cell: URI): { notebook: URI; handle: number } | undefined {617return parseUri(cell);618}619620/**621* Generates a URI for a cell output in a notebook using the output ID.622* Used when URI should be opened as text in the editor.623*/624export function generateCellOutputUriWithId(notebook: URI, outputId?: string) {625return notebook.with({626scheme: Schemas.vscodeNotebookCellOutput,627query: new URLSearchParams({628openIn: 'editor',629outputId: outputId ?? '',630notebookScheme: notebook.scheme !== Schemas.file ? notebook.scheme : '',631}).toString()632});633}634/**635* Generates a URI for a cell output in a notebook using the output index.636* Used when URI should be opened in notebook editor.637*/638export function generateCellOutputUriWithIndex(notebook: URI, cellUri: URI, outputIndex: number): URI {639return notebook.with({640scheme: Schemas.vscodeNotebookCellOutput,641fragment: cellUri.fragment,642query: new URLSearchParams({643openIn: 'notebook',644outputIndex: String(outputIndex),645}).toString()646});647}648649export function generateOutputEditorUri(notebook: URI, cellId: string, cellIndex: number, outputId: string, outputIndex: number): URI {650return notebook.with({651scheme: Schemas.vscodeNotebookCellOutput,652query: new URLSearchParams({653openIn: 'notebookOutputEditor',654notebook: notebook.toString(),655cellIndex: String(cellIndex),656outputId: outputId,657outputIndex: String(outputIndex),658}).toString()659});660}661662export function parseCellOutputUri(uri: URI): { notebook: URI; openIn: string; outputId?: string; cellFragment?: string; outputIndex?: number; cellHandle?: number; cellIndex?: number } | undefined {663return extractCellOutputDetails(uri);664}665666export function generateCellPropertyUri(notebook: URI, handle: number, scheme: string): URI {667return CellUri.generate(notebook, handle).with({ scheme: scheme });668}669670export function parseCellPropertyUri(uri: URI, propertyScheme: string) {671if (uri.scheme !== propertyScheme) {672return undefined;673}674675return CellUri.parse(uri.with({ scheme: scheme }));676}677}678679const normalizeSlashes = (str: string) => isWindows ? str.replace(/\//g, '\\') : str;680681interface IMimeTypeWithMatcher {682pattern: string;683matches: glob.ParsedPattern;684}685686export class MimeTypeDisplayOrder {687private readonly order: IMimeTypeWithMatcher[];688689constructor(690initialValue: readonly string[] = [],691private readonly defaultOrder = NOTEBOOK_DISPLAY_ORDER,692) {693this.order = [...new Set(initialValue)].map(pattern => ({694pattern,695matches: glob.parse(normalizeSlashes(pattern))696}));697}698699/**700* Returns a sorted array of the input mimetypes.701*/702public sort(mimetypes: Iterable<string>): string[] {703const remaining = new Map(Iterable.map(mimetypes, m => [m, normalizeSlashes(m)]));704let sorted: string[] = [];705706for (const { matches } of this.order) {707for (const [original, normalized] of remaining) {708if (matches(normalized)) {709sorted.push(original);710remaining.delete(original);711break;712}713}714}715716if (remaining.size) {717sorted = sorted.concat([...remaining.keys()].sort(718(a, b) => this.defaultOrder.indexOf(a) - this.defaultOrder.indexOf(b),719));720}721722return sorted;723}724725/**726* Records that the user selected the given mimetype over the other727* possible mimetypes, prioritizing it for future reference.728*/729public prioritize(chosenMimetype: string, otherMimetypes: readonly string[]) {730const chosenIndex = this.findIndex(chosenMimetype);731if (chosenIndex === -1) {732// always first, nothing more to do733this.order.unshift({ pattern: chosenMimetype, matches: glob.parse(normalizeSlashes(chosenMimetype)) });734return;735}736737// Get the other mimetypes that are before the chosenMimetype. Then, move738// them after it, retaining order.739const uniqueIndicies = new Set(otherMimetypes.map(m => this.findIndex(m, chosenIndex)));740uniqueIndicies.delete(-1);741const otherIndices = Array.from(uniqueIndicies).sort();742this.order.splice(chosenIndex + 1, 0, ...otherIndices.map(i => this.order[i]));743744for (let oi = otherIndices.length - 1; oi >= 0; oi--) {745this.order.splice(otherIndices[oi], 1);746}747}748749/**750* Gets an array of in-order mimetype preferences.751*/752public toArray() {753return this.order.map(o => o.pattern);754}755756private findIndex(mimeType: string, maxIndex = this.order.length) {757const normalized = normalizeSlashes(mimeType);758for (let i = 0; i < maxIndex; i++) {759if (this.order[i].matches(normalized)) {760return i;761}762}763764return -1;765}766}767768interface IMutableSplice<T> extends ISplice<T> {769readonly toInsert: T[];770deleteCount: number;771}772773export function diff<T>(before: T[], after: T[], contains: (a: T) => boolean, equal: (a: T, b: T) => boolean = (a: T, b: T) => a === b): ISplice<T>[] {774const result: IMutableSplice<T>[] = [];775776function pushSplice(start: number, deleteCount: number, toInsert: T[]): void {777if (deleteCount === 0 && toInsert.length === 0) {778return;779}780781const latest = result[result.length - 1];782783if (latest && latest.start + latest.deleteCount === start) {784latest.deleteCount += deleteCount;785latest.toInsert.push(...toInsert);786} else {787result.push({ start, deleteCount, toInsert });788}789}790791let beforeIdx = 0;792let afterIdx = 0;793794while (true) {795if (beforeIdx === before.length) {796pushSplice(beforeIdx, 0, after.slice(afterIdx));797break;798}799800if (afterIdx === after.length) {801pushSplice(beforeIdx, before.length - beforeIdx, []);802break;803}804805const beforeElement = before[beforeIdx];806const afterElement = after[afterIdx];807808if (equal(beforeElement, afterElement)) {809// equal810beforeIdx += 1;811afterIdx += 1;812continue;813}814815if (contains(afterElement)) {816// `afterElement` exists before, which means some elements before `afterElement` are deleted817pushSplice(beforeIdx, 1, []);818beforeIdx += 1;819} else {820// `afterElement` added821pushSplice(beforeIdx, 0, [afterElement]);822afterIdx += 1;823}824}825826return result;827}828829export interface ICellEditorViewState {830selections: editorCommon.ICursorState[];831}832833export const NOTEBOOK_EDITOR_CURSOR_BOUNDARY = new RawContextKey<'none' | 'top' | 'bottom' | 'both'>('notebookEditorCursorAtBoundary', 'none');834835export const NOTEBOOK_EDITOR_CURSOR_LINE_BOUNDARY = new RawContextKey<'none' | 'start' | 'end' | 'both'>('notebookEditorCursorAtLineBoundary', 'none');836837export interface INotebookLoadOptions {838/**839* Go to disk bypassing any cache of the model if any.840*/841forceReadFromFile?: boolean;842/**843* If provided, the size of the file will be checked against the limits844* and an error will be thrown if any limit is exceeded.845*/846readonly limits?: IFileReadLimits;847}848849export type NotebookEditorModelCreationOptions = {850limits?: IFileReadLimits;851scratchpad?: boolean;852viewType?: string;853};854855export interface IResolvedNotebookEditorModel extends INotebookEditorModel {856notebook: NotebookTextModel;857}858859export interface INotebookEditorModel extends IDisposable {860readonly onDidChangeDirty: Event<void>;861readonly onDidSave: Event<IWorkingCopySaveEvent>;862readonly onDidChangeOrphaned: Event<void>;863readonly onDidChangeReadonly: Event<void>;864readonly onDidRevertUntitled: Event<void>;865readonly resource: URI;866readonly viewType: string;867readonly notebook: INotebookTextModel | undefined;868readonly hasErrorState: boolean;869isResolved(): boolean;870isDirty(): boolean;871isModified(): boolean;872isReadonly(): boolean | IMarkdownString;873isOrphaned(): boolean;874hasAssociatedFilePath(): boolean;875load(options?: INotebookLoadOptions): Promise<IResolvedNotebookEditorModel>;876save(options?: ISaveOptions): Promise<boolean>;877saveAs(target: URI): Promise<IUntypedEditorInput | undefined>;878revert(options?: IRevertOptions): Promise<void>;879}880881export interface INotebookDiffEditorModel extends IDisposable {882original: { notebook: NotebookTextModel; resource: URI; viewType: string };883modified: { notebook: NotebookTextModel; resource: URI; viewType: string };884}885886export interface NotebookDocumentBackupData extends IWorkingCopyBackupMeta {887readonly viewType: string;888readonly backupId?: string;889readonly mtime?: number;890}891892export enum NotebookEditorPriority {893default = 'default',894option = 'option',895}896897export interface INotebookFindOptions {898regex?: boolean;899wholeWord?: boolean;900caseSensitive?: boolean;901wordSeparators?: string;902includeMarkupInput?: boolean;903includeMarkupPreview?: boolean;904includeCodeInput?: boolean;905includeOutput?: boolean;906findScope?: INotebookFindScope;907}908909export interface INotebookFindScope {910findScopeType: NotebookFindScopeType;911selectedCellRanges?: ICellRange[];912selectedTextRanges?: Range[];913}914915export enum NotebookFindScopeType {916Cells = 'cells',917Text = 'text',918None = 'none'919}920921export interface INotebookExclusiveDocumentFilter {922include?: string | glob.IRelativePattern;923exclude?: string | glob.IRelativePattern;924}925926export interface INotebookDocumentFilter {927viewType?: string | string[];928filenamePattern?: string | glob.IRelativePattern | INotebookExclusiveDocumentFilter;929}930931//TODO@rebornix test932933export function isDocumentExcludePattern(filenamePattern: string | glob.IRelativePattern | INotebookExclusiveDocumentFilter): filenamePattern is { include: string | glob.IRelativePattern; exclude: string | glob.IRelativePattern } {934const arg = filenamePattern as INotebookExclusiveDocumentFilter;935936if ((typeof arg.include === 'string' || glob.isRelativePattern(arg.include))937&& (typeof arg.exclude === 'string' || glob.isRelativePattern(arg.exclude))) {938return true;939}940941return false;942}943export function notebookDocumentFilterMatch(filter: INotebookDocumentFilter, viewType: string, resource: URI): boolean {944if (Array.isArray(filter.viewType) && filter.viewType.indexOf(viewType) >= 0) {945return true;946}947948if (filter.viewType === viewType) {949return true;950}951952if (filter.filenamePattern) {953const filenamePattern = isDocumentExcludePattern(filter.filenamePattern) ? filter.filenamePattern.include : (filter.filenamePattern as string | glob.IRelativePattern);954const excludeFilenamePattern = isDocumentExcludePattern(filter.filenamePattern) ? filter.filenamePattern.exclude : undefined;955956if (glob.match(filenamePattern, basename(resource.fsPath).toLowerCase())) {957if (excludeFilenamePattern) {958if (glob.match(excludeFilenamePattern, basename(resource.fsPath).toLowerCase())) {959// should exclude960961return false;962}963}964return true;965}966}967return false;968}969970export interface INotebookCellStatusBarItemProvider {971viewType: string;972onDidChangeStatusBarItems?: Event<void>;973provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise<INotebookCellStatusBarItemList | undefined>;974}975976977export interface INotebookDiffResult {978cellsDiff: IDiffResult;979metadataChanged: boolean;980}981982export interface INotebookCellStatusBarItem {983readonly alignment: CellStatusbarAlignment;984readonly priority?: number;985readonly text: string;986readonly color?: string | ThemeColor;987readonly backgroundColor?: string | ThemeColor;988readonly tooltip?: string | IMarkdownString;989readonly command?: string | Command;990readonly accessibilityInformation?: IAccessibilityInformation;991readonly opacity?: string;992readonly onlyShowWhenActive?: boolean;993}994995export interface INotebookCellStatusBarItemList {996items: INotebookCellStatusBarItem[];997dispose?(): void;998}9991000export type ShowCellStatusBarType = 'hidden' | 'visible' | 'visibleAfterExecute';1001export const NotebookSetting = {1002displayOrder: 'notebook.displayOrder',1003cellToolbarLocation: 'notebook.cellToolbarLocation',1004cellToolbarVisibility: 'notebook.cellToolbarVisibility',1005showCellStatusBar: 'notebook.showCellStatusBar',1006cellExecutionTimeVerbosity: 'notebook.cellExecutionTimeVerbosity',1007textDiffEditorPreview: 'notebook.diff.enablePreview',1008diffOverviewRuler: 'notebook.diff.overviewRuler',1009experimentalInsertToolbarAlignment: 'notebook.experimental.insertToolbarAlignment',1010compactView: 'notebook.compactView',1011focusIndicator: 'notebook.cellFocusIndicator',1012insertToolbarLocation: 'notebook.insertToolbarLocation',1013globalToolbar: 'notebook.globalToolbar',1014stickyScrollEnabled: 'notebook.stickyScroll.enabled',1015stickyScrollMode: 'notebook.stickyScroll.mode',1016undoRedoPerCell: 'notebook.undoRedoPerCell',1017consolidatedOutputButton: 'notebook.consolidatedOutputButton',1018openOutputInPreviewEditor: 'notebook.output.openInPreviewEditor.enabled',1019showFoldingControls: 'notebook.showFoldingControls',1020dragAndDropEnabled: 'notebook.dragAndDropEnabled',1021cellEditorOptionsCustomizations: 'notebook.editorOptionsCustomizations',1022consolidatedRunButton: 'notebook.consolidatedRunButton',1023openGettingStarted: 'notebook.experimental.openGettingStarted',1024globalToolbarShowLabel: 'notebook.globalToolbarShowLabel',1025markupFontSize: 'notebook.markup.fontSize',1026markdownLineHeight: 'notebook.markdown.lineHeight',1027interactiveWindowCollapseCodeCells: 'interactiveWindow.collapseCellInputCode',1028outputScrollingDeprecated: 'notebook.experimental.outputScrolling',1029outputScrolling: 'notebook.output.scrolling',1030textOutputLineLimit: 'notebook.output.textLineLimit',1031LinkifyOutputFilePaths: 'notebook.output.linkifyFilePaths',1032minimalErrorRendering: 'notebook.output.minimalErrorRendering',1033formatOnSave: 'notebook.formatOnSave.enabled',1034insertFinalNewline: 'notebook.insertFinalNewline',1035defaultFormatter: 'notebook.defaultFormatter',1036formatOnCellExecution: 'notebook.formatOnCellExecution',1037codeActionsOnSave: 'notebook.codeActionsOnSave',1038outputWordWrap: 'notebook.output.wordWrap',1039outputLineHeightDeprecated: 'notebook.outputLineHeight',1040outputLineHeight: 'notebook.output.lineHeight',1041outputFontSizeDeprecated: 'notebook.outputFontSize',1042outputFontSize: 'notebook.output.fontSize',1043outputFontFamilyDeprecated: 'notebook.outputFontFamily',1044outputFontFamily: 'notebook.output.fontFamily',1045findFilters: 'notebook.find.filters',1046logging: 'notebook.logging',1047confirmDeleteRunningCell: 'notebook.confirmDeleteRunningCell',1048remoteSaving: 'notebook.experimental.remoteSave',1049gotoSymbolsAllSymbols: 'notebook.gotoSymbols.showAllSymbols',1050outlineShowMarkdownHeadersOnly: 'notebook.outline.showMarkdownHeadersOnly',1051outlineShowCodeCells: 'notebook.outline.showCodeCells',1052outlineShowCodeCellSymbols: 'notebook.outline.showCodeCellSymbols',1053breadcrumbsShowCodeCells: 'notebook.breadcrumbs.showCodeCells',1054scrollToRevealCell: 'notebook.scrolling.revealNextCellOnExecute',1055cellChat: 'notebook.experimental.cellChat',1056cellGenerate: 'notebook.experimental.generate',1057notebookVariablesView: 'notebook.variablesView',1058notebookInlineValues: 'notebook.inlineValues',1059InteractiveWindowPromptToSave: 'interactiveWindow.promptToSaveOnClose',1060cellFailureDiagnostics: 'notebook.cellFailureDiagnostics',1061outputBackupSizeLimit: 'notebook.backup.sizeLimit',1062multiCursor: 'notebook.multiCursor.enabled',1063markupFontFamily: 'notebook.markup.fontFamily',1064} as const;10651066export const enum CellStatusbarAlignment {1067Left = 1,1068Right = 21069}10701071export class NotebookWorkingCopyTypeIdentifier {10721073private static _prefix = 'notebook/';10741075static create(notebookType: string, viewType?: string): string {1076return `${NotebookWorkingCopyTypeIdentifier._prefix}${notebookType}/${viewType ?? notebookType}`;1077}10781079static parse(candidate: string): { notebookType: string; viewType: string } | undefined {1080if (candidate.startsWith(NotebookWorkingCopyTypeIdentifier._prefix)) {1081const split = candidate.substring(NotebookWorkingCopyTypeIdentifier._prefix.length).split('/');1082if (split.length === 2) {1083return { notebookType: split[0], viewType: split[1] };1084}1085}1086return undefined;1087}1088}10891090export interface NotebookExtensionDescription {1091readonly id: ExtensionIdentifier;1092readonly location: UriComponents | undefined;1093}10941095const textDecoder = new TextDecoder();10961097/**1098* Given a stream of individual stdout outputs, this function will return the compressed lines, escaping some of the common terminal escape codes.1099* E.g. some terminal escape codes would result in the previous line getting cleared, such if we had 3 lines and1100* last line contained such a code, then the result string would be just the first two lines.1101* @returns a single VSBuffer with the concatenated and compressed data, and whether any compression was done.1102*/1103export function compressOutputItemStreams(outputs: Uint8Array[]) {1104const buffers: Uint8Array[] = [];1105let startAppending = false;11061107// Pick the first set of outputs with the same mime type.1108for (const output of outputs) {1109if ((buffers.length === 0 || startAppending)) {1110buffers.push(output);1111startAppending = true;1112}1113}11141115let didCompression = compressStreamBuffer(buffers);1116const concatenated = VSBuffer.concat(buffers.map(buffer => VSBuffer.wrap(buffer)));1117const data = formatStreamText(concatenated);1118didCompression = didCompression || data.byteLength !== concatenated.byteLength;1119return { data, didCompression };1120}11211122export const MOVE_CURSOR_1_LINE_COMMAND = `${String.fromCharCode(27)}[A`;1123const MOVE_CURSOR_1_LINE_COMMAND_BYTES = MOVE_CURSOR_1_LINE_COMMAND.split('').map(c => c.charCodeAt(0));1124const LINE_FEED = 10;1125function compressStreamBuffer(streams: Uint8Array[]) {1126let didCompress = false;1127streams.forEach((stream, index) => {1128if (index === 0 || stream.length < MOVE_CURSOR_1_LINE_COMMAND.length) {1129return;1130}11311132const previousStream = streams[index - 1];11331134// Remove the previous line if required.1135const command = stream.subarray(0, MOVE_CURSOR_1_LINE_COMMAND.length);1136if (command[0] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[0] && command[1] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[1] && command[2] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[2]) {1137const lastIndexOfLineFeed = previousStream.lastIndexOf(LINE_FEED);1138if (lastIndexOfLineFeed === -1) {1139return;1140}11411142didCompress = true;1143streams[index - 1] = previousStream.subarray(0, lastIndexOfLineFeed);1144streams[index] = stream.subarray(MOVE_CURSOR_1_LINE_COMMAND.length);1145}1146});1147return didCompress;1148}1149115011511152/**1153* Took this from jupyter/notebook1154* https://github.com/jupyter/notebook/blob/b8b66332e2023e83d2ee04f83d8814f567e01a4e/notebook/static/base/js/utils.js1155* Remove characters that are overridden by backspace characters1156*/1157function fixBackspace(txt: string) {1158let tmp = txt;1159do {1160txt = tmp;1161// Cancel out anything-but-newline followed by backspace1162tmp = txt.replace(/[^\n]\x08/gm, '');1163} while (tmp.length < txt.length);1164return txt;1165}11661167/**1168* Remove chunks that should be overridden by the effect of carriage return characters1169* From https://github.com/jupyter/notebook/blob/master/notebook/static/base/js/utils.js1170*/1171function fixCarriageReturn(txt: string) {1172txt = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline1173while (txt.search(/\r[^$]/g) > -1) {1174const base = txt.match(/^(.*)\r+/m)![1];1175let insert = txt.match(/\r+(.*)$/m)![1];1176insert = insert + base.slice(insert.length, base.length);1177txt = txt.replace(/\r+.*$/m, '\r').replace(/^.*\r/m, insert);1178}1179return txt;1180}11811182const BACKSPACE_CHARACTER = '\b'.charCodeAt(0);1183const CARRIAGE_RETURN_CHARACTER = '\r'.charCodeAt(0);1184function formatStreamText(buffer: VSBuffer): VSBuffer {1185// We have special handling for backspace and carriage return characters.1186// Don't unnecessary decode the bytes if we don't need to perform any processing.1187if (!buffer.buffer.includes(BACKSPACE_CHARACTER) && !buffer.buffer.includes(CARRIAGE_RETURN_CHARACTER)) {1188return buffer;1189}1190// Do the same thing jupyter is doing1191return VSBuffer.fromString(fixCarriageReturn(fixBackspace(textDecoder.decode(buffer.buffer))));1192}11931194export interface INotebookKernelSourceAction {1195readonly label: string;1196readonly description?: string;1197readonly detail?: string;1198readonly command?: string | Command;1199readonly documentation?: UriComponents | string;1200}120112021203