Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/notebook/common/notebookCommon.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { VSBuffer } from '../../../../base/common/buffer.js';
7
import { CancellationToken } from '../../../../base/common/cancellation.js';
8
import { IDiffResult } from '../../../../base/common/diff/diff.js';
9
import { Event } from '../../../../base/common/event.js';
10
import * as glob from '../../../../base/common/glob.js';
11
import { IMarkdownString } from '../../../../base/common/htmlContent.js';
12
import { Iterable } from '../../../../base/common/iterator.js';
13
import { IDisposable } from '../../../../base/common/lifecycle.js';
14
import { Mimes } from '../../../../base/common/mime.js';
15
import { Schemas } from '../../../../base/common/network.js';
16
import { basename } from '../../../../base/common/path.js';
17
import { isWindows } from '../../../../base/common/platform.js';
18
import { ISplice } from '../../../../base/common/sequence.js';
19
import { ThemeColor } from '../../../../base/common/themables.js';
20
import { URI, UriComponents } from '../../../../base/common/uri.js';
21
import { Range } from '../../../../editor/common/core/range.js';
22
import * as editorCommon from '../../../../editor/common/editorCommon.js';
23
import { Command, WorkspaceEditMetadata } from '../../../../editor/common/languages.js';
24
import { IReadonlyTextBuffer, ITextModel } from '../../../../editor/common/model.js';
25
import { IAccessibilityInformation } from '../../../../platform/accessibility/common/accessibility.js';
26
import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
27
import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';
28
import { IFileReadLimits } from '../../../../platform/files/common/files.js';
29
import { UndoRedoGroup } from '../../../../platform/undoRedo/common/undoRedo.js';
30
import { IRevertOptions, ISaveOptions, IUntypedEditorInput } from '../../../common/editor.js';
31
import { NotebookTextModel } from './model/notebookTextModel.js';
32
import { ICellExecutionError } from './notebookExecutionStateService.js';
33
import { INotebookTextModelLike } from './notebookKernelService.js';
34
import { ICellRange } from './notebookRange.js';
35
import { RegisteredEditorPriority } from '../../../services/editor/common/editorResolverService.js';
36
import { generateMetadataUri, generate as generateUri, extractCellOutputDetails, parseMetadataUri, parse as parseUri } from '../../../services/notebook/common/notebookDocumentService.js';
37
import { IWorkingCopyBackupMeta, IWorkingCopySaveEvent } from '../../../services/workingCopy/common/workingCopy.js';
38
import { SnapshotContext } from '../../../services/workingCopy/common/fileWorkingCopy.js';
39
40
export const NOTEBOOK_EDITOR_ID = 'workbench.editor.notebook';
41
export const NOTEBOOK_DIFF_EDITOR_ID = 'workbench.editor.notebookTextDiffEditor';
42
export const NOTEBOOK_MULTI_DIFF_EDITOR_ID = 'workbench.editor.notebookMultiTextDiffEditor';
43
export const INTERACTIVE_WINDOW_EDITOR_ID = 'workbench.editor.interactive';
44
export const REPL_EDITOR_ID = 'workbench.editor.repl';
45
export const NOTEBOOK_OUTPUT_EDITOR_ID = 'workbench.editor.notebookOutputEditor';
46
47
export const EXECUTE_REPL_COMMAND_ID = 'replNotebook.input.execute';
48
49
export enum CellKind {
50
Markup = 1,
51
Code = 2
52
}
53
54
export const NOTEBOOK_DISPLAY_ORDER: readonly string[] = [
55
'application/json',
56
'application/javascript',
57
'text/html',
58
'image/svg+xml',
59
Mimes.latex,
60
Mimes.markdown,
61
'image/png',
62
'image/jpeg',
63
Mimes.text
64
];
65
66
export const ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER: readonly string[] = [
67
Mimes.latex,
68
Mimes.markdown,
69
'application/json',
70
'text/html',
71
'image/svg+xml',
72
'image/png',
73
'image/jpeg',
74
Mimes.text,
75
];
76
77
/**
78
* A mapping of extension IDs who contain renderers, to notebook ids who they
79
* should be treated as the same in the renderer selection logic. This is used
80
* to prefer the 1st party Jupyter renderers even though they're in a separate
81
* extension, for instance. See #136247.
82
*/
83
export const RENDERER_EQUIVALENT_EXTENSIONS: ReadonlyMap<string, ReadonlySet<string>> = new Map([
84
['ms-toolsai.jupyter', new Set(['jupyter-notebook', 'interactive'])],
85
['ms-toolsai.jupyter-renderers', new Set(['jupyter-notebook', 'interactive'])],
86
]);
87
88
export const RENDERER_NOT_AVAILABLE = '_notAvailable';
89
90
export type ContributedNotebookRendererEntrypoint = string | { readonly extends: string; readonly path: string };
91
92
export enum NotebookRunState {
93
Running = 1,
94
Idle = 2
95
}
96
97
export type NotebookDocumentMetadata = Record<string, unknown>;
98
99
export enum NotebookCellExecutionState {
100
Unconfirmed = 1,
101
Pending = 2,
102
Executing = 3
103
}
104
export enum NotebookExecutionState {
105
Unconfirmed = 1,
106
Pending = 2,
107
Executing = 3
108
}
109
110
export interface INotebookCellPreviousExecutionResult {
111
executionOrder?: number;
112
success?: boolean;
113
duration?: number;
114
}
115
116
export interface NotebookCellMetadata {
117
/**
118
* custom metadata
119
*/
120
[key: string]: unknown;
121
}
122
123
export interface NotebookCellInternalMetadata {
124
/**
125
* Used only for diffing of Notebooks.
126
* This is not persisted and generally useful only when diffing two notebooks.
127
* Useful only after we've manually matched a few cells together so we know which cells are matching.
128
*/
129
internalId?: string;
130
executionId?: string;
131
executionOrder?: number;
132
lastRunSuccess?: boolean;
133
runStartTime?: number;
134
runStartTimeAdjustment?: number;
135
runEndTime?: number;
136
renderDuration?: { [key: string]: number };
137
error?: ICellExecutionError;
138
}
139
140
export interface NotebookCellCollapseState {
141
inputCollapsed?: boolean;
142
outputCollapsed?: boolean;
143
}
144
145
export interface NotebookCellDefaultCollapseConfig {
146
codeCell?: NotebookCellCollapseState;
147
markupCell?: NotebookCellCollapseState;
148
}
149
150
export type InteractiveWindowCollapseCodeCells = 'always' | 'never' | 'fromEditor';
151
152
export type TransientCellMetadata = { readonly [K in keyof NotebookCellMetadata]?: boolean };
153
export type CellContentMetadata = { readonly [K in keyof NotebookCellMetadata]?: boolean };
154
export type TransientDocumentMetadata = { readonly [K in keyof NotebookDocumentMetadata]?: boolean };
155
156
export interface TransientOptions {
157
readonly transientOutputs: boolean;
158
readonly transientCellMetadata: TransientCellMetadata;
159
readonly transientDocumentMetadata: TransientDocumentMetadata;
160
readonly cellContentMetadata: CellContentMetadata;
161
}
162
163
/** Note: enum values are used for sorting */
164
export const enum NotebookRendererMatch {
165
/** Renderer has a hard dependency on an available kernel */
166
WithHardKernelDependency = 0,
167
/** Renderer works better with an available kernel */
168
WithOptionalKernelDependency = 1,
169
/** Renderer is kernel-agnostic */
170
Pure = 2,
171
/** Renderer is for a different mimeType or has a hard dependency which is unsatisfied */
172
Never = 3,
173
}
174
175
/**
176
* Renderer messaging requirement. While this allows for 'optional' messaging,
177
* VS Code effectively treats it the same as true right now. "Partial
178
* activation" of extensions is a very tricky problem, which could allow
179
* solving this. But for now, optional is mostly only honored for aznb.
180
*/
181
export const enum RendererMessagingSpec {
182
Always = 'always',
183
Never = 'never',
184
Optional = 'optional',
185
}
186
187
export type NotebookRendererEntrypoint = { readonly extends: string | undefined; readonly path: URI };
188
189
export interface INotebookRendererInfo {
190
readonly id: string;
191
readonly displayName: string;
192
readonly entrypoint: NotebookRendererEntrypoint;
193
readonly extensionLocation: URI;
194
readonly extensionId: ExtensionIdentifier;
195
readonly messaging: RendererMessagingSpec;
196
197
readonly mimeTypes: readonly string[];
198
199
readonly isBuiltin: boolean;
200
201
matchesWithoutKernel(mimeType: string): NotebookRendererMatch;
202
matches(mimeType: string, kernelProvides: ReadonlyArray<string>): NotebookRendererMatch;
203
}
204
205
export interface INotebookStaticPreloadInfo {
206
readonly type: string;
207
readonly entrypoint: URI;
208
readonly extensionLocation: URI;
209
readonly localResourceRoots: readonly URI[];
210
}
211
212
export interface IOrderedMimeType {
213
mimeType: string;
214
rendererId: string;
215
isTrusted: boolean;
216
}
217
218
export interface IOutputItemDto {
219
readonly mime: string;
220
readonly data: VSBuffer;
221
}
222
223
export interface IOutputDto {
224
outputs: IOutputItemDto[];
225
outputId: string;
226
metadata?: Record<string, any>;
227
}
228
229
export interface ICellOutput {
230
readonly versionId: number;
231
outputs: IOutputItemDto[];
232
metadata?: Record<string, any>;
233
outputId: string;
234
/**
235
* Alternative output id that's reused when the output is updated.
236
*/
237
alternativeOutputId: string;
238
onDidChangeData: Event<void>;
239
replaceData(items: IOutputDto): void;
240
appendData(items: IOutputItemDto[]): void;
241
appendedSinceVersion(versionId: number, mime: string): VSBuffer | undefined;
242
asDto(): IOutputDto;
243
bumpVersion(): void;
244
dispose(): void;
245
}
246
247
export interface CellInternalMetadataChangedEvent {
248
readonly lastRunSuccessChanged?: boolean;
249
}
250
251
export interface INotebookDocumentMetadataTextModel {
252
/**
253
* Notebook Metadata Uri.
254
*/
255
readonly uri: URI;
256
/**
257
* Triggered when the Notebook Metadata changes.
258
*/
259
readonly onDidChange: Event<void>;
260
readonly metadata: Readonly<NotebookDocumentMetadata>;
261
readonly textBuffer: IReadonlyTextBuffer;
262
/**
263
* Text representation of the Notebook Metadata
264
*/
265
getValue(): string;
266
getHash(): string;
267
}
268
269
export interface ICell {
270
readonly uri: URI;
271
handle: number;
272
language: string;
273
cellKind: CellKind;
274
outputs: ICellOutput[];
275
metadata: NotebookCellMetadata;
276
internalMetadata: NotebookCellInternalMetadata;
277
getHashValue(): number;
278
textBuffer: IReadonlyTextBuffer;
279
textModel?: ITextModel;
280
onDidChangeTextModel: Event<void>;
281
getValue(): string;
282
onDidChangeOutputs?: Event<NotebookCellOutputsSplice>;
283
onDidChangeOutputItems?: Event<void>;
284
onDidChangeLanguage: Event<string>;
285
onDidChangeMetadata: Event<void>;
286
onDidChangeInternalMetadata: Event<CellInternalMetadataChangedEvent>;
287
}
288
289
export interface INotebookSnapshotOptions {
290
context: SnapshotContext;
291
outputSizeLimit: number;
292
transientOptions?: TransientOptions;
293
}
294
295
export interface INotebookTextModel extends INotebookTextModelLike {
296
readonly notebookType: string;
297
readonly viewType: string;
298
metadata: NotebookDocumentMetadata;
299
readonly transientOptions: TransientOptions;
300
readonly uri: URI;
301
readonly versionId: number;
302
readonly length: number;
303
readonly cells: readonly ICell[];
304
reset(cells: ICellDto2[], metadata: NotebookDocumentMetadata, transientOptions: TransientOptions): void;
305
createSnapshot(options: INotebookSnapshotOptions): NotebookData;
306
restoreSnapshot(snapshot: NotebookData, transientOptions?: TransientOptions): void;
307
applyEdits(rawEdits: ICellEditOperation[], synchronous: boolean, beginSelectionState: ISelectionState | undefined, endSelectionsComputer: () => ISelectionState | undefined, undoRedoGroup: UndoRedoGroup | undefined, computeUndoRedo?: boolean): boolean;
308
onDidChangeContent: Event<NotebookTextModelChangedEvent>;
309
onWillDispose: Event<void>;
310
}
311
312
export type NotebookCellTextModelSplice<T> = [
313
start: number,
314
deleteCount: number,
315
newItems: T[]
316
];
317
318
export type NotebookCellOutputsSplice = {
319
start: number /* start */;
320
deleteCount: number /* delete count */;
321
newOutputs: ICellOutput[];
322
};
323
324
export interface IMainCellDto {
325
handle: number;
326
url: string;
327
source: string[];
328
eol: string;
329
versionId: number;
330
language: string;
331
cellKind: CellKind;
332
outputs: IOutputDto[];
333
metadata?: NotebookCellMetadata;
334
internalMetadata?: NotebookCellInternalMetadata;
335
}
336
337
export enum NotebookCellsChangeType {
338
ModelChange = 1,
339
Move = 2,
340
ChangeCellLanguage = 5,
341
Initialize = 6,
342
ChangeCellMetadata = 7,
343
Output = 8,
344
OutputItem = 9,
345
ChangeCellContent = 10,
346
ChangeDocumentMetadata = 11,
347
ChangeCellInternalMetadata = 12,
348
ChangeCellMime = 13,
349
Unknown = 100
350
}
351
352
export interface NotebookCellsInitializeEvent<T> {
353
readonly kind: NotebookCellsChangeType.Initialize;
354
readonly changes: NotebookCellTextModelSplice<T>[];
355
}
356
357
export interface NotebookCellContentChangeEvent {
358
readonly kind: NotebookCellsChangeType.ChangeCellContent;
359
readonly index: number;
360
}
361
362
export interface NotebookCellsModelChangedEvent<T> {
363
readonly kind: NotebookCellsChangeType.ModelChange;
364
readonly changes: NotebookCellTextModelSplice<T>[];
365
}
366
367
export interface NotebookCellsModelMoveEvent<T> {
368
readonly kind: NotebookCellsChangeType.Move;
369
readonly index: number;
370
readonly length: number;
371
readonly newIdx: number;
372
readonly cells: T[];
373
}
374
375
export interface NotebookOutputChangedEvent {
376
readonly kind: NotebookCellsChangeType.Output;
377
readonly index: number;
378
readonly outputs: IOutputDto[];
379
readonly append: boolean;
380
}
381
382
export interface NotebookOutputItemChangedEvent {
383
readonly kind: NotebookCellsChangeType.OutputItem;
384
readonly index: number;
385
readonly outputId: string;
386
readonly outputItems: IOutputItemDto[];
387
readonly append: boolean;
388
}
389
390
export interface NotebookCellsChangeLanguageEvent {
391
readonly kind: NotebookCellsChangeType.ChangeCellLanguage;
392
readonly index: number;
393
readonly language: string;
394
}
395
396
export interface NotebookCellsChangeMimeEvent {
397
readonly kind: NotebookCellsChangeType.ChangeCellMime;
398
readonly index: number;
399
readonly mime: string | undefined;
400
}
401
402
export interface NotebookCellsChangeMetadataEvent {
403
readonly kind: NotebookCellsChangeType.ChangeCellMetadata;
404
readonly index: number;
405
readonly metadata: NotebookCellMetadata;
406
}
407
408
export interface NotebookCellsChangeInternalMetadataEvent {
409
readonly kind: NotebookCellsChangeType.ChangeCellInternalMetadata;
410
readonly index: number;
411
readonly internalMetadata: NotebookCellInternalMetadata;
412
}
413
414
export interface NotebookDocumentChangeMetadataEvent {
415
readonly kind: NotebookCellsChangeType.ChangeDocumentMetadata;
416
readonly metadata: NotebookDocumentMetadata;
417
}
418
419
export interface NotebookDocumentUnknownChangeEvent {
420
readonly kind: NotebookCellsChangeType.Unknown;
421
}
422
423
export type NotebookRawContentEventDto = NotebookCellsInitializeEvent<IMainCellDto> | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent<IMainCellDto> | NotebookCellsModelMoveEvent<IMainCellDto> | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMimeEvent | NotebookCellsChangeMetadataEvent | NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent;
424
425
export type NotebookCellsChangedEventDto = {
426
readonly rawEvents: NotebookRawContentEventDto[];
427
readonly versionId: number;
428
};
429
430
export type NotebookRawContentEvent = (NotebookCellsInitializeEvent<ICell> | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent<ICell> | NotebookCellsModelMoveEvent<ICell> | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMimeEvent | NotebookCellsChangeMetadataEvent | NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent) & { transient: boolean };
431
432
export enum SelectionStateType {
433
Handle = 0,
434
Index = 1
435
}
436
437
export interface ISelectionHandleState {
438
kind: SelectionStateType.Handle;
439
primary: number | null;
440
selections: number[];
441
}
442
443
export interface ISelectionIndexState {
444
kind: SelectionStateType.Index;
445
focus: ICellRange;
446
selections: ICellRange[];
447
}
448
449
export type ISelectionState = ISelectionHandleState | ISelectionIndexState;
450
451
export type NotebookTextModelChangedEvent = {
452
readonly rawEvents: NotebookRawContentEvent[];
453
readonly versionId: number;
454
readonly synchronous: boolean | undefined;
455
readonly endSelectionState: ISelectionState | undefined;
456
};
457
458
export type NotebookTextModelWillAddRemoveEvent = {
459
readonly rawEvent: NotebookCellsModelChangedEvent<ICell>;
460
};
461
462
export const enum CellEditType {
463
Replace = 1,
464
Output = 2,
465
Metadata = 3,
466
CellLanguage = 4,
467
DocumentMetadata = 5,
468
Move = 6,
469
OutputItems = 7,
470
PartialMetadata = 8,
471
PartialInternalMetadata = 9,
472
}
473
474
export interface ICellDto2 {
475
source: string;
476
language: string;
477
mime: string | undefined;
478
cellKind: CellKind;
479
outputs: IOutputDto[];
480
metadata?: NotebookCellMetadata;
481
internalMetadata?: NotebookCellInternalMetadata;
482
collapseState?: NotebookCellCollapseState;
483
}
484
485
export interface ICellReplaceEdit {
486
editType: CellEditType.Replace;
487
index: number;
488
count: number;
489
cells: ICellDto2[];
490
}
491
492
export interface ICellOutputEdit {
493
editType: CellEditType.Output;
494
index: number;
495
outputs: IOutputDto[];
496
append?: boolean;
497
}
498
499
export interface ICellOutputEditByHandle {
500
editType: CellEditType.Output;
501
handle: number;
502
outputs: IOutputDto[];
503
append?: boolean;
504
}
505
506
export interface ICellOutputItemEdit {
507
editType: CellEditType.OutputItems;
508
outputId: string;
509
items: IOutputItemDto[];
510
append?: boolean;
511
}
512
513
export interface ICellMetadataEdit {
514
editType: CellEditType.Metadata;
515
index: number;
516
metadata: NotebookCellMetadata;
517
}
518
519
// These types are nullable because we need to use 'null' on the EH side so it is JSON-stringified
520
export type NullablePartialNotebookCellMetadata = {
521
[Key in keyof Partial<NotebookCellMetadata>]: NotebookCellMetadata[Key] | null
522
};
523
524
export interface ICellPartialMetadataEdit {
525
editType: CellEditType.PartialMetadata;
526
index: number;
527
metadata: NullablePartialNotebookCellMetadata;
528
}
529
530
export interface ICellPartialMetadataEditByHandle {
531
editType: CellEditType.PartialMetadata;
532
handle: number;
533
metadata: NullablePartialNotebookCellMetadata;
534
}
535
536
export type NullablePartialNotebookCellInternalMetadata = {
537
[Key in keyof Partial<NotebookCellInternalMetadata>]: NotebookCellInternalMetadata[Key] | null
538
};
539
export interface ICellPartialInternalMetadataEdit {
540
editType: CellEditType.PartialInternalMetadata;
541
index: number;
542
internalMetadata: NullablePartialNotebookCellInternalMetadata;
543
}
544
545
export interface ICellPartialInternalMetadataEditByHandle {
546
editType: CellEditType.PartialInternalMetadata;
547
handle: number;
548
internalMetadata: NullablePartialNotebookCellInternalMetadata;
549
}
550
551
export interface ICellLanguageEdit {
552
editType: CellEditType.CellLanguage;
553
index: number;
554
language: string;
555
}
556
557
export interface IDocumentMetadataEdit {
558
editType: CellEditType.DocumentMetadata;
559
metadata: NotebookDocumentMetadata;
560
}
561
562
export interface ICellMoveEdit {
563
editType: CellEditType.Move;
564
index: number;
565
length: number;
566
newIdx: number;
567
}
568
569
export type IImmediateCellEditOperation = ICellOutputEditByHandle | ICellPartialMetadataEditByHandle | ICellOutputItemEdit | ICellPartialInternalMetadataEdit | ICellPartialInternalMetadataEditByHandle | ICellPartialMetadataEdit;
570
export type ICellEditOperation = IImmediateCellEditOperation | ICellReplaceEdit | ICellOutputEdit | ICellMetadataEdit | ICellPartialMetadataEdit | ICellPartialInternalMetadataEdit | IDocumentMetadataEdit | ICellMoveEdit | ICellOutputItemEdit | ICellLanguageEdit;
571
572
573
export interface IWorkspaceNotebookCellEdit {
574
metadata?: WorkspaceEditMetadata;
575
resource: URI;
576
notebookVersionId: number | undefined;
577
cellEdit: ICellPartialMetadataEdit | IDocumentMetadataEdit | ICellReplaceEdit;
578
}
579
580
export interface IWorkspaceNotebookCellEditDto {
581
metadata?: WorkspaceEditMetadata;
582
resource: URI;
583
notebookVersionId: number | undefined;
584
cellEdit: ICellPartialMetadataEdit | IDocumentMetadataEdit | ICellReplaceEdit;
585
}
586
587
export interface NotebookData {
588
readonly cells: ICellDto2[];
589
readonly metadata: NotebookDocumentMetadata;
590
}
591
592
593
export interface INotebookContributionData {
594
extension?: ExtensionIdentifier;
595
providerDisplayName: string;
596
displayName: string;
597
filenamePattern: (string | glob.IRelativePattern | INotebookExclusiveDocumentFilter)[];
598
priority?: RegisteredEditorPriority;
599
}
600
601
export namespace NotebookMetadataUri {
602
export const scheme = Schemas.vscodeNotebookMetadata;
603
export function generate(notebook: URI): URI {
604
return generateMetadataUri(notebook);
605
}
606
export function parse(metadata: URI): URI | undefined {
607
return parseMetadataUri(metadata);
608
}
609
}
610
611
export namespace CellUri {
612
export const scheme = Schemas.vscodeNotebookCell;
613
export function generate(notebook: URI, handle: number): URI {
614
return generateUri(notebook, handle);
615
}
616
617
export function parse(cell: URI): { notebook: URI; handle: number } | undefined {
618
return parseUri(cell);
619
}
620
621
/**
622
* Generates a URI for a cell output in a notebook using the output ID.
623
* Used when URI should be opened as text in the editor.
624
*/
625
export function generateCellOutputUriWithId(notebook: URI, outputId?: string) {
626
return notebook.with({
627
scheme: Schemas.vscodeNotebookCellOutput,
628
query: new URLSearchParams({
629
openIn: 'editor',
630
outputId: outputId ?? '',
631
notebookScheme: notebook.scheme !== Schemas.file ? notebook.scheme : '',
632
}).toString()
633
});
634
}
635
/**
636
* Generates a URI for a cell output in a notebook using the output index.
637
* Used when URI should be opened in notebook editor.
638
*/
639
export function generateCellOutputUriWithIndex(notebook: URI, cellUri: URI, outputIndex: number): URI {
640
return notebook.with({
641
scheme: Schemas.vscodeNotebookCellOutput,
642
fragment: cellUri.fragment,
643
query: new URLSearchParams({
644
openIn: 'notebook',
645
outputIndex: String(outputIndex),
646
}).toString()
647
});
648
}
649
650
export function generateOutputEditorUri(notebook: URI, cellId: string, cellIndex: number, outputId: string, outputIndex: number): URI {
651
return notebook.with({
652
scheme: Schemas.vscodeNotebookCellOutput,
653
query: new URLSearchParams({
654
openIn: 'notebookOutputEditor',
655
notebook: notebook.toString(),
656
cellIndex: String(cellIndex),
657
outputId: outputId,
658
outputIndex: String(outputIndex),
659
}).toString()
660
});
661
}
662
663
export function parseCellOutputUri(uri: URI): { notebook: URI; openIn: string; outputId?: string; cellFragment?: string; outputIndex?: number; cellHandle?: number; cellIndex?: number } | undefined {
664
return extractCellOutputDetails(uri);
665
}
666
667
export function generateCellPropertyUri(notebook: URI, handle: number, scheme: string): URI {
668
return CellUri.generate(notebook, handle).with({ scheme: scheme });
669
}
670
671
export function parseCellPropertyUri(uri: URI, propertyScheme: string) {
672
if (uri.scheme !== propertyScheme) {
673
return undefined;
674
}
675
676
return CellUri.parse(uri.with({ scheme: scheme }));
677
}
678
}
679
680
const normalizeSlashes = (str: string) => isWindows ? str.replace(/\//g, '\\') : str;
681
682
interface IMimeTypeWithMatcher {
683
pattern: string;
684
matches: glob.ParsedPattern;
685
}
686
687
export class MimeTypeDisplayOrder {
688
private readonly order: IMimeTypeWithMatcher[];
689
690
constructor(
691
initialValue: readonly string[] = [],
692
private readonly defaultOrder = NOTEBOOK_DISPLAY_ORDER,
693
) {
694
this.order = [...new Set(initialValue)].map(pattern => ({
695
pattern,
696
matches: glob.parse(normalizeSlashes(pattern))
697
}));
698
}
699
700
/**
701
* Returns a sorted array of the input mimetypes.
702
*/
703
public sort(mimetypes: Iterable<string>): string[] {
704
const remaining = new Map(Iterable.map(mimetypes, m => [m, normalizeSlashes(m)]));
705
let sorted: string[] = [];
706
707
for (const { matches } of this.order) {
708
for (const [original, normalized] of remaining) {
709
if (matches(normalized)) {
710
sorted.push(original);
711
remaining.delete(original);
712
break;
713
}
714
}
715
}
716
717
if (remaining.size) {
718
sorted = sorted.concat([...remaining.keys()].sort(
719
(a, b) => this.defaultOrder.indexOf(a) - this.defaultOrder.indexOf(b),
720
));
721
}
722
723
return sorted;
724
}
725
726
/**
727
* Records that the user selected the given mimetype over the other
728
* possible mimetypes, prioritizing it for future reference.
729
*/
730
public prioritize(chosenMimetype: string, otherMimetypes: readonly string[]) {
731
const chosenIndex = this.findIndex(chosenMimetype);
732
if (chosenIndex === -1) {
733
// always first, nothing more to do
734
this.order.unshift({ pattern: chosenMimetype, matches: glob.parse(normalizeSlashes(chosenMimetype)) });
735
return;
736
}
737
738
// Get the other mimetypes that are before the chosenMimetype. Then, move
739
// them after it, retaining order.
740
const uniqueIndicies = new Set(otherMimetypes.map(m => this.findIndex(m, chosenIndex)));
741
uniqueIndicies.delete(-1);
742
const otherIndices = Array.from(uniqueIndicies).sort();
743
this.order.splice(chosenIndex + 1, 0, ...otherIndices.map(i => this.order[i]));
744
745
for (let oi = otherIndices.length - 1; oi >= 0; oi--) {
746
this.order.splice(otherIndices[oi], 1);
747
}
748
}
749
750
/**
751
* Gets an array of in-order mimetype preferences.
752
*/
753
public toArray() {
754
return this.order.map(o => o.pattern);
755
}
756
757
private findIndex(mimeType: string, maxIndex = this.order.length) {
758
const normalized = normalizeSlashes(mimeType);
759
for (let i = 0; i < maxIndex; i++) {
760
if (this.order[i].matches(normalized)) {
761
return i;
762
}
763
}
764
765
return -1;
766
}
767
}
768
769
interface IMutableSplice<T> extends ISplice<T> {
770
readonly toInsert: T[];
771
deleteCount: number;
772
}
773
774
export 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>[] {
775
const result: IMutableSplice<T>[] = [];
776
777
function pushSplice(start: number, deleteCount: number, toInsert: T[]): void {
778
if (deleteCount === 0 && toInsert.length === 0) {
779
return;
780
}
781
782
const latest = result[result.length - 1];
783
784
if (latest && latest.start + latest.deleteCount === start) {
785
latest.deleteCount += deleteCount;
786
latest.toInsert.push(...toInsert);
787
} else {
788
result.push({ start, deleteCount, toInsert });
789
}
790
}
791
792
let beforeIdx = 0;
793
let afterIdx = 0;
794
795
while (true) {
796
if (beforeIdx === before.length) {
797
pushSplice(beforeIdx, 0, after.slice(afterIdx));
798
break;
799
}
800
801
if (afterIdx === after.length) {
802
pushSplice(beforeIdx, before.length - beforeIdx, []);
803
break;
804
}
805
806
const beforeElement = before[beforeIdx];
807
const afterElement = after[afterIdx];
808
809
if (equal(beforeElement, afterElement)) {
810
// equal
811
beforeIdx += 1;
812
afterIdx += 1;
813
continue;
814
}
815
816
if (contains(afterElement)) {
817
// `afterElement` exists before, which means some elements before `afterElement` are deleted
818
pushSplice(beforeIdx, 1, []);
819
beforeIdx += 1;
820
} else {
821
// `afterElement` added
822
pushSplice(beforeIdx, 0, [afterElement]);
823
afterIdx += 1;
824
}
825
}
826
827
return result;
828
}
829
830
export interface ICellEditorViewState {
831
selections: editorCommon.ICursorState[];
832
}
833
834
export const NOTEBOOK_EDITOR_CURSOR_BOUNDARY = new RawContextKey<'none' | 'top' | 'bottom' | 'both'>('notebookEditorCursorAtBoundary', 'none');
835
836
export const NOTEBOOK_EDITOR_CURSOR_LINE_BOUNDARY = new RawContextKey<'none' | 'start' | 'end' | 'both'>('notebookEditorCursorAtLineBoundary', 'none');
837
838
export interface INotebookLoadOptions {
839
/**
840
* Go to disk bypassing any cache of the model if any.
841
*/
842
forceReadFromFile?: boolean;
843
/**
844
* If provided, the size of the file will be checked against the limits
845
* and an error will be thrown if any limit is exceeded.
846
*/
847
readonly limits?: IFileReadLimits;
848
}
849
850
export type NotebookEditorModelCreationOptions = {
851
limits?: IFileReadLimits;
852
scratchpad?: boolean;
853
viewType?: string;
854
};
855
856
export interface IResolvedNotebookEditorModel extends INotebookEditorModel {
857
notebook: NotebookTextModel;
858
}
859
860
export interface INotebookEditorModel extends IDisposable {
861
readonly onDidChangeDirty: Event<void>;
862
readonly onDidSave: Event<IWorkingCopySaveEvent>;
863
readonly onDidChangeOrphaned: Event<void>;
864
readonly onDidChangeReadonly: Event<void>;
865
readonly onDidRevertUntitled: Event<void>;
866
readonly resource: URI;
867
readonly viewType: string;
868
readonly notebook: INotebookTextModel | undefined;
869
readonly hasErrorState: boolean;
870
isResolved(): boolean;
871
isDirty(): boolean;
872
isModified(): boolean;
873
isReadonly(): boolean | IMarkdownString;
874
isOrphaned(): boolean;
875
hasAssociatedFilePath(): boolean;
876
load(options?: INotebookLoadOptions): Promise<IResolvedNotebookEditorModel>;
877
save(options?: ISaveOptions): Promise<boolean>;
878
saveAs(target: URI): Promise<IUntypedEditorInput | undefined>;
879
revert(options?: IRevertOptions): Promise<void>;
880
}
881
882
export interface INotebookDiffEditorModel extends IDisposable {
883
original: { notebook: NotebookTextModel; resource: URI; viewType: string };
884
modified: { notebook: NotebookTextModel; resource: URI; viewType: string };
885
}
886
887
export interface NotebookDocumentBackupData extends IWorkingCopyBackupMeta {
888
readonly viewType: string;
889
readonly backupId?: string;
890
readonly mtime?: number;
891
}
892
893
export enum NotebookEditorPriority {
894
default = 'default',
895
option = 'option',
896
}
897
898
export interface INotebookFindOptions {
899
regex?: boolean;
900
wholeWord?: boolean;
901
caseSensitive?: boolean;
902
wordSeparators?: string;
903
includeMarkupInput?: boolean;
904
includeMarkupPreview?: boolean;
905
includeCodeInput?: boolean;
906
includeOutput?: boolean;
907
findScope?: INotebookFindScope;
908
}
909
910
export interface INotebookFindScope {
911
findScopeType: NotebookFindScopeType;
912
selectedCellRanges?: ICellRange[];
913
selectedTextRanges?: Range[];
914
}
915
916
export enum NotebookFindScopeType {
917
Cells = 'cells',
918
Text = 'text',
919
None = 'none'
920
}
921
922
export interface INotebookExclusiveDocumentFilter {
923
include?: string | glob.IRelativePattern;
924
exclude?: string | glob.IRelativePattern;
925
}
926
927
export interface INotebookDocumentFilter {
928
viewType?: string | string[];
929
filenamePattern?: string | glob.IRelativePattern | INotebookExclusiveDocumentFilter;
930
}
931
932
//TODO@rebornix test
933
934
export function isDocumentExcludePattern(filenamePattern: string | glob.IRelativePattern | INotebookExclusiveDocumentFilter): filenamePattern is { include: string | glob.IRelativePattern; exclude: string | glob.IRelativePattern } {
935
const arg = filenamePattern as INotebookExclusiveDocumentFilter;
936
937
if ((typeof arg.include === 'string' || glob.isRelativePattern(arg.include))
938
&& (typeof arg.exclude === 'string' || glob.isRelativePattern(arg.exclude))) {
939
return true;
940
}
941
942
return false;
943
}
944
export function notebookDocumentFilterMatch(filter: INotebookDocumentFilter, viewType: string, resource: URI): boolean {
945
if (Array.isArray(filter.viewType) && filter.viewType.indexOf(viewType) >= 0) {
946
return true;
947
}
948
949
if (filter.viewType === viewType) {
950
return true;
951
}
952
953
if (filter.filenamePattern) {
954
const filenamePattern = isDocumentExcludePattern(filter.filenamePattern) ? filter.filenamePattern.include : (filter.filenamePattern as string | glob.IRelativePattern);
955
const excludeFilenamePattern = isDocumentExcludePattern(filter.filenamePattern) ? filter.filenamePattern.exclude : undefined;
956
957
if (glob.match(filenamePattern, basename(resource.fsPath).toLowerCase())) {
958
if (excludeFilenamePattern) {
959
if (glob.match(excludeFilenamePattern, basename(resource.fsPath).toLowerCase())) {
960
// should exclude
961
962
return false;
963
}
964
}
965
return true;
966
}
967
}
968
return false;
969
}
970
971
export interface INotebookCellStatusBarItemProvider {
972
viewType: string;
973
onDidChangeStatusBarItems?: Event<void>;
974
provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise<INotebookCellStatusBarItemList | undefined>;
975
}
976
977
978
export interface INotebookDiffResult {
979
cellsDiff: IDiffResult;
980
metadataChanged: boolean;
981
}
982
983
export interface INotebookCellStatusBarItem {
984
readonly alignment: CellStatusbarAlignment;
985
readonly priority?: number;
986
readonly text: string;
987
readonly color?: string | ThemeColor;
988
readonly backgroundColor?: string | ThemeColor;
989
readonly tooltip?: string | IMarkdownString;
990
readonly command?: string | Command;
991
readonly accessibilityInformation?: IAccessibilityInformation;
992
readonly opacity?: string;
993
readonly onlyShowWhenActive?: boolean;
994
}
995
996
export interface INotebookCellStatusBarItemList {
997
items: INotebookCellStatusBarItem[];
998
dispose?(): void;
999
}
1000
1001
export type ShowCellStatusBarType = 'hidden' | 'visible' | 'visibleAfterExecute';
1002
export const NotebookSetting = {
1003
displayOrder: 'notebook.displayOrder',
1004
cellToolbarLocation: 'notebook.cellToolbarLocation',
1005
cellToolbarVisibility: 'notebook.cellToolbarVisibility',
1006
showCellStatusBar: 'notebook.showCellStatusBar',
1007
cellExecutionTimeVerbosity: 'notebook.cellExecutionTimeVerbosity',
1008
textDiffEditorPreview: 'notebook.diff.enablePreview',
1009
diffOverviewRuler: 'notebook.diff.overviewRuler',
1010
experimentalInsertToolbarAlignment: 'notebook.experimental.insertToolbarAlignment',
1011
compactView: 'notebook.compactView',
1012
focusIndicator: 'notebook.cellFocusIndicator',
1013
insertToolbarLocation: 'notebook.insertToolbarLocation',
1014
globalToolbar: 'notebook.globalToolbar',
1015
stickyScrollEnabled: 'notebook.stickyScroll.enabled',
1016
stickyScrollMode: 'notebook.stickyScroll.mode',
1017
undoRedoPerCell: 'notebook.undoRedoPerCell',
1018
consolidatedOutputButton: 'notebook.consolidatedOutputButton',
1019
openOutputInPreviewEditor: 'notebook.output.openInPreviewEditor.enabled',
1020
showFoldingControls: 'notebook.showFoldingControls',
1021
dragAndDropEnabled: 'notebook.dragAndDropEnabled',
1022
cellEditorOptionsCustomizations: 'notebook.editorOptionsCustomizations',
1023
consolidatedRunButton: 'notebook.consolidatedRunButton',
1024
openGettingStarted: 'notebook.experimental.openGettingStarted',
1025
globalToolbarShowLabel: 'notebook.globalToolbarShowLabel',
1026
markupFontSize: 'notebook.markup.fontSize',
1027
markdownLineHeight: 'notebook.markdown.lineHeight',
1028
interactiveWindowCollapseCodeCells: 'interactiveWindow.collapseCellInputCode',
1029
outputScrollingDeprecated: 'notebook.experimental.outputScrolling',
1030
outputScrolling: 'notebook.output.scrolling',
1031
textOutputLineLimit: 'notebook.output.textLineLimit',
1032
LinkifyOutputFilePaths: 'notebook.output.linkifyFilePaths',
1033
minimalErrorRendering: 'notebook.output.minimalErrorRendering',
1034
formatOnSave: 'notebook.formatOnSave.enabled',
1035
insertFinalNewline: 'notebook.insertFinalNewline',
1036
defaultFormatter: 'notebook.defaultFormatter',
1037
formatOnCellExecution: 'notebook.formatOnCellExecution',
1038
codeActionsOnSave: 'notebook.codeActionsOnSave',
1039
outputWordWrap: 'notebook.output.wordWrap',
1040
outputLineHeightDeprecated: 'notebook.outputLineHeight',
1041
outputLineHeight: 'notebook.output.lineHeight',
1042
outputFontSizeDeprecated: 'notebook.outputFontSize',
1043
outputFontSize: 'notebook.output.fontSize',
1044
outputFontFamilyDeprecated: 'notebook.outputFontFamily',
1045
outputFontFamily: 'notebook.output.fontFamily',
1046
findFilters: 'notebook.find.filters',
1047
logging: 'notebook.logging',
1048
confirmDeleteRunningCell: 'notebook.confirmDeleteRunningCell',
1049
remoteSaving: 'notebook.experimental.remoteSave',
1050
gotoSymbolsAllSymbols: 'notebook.gotoSymbols.showAllSymbols',
1051
outlineShowMarkdownHeadersOnly: 'notebook.outline.showMarkdownHeadersOnly',
1052
outlineShowCodeCells: 'notebook.outline.showCodeCells',
1053
outlineShowCodeCellSymbols: 'notebook.outline.showCodeCellSymbols',
1054
breadcrumbsShowCodeCells: 'notebook.breadcrumbs.showCodeCells',
1055
scrollToRevealCell: 'notebook.scrolling.revealNextCellOnExecute',
1056
cellChat: 'notebook.experimental.cellChat',
1057
cellGenerate: 'notebook.experimental.generate',
1058
notebookVariablesView: 'notebook.variablesView',
1059
notebookInlineValues: 'notebook.inlineValues',
1060
InteractiveWindowPromptToSave: 'interactiveWindow.promptToSaveOnClose',
1061
cellFailureDiagnostics: 'notebook.cellFailureDiagnostics',
1062
outputBackupSizeLimit: 'notebook.backup.sizeLimit',
1063
multiCursor: 'notebook.multiCursor.enabled',
1064
markupFontFamily: 'notebook.markup.fontFamily',
1065
} as const;
1066
1067
export const enum CellStatusbarAlignment {
1068
Left = 1,
1069
Right = 2
1070
}
1071
1072
export class NotebookWorkingCopyTypeIdentifier {
1073
1074
private static _prefix = 'notebook/';
1075
1076
static create(notebookType: string, viewType?: string): string {
1077
return `${NotebookWorkingCopyTypeIdentifier._prefix}${notebookType}/${viewType ?? notebookType}`;
1078
}
1079
1080
static parse(candidate: string): { notebookType: string; viewType: string } | undefined {
1081
if (candidate.startsWith(NotebookWorkingCopyTypeIdentifier._prefix)) {
1082
const split = candidate.substring(NotebookWorkingCopyTypeIdentifier._prefix.length).split('/');
1083
if (split.length === 2) {
1084
return { notebookType: split[0], viewType: split[1] };
1085
}
1086
}
1087
return undefined;
1088
}
1089
}
1090
1091
export interface NotebookExtensionDescription {
1092
readonly id: ExtensionIdentifier;
1093
readonly location: UriComponents | undefined;
1094
}
1095
1096
const textDecoder = new TextDecoder();
1097
1098
/**
1099
* Given a stream of individual stdout outputs, this function will return the compressed lines, escaping some of the common terminal escape codes.
1100
* E.g. some terminal escape codes would result in the previous line getting cleared, such if we had 3 lines and
1101
* last line contained such a code, then the result string would be just the first two lines.
1102
* @returns a single VSBuffer with the concatenated and compressed data, and whether any compression was done.
1103
*/
1104
export function compressOutputItemStreams(outputs: Uint8Array[]) {
1105
const buffers: Uint8Array[] = [];
1106
let startAppending = false;
1107
1108
// Pick the first set of outputs with the same mime type.
1109
for (const output of outputs) {
1110
if ((buffers.length === 0 || startAppending)) {
1111
buffers.push(output);
1112
startAppending = true;
1113
}
1114
}
1115
1116
let didCompression = compressStreamBuffer(buffers);
1117
const concatenated = VSBuffer.concat(buffers.map(buffer => VSBuffer.wrap(buffer)));
1118
const data = formatStreamText(concatenated);
1119
didCompression = didCompression || data.byteLength !== concatenated.byteLength;
1120
return { data, didCompression };
1121
}
1122
1123
export const MOVE_CURSOR_1_LINE_COMMAND = `${String.fromCharCode(27)}[A`;
1124
const MOVE_CURSOR_1_LINE_COMMAND_BYTES = MOVE_CURSOR_1_LINE_COMMAND.split('').map(c => c.charCodeAt(0));
1125
const LINE_FEED = 10;
1126
function compressStreamBuffer(streams: Uint8Array[]) {
1127
let didCompress = false;
1128
streams.forEach((stream, index) => {
1129
if (index === 0 || stream.length < MOVE_CURSOR_1_LINE_COMMAND.length) {
1130
return;
1131
}
1132
1133
const previousStream = streams[index - 1];
1134
1135
// Remove the previous line if required.
1136
const command = stream.subarray(0, MOVE_CURSOR_1_LINE_COMMAND.length);
1137
if (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]) {
1138
const lastIndexOfLineFeed = previousStream.lastIndexOf(LINE_FEED);
1139
if (lastIndexOfLineFeed === -1) {
1140
return;
1141
}
1142
1143
didCompress = true;
1144
streams[index - 1] = previousStream.subarray(0, lastIndexOfLineFeed);
1145
streams[index] = stream.subarray(MOVE_CURSOR_1_LINE_COMMAND.length);
1146
}
1147
});
1148
return didCompress;
1149
}
1150
1151
1152
1153
/**
1154
* Took this from jupyter/notebook
1155
* https://github.com/jupyter/notebook/blob/b8b66332e2023e83d2ee04f83d8814f567e01a4e/notebook/static/base/js/utils.js
1156
* Remove characters that are overridden by backspace characters
1157
*/
1158
function fixBackspace(txt: string) {
1159
let tmp = txt;
1160
do {
1161
txt = tmp;
1162
// Cancel out anything-but-newline followed by backspace
1163
tmp = txt.replace(/[^\n]\x08/gm, '');
1164
} while (tmp.length < txt.length);
1165
return txt;
1166
}
1167
1168
/**
1169
* Remove chunks that should be overridden by the effect of carriage return characters
1170
* From https://github.com/jupyter/notebook/blob/master/notebook/static/base/js/utils.js
1171
*/
1172
function fixCarriageReturn(txt: string) {
1173
txt = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
1174
while (txt.search(/\r[^$]/g) > -1) {
1175
const base = txt.match(/^(.*)\r+/m)![1];
1176
let insert = txt.match(/\r+(.*)$/m)![1];
1177
insert = insert + base.slice(insert.length, base.length);
1178
txt = txt.replace(/\r+.*$/m, '\r').replace(/^.*\r/m, insert);
1179
}
1180
return txt;
1181
}
1182
1183
const BACKSPACE_CHARACTER = '\b'.charCodeAt(0);
1184
const CARRIAGE_RETURN_CHARACTER = '\r'.charCodeAt(0);
1185
function formatStreamText(buffer: VSBuffer): VSBuffer {
1186
// We have special handling for backspace and carriage return characters.
1187
// Don't unnecessary decode the bytes if we don't need to perform any processing.
1188
if (!buffer.buffer.includes(BACKSPACE_CHARACTER) && !buffer.buffer.includes(CARRIAGE_RETURN_CHARACTER)) {
1189
return buffer;
1190
}
1191
// Do the same thing jupyter is doing
1192
return VSBuffer.fromString(fixCarriageReturn(fixBackspace(textDecoder.decode(buffer.buffer))));
1193
}
1194
1195
export interface INotebookKernelSourceAction {
1196
readonly label: string;
1197
readonly description?: string;
1198
readonly detail?: string;
1199
readonly command?: string | Command;
1200
readonly documentation?: UriComponents | string;
1201
}
1202
1203