@finos/legend-application-pure-ide
Version:
Legend Pure IDE application core
131 lines • 7.99 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useEffect, useRef, useState } from 'react';
import { observer } from 'mobx-react-lite';
import { editor as monacoEditorAPI } from 'monaco-editor';
import { useApplicationStore, useCommands } from '@finos/legend-application';
import { clsx, Dialog, WordWrapIcon } from '@finos/legend-art';
import { usePureIDEStore } from '../PureIDEStoreProvider.js';
import { at, returnUndefOnError } from '@finos/legend-shared';
import { CODE_EDITOR_THEME, getBaseCodeEditorOptions, moveCursorToPosition, } from '@finos/legend-code-editor';
const POSITION_PATTERN = /[0-9]+(?::[0-9]+)?/;
const getPositionFromGoToLinePromptInputValue = (val) => {
const parts = val.split(':');
if (parts.length < 1 || parts.length > 2) {
return [1, undefined];
}
return [
returnUndefOnError(() => parseInt(at(parts, 0))) ?? 1,
returnUndefOnError(() => parseInt(at(parts, -1))),
];
};
export const GoToLinePrompt = observer((props) => {
const { fileEditorState } = props;
const [value, setValue] = useState('');
const inputRef = useRef(null);
// validation
const isValidValue = Boolean(value.match(POSITION_PATTERN));
const currentEditorCursorPosition = fileEditorState.textEditorState.editor?.getPosition();
const [currentLine, currentColumn] = getPositionFromGoToLinePromptInputValue(value);
const isValidLineNumber = 1 <= currentLine &&
currentLine <= fileEditorState.textEditorState.model.getLineCount();
const error = !isValidValue
? 'Invalid value (format [line:column] - e.g. 123:45)'
: !isValidLineNumber
? `Invalid line number`
: undefined;
// actions
const closeModal = () => fileEditorState.setShowGoToLinePrompt(false);
const onValueChange = (event) => setValue(event.target.value);
const create = (event) => {
event.preventDefault();
closeModal();
fileEditorState.textEditorState.setForcedCursorPosition({
lineNumber: currentLine,
column: currentColumn ?? 1,
});
};
const handleEnter = () => inputRef.current?.focus();
return (_jsx(Dialog, { open: true, onClose: closeModal, TransitionProps: {
onEnter: handleEnter,
}, classes: { container: 'command-modal__container' }, PaperProps: { classes: { root: 'command-modal__inner-container' } }, children: _jsxs("div", { className: "modal modal--dark command-modal", children: [_jsx("div", { className: "modal__title", children: "Go to..." }), _jsxs("div", { className: "command-modal__content", children: [_jsx("form", { className: "command-modal__content__form", onSubmit: create, children: _jsxs("div", { className: "input-group command-modal__content__input", children: [_jsx("input", { ref: inputRef, className: "input input--dark", onChange: onValueChange, placeholder: currentEditorCursorPosition
? `Current Line: ${currentEditorCursorPosition.lineNumber}, Col: ${currentEditorCursorPosition.column}. Type a line between 1 and ${fileEditorState.textEditorState.model.getLineCount()} to navigate to`
: undefined, value: value, spellCheck: false }), value !== '' && error && (_jsx("div", { className: "input-group__error-message", children: error }))] }) }), _jsx("button", { className: "command-modal__content__submit-btn btn--dark", disabled: value === '' || Boolean(error), onClick: create, children: "Go" })] })] }) }));
});
export const GenericFileEditor = observer((props) => {
const { editorState } = props;
const ideStore = usePureIDEStore();
const applicationStore = useApplicationStore();
const textInputRef = useRef(null);
const [editor, setEditor] = useState();
useEffect(() => {
if (!editor && textInputRef.current) {
const element = textInputRef.current;
const newEditor = monacoEditorAPI.create(element, {
...getBaseCodeEditorOptions(),
theme: CODE_EDITOR_THEME.DEFAULT_DARK,
wordWrap: editorState.textEditorState.wrapText ? 'on' : 'off',
readOnly: editorState.file.RO,
});
newEditor.onDidChangeModelContent(() => {
const currentVal = newEditor.getValue();
if (currentVal !== editorState.file.content) {
// the assertion above is to ensure we don't accidentally clear error on initialization of the editor
editorState.clearError(); // clear error on content change/typing
}
editorState.file.setContent(currentVal);
});
// manual trigger to support cursor observability
newEditor.onDidChangeCursorPosition(() => {
editorState.textEditorState.notifyCursorObserver();
});
newEditor.onDidChangeCursorSelection(() => {
editorState.textEditorState.notifyCursorObserver();
});
// Restore the editor model and view state
newEditor.setModel(editorState.textEditorState.model);
if (editorState.textEditorState.viewState) {
newEditor.restoreViewState(editorState.textEditorState.viewState);
}
newEditor.focus(); // focus on the editor initially
editorState.textEditorState.setEditor(newEditor);
setEditor(newEditor);
}
}, [ideStore, applicationStore, editorState, editor]);
useCommands(editorState);
useEffect(() => {
if (editor) {
if (editorState.textEditorState.forcedCursorPosition) {
moveCursorToPosition(editor, editorState.textEditorState.forcedCursorPosition);
editorState.textEditorState.setForcedCursorPosition(undefined);
}
}
}, [editor, editorState, editorState.textEditorState.forcedCursorPosition]);
// clean up
useEffect(() => () => {
if (editor) {
// persist editor view state (cursor, scroll, etc.) to restore on re-open
editorState.textEditorState.setViewState(editor.saveViewState() ?? undefined);
// NOTE: dispose the editor to prevent potential memory-leak
editor.dispose();
}
}, [editorState, editor]);
return (_jsxs("div", { className: "panel editor-group file-editor", children: [_jsx("div", { className: "panel__header file-editor__header", children: _jsxs("div", { className: "file-editor__header__actions", children: [_jsx("button", { className: clsx('file-editor__header__action', {
'file-editor__header__action--active': editorState.textEditorState.wrapText,
}), tabIndex: -1, onClick: () => editorState.textEditorState.setWrapText(!editorState.textEditorState.wrapText), title: "Toggle Text Wrap", children: _jsx(WordWrapIcon, { className: "file-editor__icon--text-wrap" }) }), editorState.showGoToLinePrompt && (_jsx(GoToLinePrompt, { fileEditorState: editorState }))] }) }), _jsx("div", { className: "panel__content file-editor__content", children: _jsx("div", { className: "code-editor__container", children: _jsx("div", { className: "code-editor__body", ref: textInputRef }) }) })] }));
});
//# sourceMappingURL=GenericFileEditor.js.map