@theia/notebook 1.52.0 → 1.53.0-next.4
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 +3 -2
- package/lib/browser/contributions/cell-operations.js.map +1 -1
- package/lib/browser/contributions/notebook-actions-contribution.d.ts +1 -0
- package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-actions-contribution.js +17 -20
- package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.js +21 -13
- package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
- 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 +39 -1
- 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 +3 -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/view/notebook-cell-editor.d.ts +7 -1
- package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-editor.js +75 -5
- package/lib/browser/view/notebook-cell-editor.js.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.d.ts +2 -0
- package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-list-view.js +22 -1
- 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 +24 -1
- package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-cell-model.js +71 -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 +3 -2
- package/src/browser/contributions/notebook-actions-contribution.ts +20 -20
- package/src/browser/contributions/notebook-cell-actions-contribution.ts +22 -14
- package/src/browser/contributions/notebook-undo-redo-handler.ts +41 -0
- package/src/browser/notebook-editor-widget.tsx +49 -2
- package/src/browser/notebook-frontend-module.ts +5 -1
- package/src/browser/service/notebook-context-manager.ts +8 -4
- package/src/browser/style/index.css +158 -3
- package/src/browser/view/notebook-cell-editor.tsx +79 -6
- package/src/browser/view/notebook-cell-list-view.tsx +29 -3
- 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 +94 -8
- package/src/browser/view-model/notebook-model.ts +77 -20
|
@@ -22,7 +22,8 @@ import { NotebookCellModel } from '../view-model/notebook-cell-model';
|
|
|
22
22
|
import {
|
|
23
23
|
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE,
|
|
24
24
|
NotebookContextKeys, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_EDITOR_FOCUSED,
|
|
25
|
-
NOTEBOOK_CELL_FOCUSED
|
|
25
|
+
NOTEBOOK_CELL_FOCUSED,
|
|
26
|
+
NOTEBOOK_CELL_LIST_FOCUSED
|
|
26
27
|
} from './notebook-context-keys';
|
|
27
28
|
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
|
28
29
|
import { NotebookExecutionService } from '../service/notebook-execution-service';
|
|
@@ -369,18 +370,25 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
369
370
|
return;
|
|
370
371
|
}
|
|
371
372
|
const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language);
|
|
372
|
-
if (!language?.value || language.value === 'autoDetect') {
|
|
373
|
+
if (!language?.value || language.value === 'autoDetect' || language.value.id === selectedCell.language) {
|
|
373
374
|
return;
|
|
374
375
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
376
|
+
const isMarkdownCell = selectedCell.cellKind === CellKind.Markup;
|
|
377
|
+
const isMarkdownLanguage = language.value.id === 'markdown';
|
|
378
|
+
if (isMarkdownLanguage) {
|
|
379
|
+
if (!isMarkdownCell) {
|
|
380
|
+
changeCellType(activeNotebook, selectedCell, CellKind.Markup, language.value.id);
|
|
381
|
+
}
|
|
378
382
|
} else {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
383
|
+
if (isMarkdownCell) {
|
|
384
|
+
changeCellType(activeNotebook, selectedCell, CellKind.Code, language.value.id);
|
|
385
|
+
} else {
|
|
386
|
+
this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{
|
|
387
|
+
editType: CellEditType.CellLanguage,
|
|
388
|
+
index: activeNotebook.cells.indexOf(selectedCell),
|
|
389
|
+
language: language.value.id
|
|
390
|
+
}], true);
|
|
391
|
+
}
|
|
384
392
|
}
|
|
385
393
|
}
|
|
386
394
|
});
|
|
@@ -418,12 +426,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
418
426
|
{
|
|
419
427
|
command: NotebookCellCommands.EDIT_COMMAND.id,
|
|
420
428
|
keybinding: 'Enter',
|
|
421
|
-
when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
|
|
429
|
+
when: `!editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
|
|
422
430
|
},
|
|
423
431
|
{
|
|
424
432
|
command: NotebookCellCommands.STOP_EDIT_COMMAND.id,
|
|
425
433
|
keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(),
|
|
426
|
-
when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}`,
|
|
434
|
+
when: `editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED}`,
|
|
427
435
|
},
|
|
428
436
|
{
|
|
429
437
|
command: NotebookCellCommands.STOP_EDIT_COMMAND.id,
|
|
@@ -433,12 +441,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
433
441
|
{
|
|
434
442
|
command: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id,
|
|
435
443
|
keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.CtrlCmd] }).toString(),
|
|
436
|
-
when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`,
|
|
444
|
+
when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`,
|
|
437
445
|
},
|
|
438
446
|
{
|
|
439
447
|
command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND.id,
|
|
440
448
|
keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Shift] }).toString(),
|
|
441
|
-
when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
|
|
449
|
+
when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
|
|
442
450
|
},
|
|
443
451
|
{
|
|
444
452
|
command: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 TypeFox and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
18
|
+
import { ApplicationShell, UndoRedoHandler } from '@theia/core/lib/browser';
|
|
19
|
+
import { NotebookEditorWidget } from '../notebook-editor-widget';
|
|
20
|
+
|
|
21
|
+
@injectable()
|
|
22
|
+
export class NotebookUndoRedoHandler implements UndoRedoHandler<NotebookEditorWidget> {
|
|
23
|
+
|
|
24
|
+
@inject(ApplicationShell)
|
|
25
|
+
protected readonly applicationShell: ApplicationShell;
|
|
26
|
+
|
|
27
|
+
priority = 200;
|
|
28
|
+
select(): NotebookEditorWidget | undefined {
|
|
29
|
+
const current = this.applicationShell.currentWidget;
|
|
30
|
+
if (current instanceof NotebookEditorWidget) {
|
|
31
|
+
return current;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
undo(item: NotebookEditorWidget): void {
|
|
36
|
+
item.undo();
|
|
37
|
+
}
|
|
38
|
+
redo(item: NotebookEditorWidget): void {
|
|
39
|
+
item.redo();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -33,6 +33,8 @@ import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
|
|
|
33
33
|
import { NotebookContextManager } from './service/notebook-context-manager';
|
|
34
34
|
import { NotebookViewportService } from './view/notebook-viewport-service';
|
|
35
35
|
import { NotebookCellCommands } from './contributions/notebook-cell-actions-contribution';
|
|
36
|
+
import { NotebookFindWidget } from './view/notebook-find-widget';
|
|
37
|
+
import debounce = require('lodash/debounce');
|
|
36
38
|
const PerfectScrollbar = require('react-perfect-scrollbar');
|
|
37
39
|
|
|
38
40
|
export const NotebookEditorWidgetContainerFactory = Symbol('NotebookEditorWidgetContainerFactory');
|
|
@@ -126,7 +128,16 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
|
|
|
126
128
|
protected readonly renderers = new Map<CellKind, CellRenderer>();
|
|
127
129
|
protected _model?: NotebookModel;
|
|
128
130
|
protected _ready: Deferred<NotebookModel> = new Deferred();
|
|
131
|
+
protected _findWidgetVisible = false;
|
|
132
|
+
protected _findWidgetRef = React.createRef<NotebookFindWidget>();
|
|
129
133
|
protected scrollBarRef = React.createRef<{ updateScroll(): void }>();
|
|
134
|
+
protected debounceFind = debounce(() => {
|
|
135
|
+
this._findWidgetRef.current?.search({});
|
|
136
|
+
}, 30, {
|
|
137
|
+
trailing: true,
|
|
138
|
+
maxWait: 100,
|
|
139
|
+
leading: false
|
|
140
|
+
});
|
|
130
141
|
|
|
131
142
|
get notebookType(): string {
|
|
132
143
|
return this.props.notebookType;
|
|
@@ -177,6 +188,11 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
|
|
|
177
188
|
// Wait one frame to ensure that the content has been rendered
|
|
178
189
|
animationFrame().then(() => this.scrollBarRef.current?.updateScroll());
|
|
179
190
|
}));
|
|
191
|
+
this.toDispose.push(this._model.onContentChanged(() => {
|
|
192
|
+
if (this._findWidgetVisible) {
|
|
193
|
+
this.debounceFind();
|
|
194
|
+
}
|
|
195
|
+
}));
|
|
180
196
|
this.toDispose.push(this._model.onDidChangeReadOnly(readOnly => {
|
|
181
197
|
if (readOnly) {
|
|
182
198
|
lock(this.title);
|
|
@@ -220,18 +236,41 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
|
|
|
220
236
|
protected render(): ReactNode {
|
|
221
237
|
if (this._model) {
|
|
222
238
|
return <div className='theia-notebook-main-container'>
|
|
239
|
+
<div className='theia-notebook-overlay'>
|
|
240
|
+
<NotebookFindWidget
|
|
241
|
+
ref={this._findWidgetRef}
|
|
242
|
+
hidden={!this._findWidgetVisible}
|
|
243
|
+
onClose={() => {
|
|
244
|
+
this._findWidgetVisible = false;
|
|
245
|
+
this._model?.findMatches({
|
|
246
|
+
activeFilters: [],
|
|
247
|
+
matchCase: false,
|
|
248
|
+
regex: false,
|
|
249
|
+
search: '',
|
|
250
|
+
wholeWord: false
|
|
251
|
+
});
|
|
252
|
+
this.update();
|
|
253
|
+
}}
|
|
254
|
+
onSearch={options => this._model?.findMatches(options) ?? []}
|
|
255
|
+
onReplace={(matches, replaceText) => this._model?.replaceAll(matches, replaceText)}
|
|
256
|
+
/>
|
|
257
|
+
</div>
|
|
223
258
|
{this.notebookMainToolbarRenderer.render(this._model, this.node)}
|
|
224
|
-
<div
|
|
259
|
+
<div
|
|
260
|
+
className='theia-notebook-viewport'
|
|
261
|
+
ref={(ref: HTMLDivElement) => this.viewportService.viewportElement = ref}
|
|
262
|
+
>
|
|
225
263
|
<PerfectScrollbar className='theia-notebook-scroll-container'
|
|
226
264
|
ref={this.scrollBarRef}
|
|
227
265
|
onScrollY={(e: HTMLDivElement) => this.viewportService.onScroll(e)}>
|
|
228
266
|
<NotebookCellListView renderers={this.renderers}
|
|
229
267
|
notebookModel={this._model}
|
|
268
|
+
notebookContext={this.notebookContextManager}
|
|
230
269
|
toolbarRenderer={this.cellToolbarFactory}
|
|
231
270
|
commandRegistry={this.commandRegistry} />
|
|
232
271
|
</PerfectScrollbar>
|
|
233
272
|
</div>
|
|
234
|
-
</div
|
|
273
|
+
</div>;
|
|
235
274
|
} else {
|
|
236
275
|
return <div className='theia-notebook-main-container'>
|
|
237
276
|
<div className='theia-notebook-main-loading-indicator'></div>
|
|
@@ -260,6 +299,14 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
|
|
|
260
299
|
this.onDidChangeOutputInputFocusEmitter.fire(focused);
|
|
261
300
|
}
|
|
262
301
|
|
|
302
|
+
showFindWidget(): void {
|
|
303
|
+
if (!this._findWidgetVisible) {
|
|
304
|
+
this._findWidgetVisible = true;
|
|
305
|
+
this.update();
|
|
306
|
+
}
|
|
307
|
+
this._findWidgetRef.current?.focusSearch(this._model?.selectedText);
|
|
308
|
+
}
|
|
309
|
+
|
|
263
310
|
override dispose(): void {
|
|
264
311
|
this.notebookContextManager.dispose();
|
|
265
312
|
this.onDidChangeModelEmitter.dispose();
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import '../../src/browser/style/index.css';
|
|
17
17
|
|
|
18
18
|
import { ContainerModule } from '@theia/core/shared/inversify';
|
|
19
|
-
import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, WidgetFactory } from '@theia/core/lib/browser';
|
|
19
|
+
import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, UndoRedoHandler, WidgetFactory } from '@theia/core/lib/browser';
|
|
20
20
|
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
|
|
21
21
|
import { NotebookOpenHandler } from './notebook-open-handler';
|
|
22
22
|
import { CommandContribution, MenuContribution, ResourceResolver, } from '@theia/core';
|
|
@@ -46,6 +46,7 @@ import { NotebookOutputActionContribution } from './contributions/notebook-outpu
|
|
|
46
46
|
import { NotebookClipboardService } from './service/notebook-clipboard-service';
|
|
47
47
|
import { bindNotebookPreferences } from './contributions/notebook-preferences';
|
|
48
48
|
import { NotebookOptionsService } from './service/notebook-options';
|
|
49
|
+
import { NotebookUndoRedoHandler } from './contributions/notebook-undo-redo-handler';
|
|
49
50
|
|
|
50
51
|
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
51
52
|
bind(NotebookColorContribution).toSelf().inSingletonScope();
|
|
@@ -108,4 +109,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
|
108
109
|
|
|
109
110
|
bindNotebookPreferences(bind);
|
|
110
111
|
bind(NotebookOptionsService).toSelf().inSingletonScope();
|
|
112
|
+
|
|
113
|
+
bind(NotebookUndoRedoHandler).toSelf().inSingletonScope();
|
|
114
|
+
bind(UndoRedoHandler).toService(NotebookUndoRedoHandler);
|
|
111
115
|
});
|
|
@@ -21,7 +21,7 @@ import { NotebookKernelService } from './notebook-kernel-service';
|
|
|
21
21
|
import {
|
|
22
22
|
NOTEBOOK_CELL_EDITABLE,
|
|
23
23
|
NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE,
|
|
24
|
-
NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE,
|
|
24
|
+
NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE,
|
|
25
25
|
NOTEBOOK_CELL_TYPE, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED,
|
|
26
26
|
NOTEBOOK_OUTPUT_INPUT_FOCUSED,
|
|
27
27
|
NOTEBOOK_VIEW_TYPE
|
|
@@ -85,7 +85,7 @@ export class NotebookContextManager {
|
|
|
85
85
|
|
|
86
86
|
this.scopedStore.setContext(NOTEBOOK_HAS_OUTPUTS, !!widget.model?.cells.find(cell => cell.outputs.length > 0));
|
|
87
87
|
|
|
88
|
-
// Cell Selection
|
|
88
|
+
// Cell Selection related keys
|
|
89
89
|
this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!widget.model?.selectedCell);
|
|
90
90
|
widget.model?.onDidChangeSelectedCell(e => {
|
|
91
91
|
this.selectedCellChanged(e.cell);
|
|
@@ -144,8 +144,12 @@ export class NotebookContextManager {
|
|
|
144
144
|
return this.contextKeyService.createOverlay(Object.entries(this.cellContexts.get(cellHandle) ?? {}));
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
this.scopedStore.setContext(
|
|
147
|
+
changeCellFocus(focus: boolean): void {
|
|
148
|
+
this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, focus);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
changeCellListFocus(focus: boolean): void {
|
|
152
|
+
this.scopedStore.setContext(NOTEBOOK_CELL_LIST_FOCUSED, focus);
|
|
149
153
|
}
|
|
150
154
|
|
|
151
155
|
createContextKeyChangedEvent(affectedKeys: string[]): ContextKeyChangeEvent {
|
|
@@ -62,31 +62,36 @@
|
|
|
62
62
|
width: calc(100% - 15px);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/* Rendered Markdown Content */
|
|
66
|
+
|
|
65
67
|
.theia-notebook-markdown-content {
|
|
66
68
|
padding: 8px 16px 8px 36px;
|
|
67
69
|
font-size: var(--theia-notebook-markdown-size);
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
.theia-notebook-markdown-content>* {
|
|
73
|
+
font-weight: 400;
|
|
74
|
+
}
|
|
75
|
+
|
|
70
76
|
.theia-notebook-markdown-content>*:first-child {
|
|
71
77
|
margin-top: 0;
|
|
72
78
|
padding-top: 0;
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
.theia-notebook-markdown-content>*:only-child,
|
|
76
81
|
.theia-notebook-markdown-content>*:last-child {
|
|
77
82
|
margin-bottom: 0;
|
|
78
83
|
padding-bottom: 0;
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
/* Markdown cell edit mode */
|
|
82
|
-
.theia-notebook-cell-content:has(
|
|
87
|
+
.theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) {
|
|
83
88
|
margin-left: 37px;
|
|
84
89
|
margin-right: var(--theia-notebook-cell-editor-margin-right);
|
|
85
90
|
outline: 1px solid var(--theia-notebook-cellBorderColor);
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
/* Markdown cell edit mode focused */
|
|
89
|
-
.theia-notebook-cell.focused .theia-notebook-cell-content:has(
|
|
94
|
+
.theia-notebook-cell.focused .theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) {
|
|
90
95
|
outline-color: var(--theia-notebook-focusedEditorBorder);
|
|
91
96
|
}
|
|
92
97
|
|
|
@@ -310,3 +315,153 @@
|
|
|
310
315
|
min-height: 100px;
|
|
311
316
|
background-color: var(--theia-editor-background);
|
|
312
317
|
}
|
|
318
|
+
|
|
319
|
+
/* Notebook Find Widget */
|
|
320
|
+
|
|
321
|
+
.theia-notebook-overlay {
|
|
322
|
+
position: absolute;
|
|
323
|
+
z-index: 100;
|
|
324
|
+
right: 18px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.theia-notebook-find-widget {
|
|
328
|
+
/* position: absolute;
|
|
329
|
+
z-index: 35;
|
|
330
|
+
height: 33px;
|
|
331
|
+
overflow: hidden; */
|
|
332
|
+
line-height: 19px;
|
|
333
|
+
transition: transform 200ms linear;
|
|
334
|
+
display: flex;
|
|
335
|
+
flex-direction: row;
|
|
336
|
+
padding: 0 4px;
|
|
337
|
+
box-sizing: border-box;
|
|
338
|
+
box-shadow: 0 0 8px 2px var(--theia-widget-shadow);
|
|
339
|
+
background-color: var(--theia-editorWidget-background);
|
|
340
|
+
color: var(--theia-editorWidget-foreground);
|
|
341
|
+
border-left: 1px solid var(--theia-widget-border);
|
|
342
|
+
border-right: 1px solid var(--theia-widget-border);
|
|
343
|
+
border-bottom: 1px solid var(--theia-widget-border);
|
|
344
|
+
border-bottom-left-radius: 4px;
|
|
345
|
+
border-bottom-right-radius: 4px;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.theia-notebook-find-widget.hidden {
|
|
349
|
+
display: none;
|
|
350
|
+
transform: translateY(calc(-100% - 10px));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.theia-notebook-find-widget.search-mode > * > *:nth-child(2) {
|
|
354
|
+
display: none;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.theia-notebook-find-widget-expand {
|
|
358
|
+
display: flex;
|
|
359
|
+
flex-direction: row;
|
|
360
|
+
align-items: center;
|
|
361
|
+
cursor: pointer;
|
|
362
|
+
border-radius: 0;
|
|
363
|
+
margin-right: 4px;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.theia-notebook-find-widget-expand:focus {
|
|
367
|
+
outline: 1px solid var(--theia-focusBorder);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.theia-notebook-find-widget-expand:hover {
|
|
371
|
+
background-color: var(--theia-toolbar-hoverBackground);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.theia-notebook-find-widget-buttons-first {
|
|
375
|
+
margin-bottom: 4px;
|
|
376
|
+
height: 26px;
|
|
377
|
+
display: flex;
|
|
378
|
+
flex-direction: row;
|
|
379
|
+
align-items: center;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.theia-notebook-find-widget-buttons-first > div,
|
|
383
|
+
.theia-notebook-find-widget-buttons-second > div {
|
|
384
|
+
margin-right: 4px;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.theia-notebook-find-widget-buttons-second {
|
|
388
|
+
height: 26px;
|
|
389
|
+
display: flex;
|
|
390
|
+
flex-direction: row;
|
|
391
|
+
align-items: center;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.theia-notebook-find-widget-inputs {
|
|
395
|
+
margin-top: 4px;
|
|
396
|
+
display: flex;
|
|
397
|
+
flex-direction: column;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.theia-notebook-find-widget-buttons {
|
|
401
|
+
margin-top: 4px;
|
|
402
|
+
margin-left: 4px;
|
|
403
|
+
display: flex;
|
|
404
|
+
flex-direction: column;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.theia-notebook-find-widget-matches-count {
|
|
408
|
+
width: 72px;
|
|
409
|
+
box-sizing: border-box;
|
|
410
|
+
overflow: hidden;
|
|
411
|
+
text-align: center;
|
|
412
|
+
text-overflow: ellipsis;
|
|
413
|
+
white-space: nowrap;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.theia-notebook-find-widget-input-wrapper {
|
|
417
|
+
display: flex;
|
|
418
|
+
align-items: center;
|
|
419
|
+
background: var(--theia-input-background);
|
|
420
|
+
border-style: solid;
|
|
421
|
+
border-width: var(--theia-border-width);
|
|
422
|
+
border-color: var(--theia-input-background);
|
|
423
|
+
border-radius: 2px;
|
|
424
|
+
margin-bottom: 4px;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.theia-notebook-find-widget-input-wrapper:focus-within {
|
|
428
|
+
border-color: var(--theia-focusBorder);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.theia-notebook-find-widget-input-wrapper .option.enabled {
|
|
432
|
+
color: var(--theia-inputOption-activeForeground);
|
|
433
|
+
outline: 1px solid var(--theia-inputOption-activeBorder);
|
|
434
|
+
background-color: var(--theia-inputOption-activeBackground);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.theia-notebook-find-widget-input-wrapper .option {
|
|
438
|
+
margin: 2px;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.theia-notebook-find-widget-input-wrapper .theia-notebook-find-widget-input:focus {
|
|
442
|
+
border: none;
|
|
443
|
+
outline: none;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.theia-notebook-find-widget-input-wrapper .theia-notebook-find-widget-input {
|
|
447
|
+
background: none;
|
|
448
|
+
border: none;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.theia-notebook-find-widget-replace {
|
|
452
|
+
margin-bottom: 4px;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.theia-notebook-find-widget-buttons .disabled {
|
|
456
|
+
opacity: 0.5;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
mark.theia-find-match {
|
|
460
|
+
color: var(--theia-editor-findMatchHighlightForeground);
|
|
461
|
+
background-color: var(--theia-editor-findMatchHighlightBackground);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
mark.theia-find-match.theia-find-match-selected {
|
|
465
|
+
color: var(--theia-editor-findMatchForeground);
|
|
466
|
+
background-color: var(--theia-editor-findMatchBackground);
|
|
467
|
+
}
|
|
@@ -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,6 @@ 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);
|
|
71
|
-
|
|
72
102
|
}));
|
|
73
103
|
|
|
74
104
|
this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => {
|
|
@@ -79,6 +109,26 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
79
109
|
this.editor?.setLanguage(language);
|
|
80
110
|
}));
|
|
81
111
|
|
|
112
|
+
this.toDispose.push(this.props.cell.onDidFindMatches(matches => {
|
|
113
|
+
this.matches = matches;
|
|
114
|
+
animationFrame().then(() => this.setMatches());
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
this.toDispose.push(this.props.cell.onDidSelectFindMatch(match => {
|
|
118
|
+
const editorDomNode = this.editor?.getControl().getDomNode();
|
|
119
|
+
if (editorDomNode) {
|
|
120
|
+
editorDomNode.scrollIntoView({
|
|
121
|
+
behavior: 'instant',
|
|
122
|
+
block: 'center'
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
this.container?.scrollIntoView({
|
|
126
|
+
behavior: 'instant',
|
|
127
|
+
block: 'center'
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}));
|
|
131
|
+
|
|
82
132
|
this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => {
|
|
83
133
|
if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) {
|
|
84
134
|
this.props.notebookContextManager.context?.focus();
|
|
@@ -130,11 +180,11 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
130
180
|
notebookModel.cellDirtyChanged(cell, true);
|
|
131
181
|
}));
|
|
132
182
|
this.toDispose.push(this.editor.getControl().onDidFocusEditorText(() => {
|
|
133
|
-
this.props.notebookContextManager.onDidEditorTextFocus(true);
|
|
134
183
|
this.props.notebookModel.setSelectedCell(cell, false);
|
|
135
184
|
}));
|
|
136
|
-
this.toDispose.push(this.editor.getControl().
|
|
137
|
-
this.
|
|
185
|
+
this.toDispose.push(this.editor.getControl().onDidChangeCursorSelection(e => {
|
|
186
|
+
const selectedText = this.editor!.getControl().getModel()!.getValueInRange(e.selection);
|
|
187
|
+
this.props.notebookModel.selectedText = selectedText;
|
|
138
188
|
}));
|
|
139
189
|
this.toDispose.push(this.editor.getControl().onDidChangeCursorPosition(e => {
|
|
140
190
|
if (e.secondaryPositions.length === 0) {
|
|
@@ -149,7 +199,30 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
|
|
|
149
199
|
if (cell.editing && notebookModel.selectedCell === cell) {
|
|
150
200
|
this.editor.getControl().focus();
|
|
151
201
|
}
|
|
202
|
+
this.setMatches();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
protected setMatches(): void {
|
|
207
|
+
if (!this.editor) {
|
|
208
|
+
return;
|
|
152
209
|
}
|
|
210
|
+
const decorations: IModelDeltaDecoration[] = [];
|
|
211
|
+
for (const match of this.matches) {
|
|
212
|
+
const decoration = match.selected ? CURRENT_FIND_MATCH_DECORATION : FIND_MATCH_DECORATION;
|
|
213
|
+
decorations.push({
|
|
214
|
+
range: {
|
|
215
|
+
startLineNumber: match.range.start.line,
|
|
216
|
+
startColumn: match.range.start.character,
|
|
217
|
+
endLineNumber: match.range.end.line,
|
|
218
|
+
endColumn: match.range.end.character
|
|
219
|
+
},
|
|
220
|
+
options: decoration
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this.oldMatchDecorations = this.editor.getControl()
|
|
225
|
+
.changeDecorations(accessor => accessor.deltaDecorations(this.oldMatchDecorations, decorations));
|
|
153
226
|
}
|
|
154
227
|
|
|
155
228
|
protected setContainer(component: HTMLDivElement | null): void {
|
|
@@ -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
|
}
|
|
@@ -66,6 +68,13 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
66
68
|
}
|
|
67
69
|
}));
|
|
68
70
|
|
|
71
|
+
this.toDispose.push(props.notebookModel.onDidChangeContent(events => {
|
|
72
|
+
if (events.some(e => e.kind === NotebookCellsChangeType.Move)) {
|
|
73
|
+
// When a cell has been moved, we need to rerender the whole component
|
|
74
|
+
this.forceUpdate();
|
|
75
|
+
}
|
|
76
|
+
}));
|
|
77
|
+
|
|
69
78
|
this.toDispose.push(props.notebookModel.onDidChangeSelectedCell(e => {
|
|
70
79
|
this.setState({
|
|
71
80
|
...this.state,
|
|
@@ -80,7 +89,24 @@ export class NotebookCellListView extends React.Component<CellListProps, Noteboo
|
|
|
80
89
|
}
|
|
81
90
|
|
|
82
91
|
override render(): React.ReactNode {
|
|
83
|
-
return <ul className='theia-notebook-cell-list'
|
|
92
|
+
return <ul className='theia-notebook-cell-list' ref={
|
|
93
|
+
ref => {
|
|
94
|
+
this.toDispose.push(onDomEvent(document, 'focusin', () => {
|
|
95
|
+
animationFrame().then(() => {
|
|
96
|
+
let hasCellFocus = false;
|
|
97
|
+
let hasFocus = false;
|
|
98
|
+
if (ref?.contains(document.activeElement)) {
|
|
99
|
+
if (this.props.notebookModel.selectedCell) {
|
|
100
|
+
hasCellFocus = true;
|
|
101
|
+
}
|
|
102
|
+
hasFocus = true;
|
|
103
|
+
}
|
|
104
|
+
this.props.notebookContext.changeCellFocus(hasCellFocus);
|
|
105
|
+
this.props.notebookContext.changeCellListFocus(hasFocus);
|
|
106
|
+
});
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
}>
|
|
84
110
|
{this.props.notebookModel.cells
|
|
85
111
|
.map((cell, index) =>
|
|
86
112
|
<React.Fragment key={'cell-' + cell.handle}>
|