Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions zeppelin-web-angular/e2e/models/editor-search-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,22 @@ export class EditorSearchPage extends BasePage {
this.matchesCount = this.findWidget.locator('.matchesCount').first();
// Monaco decorates every match with .findMatch and the active one with .currentFindMatch.
this.matchHighlights = this.editor.locator('.findMatch, .currentFindMatch');
this.nextMatchButton = this.findWidget.locator('.button.next, [title^="Next Match"]').first();
this.previousMatchButton = this.findWidget.locator('.button.previous, [title^="Previous Match"]').first();
this.toggleReplaceButton = this.findWidget.locator('.button.toggle, [title^="Toggle Replace"]').first();
this.replaceAllButton = this.findWidget.locator('.button.replace-all, [title^="Replace All"]').first();
this.nextMatchButton = this.findWidget
.locator('.button.next, .button.codicon-find-next-match, [aria-label^="Next Match"], [title^="Next Match"]')
.first();
this.previousMatchButton = this.findWidget
.locator(
'.button.previous, .button.codicon-find-previous-match, [aria-label^="Previous Match"], [title^="Previous Match"]'
)
.first();
this.toggleReplaceButton = this.findWidget
.locator('.button.toggle, [aria-label^="Toggle Replace"], [title^="Toggle Replace"]')
.first();
this.replaceAllButton = this.findWidget
.locator(
'.button.replace-all, .button.codicon-find-replace-all, [aria-label^="Replace All"], [title^="Replace All"]'
)
.first();
}

