@theia/notebook 1.52.0 → 1.53.0-next.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/contributions/cell-operations.d.ts +1 -1
- package/lib/browser/contributions/cell-operations.d.ts.map +1 -1
- package/lib/browser/contributions/cell-operations.js +10 -2
- package/lib/browser/contributions/cell-operations.js.map +1 -1
- package/lib/browser/contributions/notebook-actions-contribution.d.ts +2 -0
- package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-actions-contribution.js +43 -24
- package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +4 -0
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.js +61 -16
- package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-status-bar-contribution.d.ts +14 -0
- package/lib/browser/contributions/notebook-status-bar-contribution.d.ts.map +1 -0
- package/lib/browser/contributions/notebook-status-bar-contribution.js +75 -0
- package/lib/browser/contributions/notebook-status-bar-contribution.js.map +1 -0
- package/lib/browser/contributions/notebook-undo-redo-handler.d.ts +10 -0
- package/lib/browser/contributions/notebook-undo-redo-handler.d.ts.map +1 -0
- package/lib/browser/contributions/notebook-undo-redo-handler.js +49 -0
- package/lib/browser/contributions/notebook-undo-redo-handler.js.map +1 -0
- package/lib/browser/notebook-editor-widget.d.ts +6 -0
- package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
- package/lib/browser/notebook-editor-widget.js +40 -4
- package/lib/browser/notebook-editor-widget.js.map +1 -1
- package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
- package/lib/browser/notebook-frontend-module.js +6 -0
- package/lib/browser/notebook-frontend-module.js.map +1 -1
- package/lib/browser/service/notebook-context-manager.d.ts +2 -1
- package/lib/browser/service/notebook-context-manager.d.ts.map +1 -1
- package/lib/browser/service/notebook-context-manager.js +6 -3
- package/lib/browser/service/notebook-context-manager.js.map +1 -1
- package/lib/browser/service/notebook-options.d.ts +1 -0
- package/lib/browser/service/notebook-options.d.ts.map +1 -1
- package/lib/browser/service/notebook-options.js +1 -0
- package/lib/browser/service/notebook-options.js.map +1 -1
- package/lib/browser/service/notebook-service.d.ts +1 -0
- package/lib/browser/service/notebook-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-service.js +7 -0
- package/lib/browser/service/notebook-service.js.map +1 -1
- package/lib/browser/view/notebook-cell-editor.d.ts +8 -1
- package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-editor.js +89 -5
- package/lib/browser/view/notebook-cell-editor.js.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.d.ts +3 -0
- package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.js +33 -2
- package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
- package/lib/browser/view/notebook-code-cell-view.d.ts +1 -1
- package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-code-cell-view.js +19 -17
- package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
- package/lib/browser/view/notebook-find-widget.d.ts +63 -0
- package/lib/browser/view/notebook-find-widget.d.ts.map +1 -0
- package/lib/browser/view/notebook-find-widget.js +225 -0
- package/lib/browser/view/notebook-find-widget.js.map +1 -0
- package/lib/browser/view/notebook-markdown-cell-view.d.ts +4 -0
- package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-markdown-cell-view.js +105 -8
- package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
- package/lib/browser/view-model/notebook-cell-model.d.ts +27 -1
- package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-cell-model.js +76 -1
- package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
- package/lib/browser/view-model/notebook-model.d.ts +8 -1
- package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-model.js +66 -14
- package/lib/browser/view-model/notebook-model.js.map +1 -1
- package/package.json +8 -7
- package/src/browser/contributions/cell-operations.ts +8 -2
- package/src/browser/contributions/notebook-actions-contribution.ts +47 -22
- package/src/browser/contributions/notebook-cell-actions-contribution.ts +65 -17
- package/src/browser/contributions/notebook-status-bar-contribution.ts +77 -0
- package/src/browser/contributions/notebook-undo-redo-handler.ts +41 -0
- package/src/browser/notebook-editor-widget.tsx +50 -5
- package/src/browser/notebook-frontend-module.ts +9 -1
- package/src/browser/service/notebook-context-manager.ts +8 -4
- package/src/browser/service/notebook-options.ts +2 -1
- package/src/browser/service/notebook-service.ts +7 -1
- package/src/browser/style/index.css +165 -6
- package/src/browser/view/notebook-cell-editor.tsx +94 -5
- package/src/browser/view/notebook-cell-list-view.tsx +40 -4
- package/src/browser/view/notebook-code-cell-view.tsx +19 -17
- package/src/browser/view/notebook-find-widget.tsx +335 -0
- package/src/browser/view/notebook-markdown-cell-view.tsx +134 -17
- package/src/browser/view-model/notebook-cell-model.ts +101 -8
- package/src/browser/view-model/notebook-model.ts +77 -20
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
margin: 10px 0px;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
.theia-notebook-cell:focus {
|
|
35
|
+
outline: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
34
38
|
.theia-notebook-cell.draggable {
|
|
35
39
|
cursor: grab;
|
|
36
40
|
}
|
|
@@ -62,31 +66,36 @@
|
|
|
62
66
|
width: calc(100% - 15px);
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
/* Rendered Markdown Content */
|
|
70
|
+
|
|
65
71
|
.theia-notebook-markdown-content {
|
|
66
72
|
padding: 8px 16px 8px 36px;
|
|
67
73
|
font-size: var(--theia-notebook-markdown-size);
|
|
68
74
|
}
|
|
69
75
|
|
|
76
|
+
.theia-notebook-markdown-content>* {
|
|
77
|
+
font-weight: 400;
|
|
78
|
+
}
|
|
79
|
+
|
|
70
80
|
.theia-notebook-markdown-content>*:first-child {
|
|
71
81
|
margin-top: 0;
|
|
72
82
|
padding-top: 0;
|
|
73
83
|
}
|
|
74
84
|
|
|
75
|
-
.theia-notebook-markdown-content>*:only-child,
|
|
76
85
|
.theia-notebook-markdown-content>*:last-child {
|
|
77
86
|
margin-bottom: 0;
|
|
78
87
|
padding-bottom: 0;
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
/* Markdown cell edit mode */
|
|
82
|
-
.theia-notebook-cell-content:has(
|
|
83
|
-
margin-left:
|
|
91
|
+
.theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) {
|
|
92
|
+
margin-left: 36px;
|
|
84
93
|
margin-right: var(--theia-notebook-cell-editor-margin-right);
|
|
85
94
|
outline: 1px solid var(--theia-notebook-cellBorderColor);
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
/* Markdown cell edit mode focused */
|
|
89
|
-
.theia-notebook-cell.focused .theia-notebook-cell-content:has(
|
|
98
|
+
.theia-notebook-cell.focused .theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) {
|
|
90
99
|
outline-color: var(--theia-notebook-focusedEditorBorder);
|
|
91
100
|
}
|
|
92
101
|
|
|
@@ -102,7 +111,7 @@
|
|
|
102
111
|
width: calc(100% - 46px);
|
|
103
112
|
flex: 1;
|
|
104
113
|
outline: 1px solid var(--theia-notebook-cellBorderColor);
|
|
105
|
-
margin: 0px 10px;
|
|
114
|
+
margin: 0px 16px 0px 10px;
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
.theia-notebook-cell.focused .theia-notebook-cell-editor-container {
|
|
@@ -283,7 +292,7 @@
|
|
|
283
292
|
|
|
284
293
|
.theia-notebook-cell-output-webview {
|
|
285
294
|
padding: 5px 0px;
|
|
286
|
-
margin: 0px
|
|
295
|
+
margin: 0px 15px 0px 9px;
|
|
287
296
|
width: 100%;
|
|
288
297
|
}
|
|
289
298
|
|
|
@@ -310,3 +319,153 @@
|
|
|
310
319
|
min-height: 100px;
|
|
311
320
|
background-color: var(--theia-editor-background);
|
|
312
321
|
}
|
|
322
|
+
|
|
323
|
+
/* Notebook Find Widget */
|
|
324
|
+
|
|
325
|
+
.theia-notebook-overlay {
|
|
326
|
+
position: absolute;
|
|
327
|
+
z-index: 100;
|
|
328
|
+
right: 18px;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.theia-notebook-find-widget {
|
|
332
|
+
/* position: absolute;
|
|
333
|
+
z-index: 35;
|
|
334
|
+
height: 33px;
|
|
335
|
+
overflow: hidden; */
|
|
336
|
+
line-height: 19px;
|
|
337
|
+
transition: transform 200ms linear;
|
|
338
|
+
display: flex;
|
|
339
|
+
flex-direction: row;
|
|
340
|
+
padding: 0 4px;
|
|
341
|
+
box-sizing: border-box;
|
|
342
|
+
box-shadow: 0 0 8px 2px var(--theia-widget-shadow);
|
|
343
|
+
background-color: var(--theia-editorWidget-background);
|
|
344
|
+
color: var(--theia-editorWidget-foreground);
|
|
345
|
+
border-left: 1px solid var(--theia-widget-border);
|
|
346
|
+
border-right: 1px solid var(--theia-widget-border);
|
|
347
|
+
border-bottom: 1px solid var(--theia-widget-border);
|
|
348
|
+
border-bottom-left-radius: 4px;
|
|
349
|
+
border-bottom-right-radius: 4px;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.theia-notebook-find-widget.hidden {
|
|
353
|
+
display: none;
|
|
354
|
+
transform: translateY(calc(-100% - 10px));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.theia-notebook-find-widget.search-mode>*>*:nth-child(2) {
|
|
358
|
+
display: none;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.theia-notebook-find-widget-expand {
|
|
362
|
+
display: flex;
|
|
363
|
+
flex-direction: row;
|
|
364
|
+
align-items: center;
|
|
365
|
+
cursor: pointer;
|
|
366
|
+
border-radius: 0;
|
|
367
|
+
margin-right: 4px;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.theia-notebook-find-widget-expand:focus {
|
|
371
|
+
outline: 1px solid var(--theia-focusBorder);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.theia-notebook-find-widget-expand:hover {
|
|
375
|
+
background-color: var(--theia-toolbar-hoverBackground);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.theia-notebook-find-widget-buttons-first {
|
|
379
|
+
margin-bottom: 4px;
|
|
380
|
+
height: 26px;
|
|
381
|
+
display: flex;
|
|
382
|
+
flex-direction: row;
|
|
383
|
+
align-items: center;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.theia-notebook-find-widget-buttons-first>div,
|
|
387
|
+
.theia-notebook-find-widget-buttons-second>div {
|
|
388
|
+
margin-right: 4px;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.theia-notebook-find-widget-buttons-second {
|
|
392
|
+
height: 26px;
|
|
393
|
+
display: flex;
|
|
394
|
+
flex-direction: row;
|
|
395
|
+
align-items: center;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.theia-notebook-find-widget-inputs {
|
|
399
|
+
margin-top: 4px;
|
|
400
|
+
display: flex;
|
|
401
|
+
flex-direction: column;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.theia-notebook-find-widget-buttons {
|
|
405
|
+
margin-top: 4px;
|
|
406
|
+
margin-left: 4px;
|
|
407
|
+
display: flex;
|
|
408
|
+
flex-direction: column;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.theia-notebook-find-widget-matches-count {
|
|
412
|
+
width: 72px;
|
|
413
|
+
box-sizing: border-box;
|
|
414
|
+
overflow: hidden;
|
|
415
|
+
text-align: center;
|
|
416
|
+
text-overflow: ellipsis;
|
|
417
|
+
white-space: nowrap;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.theia-notebook-find-widget-input-wrapper {
|
|
421
|
+
display: flex;
|
|
422
|
+
align-items: center;
|
|
423
|
+
background: var(--theia-input-background);
|
|
424
|
+
border-style: solid;
|
|
425
|
+
border-width: var(--theia-border-width);
|
|
426
|
+
border-color: var(--theia-input-background);
|
|
427
|
+
border-radius: 2px;
|
|
428
|
+
margin-bottom: 4px;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.theia-notebook-find-widget-input-wrapper:focus-within {
|
|
432
|
+
border-color: var(--theia-focusBorder);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.theia-notebook-find-widget-input-wrapper .option.enabled {
|
|
436
|
+
color: var(--theia-inputOption-activeForeground);
|
|
437
|
+
outline: 1px solid var(--theia-inputOption-activeBorder);
|
|
438
|
+
background-color: var(--theia-inputOption-activeBackground);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.theia-notebook-find-widget-input-wrapper .option {
|
|
442
|
+
margin: 2px;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.theia-notebook-find-widget-input-wrapper .theia-notebook-find-widget-input:focus {
|
|
446
|
+
border: none;
|
|
447
|
+
outline: none;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.theia-notebook-find-widget-input-wrapper .theia-notebook-find-widget-input {
|
|
451
|
+
background: none;
|
|
452
|
+
border: none;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.theia-notebook-find-widget-replace {
|
|
456
|
+
margin-bottom: 4px;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.theia-notebook-find-widget-buttons .disabled {
|
|
460
|
+
opacity: 0.5;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
mark.theia-find-match {
|
|
464
|
+
color: var(--theia-editor-findMatchHighlightForeground);
|
|
465
|
+
background-color: var(--theia-editor-findMatchHighlightBackground);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
mark.theia-find-match.theia-find-match-selected {
|
|
469
|
+
color: var(--theia-editor-findMatchForeground);
|
|
470
|
+
background-color: var(--theia-editor-findMatchBackground);
|
|
471
|
+
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import * as React from '@theia/core/shared/react';
|
|
18
18
|
import { NotebookModel } from '../view-model/notebook-model';
|
|
19
|
-
import { NotebookCellModel } from '../view-model/notebook-cell-model';
|
|
19
|
+
import { NotebookCellModel, NotebookCodeEditorFindMatch } from '../view-model/notebook-cell-model';
|
|
20
20
|
import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
|
|
21
21
|
import { MonacoEditor, MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor';
|
|
22
22
|
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
|
@@ -27,6 +27,9 @@ import { NotebookViewportService } from './notebook-viewport-service';
|
|
|
27
27
|
import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo';
|
|
28
28
|
import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE } from '../contributions/notebook-context-keys';
|
|
29
29
|
import { EditorExtensionsRegistry } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorExtensions';
|
|
30
|
+
import { ModelDecorationOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/model/textModel';
|
|
31
|
+
import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from '@theia/monaco-editor-core/esm/vs/editor/common/model';
|
|
32
|
+
import { animationFrame } from '@theia/core/lib/browser';
|
|
30
33
|
|
|
31
34
|
interface CellEditorProps {
|
|
32
35
|
notebookModel: NotebookModel,
|
|
@@ -48,18 +51,46 @@ const DEFAULT_EDITOR_OPTIONS: MonacoEditor.IOptions = {
|
|
|
48
51
|
lineDecorationsWidth: 10,
|
|
49
52
|
};
|
|
50
53
|
|
|
54
|
+
export const CURRENT_FIND_MATCH_DECORATION = ModelDecorationOptions.register({
|
|
55
|
+
description: 'current-find-match',
|
|
56
|
+
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
|
57
|
+
zIndex: 13,
|
|
58
|
+
className: 'currentFindMatch',
|
|
59
|
+
inlineClassName: 'currentFindMatchInline',
|
|
60
|
+
showIfCollapsed: true,
|
|
61
|
+
overviewRuler: {
|
|
62
|
+
color: 'editorOverviewRuler.findMatchForeground',
|
|
63
|
+
position: OverviewRulerLane.Center
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
export const FIND_MATCH_DECORATION = ModelDecorationOptions.register({
|
|
68
|
+
description: 'find-match',
|
|
69
|
+
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
|
70
|
+
zIndex: 10,
|
|
71
|
+
className: 'findMatch',
|
|
72
|
+
inlineClassName: 'findMatchInline',
|
|
73
|
+
showIfCollapsed: true,
|
|
74
|
+
overviewRuler: {
|
|
75
|
+
color: 'editorOverviewRuler.findMatchForeground',
|
|
76
|
+
position: OverviewRulerLane.Center
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
51
80
|
export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
52
81
|
|
|
53
82
|
protected editor?: SimpleMonacoEditor;
|
|
54
83
|
protected toDispose = new DisposableCollection();
|
|
55
84
|
protected container?: HTMLDivElement;
|
|
85
|
+
protected matches: NotebookCodeEditorFindMatch[] = [];
|
|
86
|
+
protected oldMatchDecorations: string[] = [];
|
|
56
87
|
|
|
57
88
|
override componentDidMount(): void {
|
|
58
89
|
this.disposeEditor();
|
|
59
90
|
this.toDispose.push(this.props.cell.onWillFocusCellEditor(focusRequest => {
|
|
60
91
|
this.editor?.getControl().focus();
|
|
61
92
|
const lineCount = this.editor?.getControl().getModel()?.getLineCount();
|
|
62
|
-
if (focusRequest && lineCount) {
|
|
93
|
+
if (focusRequest && lineCount !== undefined) {
|
|
63
94
|
this.editor?.getControl().setPosition(focusRequest === 'lastLine' ?
|
|
64
95
|
{ lineNumber: lineCount, column: 1 } :
|
|
65
96
|
{ lineNumber: focusRequest, column: 1 },
|
|
@@ -68,7 +99,16 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
68
99
|
const currentLine = this.editor?.getControl().getPosition()?.lineNumber;
|
|
69
100
|
this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_FIRST_LINE, currentLine === 1);
|
|
70
101
|
this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, currentLine === lineCount);
|
|
102
|
+
}));
|
|
71
103
|
|
|
104
|
+
this.toDispose.push(this.props.cell.onWillBlurCellEditor(() => {
|
|
105
|
+
let parent = this.container?.parentElement;
|
|
106
|
+
while (parent && !parent.classList.contains('theia-notebook-cell')) {
|
|
107
|
+
parent = parent.parentElement;
|
|
108
|
+
}
|
|
109
|
+
if (parent) {
|
|
110
|
+
parent.focus();
|
|
111
|
+
}
|
|
72
112
|
}));
|
|
73
113
|
|
|
74
114
|
this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => {
|
|
@@ -79,6 +119,13 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
79
119
|
this.editor?.setLanguage(language);
|
|
80
120
|
}));
|
|
81
121
|
|
|
122
|
+
this.toDispose.push(this.props.cell.onDidFindMatches(matches => {
|
|
123
|
+
this.matches = matches;
|
|
124
|
+
animationFrame().then(() => this.setMatches());
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
this.toDispose.push(this.props.cell.onDidSelectFindMatch(match => this.centerEditorInView()));
|
|
128
|
+
|
|
82
129
|
this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => {
|
|
83
130
|
if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) {
|
|
84
131
|
this.props.notebookContextManager.context?.focus();
|
|
@@ -95,6 +142,10 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
95
142
|
});
|
|
96
143
|
this.toDispose.push(disposable);
|
|
97
144
|
}
|
|
145
|
+
|
|
146
|
+
this.toDispose.push(this.props.cell.onDidRequestCenterEditor(() => {
|
|
147
|
+
this.centerEditorInView();
|
|
148
|
+
}));
|
|
98
149
|
}
|
|
99
150
|
|
|
100
151
|
override componentWillUnmount(): void {
|
|
@@ -106,6 +157,21 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
106
157
|
this.toDispose = new DisposableCollection();
|
|
107
158
|
}
|
|
108
159
|
|
|
160
|
+
protected centerEditorInView(): void {
|
|
161
|
+
const editorDomNode = this.editor?.getControl().getDomNode();
|
|
162
|
+
if (editorDomNode) {
|
|
163
|
+
editorDomNode.scrollIntoView({
|
|
164
|
+
behavior: 'instant',
|
|
165
|
+
block: 'center'
|
|
166
|
+
});
|
|
167
|
+
} else {
|
|
168
|
+
this.container?.scrollIntoView({
|
|
169
|
+
behavior: 'instant',
|
|
170
|
+
block: 'center'
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
109
175
|
protected async initEditor(): Promise<void> {
|
|
110
176
|
const { cell, notebookModel, monacoServices } = this.props;
|
|
111
177
|
if (this.container) {
|
|
@@ -130,11 +196,11 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
130
196
|
notebookModel.cellDirtyChanged(cell, true);
|
|
131
197
|
}));
|
|
132
198
|
this.toDispose.push(this.editor.getControl().onDidFocusEditorText(() => {
|
|
133
|
-
this.props.notebookContextManager.onDidEditorTextFocus(true);
|
|
134
199
|
this.props.notebookModel.setSelectedCell(cell, false);
|
|
135
200
|
}));
|
|
136
|
-
this.toDispose.push(this.editor.getControl().
|
|
137
|
-
this.
|
|
201
|
+
this.toDispose.push(this.editor.getControl().onDidChangeCursorSelection(e => {
|
|
202
|
+
const selectedText = this.editor!.getControl().getModel()!.getValueInRange(e.selection);
|
|
203
|
+
this.props.notebookModel.selectedText = selectedText;
|
|
138
204
|
}));
|
|
139
205
|
this.toDispose.push(this.editor.getControl().onDidChangeCursorPosition(e => {
|
|
140
206
|
if (e.secondaryPositions.length === 0) {
|
|
@@ -149,9 +215,32 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
149
215
|
if (cell.editing && notebookModel.selectedCell === cell) {
|
|
150
216
|
this.editor.getControl().focus();
|
|
151
217
|
}
|
|
218
|
+
this.setMatches();
|
|
152
219
|
}
|
|
153
220
|
}
|
|
154
221
|
|
|
222
|
+
protected setMatches(): void {
|
|
223
|
+
if (!this.editor) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const decorations: IModelDeltaDecoration[] = [];
|
|
227
|
+
for (const match of this.matches) {
|
|
228
|
+
const decoration = match.selected ? CURRENT_FIND_MATCH_DECORATION : FIND_MATCH_DECORATION;
|
|
229
|
+
decorations.push({
|
|
230
|
+
range: {
|
|
231
|
+
startLineNumber: match.range.start.line,
|
|
232
|
+
startColumn: match.range.start.character,
|
|
233
|
+
endLineNumber: match.range.end.line,
|
|
234
|
+
endColumn: match.range.end.character
|
|
235
|
+
},
|
|
236
|
+
options: decoration
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this.oldMatchDecorations = this.editor.getControl()
|
|
241
|
+
.changeDecorations(accessor => accessor.deltaDecorations(this.oldMatchDecorations, decorations));
|
|
242
|
+
}
|
|
243
|
+
|
|
155
244
|
protected setContainer(component: HTMLDivElement | null): void {
|
|
156
245
|
this.container = component ?? undefined;
|
|
157
246
|
};
|
|
@@ -14,14 +14,15 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
import * as React from '@theia/core/shared/react';
|
|
17
|
-
import { CellEditType, CellKind } from '../../common';
|
|
17
|
+
import { CellEditType, CellKind, NotebookCellsChangeType } from '../../common';
|
|
18
18
|
import { NotebookCellModel } from '../view-model/notebook-cell-model';
|
|
19
19
|
import { NotebookModel } from '../view-model/notebook-model';
|
|
20
20
|
import { NotebookCellToolbarFactory } from './notebook-cell-toolbar-factory';
|
|
21
|
-
import { codicon } from '@theia/core/lib/browser';
|
|
21
|
+
import { animationFrame, codicon, onDomEvent } from '@theia/core/lib/browser';
|
|
22
22
|
import { CommandRegistry, DisposableCollection, nls } from '@theia/core';
|
|
23
23
|
import { NotebookCommands } from '../contributions/notebook-actions-contribution';
|
|
24
24
|
import { NotebookCellActionContribution } from '../contributions/notebook-cell-actions-contribution';
|
|
25
|
+
import { NotebookContextManager } from '../service/notebook-context-manager';
|
|
25
26
|
|
|
26
27
|
export interface CellRenderer {
|
|
27
28
|
render(notebookData: NotebookModel, cell: NotebookCellModel, index: number): React.ReactNode
|
|
@@ -31,6 +32,7 @@ export interface CellRenderer {
|
|
|
31
32
|
interface CellListProps {
|
|
32
33
|
renderers: Map<CellKind, CellRenderer>;
|
|
33
34
|
notebookModel: NotebookModel;
|
|
35
|
+
notebookContext: NotebookContextManager;
|
|
34
36
|
toolbarRenderer: NotebookCellToolbarFactory;
|
|
35
37
|
commandRegistry: CommandRegistry
|
|
36
38
|
}
|
|
@@ -46,6 +48,7 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
46
48
|
protected toDispose = new DisposableCollection();
|
|
47
49
|
|
|
48
50
|
protected static dragGhost: HTMLElement | undefined;
|
|
51
|
+
protected cellListRef: React.RefObject<HTMLUListElement> = React.createRef();
|
|
49
52
|
|
|
50
53
|
constructor(props: CellListProps) {
|
|
51
54
|
super(props);
|
|
@@ -66,6 +69,13 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
66
69
|
}
|
|
67
70
|
}));
|
|
68
71
|
|
|
72
|
+
this.toDispose.push(props.notebookModel.onDidChangeContent(events => {
|
|
73
|
+
if (events.some(e => e.kind === NotebookCellsChangeType.Move)) {
|
|
74
|
+
// When a cell has been moved, we need to rerender the whole component
|
|
75
|
+
this.forceUpdate();
|
|
76
|
+
}
|
|
77
|
+
}));
|
|
78
|
+
|
|
69
79
|
this.toDispose.push(props.notebookModel.onDidChangeSelectedCell(e => {
|
|
70
80
|
this.setState({
|
|
71
81
|
...this.state,
|
|
@@ -73,6 +83,24 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
73
83
|
scrollIntoView: e.scrollIntoView
|
|
74
84
|
});
|
|
75
85
|
}));
|
|
86
|
+
|
|
87
|
+
this.toDispose.push(onDomEvent(document, 'focusin', () => {
|
|
88
|
+
animationFrame().then(() => {
|
|
89
|
+
if (!this.cellListRef.current) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
let hasCellFocus = false;
|
|
93
|
+
let hasFocus = false;
|
|
94
|
+
if (this.cellListRef.current.contains(document.activeElement)) {
|
|
95
|
+
if (this.props.notebookModel.selectedCell) {
|
|
96
|
+
hasCellFocus = true;
|
|
97
|
+
}
|
|
98
|
+
hasFocus = true;
|
|
99
|
+
}
|
|
100
|
+
this.props.notebookContext.changeCellFocus(hasCellFocus);
|
|
101
|
+
this.props.notebookContext.changeCellListFocus(hasFocus);
|
|
102
|
+
});
|
|
103
|
+
}));
|
|
76
104
|
}
|
|
77
105
|
|
|
78
106
|
override componentWillUnmount(): void {
|
|
@@ -80,7 +108,7 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
80
108
|
}
|
|
81
109
|
|
|
82
110
|
override render(): React.ReactNode {
|
|
83
|
-
return <ul className='theia-notebook-cell-list'>
|
|
111
|
+
return <ul className='theia-notebook-cell-list' ref={this.cellListRef}>
|
|
84
112
|
{this.props.notebookModel.cells
|
|
85
113
|
.map((cell, index) =>
|
|
86
114
|
<React.Fragment key={'cell-' + cell.handle}>
|
|
@@ -103,7 +131,15 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
103
131
|
onDragOver={e => this.onDragOver(e, cell)}
|
|
104
132
|
onDrop={e => this.onDrop(e, index)}
|
|
105
133
|
draggable={true}
|
|
106
|
-
|
|
134
|
+
tabIndex={-1}
|
|
135
|
+
ref={ref => {
|
|
136
|
+
if (ref && cell === this.state.selectedCell && this.state.scrollIntoView) {
|
|
137
|
+
ref.scrollIntoView({ block: 'nearest' });
|
|
138
|
+
if (cell.cellKind === CellKind.Markup && !cell.editing) {
|
|
139
|
+
ref.focus();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}}>
|
|
107
143
|
<div className={'theia-notebook-cell-marker' + (this.state.selectedCell === cell ? ' theia-notebook-cell-marker-selected' : '')}></div>
|
|
108
144
|
<div className='theia-notebook-cell-content'>
|
|
109
145
|
{this.renderCellContent(cell, index)}
|
|
@@ -150,7 +150,7 @@ export interface NotebookCodeCellStatusProps {
|
|
|
150
150
|
notebook: NotebookModel;
|
|
151
151
|
cell: NotebookCellModel;
|
|
152
152
|
commandRegistry: CommandRegistry;
|
|
153
|
-
executionStateService
|
|
153
|
+
executionStateService?: NotebookExecutionStateService;
|
|
154
154
|
onClick: () => void;
|
|
155
155
|
}
|
|
156
156
|
|
|
@@ -171,22 +171,24 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
|
|
|
171
171
|
};
|
|
172
172
|
|
|
173
173
|
let currentInterval: NodeJS.Timeout | undefined;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
174
|
+
if (props.executionStateService) {
|
|
175
|
+
this.toDispose.push(props.executionStateService.onDidChangeExecution(event => {
|
|
176
|
+
if (event.affectsCell(this.props.cell.uri)) {
|
|
177
|
+
this.setState({ currentExecution: event.changed, executionTime: 0 });
|
|
178
|
+
clearInterval(currentInterval);
|
|
179
|
+
if (event.changed?.state === NotebookCellExecutionState.Executing) {
|
|
180
|
+
const startTime = Date.now();
|
|
181
|
+
// The resolution of the time display is only a single digit after the decimal point.
|
|
182
|
+
// Therefore, we only need to update the display every 100ms.
|
|
183
|
+
currentInterval = setInterval(() => {
|
|
184
|
+
this.setState({
|
|
185
|
+
executionTime: Date.now() - startTime
|
|
186
|
+
});
|
|
187
|
+
}, 100);
|
|
188
|
+
}
|
|
187
189
|
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
190
192
|
|
|
191
193
|
this.toDispose.push(props.cell.onDidChangeLanguage(() => {
|
|
192
194
|
this.forceUpdate();
|
|
@@ -200,7 +202,7 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
|
|
|
200
202
|
override render(): React.ReactNode {
|
|
201
203
|
return <div className='notebook-cell-status' onClick={() => this.props.onClick()}>
|
|
202
204
|
<div className='notebook-cell-status-left'>
|
|
203
|
-
{this.renderExecutionState()}
|
|
205
|
+
{this.props.executionStateService && this.renderExecutionState()}
|
|
204
206
|
</div>
|
|
205
207
|
<div className='notebook-cell-status-right'>
|
|
206
208
|
<span className='notebook-cell-language-label' onClick={() => {
|