From 45d6ca6166f744ba35af169d18a93eca6ef39656 Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Fri, 26 May 2023 00:27:53 +0300 Subject: [PATCH] RED-6786: faster text editor --- .../dictionary-manager.component.html | 1 + .../dictionary-manager.component.ts | 2 +- .../components/editor/editor.component.html | 4 +- .../components/editor/editor.component.ts | 57 ++++++++++++------- 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html b/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html index 2d2e5c915..949b9c22a 100644 --- a/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html +++ b/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html @@ -94,6 +94,7 @@
lineChange.originalEndLineNumber != templateUrl: './editor.component.html', styleUrls: ['./editor.component.scss'], }) -export class EditorComponent implements OnInit, OnChanges { +export class EditorComponent implements OnInit, OnChanges, OnDestroy { @Input() showDiffEditor = false; @Input() diffEditorText: string; @Input() @OnChange('revert') initialEntries: List; @@ -39,10 +41,25 @@ export class EditorComponent implements OnInit, OnChanges { editorOptions: IStandaloneEditorConstructionOptions = {}; codeEditor: ICodeEditor; value: string; + protected readonly _editorTextChanged$ = new Subject(); private _diffEditor: IDiffEditor; private _decorations: string[] = []; + private readonly _sub$ = new Subscription(); + private _initialEntriesSet = new Set(); - constructor(private readonly _loadingService: LoadingService, private readonly _editorThemeService: EditorThemeService) {} + constructor(private readonly _loadingService: LoadingService, private readonly _editorThemeService: EditorThemeService) { + const textChanged$ = this._editorTextChanged$.pipe( + debounceTime(300), // prevent race condition with onPaste event + filter(text => text.length > 0), + tap(newText => { + const newDecorations = this._getDecorations(newText); + this._decorations = this.codeEditor.deltaDecorations(this._decorations, newDecorations); + this.diffValue = this.value; + this._loadingService.stop(); + }), + ); + this._sub$.add(textChanged$.subscribe()); + } get currentEntries(): string[] { return this.value.split('\n'); @@ -56,6 +73,10 @@ export class EditorComponent implements OnInit, OnChanges { return this.currentEntries.length; } + ngOnDestroy() { + this._sub$.unsubscribe(); + } + ngOnChanges(changes: SimpleChanges) { if (changes.diffEditorText) { this._diffEditor?.getOriginalEditor().setValue(this.diffEditorText); @@ -91,21 +112,17 @@ export class EditorComponent implements OnInit, OnChanges { this._setTheme(); } - @Debounce() - codeEditorTextChanged() { - const newDecorations = this._getDecorations(); - this._decorations = this.codeEditor.deltaDecorations(this._decorations, newDecorations); - this.diffValue = this.value; - this._loadingService.stop(); - } - - _getDecorations() { - const currentEntries = this.currentEntries; + _getDecorations(text: string) { + const currentEntries = text.split('\n'); const newDecorations: IModelDeltaDecoration[] = []; - for (let index = 0; index < currentEntries.length; ++index) { - const entry = currentEntries.at(index); - if (!this._isNew(entry)) { + for (let index = 0; index < currentEntries.length; index++) { + const entry = currentEntries.at(index)?.trim(); + if (!entry || entry.length === 0) { + continue; + } + + if (this._initialEntriesSet.has(entry)) { continue; } @@ -118,8 +135,10 @@ export class EditorComponent implements OnInit, OnChanges { revert() { this.value = this.initialEntries.join('\n'); + this._initialEntriesSet = new Set(this.initialEntries); this.diffValue = this.value; this._diffEditor?.getModifiedEditor().setValue(this.diffValue); + this._editorTextChanged$.next(this.value); } onPaste() { @@ -178,10 +197,6 @@ export class EditorComponent implements OnInit, OnChanges { }); } - private _isNew(entry: string): boolean { - return this.initialEntries.indexOf(entry) < 0 && entry?.trim().length > 0; - } - private _getDecoration(entry: string, line: number): IModelDeltaDecoration { const cssClass = entry.length < MIN_WORD_LENGTH ? 'too-short-marker' : 'changed-row-marker'; const range = new monaco.Range(line, 1, line, 1);