All files / services/editor-factory editor-factory.service.ts

55.31% Statements 26/47
0% Branches 0/13
25% Functions 4/16
57.14% Lines 24/42

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 1101x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 9x           9x 9x     9x                                                                                                                                       9x       9x     9x        
import { TOOLBAR_GROUPS, ToolbarAction } from '../../components/editor/editor-toolbar.config';
import { russianPhrases } from '../../constants/editor-phrases';
import { insertTagInView } from '../../helpers/insert-tag';
import { continueLists, decreaseListIndent, increaseListIndent } from '../../helpers/list-commands';
import { quoteKeymap } from '../../helpers/quote-commands';
import { ValidationResult } from '../../validation/models/validation-result.model';
import { wikiLinter, wikiLintGutter, wikiLintKeymap } from '../../validation/wiki-linter';
import { WikiHighlighterService } from '../wiki-highlighter/wiki-highlighter.service';
import { isPlatformServer } from '@angular/common';
import { inject, Injectable, PLATFORM_ID } from '@angular/core';
import { closeBrackets } from '@codemirror/autocomplete';
import { history, defaultKeymap, historyKeymap } from '@codemirror/commands';
import { bracketMatching, defaultHighlightStyle, indentOnInput, syntaxHighlighting } from '@codemirror/language';
import { forEachDiagnostic, setDiagnosticsEffect } from '@codemirror/lint';
import { openSearchPanel, search, searchKeymap } from '@codemirror/search';
import { EditorState, Extension } from '@codemirror/state';
import { drawSelection, dropCursor, EditorView, highlightSpecialChars, keymap, ViewUpdate } from '@codemirror/view';
 
@Injectable()
export class EditorFactoryService {
    private changeHandler: (text: string) => void = () => {
        // Should be changed by setChangeHandler
    };
 
    private validationHandler?: (result: ValidationResult) => void;
 
    private readonly wikiHighlighter = inject(WikiHighlighterService);
    private readonly platformId = inject<object>(PLATFORM_ID);
 
    public isServer(): boolean {
        return isPlatformServer(this.platformId);
    }
 
    public createState(doc: string, customExtensions: Extension[] = []): EditorState {
        return EditorState.create({
            doc,
            extensions: [
                EditorState.phrases.of(russianPhrases),
                highlightSpecialChars(),
                drawSelection(),
                dropCursor(),
                syntaxHighlighting(defaultHighlightStyle),
                indentOnInput(),
                EditorView.lineWrapping,
                history(),
                closeBrackets(),
                bracketMatching(),
                this.wikiHighlighter.wikiHighlighter,
                wikiLinter,
                wikiLintGutter,
                wikiLintKeymap,
                // listener
                EditorView.updateListener.of((v: ViewUpdate) => {
                    if (v.docChanged) {
                        this.onContentChanged(v.state.doc.toString());
                    }
                    const hasLintUpdate = v.transactions.some(tr =>
                        tr.effects.some(e => e.is(setDiagnosticsEffect)),
                    );
                    if (hasLintUpdate) {
                        let errors = 0;
                        let warnings = 0;
                        forEachDiagnostic(v.state, d => {
                            if (d.severity === 'error') errors++;
                            else if (d.severity === 'warning') warnings++;
                        });
                        this.validationHandler?.({ errors, warnings });
                    }
                }),
                keymap.of([
                    { key: 'Enter', run: continueLists },
                    { key: 'Tab', run: increaseListIndent },
                    { key: 'Shift-Tab', run: decreaseListIndent },
                    ...TOOLBAR_GROUPS.flatMap(g => g.actions)
                        .filter((a): a is ToolbarAction & { readonly keyBinding: string } => !!a.keyBinding)
                        .map(a => ({
                            key: a.keyBinding,
                            run: (view: EditorView) => insertTagInView(view, a.command),
                        })),
                    ...defaultKeymap,
                    ...historyKeymap,
                    ...searchKeymap,
                    ...quoteKeymap,
                    { key: 'Mod-f', run: openSearchPanel },
                ]),
                search({
                    scrollToMatch: ({ from }) =>
                        EditorView.scrollIntoView(from, {
                            y: 'start',
                            x: 'nearest',
                        }),
                }),
                ...customExtensions,
            ],
        });
    }
 
    public setChangeHandler(handler: (text: string) => void): void {
        this.changeHandler = handler;
    }
 
    public setValidationHandler(handler: (result: ValidationResult) => void): void {
        this.validationHandler = handler;
    }
 
    private onContentChanged = (text: string) => {
        this.changeHandler(text);
    };
}