Search code examples
angularmonaco-editor

Microsoft Monaco Editor in angular 14


enter image description hereIm using monaco-editor in my angular application, in the editor there is extra space at bottom of text im typing. I want to remove space, but cant.

I tried to play with editor options and with css also(to remove some padding or something else ...)

Here is screenshot of problem, as you see 27lines is possible to render at my area, but there is unnecessary space and scroll

here is my codes

/* eslint-disable @typescript-eslint/member-ordering */
import {
    Component,
    EventEmitter,
    forwardRef,
    Inject,
    Input,
    NgZone,
    OnDestroy,
    OnInit,
    Output,
    ViewEncapsulation,
} from '@angular/core';
import { LANGUAGE_NAME } from '../../config/language-name';
import { editor, IDisposable } from 'monaco-editor';
import {
    EditorComponent,
    NGX_MONACO_EDITOR_CONFIG,
    NgxMonacoEditorConfig,
} from 'ngx-monaco-editor';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { EDITOR_OPTIONS } from '../../config/editor-options';
import { EDITOR_CONFIG } from '../../config/editor-config';
import { SemanticTokenProviderService } from '../../services/semantic-token-provider/semantic-token-provider.service';
import { CompletionProviderService } from '../../services/completion-provider/completion-provider.service';
import { EditorEventsService } from '../../services/editor-events/editor-events.service';
import { EditorHoverService } from '../../services/editor-hover/editor-hover.service';
import { SpelEditorDecoration } from '../../interfaces/spel-editor-decoration';
import { EditorErrorsService } from '../../services/editor-errors/editor-errors.service';

@Component({
    selector: 'app-spel-editor',
    templateUrl: './spel-editor.component.html',
    styleUrls: ['./spel-editor.component.less'],
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => SpelEditorComponent),
            multi: true,
        },
        {
            provide: NGX_MONACO_EDITOR_CONFIG,
            useValue: EDITOR_CONFIG,
        },
        SemanticTokenProviderService,
        CompletionProviderService,
        EditorEventsService,
        EditorHoverService,
        EditorErrorsService,
    ],
    encapsulation: ViewEncapsulation.None,
})
export class SpelEditorComponent extends EditorComponent implements OnInit, OnDestroy {
    @Output() ctrlECommand = new EventEmitter<void>();

    @Input() isReadonly = false;

    @Input() set decoration(decoration: SpelEditorDecoration | undefined) {
        this.editorErrorsService.setErrors([decoration].filter(Boolean));
    }

    // eslint-disable-next-line rxjs/finnish
    @Output() entitiesChange = this.semanticTokenProviderService.entities$;

    private unregisterSemanticTokensProvider: IDisposable = {
        dispose() {},
    };

    private unregisterCompletionProvider: IDisposable = {
        dispose() {},
    };

    constructor(
        zone: NgZone,
        @Inject(NGX_MONACO_EDITOR_CONFIG) editorConfig: NgxMonacoEditorConfig,
        private readonly semanticTokenProviderService: SemanticTokenProviderService,
        private readonly completionProviderService: CompletionProviderService,
        private readonly editorEventsService: EditorEventsService,
        private readonly editorHoverService: EditorHoverService,
        private readonly editorErrorsService: EditorErrorsService,
    ) {
        super(zone, editorConfig);
        this.onInit.subscribe(editor => this.onEditorInit(editor));
    }

    ngOnInit(): void {
        this.options = {
            ...EDITOR_OPTIONS,
            language: LANGUAGE_NAME,
            domReadOnly: this.isReadonly,
            readOnly: this.isReadonly,
        };
    }

    ngOnDestroy(): void {
        super.ngOnDestroy();
        this.unregisterSemanticTokensProvider.dispose();
        this.unregisterCompletionProvider.dispose();
    }

    public onEditorInit(editor: editor.IStandaloneCodeEditor): void {
        const monaco = window.monaco;

        // @ts-ignore
        // eslint-disable-next-line no-bitwise
        editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyE, () => {
            this.ctrlECommand.emit();
        });

        editor.focus();

        editor.setPosition({ lineNumber: 0, column: 0 });

        this.unregisterSemanticTokensProvider =
            monaco.languages.registerDocumentSemanticTokensProvider(
                LANGUAGE_NAME,
                this.semanticTokenProviderService,
            );

        this.unregisterCompletionProvider =
            monaco.languages.registerCompletionItemProvider(
                LANGUAGE_NAME,
                this.completionProviderService,
            );

        this.editorEventsService.setEditor(editor);
        this.editorHoverService.setEditor(editor);
        this.editorErrorsService.setEditor(editor);
    }
}

here is my initialization code

template

<div #editorContainer class="editor-container"></div>

styles

:host {
    display: block;
}

.editor-container {
    width: 100%;
    height: 98%;

    /* stylelint-disable */
    --editor-monospace-font: -apple-system, 'BlinkMacSystemFont', 'SF Mono', Consolas, monospace;
}


.bracket-highlighting-0, .bracket-highlighting-1, .bracket-highlighting-2, .bracket-highlighting-3 {
    color: #22863a !important;
}

.unexpected-closing-bracket {
    color: #F00 !important;
    font-weight: bold !important;
}