async openNotebook(noteId: string): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,18 +461,22 @@ test.describe.serial('Comprehensive Keyboard Shortcuts (ShortcutsMap)', () => {
// ===== UI TOGGLE SHORTCUTS =====

test.describe('ParagraphActions.SwitchEditor: Control+Alt+E', () => {
test('should toggle editor visibility with Control+Alt+E', async () => {
// Given: A paragraph with visible editor
await keyboardPage.tryFocusCodeEditor();
await keyboardPage.setCodeEditorContent('%python\nprint("Test editor toggle")');
test('should toggle the focused editor with Control+Alt+E', async () => {
await keyboardPage.tryFocusCodeEditor(0);
await keyboardPage.setCodeEditorContent('%python\nprint("First paragraph")', 0);
await keyboardPage.pressInsertBelow();
await keyboardPage.waitForParagraphCountChange(2);
await keyboardPage.tryFocusCodeEditor(1);
await keyboardPage.setCodeEditorContent('%python\nprint("Second paragraph")', 1);
await keyboardPage.tryFocusCodeEditor(0);

const initialEditorVisibility = await keyboardPage.isEditorVisible(0);
const secondEditorVisibility = await keyboardPage.isEditorVisible(1);

// When: User presses Control+Alt+E
await keyboardPage.pressSwitchEditor();

// Then: editor visibility toggles
await expect.poll(() => keyboardPage.isEditorVisible(0), { timeout: 10000 }).toBe(!initialEditorVisibility);
expect(await keyboardPage.isEditorVisible(1)).toBe(secondEditorVisibility);
});
});

Expand Down
63 changes: 23 additions & 40 deletions zeppelin-web-angular/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions zeppelin-web-angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"jquery-ui": "1.14.0",
"lodash": "^4.17.21",
"mathjax": "2.7.5",
"monaco-editor": "0.31.1",
"monaco-editor": "0.52.2",
"ng-zorro-antd": "^21.3.0",
"nvd3": "1.8.6",
"parse5": "^5.1.1",
Expand Down Expand Up @@ -102,7 +102,7 @@
"https-proxy-agent": "^2.2.1",
"husky": "9.1.7",
"lint-staged": "^15.5.2",
"monaco-editor-webpack-plugin": "7.0.1",
"monaco-editor-webpack-plugin": "7.1.1",
"ng-packagr": "^21.2.3",
"ngx-build-plus": "^20.0.0",
"prettier": "^3.6.2",
Expand Down
16 changes: 13 additions & 3 deletions zeppelin-web-angular/src/app/key-binding/key-binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ import { map, mergeMap, takeUntil } from 'rxjs/operators';
import { ShortcutService } from '@zeppelin/services';
import { castArray, chain, isNil } from 'lodash';
import { KeyCodeConverter } from './key-code-converter';
import { MonacoHandledParagraphActions } from './notebook-paragraph-keyboard-event-handler';
import { ParagraphActions } from './paragraph-actions';
import { ShortcutsMap } from './shortcuts-map';

export class KeyBinder {
private static nextMonacoContextId = 0;

private events$ = new Subject<{
action: ParagraphActions;
event: KeyboardEvent | null;
}>();
private readonly monacoContext = `zeppelin.paragraphEditor.${KeyBinder.nextMonacoContextId++}`;

constructor(
private destroySubject: Observable<unknown>,
Expand Down Expand Up @@ -56,17 +60,23 @@ export class KeyBinder {
}

initKeyBindingsOnMonaco(editor: MonacoEditor.IStandaloneCodeEditor) {
editor.createContextKey(this.monacoContext, true);
chain(ShortcutsMap)
.toPairs()
.filter(([action]) => MonacoHandledParagraphActions.some(monacoAction => monacoAction === action))
.flatMap(([action, keys]) => castArray(keys).map(key => ({ action, key })))
.forEach(({ action, key }) => {
const keyBinding = KeyCodeConverter.angularToMonacoKeyBinding(key);
if (isNil(keyBinding)) {
return;
}
editor.addCommand(keyBinding, () => {
this.events$.next({ action: action as ParagraphActions, event: null });
});
editor.addCommand(
keyBinding,
() => {
this.events$.next({ action: action as ParagraphActions, event: null });
},
this.monacoContext
);
})
.value();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ export const ParagraphActionToHandlerName = {
[ParagraphActions.FindInCode]: 'handleFindInCode'
} as const satisfies Record<ParagraphActions, keyof NotebookParagraphKeyboardEventHandler>;

// Referenced only via `typeof` below to derive a type; the runtime binding is intentionally unused.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const MonacoHandledParagraphActions = [
export const MonacoHandledParagraphActions = [
ParagraphActions.MoveCursorUp,
ParagraphActions.MoveCursorDown,
ParagraphActions.SwitchEditor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
import { editor as MonacoEditor, IDisposable, IPosition, KeyCode } from 'monaco-editor';

import { InterpreterBindingItem } from '@zeppelin/sdk';
import { CompletionService, MessageService } from '@zeppelin/services';
import { CompletionService, InlineCompletionService, MessageService } from '@zeppelin/services';

import { MonacoKeyboardEventHandler, ParagraphActions, ParagraphActionToHandlerName } from '@zeppelin/key-binding';
import { pt2px } from '@zeppelin/utility';
Expand Down Expand Up @@ -160,7 +160,7 @@ export class NotebookParagraphCodeEditorComponent

handleShowFind() {
if (this.editor) {
this.editor.getAction('actions.find').run();
this.editor.getAction('actions.find')?.run();

// Focus on the find widget input field
const findInput = document.querySelector('.find-widget .input') as HTMLInputElement;
Expand Down Expand Up @@ -216,6 +216,7 @@ export class NotebookParagraphCodeEditorComponent
this.initEditorListener(this.editor);
this.initEditorFocus();
this.initCompletionService(this.editor);
this.initInlineCompletionService(this.editor);
this.setEditorValue(this.editor);
setTimeout(() => {
this.autoAdjustEditorHeight();
Expand Down Expand Up @@ -280,6 +281,14 @@ export class NotebookParagraphCodeEditorComponent
this.completionService.registerAsCompletionReceiver(model, this.paragraphControl.pid);
}

initInlineCompletionService(editor: IStandaloneCodeEditor): void {
const model = editor.getModel();
if (!model) {
return;
}
this.inlineCompletionService.register(model, this.paragraphControl.pid);
}

initEditorFocus() {
if (this.focus && this.editor) {
this.editor.focus();
Expand All @@ -299,6 +308,7 @@ export class NotebookParagraphCodeEditorComponent
contextmenu: false,
matchBrackets: 'always',
wordWrap: 'on',
inlineSuggest: { enabled: true },
scrollbar: {
handleMouseWheel: false,
alwaysConsumeMouseWheel: false
Expand Down Expand Up @@ -390,7 +400,8 @@ export class NotebookParagraphCodeEditorComponent
private cdr: ChangeDetectorRef,
private ngZone: NgZone,
private messageService: MessageService,
private completionService: CompletionService
private completionService: CompletionService,
private inlineCompletionService: InlineCompletionService
) {}

ngOnChanges(changes: SimpleChanges): void {
Expand Down Expand Up @@ -421,6 +432,7 @@ export class NotebookParagraphCodeEditorComponent
const model = this.editor?.getModel();
if (model) {
this.completionService.unregister(model);
this.inlineCompletionService.unregister(model);
}
this.monacoDisposables.forEach(d => d.dispose());
}
Expand Down
Loading
Loading