RED-6786: faster text editor

This commit is contained in:
Dan Percic 2023-05-26 00:07:16 +03:00
parent 4fc7fa40d8
commit c68e92f726
2 changed files with 35 additions and 31 deletions

View File

@ -1,10 +1,10 @@
<ngx-monaco-editor
(init)="onCodeEditorInit($event)"
(ngModelChange)="codeEditorTextChanged($event)"
(ngModelChange)="_editorTextChanged$.next($event)"
(paste)="onPaste()"
*ngIf="!showDiffEditor"
[(ngModel)]="value"
[options]="editorOptions"
(paste)="onPaste()"
></ngx-monaco-editor>
<ngx-monaco-diff-editor
@ -14,5 +14,3 @@
[options]="editorOptions"
[original]="diffEditorText"
></ngx-monaco-diff-editor>
<ng-container *ngIf="_codeEditorTextChangedHandler$ | async"></ng-container>

View File

@ -1,14 +1,15 @@
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { List, LoadingService, OnChange } from '@iqser/common-ui';
import { EditorThemeService } from '@services/editor-theme.service';
import { Subject } from 'rxjs';
import { debounceTime, filter, tap } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
import ICodeEditor = monaco.editor.ICodeEditor;
import IDiffEditor = monaco.editor.IDiffEditor;
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import ILineChange = monaco.editor.ILineChange;
import IEditorMouseEvent = monaco.editor.IEditorMouseEvent;
import { BehaviorSubject } from 'rxjs';
import { debounceTime, filter, tap } from 'rxjs/operators';
const MIN_WORD_LENGTH = 2;
const lineChangeToDecoration = ({ originalEndLineNumber, originalStartLineNumber }: ILineChange) =>
@ -41,28 +42,35 @@ export class EditorComponent implements OnInit, OnChanges {
editorOptions: IStandaloneEditorConstructionOptions = {};
codeEditor: ICodeEditor;
value: string;
protected _initialEntriesMap = new Set<string>();
protected readonly _editorTextChanged$ = new Subject<string>();
private _diffEditor: IDiffEditor;
private _decorations: string[] = [];
private readonly _codeEditorTextChanged$ = new BehaviorSubject<string>('');
protected readonly _codeEditorTextChangedHandler$ = this._codeEditorTextChanged$.asObservable().pipe(
filter(Boolean),
debounceTime(0), // prevent race condition with onPaste event
tap(newText => {
const newDecorations = this.#getDecorations(newText);
this._decorations = this.codeEditor.deltaDecorations(this._decorations, newDecorations);
this.diffValue = this.value;
this._loadingService.stop();
}),
);
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
takeUntilDestroyed(),
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();
}),
);
// eslint-disable-next-line rxjs/no-ignored-subscription
textChanged$.subscribe();
}
get currentEntries(): string[] {
return this.value.split('\n');
}
// TODO: this is called way too often, we need a better option
get hasChanges(): boolean {
return this.currentEntries.toString() !== this.initialEntries.toString();
return this.value.trim() !== this.initialEntries.join('\n');
}
get #currentEntriesCount(): number {
@ -73,10 +81,6 @@ export class EditorComponent implements OnInit, OnChanges {
this._loadingService.start();
}
codeEditorTextChanged(event) {
this._codeEditorTextChanged$.next(event);
}
ngOnChanges(changes: SimpleChanges) {
if (changes.diffEditorText) {
this._diffEditor?.getOriginalEditor().setValue(this.diffEditorText);
@ -114,17 +118,23 @@ export class EditorComponent implements OnInit, OnChanges {
revert() {
this.value = this.initialEntries.join('\n');
this._initialEntriesMap = new Set<string>(this.initialEntries);
this.diffValue = this.value;
this._diffEditor?.getModifiedEditor().setValue(this.diffValue);
this._editorTextChanged$.next(this.value);
}
#getDecorations(newText: string) {
const currentEntries = newText.split('/n');
const currentEntries = newText.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._initialEntriesMap.has(entry)) {
continue;
}
@ -187,10 +197,6 @@ export class EditorComponent implements OnInit, OnChanges {
});
}
#isNew(entry: string): boolean {
return this.initialEntries.indexOf(entry) < 0 && entry?.trim().length > 0;
}
#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);