.hovered {
    &-unknown {
        background: rgba(255, 0, 0, 0.1);
    }

    &-deprecated {
        background: rgba(0, 0, 0, 0.54);
        opacity: 0.1;
    }

    &-function {
        background: rgba(153, 48, 111, 0.1);
    }

    &-variable {
        background: rgba(0, 92, 197, 0.1);
    }

    &-call {
        background: rgba(176, 136, 0, 0.1);
    }
}

.problem {
    &-error {
        background: rgba(255, 0, 0, 0.1);
    }

    &-warning {
        background: rgba(250, 182, 25, 0.12);
    }
}

// Делаем скроллбары похожие на тайгу

.slider {
    box-sizing: border-box !important;
    border: 0.25rem solid transparent !important;
    border-radius: 6.25rem !important;
    background: rgba(0, 0, 0, 0.2) !important;
    background-clip: content-box !important;
    transition-duration: .15s !important;
    transition-timing-function: ease-in-out !important;
    cursor: pointer !important;
    pointer-events: auto !important;
    transition-property: width, height !important;
}

.scrollbar.vertical {
    .slider {
        right: 0 !important;
        left: unset !important;
        width: 12px !important;

        &:hover {
            width: 14px !important;
        }
    }
}

.scrollbar.horizontal {
    .slider {
        top: unset !important;
        bottom: 0 !important;
        height: 12px !important;

        &:hover {
            height: 14px !important;
        }
    }
}

.decorationsOverviewRuler {
    display: none !important;
}

.suggest-widget {
    --vscode-editorSuggestWidget-background: #FFFFFF;
    --vscode-editorSuggestWidget-selectedBackground: #0000000A;
    --vscode-editorSuggestWidget-selectedForeground: #000;
    --vscode-editorSuggestWidget-focusHighlightForeground: var(--vscode-editorSuggestWidget-highlightForeground);
    border-radius: 8px;
    border: 1px solid #DDDFE0;
    box-shadow: 0 8px 16px 0 #00000029;
    z-index: 999 !important;

    .left, .right {
        font-family: var(--editor-monospace-font);
    }

    .details-label {
        color: var(--tui-text-02);
        font-size: 11px;
        margin-left: 0 !important;
    }

    .monaco-list-row {
        font-family: var(--tui-font-text) !important;
        color: #000000CC !important;
        padding-right: 12px !important;

        .contents {
            padding-left: 12px !important;
        }

        .suggest-icon {
            margin-right: 8px !important;
        }
    }
}

.codicon-symbol-variable:before {
    content: url('/assets/svg/editor-completion/variable-completion.svg') !important;
}

.codicon-symbol-function:before {
    content: url('/assets/svg/editor-completion/function-completion.svg') !important;
}

.codicon-symbol-method:before {
    content: url('/assets/svg/editor-completion/call-completion.svg') !important;
}

configs

import { LANGUAGE_THEME_NAME } from './language-theme';
import { editor } from 'monaco-editor';

export const EDITOR_OPTIONS: editor.IStandaloneEditorConstructionOptions = {
    theme: LANGUAGE_THEME_NAME,
    wordBasedSuggestions: false,
    automaticLayout: true,
    // Пришлось указать явно, а не var(--editor-monospace-font), потому что иначе ломается wordWrap логика
    // Автопереносы длинных строк начинают работать неправильно
    fontFamily: "-apple-system, 'BlinkMacSystemFont', 'SF Mono', Consolas, monospace",
    fontSize: 15,
    lineHeight: 24,
    suggestLineHeight: 32,
    suggestFontSize: 13,
    fontWeight: '400',
    lineNumbersMinChars: 3,
    folding: false,
    lineDecorationsWidth: 14,
    renderLineHighlight: 'none',
    bracketPairColorization: {
        enabled: true,
    },
    scrollbar: {
        useShadows: false,
    },
    minimap: {
        enabled: false,
    },
    unicodeHighlight: {
        ambiguousCharacters: false,
    },
    fixedOverflowWidgets: true,
    'semanticHighlighting.enabled': true,
    wordWrap: 'on',
    wrappingStrategy: 'advanced',
};


import { NgxMonacoEditorConfig } from 'ngx-monaco-editor';
import { LANGUAGE_NAME } from './language-name';
import { LANGUAGE_CONFIGURATION } from './language-configuration';
import { LANGUAGE_MONARCH_TOKENS } from './language-monarch-tokens';
import * as monaco from 'monaco-editor';
import { LANGUAGE_THEME, LANGUAGE_THEME_NAME } from './language-theme';

declare global {
    interface Window {
        monaco: typeof monaco;
    }
}

export const EDITOR_CONFIG: NgxMonacoEditorConfig = {
    onMonacoLoad: () => {
        window.monaco.languages.register({ id: LANGUAGE_NAME });

        window.monaco.languages.setLanguageConfiguration(
            LANGUAGE_NAME,
            LANGUAGE_CONFIGURATION,
        );

        window.monaco.languages.setMonarchTokensProvider(
            LANGUAGE_NAME,
            LANGUAGE_MONARCH_TOKENS,
        );

        window.monaco.editor.defineTheme(LANGUAGE_THEME_NAME, LANGUAGE_THEME);
    },
};


And here is options configs and styles


Solution

  • On ngx-monaco-editor (a wrapper for Monaco Editor), it was impossible for me to solve this problem. So, I tried upgrading to ngx-monaco-editor-v2, and after setting the property scrollBeyondLastLine: false, the problem was solved.