@theia/notebook 1.51.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 +23 -7
- package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-preferences.d.ts +3 -0
- package/lib/browser/contributions/notebook-preferences.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-preferences.js +9 -1
- package/lib/browser/contributions/notebook-preferences.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 +4 -1
- 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 +9 -8
- 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 +23 -8
- package/src/browser/contributions/notebook-preferences.ts +10 -1
- 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 +7 -3
- package/src/browser/service/notebook-context-manager.ts +8 -4
- package/src/browser/style/index.css +172 -5
- 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';
|
|
@@ -365,9 +366,23 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
365
366
|
execute: async (notebook?: NotebookModel, cell?: NotebookCellModel) => {
|
|
366
367
|
const selectedCell = cell ?? this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell;
|
|
367
368
|
const activeNotebook = notebook ?? this.notebookEditorWidgetService.focusedEditor?.model;
|
|
368
|
-
if (selectedCell
|
|
369
|
-
|
|
370
|
-
|
|
369
|
+
if (!selectedCell || !activeNotebook) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language);
|
|
373
|
+
if (!language?.value || language.value === 'autoDetect' || language.value.id === selectedCell.language) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
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
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
if (isMarkdownCell) {
|
|
384
|
+
changeCellType(activeNotebook, selectedCell, CellKind.Code, language.value.id);
|
|
385
|
+
} else {
|
|
371
386
|
this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{
|
|
372
387
|
editType: CellEditType.CellLanguage,
|
|
373
388
|
index: activeNotebook.cells.indexOf(selectedCell),
|
|
@@ -411,12 +426,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
411
426
|
{
|
|
412
427
|
command: NotebookCellCommands.EDIT_COMMAND.id,
|
|
413
428
|
keybinding: 'Enter',
|
|
414
|
-
when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
|
|
429
|
+
when: `!editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
|
|
415
430
|
},
|
|
416
431
|
{
|
|
417
432
|
command: NotebookCellCommands.STOP_EDIT_COMMAND.id,
|
|
418
433
|
keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(),
|
|
419
|
-
when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}`,
|
|
434
|
+
when: `editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED}`,
|
|
420
435
|
},
|
|
421
436
|
{
|
|
422
437
|
command: NotebookCellCommands.STOP_EDIT_COMMAND.id,
|
|
@@ -426,12 +441,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command
|
|
|
426
441
|
{
|
|
427
442
|
command: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id,
|
|
428
443
|
keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.CtrlCmd] }).toString(),
|
|
429
|
-
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'`,
|
|
430
445
|
},
|
|
431
446
|
{
|
|
432
447
|
command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND.id,
|
|
433
448
|
keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Shift] }).toString(),
|
|
434
|
-
when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
|
|
449
|
+
when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
|
|
435
450
|
},
|
|
436
451
|
{
|
|
437
452
|
command: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id,
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
*--------------------------------------------------------------------------------------------*/
|
|
20
20
|
|
|
21
21
|
import { nls } from '@theia/core';
|
|
22
|
-
import {
|
|
22
|
+
import { interfaces } from '@theia/core/shared/inversify';
|
|
23
|
+
import { PreferenceContribution, PreferenceSchema } from '@theia/core/lib/browser';
|
|
23
24
|
|
|
24
25
|
export namespace NotebookPreferences {
|
|
25
26
|
export const NOTEBOOK_LINE_NUMBERS = 'notebook.lineNumbers';
|
|
@@ -81,3 +82,11 @@ export const notebookPreferenceSchema: PreferenceSchema = {
|
|
|
81
82
|
|
|
82
83
|
}
|
|
83
84
|
};
|
|
85
|
+
|
|
86
|
+
export const NotebookPreferenceContribution = Symbol('NotebookPreferenceContribution');
|
|
87
|
+
|
|
88
|
+
export function bindNotebookPreferences(bind: interfaces.Bind): void {
|
|
89
|
+
// We don't need a NotebookPreferenceConfiguration class, so there's no preference proxy to bind
|
|
90
|
+
bind(NotebookPreferenceContribution).toConstantValue({ schema: notebookPreferenceSchema });
|
|
91
|
+
bind(PreferenceContribution).toService(NotebookPreferenceContribution);
|
|
92
|
+
}
|
|
@@ -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,
|
|
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';
|
|
@@ -44,8 +44,9 @@ import { NotebookOutlineContribution } from './contributions/notebook-outline-co
|
|
|
44
44
|
import { NotebookLabelProviderContribution } from './contributions/notebook-label-provider-contribution';
|
|
45
45
|
import { NotebookOutputActionContribution } from './contributions/notebook-output-action-contribution';
|
|
46
46
|
import { NotebookClipboardService } from './service/notebook-clipboard-service';
|
|
47
|
-
import {
|
|
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();
|
|
@@ -106,6 +107,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|
|
106
107
|
bind(NotebookLabelProviderContribution).toSelf().inSingletonScope();
|
|
107
108
|
bind(LabelProviderContribution).toService(NotebookLabelProviderContribution);
|
|
108
109
|
|
|
109
|
-
bind
|
|
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 {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
:root {
|
|
18
18
|
--theia-notebook-markdown-size: 17px;
|
|
19
|
+
--theia-notebook-cell-editor-margin-right: 10px;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
.theia-notebook-cell-list {
|
|
@@ -61,22 +62,39 @@
|
|
|
61
62
|
width: calc(100% - 15px);
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
/* Rendered Markdown Content */
|
|
66
|
+
|
|
64
67
|
.theia-notebook-markdown-content {
|
|
65
68
|
padding: 8px 16px 8px 36px;
|
|
66
69
|
font-size: var(--theia-notebook-markdown-size);
|
|
67
70
|
}
|
|
68
71
|
|
|
69
|
-
.theia-notebook-markdown-content
|
|
72
|
+
.theia-notebook-markdown-content>* {
|
|
73
|
+
font-weight: 400;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.theia-notebook-markdown-content>*:first-child {
|
|
70
77
|
margin-top: 0;
|
|
71
78
|
padding-top: 0;
|
|
72
79
|
}
|
|
73
80
|
|
|
74
|
-
.theia-notebook-markdown-content
|
|
75
|
-
.theia-notebook-markdown-content > *:last-child {
|
|
81
|
+
.theia-notebook-markdown-content>*:last-child {
|
|
76
82
|
margin-bottom: 0;
|
|
77
83
|
padding-bottom: 0;
|
|
78
84
|
}
|
|
79
85
|
|
|
86
|
+
/* Markdown cell edit mode */
|
|
87
|
+
.theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) {
|
|
88
|
+
margin-left: 37px;
|
|
89
|
+
margin-right: var(--theia-notebook-cell-editor-margin-right);
|
|
90
|
+
outline: 1px solid var(--theia-notebook-cellBorderColor);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Markdown cell edit mode focused */
|
|
94
|
+
.theia-notebook-cell.focused .theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) {
|
|
95
|
+
outline-color: var(--theia-notebook-focusedEditorBorder);
|
|
96
|
+
}
|
|
97
|
+
|
|
80
98
|
.theia-notebook-empty-markdown {
|
|
81
99
|
opacity: 0.6;
|
|
82
100
|
}
|
|
@@ -189,8 +207,7 @@
|
|
|
189
207
|
|
|
190
208
|
.theia-notebook-main-container .theia-notebook-main-loading-indicator {
|
|
191
209
|
/* `progress-animation` is defined in `packages/core/src/browser/style/progress-bar.css` */
|
|
192
|
-
animation: progress-animation 1.8s 0s infinite
|
|
193
|
-
cubic-bezier(0.645, 0.045, 0.355, 1);
|
|
210
|
+
animation: progress-animation 1.8s 0s infinite cubic-bezier(0.645, 0.045, 0.355, 1);
|
|
194
211
|
background-color: var(--theia-progressBar-background);
|
|
195
212
|
height: 2px;
|
|
196
213
|
}
|
|
@@ -298,3 +315,153 @@
|
|
|
298
315
|
min-height: 100px;
|
|
299
316
|
background-color: var(--theia-editor-background);
|
|
300
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 {
|