@finos/legend-application-studio 28.13.14 → 28.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/__lib__/LegendStudioEvent.d.ts +4 -1
- package/lib/__lib__/LegendStudioEvent.d.ts.map +1 -1
- package/lib/__lib__/LegendStudioEvent.js +4 -0
- package/lib/__lib__/LegendStudioEvent.js.map +1 -1
- package/lib/components/editor/editor-group/EditorGroup.js +1 -1
- package/lib/components/editor/editor-group/EditorGroup.js.map +1 -1
- package/lib/components/editor/editor-group/GrammarTextEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/GrammarTextEditor.js +258 -215
- package/lib/components/editor/editor-group/GrammarTextEditor.js.map +1 -1
- package/lib/components/workspace-setup/WorkspaceSetup.js +7 -7
- package/lib/components/workspace-setup/WorkspaceSetup.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/editor/EditorGraphState.d.ts.map +1 -1
- package/lib/stores/editor/EditorGraphState.js +8 -1
- package/lib/stores/editor/EditorGraphState.js.map +1 -1
- package/lib/stores/editor/EditorStore.d.ts.map +1 -1
- package/lib/stores/editor/EditorStore.js +8 -2
- package/lib/stores/editor/EditorStore.js.map +1 -1
- package/lib/stores/editor/GraphEditFormModeState.js +1 -1
- package/lib/stores/editor/GraphEditFormModeState.js.map +1 -1
- package/lib/stores/editor/GraphEditGrammarModeState.d.ts +4 -0
- package/lib/stores/editor/GraphEditGrammarModeState.d.ts.map +1 -1
- package/lib/stores/editor/GraphEditGrammarModeState.js +29 -2
- package/lib/stores/editor/GraphEditGrammarModeState.js.map +1 -1
- package/lib/stores/editor/editor-state/GrammarTextEditorState.d.ts +2 -1
- package/lib/stores/editor/editor-state/GrammarTextEditorState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/GrammarTextEditorState.js +5 -2
- package/lib/stores/editor/editor-state/GrammarTextEditorState.js.map +1 -1
- package/package.json +4 -4
- package/src/__lib__/LegendStudioEvent.ts +5 -0
- package/src/components/editor/editor-group/EditorGroup.tsx +1 -1
- package/src/components/editor/editor-group/GrammarTextEditor.tsx +418 -351
- package/src/components/workspace-setup/WorkspaceSetup.tsx +15 -15
- package/src/stores/editor/EditorGraphState.ts +12 -0
- package/src/stores/editor/EditorStore.ts +16 -2
- package/src/stores/editor/GraphEditFormModeState.ts +1 -1
- package/src/stores/editor/GraphEditGrammarModeState.ts +49 -1
- package/src/stores/editor/editor-state/GrammarTextEditorState.ts +7 -2
@@ -16,21 +16,22 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
16
16
|
*/
|
17
17
|
import { useEffect, useState, useRef, useCallback, forwardRef } from 'react';
|
18
18
|
import { observer } from 'mobx-react-lite';
|
19
|
-
import { editor as monacoEditorAPI, languages as monacoLanguagesAPI, } from 'monaco-editor';
|
20
|
-
import { ContextMenu,
|
19
|
+
import { editor as monacoEditorAPI, languages as monacoLanguagesAPI, KeyCode, KeyMod, } from 'monaco-editor';
|
20
|
+
import { ContextMenu, PanelContent, MenuContent, MenuContentItem, PanelLoadingIndicator, CaretDownIcon, DropdownMenu, MenuContentItemIcon, CheckIcon, MenuContentItemLabel, } from '@finos/legend-art';
|
21
21
|
import { DEFAULT_TAB_SIZE, useApplicationStore, useApplicationNavigationContext, } from '@finos/legend-application';
|
22
22
|
import { disposeCodeEditor, getBaseCodeEditorOptions, resetLineNumberGutterWidth, getCodeEditorValue, normalizeLineEnding, setWarningMarkers, clearMarkers, setErrorMarkers, moveCursorToPosition, CODE_EDITOR_THEME, CODE_EDITOR_LANGUAGE, getInlineSnippetSuggestions, getParserKeywordSuggestions, getParserElementSnippetSuggestions, getSectionParserNameFromLineText, } from '@finos/legend-lego/code-editor';
|
23
23
|
import { CORE_DND_TYPE, } from '../../../stores/editor/utils/DnDUtils.js';
|
24
24
|
import { useDrop } from 'react-dnd';
|
25
25
|
import { flowResult } from 'mobx';
|
26
26
|
import { useEditorStore } from '../EditorStoreProvider.js';
|
27
|
-
import { hasWhiteSpace, isNonNullable } from '@finos/legend-shared';
|
28
|
-
import { PARSER_SECTION_MARKER, PURE_CONNECTION_NAME, PURE_ELEMENT_NAME, PURE_PARSER, } from '@finos/legend-graph';
|
27
|
+
import { LogEvent, assertErrorThrown, assertTrue, hasWhiteSpace, isNonNullable, } from '@finos/legend-shared';
|
28
|
+
import { ELEMENT_PATH_DELIMITER, PARSER_SECTION_MARKER, PURE_CONNECTION_NAME, PURE_ELEMENT_NAME, PURE_PARSER, isValidFullPath, } from '@finos/legend-graph';
|
29
29
|
import { LEGEND_STUDIO_DOCUMENTATION_KEY } from '../../../__lib__/LegendStudioDocumentation.js';
|
30
30
|
import { BLANK_CLASS_SNIPPET, CLASS_WITH_CONSTRAINT_SNIPPET, CLASS_WITH_INHERITANCE_SNIPPET, CLASS_WITH_PROPERTY_SNIPPET, DATA_WITH_EXTERNAL_FORMAT_SNIPPET, DATA_WITH_MODEL_STORE_SNIPPET, createDataElementSnippetWithEmbeddedDataSuggestionSnippet, SIMPLE_PROFILE_SNIPPET, SIMPLE_ENUMERATION_SNIPPET, SIMPLE_ASSOCIATION_SNIPPET, SIMPLE_MEASURE_SNIPPET, BLANK_FUNCTION_SNIPPET, SIMPLE_FUNCTION_SNIPPET, SIMPLE_RUNTIME_SNIPPET, JSON_MODEL_CONNECTION_SNIPPET, XML_MODEL_CONNECTION_SNIPPET, MODEL_CHAIN_CONNECTION_SNIPPET, RELATIONAL_DATABASE_CONNECTION_SNIPPET, BLANK_RELATIONAL_DATABASE_SNIPPET, SIMPLE_GENERATION_SPECIFICATION_SNIPPET, BLANK_SERVICE_SNIPPET, SERVICE_WITH_SINGLE_EXECUTION_SNIPPET, SERVICE_WITH_MULTI_EXECUTION_SNIPPET, BLANK_MAPPING_SNIPPET, MAPPING_WITH_M2M_CLASS_MAPPING_SNIPPET, MAPPING_WITH_ENUMERATION_MAPPING_SNIPPET, MAPPING_WITH_RELATIONAL_CLASS_MAPPING_SNIPPET, POST_PROCESSOR_RELATIONAL_DATABASE_CONNECTION_SNIPPET, createConnectionSnippetWithPostProcessorSuggestionSnippet, } from '../../../__lib__/LegendStudioCodeSnippet.js';
|
31
31
|
import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../__lib__/LegendStudioApplicationNavigationContext.js';
|
32
32
|
import { LEGEND_STUDIO_SETTING_KEY } from '../../../__lib__/LegendStudioSetting.js';
|
33
|
-
import { GraphEditGrammarModeState } from '../../../stores/editor/GraphEditGrammarModeState.js';
|
33
|
+
import { GRAMMAR_MODE_EDITOR_ACTION, GraphEditGrammarModeState, } from '../../../stores/editor/GraphEditGrammarModeState.js';
|
34
|
+
import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
|
34
35
|
export const GrammarTextEditorHeaderTabContextMenu = observer(forwardRef(function GrammarTextEditorHeaderTabContextMenu(props, ref) {
|
35
36
|
const editorStore = useEditorStore();
|
36
37
|
const applicationStore = useApplicationStore();
|
@@ -451,6 +452,55 @@ const collectParserElementSnippetSuggestions = (editorStore, parserKeyword) => {
|
|
451
452
|
}
|
452
453
|
return [];
|
453
454
|
};
|
455
|
+
const resolveElementPathFromCurrentPosition = (_editor, grammarModeState) => {
|
456
|
+
let elementPath = '';
|
457
|
+
try {
|
458
|
+
const model = _editor.getModel();
|
459
|
+
let position = _editor.getPosition();
|
460
|
+
let maxWords = 0;
|
461
|
+
if (model) {
|
462
|
+
while (position && maxWords < 30) {
|
463
|
+
const currentWord = model.getWordAtPosition(position);
|
464
|
+
const lineNumber = position.lineNumber;
|
465
|
+
position = null;
|
466
|
+
maxWords += 1;
|
467
|
+
if (currentWord) {
|
468
|
+
const wordStartPost = {
|
469
|
+
lineNumber: lineNumber,
|
470
|
+
column: currentWord.startColumn,
|
471
|
+
};
|
472
|
+
const startPost = model.modifyPosition(wordStartPost, -2);
|
473
|
+
elementPath = currentWord.word + elementPath;
|
474
|
+
const pathDelimiterRange = {
|
475
|
+
startLineNumber: startPost.lineNumber,
|
476
|
+
startColumn: startPost.column,
|
477
|
+
endLineNumber: wordStartPost.lineNumber,
|
478
|
+
endColumn: wordStartPost.column,
|
479
|
+
};
|
480
|
+
const packageRange = model.getValueInRange(model.validateRange(pathDelimiterRange));
|
481
|
+
if (packageRange === ELEMENT_PATH_DELIMITER) {
|
482
|
+
elementPath = packageRange + elementPath;
|
483
|
+
position = model.modifyPosition(startPost, -1);
|
484
|
+
}
|
485
|
+
}
|
486
|
+
}
|
487
|
+
}
|
488
|
+
assertTrue(isValidFullPath(elementPath), `Unable to go to element definition. Not valid element path: ${elementPath}`);
|
489
|
+
return elementPath;
|
490
|
+
}
|
491
|
+
catch (error) {
|
492
|
+
assertErrorThrown(error);
|
493
|
+
grammarModeState.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.TEXT_MODE_ACTION_KEYBOARD_SHORTCUT_GO_TO_DEFINITION__ERROR), error);
|
494
|
+
}
|
495
|
+
return undefined;
|
496
|
+
};
|
497
|
+
const goToElement = (_editor, grammarModeState) => {
|
498
|
+
grammarModeState.editorStore.applicationStore.logService.info(LogEvent.create(LEGEND_STUDIO_APP_EVENT.TEXT_MODE_ACTION_KEYBOARD_SHORTCUT_GO_TO_DEFINITION__LAUNCH));
|
499
|
+
const elementPath = resolveElementPathFromCurrentPosition(_editor, grammarModeState);
|
500
|
+
if (elementPath) {
|
501
|
+
flowResult(grammarModeState.goToElement(elementPath)).catch(grammarModeState.editorStore.applicationStore.alertUnhandledError);
|
502
|
+
}
|
503
|
+
};
|
454
504
|
export const GrammarTextEditor = observer(() => {
|
455
505
|
const [editor, setEditor] = useState();
|
456
506
|
const editorStore = useEditorStore();
|
@@ -458,13 +508,16 @@ export const GrammarTextEditor = observer(() => {
|
|
458
508
|
const grammarModeState = editorStore.getGraphEditorMode(GraphEditGrammarModeState);
|
459
509
|
const grammarTextEditorState = grammarModeState.grammarTextEditorState;
|
460
510
|
const error = editorStore.graphState.error;
|
511
|
+
const warnings = editorStore.graphState.warnings;
|
461
512
|
const [elementsFolded, setFoldingElements] = useState(false);
|
462
513
|
const forcedCursorPosition = grammarTextEditorState.forcedCursorPosition;
|
514
|
+
const wordWrapOtion = grammarTextEditorState.wordWrapOtion;
|
463
515
|
const value = normalizeLineEnding(grammarTextEditorState.graphGrammarText);
|
464
516
|
const textEditorRef = useRef(null);
|
465
517
|
const hoverProviderDisposer = useRef(undefined);
|
466
518
|
const suggestionProviderDisposer = useRef(undefined);
|
467
519
|
const leaveTextMode = applicationStore.guardUnhandledError(() => flowResult(editorStore.toggleTextMode()));
|
520
|
+
const globalCompile = applicationStore.guardUnhandledError(() => flowResult(grammarModeState.globalCompile()));
|
468
521
|
const toggleWordWrap = () => {
|
469
522
|
grammarTextEditorState.setWrapText(!grammarTextEditorState.wrapText);
|
470
523
|
editorStore.applicationStore.settingService.persistValue(LEGEND_STUDIO_SETTING_KEY.EDITOR_WRAP_TEXT, grammarTextEditorState.wrapText);
|
@@ -477,18 +530,36 @@ export const GrammarTextEditor = observer(() => {
|
|
477
530
|
language: CODE_EDITOR_LANGUAGE.PURE,
|
478
531
|
theme: CODE_EDITOR_THEME.DEFAULT_DARK,
|
479
532
|
renderValidationDecorations: 'on',
|
533
|
+
wordWrap: grammarTextEditorState.wordWrapOtion,
|
534
|
+
readOnly: editorStore.editorMode.disableEditing,
|
480
535
|
});
|
481
536
|
_editor.onDidChangeModelContent(() => {
|
482
537
|
grammarTextEditorState.setGraphGrammarText(getCodeEditorValue(_editor));
|
483
538
|
clearMarkers();
|
484
539
|
// NOTE: we can technically can reset the current element label regex string here
|
485
540
|
// but if we do that on first load, the cursor will not jump to the current element
|
486
|
-
// also, it's better to place that logic in an effect that watches for the regex string
|
541
|
+
// also, it's better to place that logic in an effect that watches for the regex string.
|
542
|
+
// this is done by watching `forcedCursorPosition` in the useEffect
|
487
543
|
});
|
488
544
|
_editor.focus(); // focus on the editor initially
|
545
|
+
_editor.getModel()?.updateOptions({ tabSize: DEFAULT_TAB_SIZE });
|
546
|
+
_editor.addAction({
|
547
|
+
id: GRAMMAR_MODE_EDITOR_ACTION.GO_TO_ELEMENT_DEFINITION,
|
548
|
+
label: 'Go To Element',
|
549
|
+
keybindings: [KeyMod.CtrlCmd | KeyCode.KeyB],
|
550
|
+
run: (ed) => {
|
551
|
+
goToElement(ed, grammarModeState);
|
552
|
+
},
|
553
|
+
});
|
489
554
|
setEditor(_editor);
|
490
555
|
}
|
491
|
-
}, [
|
556
|
+
}, [
|
557
|
+
editorStore,
|
558
|
+
applicationStore,
|
559
|
+
editor,
|
560
|
+
grammarTextEditorState,
|
561
|
+
grammarModeState,
|
562
|
+
]);
|
492
563
|
// Drag and Drop
|
493
564
|
const extraElementDragTypes = editorStore.pluginManager
|
494
565
|
.getApplicationPlugins()
|
@@ -529,146 +600,112 @@ export const GrammarTextEditor = observer(() => {
|
|
529
600
|
if (currentValue !== value) {
|
530
601
|
editor.setValue(value);
|
531
602
|
}
|
532
|
-
editor.updateOptions({
|
533
|
-
wordWrap: grammarTextEditorState.wrapText ? 'on' : 'off',
|
534
|
-
});
|
535
603
|
resetLineNumberGutterWidth(editor);
|
536
604
|
const editorModel = editor.getModel();
|
537
605
|
if (editorModel) {
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
startColumn: error.sourceInformation.startColumn,
|
546
|
-
endLineNumber: error.sourceInformation.endLine,
|
547
|
-
endColumn: error.sourceInformation.endColumn,
|
548
|
-
},
|
549
|
-
]);
|
550
|
-
}
|
551
|
-
if (!editorStore.graphState.areProblemsStale &&
|
552
|
-
editorStore.graphState.warnings.length) {
|
553
|
-
setWarningMarkers(editorModel, editorStore.graphState.warnings
|
554
|
-
.map((warning) => {
|
555
|
-
if (!warning.sourceInformation) {
|
556
|
-
return undefined;
|
606
|
+
// hover
|
607
|
+
hoverProviderDisposer.current?.dispose();
|
608
|
+
hoverProviderDisposer.current = monacoLanguagesAPI.registerHoverProvider(CODE_EDITOR_LANGUAGE.PURE, {
|
609
|
+
provideHover: (model, position) => {
|
610
|
+
const currentWord = model.getWordAtPosition(position);
|
611
|
+
if (!currentWord) {
|
612
|
+
return { contents: [] };
|
557
613
|
}
|
558
|
-
|
559
|
-
|
560
|
-
startLineNumber:
|
561
|
-
startColumn:
|
562
|
-
endLineNumber:
|
563
|
-
endColumn:
|
614
|
+
// show documention for parser section
|
615
|
+
const lineTextIncludingWordRange = {
|
616
|
+
startLineNumber: position.lineNumber,
|
617
|
+
startColumn: 1,
|
618
|
+
endLineNumber: position.lineNumber,
|
619
|
+
endColumn: currentWord.endColumn,
|
564
620
|
};
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
if (!hasWhiteSpace(lineTextIncludingWord) &&
|
590
|
-
lineTextIncludingWord.startsWith(PARSER_SECTION_MARKER)) {
|
591
|
-
const parserKeyword = lineTextIncludingWord.substring(PARSER_SECTION_MARKER.length);
|
592
|
-
const doc = getParserDocumetation(editorStore, parserKeyword);
|
593
|
-
if (doc) {
|
594
|
-
return {
|
595
|
-
range: lineTextIncludingWordRange,
|
596
|
-
contents: [
|
597
|
-
doc.markdownText
|
598
|
-
? {
|
599
|
-
value: doc.markdownText.value,
|
600
|
-
}
|
601
|
-
: undefined,
|
602
|
-
doc.url
|
603
|
-
? {
|
604
|
-
value: `[See documentation](${doc.url})`,
|
605
|
-
}
|
606
|
-
: undefined,
|
607
|
-
].filter(isNonNullable),
|
608
|
-
};
|
621
|
+
const lineTextIncludingWord = model.getValueInRange(lineTextIncludingWordRange);
|
622
|
+
// NOTE: we don't need to trim here since the leading whitespace in front of
|
623
|
+
// the section header is considered invalid syntax in the grammar
|
624
|
+
if (!hasWhiteSpace(lineTextIncludingWord) &&
|
625
|
+
lineTextIncludingWord.startsWith(PARSER_SECTION_MARKER)) {
|
626
|
+
const parserKeyword = lineTextIncludingWord.substring(PARSER_SECTION_MARKER.length);
|
627
|
+
const doc = getParserDocumetation(editorStore, parserKeyword);
|
628
|
+
if (doc) {
|
629
|
+
return {
|
630
|
+
range: lineTextIncludingWordRange,
|
631
|
+
contents: [
|
632
|
+
doc.markdownText
|
633
|
+
? {
|
634
|
+
value: doc.markdownText.value,
|
635
|
+
}
|
636
|
+
: undefined,
|
637
|
+
doc.url
|
638
|
+
? {
|
639
|
+
value: `[See documentation](${doc.url})`,
|
640
|
+
}
|
641
|
+
: undefined,
|
642
|
+
].filter(isNonNullable),
|
643
|
+
};
|
644
|
+
}
|
609
645
|
}
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
.
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
646
|
+
// show documentation for parser element
|
647
|
+
const textUntilPosition = model.getValueInRange({
|
648
|
+
startLineNumber: 1,
|
649
|
+
startColumn: 1,
|
650
|
+
endLineNumber: position.lineNumber,
|
651
|
+
endColumn: position.column,
|
652
|
+
});
|
653
|
+
const allParserSectionHeaders =
|
654
|
+
// NOTE: since `###Pure` is implicitly considered as the first section, we prepend it to the text
|
655
|
+
`${PARSER_SECTION_MARKER}${PURE_PARSER.PURE}\n${textUntilPosition}`
|
656
|
+
.split('\n')
|
657
|
+
.filter((line) => line.startsWith(PARSER_SECTION_MARKER));
|
658
|
+
const currentSectionParserKeyword = getSectionParserNameFromLineText(allParserSectionHeaders[allParserSectionHeaders.length - 1] ??
|
659
|
+
'');
|
660
|
+
if (currentSectionParserKeyword) {
|
661
|
+
const doc = getParserElementDocumentation(editorStore, currentSectionParserKeyword, currentWord.word);
|
662
|
+
if (doc) {
|
663
|
+
return {
|
664
|
+
range: {
|
665
|
+
startLineNumber: position.lineNumber,
|
666
|
+
startColumn: currentWord.startColumn,
|
667
|
+
endLineNumber: position.lineNumber,
|
668
|
+
endColumn: currentWord.endColumn,
|
669
|
+
},
|
670
|
+
contents: [
|
671
|
+
doc.markdownText
|
672
|
+
? {
|
673
|
+
value: doc.markdownText.value,
|
674
|
+
}
|
675
|
+
: undefined,
|
676
|
+
doc.url
|
677
|
+
? {
|
678
|
+
value: `[See documentation](${doc.url})`,
|
679
|
+
}
|
680
|
+
: undefined,
|
681
|
+
].filter(isNonNullable),
|
682
|
+
};
|
683
|
+
}
|
647
684
|
}
|
648
|
-
|
649
|
-
return { contents: [] };
|
650
|
-
},
|
651
|
-
});
|
652
|
-
// suggestion
|
653
|
-
suggestionProviderDisposer.current?.dispose();
|
654
|
-
suggestionProviderDisposer.current =
|
655
|
-
monacoLanguagesAPI.registerCompletionItemProvider(CODE_EDITOR_LANGUAGE.PURE, {
|
656
|
-
// NOTE: we need to specify this to show suggestions for section
|
657
|
-
// because by default, only alphanumeric characters trigger completion item provider
|
658
|
-
// See https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.CompletionContext.html#triggerCharacter
|
659
|
-
// See https://github.com/microsoft/monaco-editor/issues/2530#issuecomment-861757198
|
660
|
-
triggerCharacters: ['#'],
|
661
|
-
provideCompletionItems: (model, position) => {
|
662
|
-
let suggestions = [];
|
663
|
-
// suggestions for parser keyword
|
664
|
-
suggestions = suggestions.concat(getParserKeywordSuggestions(position, model, collectParserKeywordSuggestions(editorStore)));
|
665
|
-
// suggestions for parser element snippets
|
666
|
-
suggestions = suggestions.concat(getParserElementSnippetSuggestions(position, model, (parserName) => collectParserElementSnippetSuggestions(editorStore, parserName)));
|
667
|
-
// inline code snippet suggestions
|
668
|
-
suggestions = suggestions.concat(getInlineSnippetSuggestions(position, model));
|
669
|
-
return { suggestions };
|
685
|
+
return { contents: [] };
|
670
686
|
},
|
671
687
|
});
|
688
|
+
// suggestion
|
689
|
+
suggestionProviderDisposer.current?.dispose();
|
690
|
+
suggestionProviderDisposer.current =
|
691
|
+
monacoLanguagesAPI.registerCompletionItemProvider(CODE_EDITOR_LANGUAGE.PURE, {
|
692
|
+
// NOTE: we need to specify this to show suggestions for section
|
693
|
+
// because by default, only alphanumeric characters trigger completion item provider
|
694
|
+
// See https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.CompletionContext.html#triggerCharacter
|
695
|
+
// See https://github.com/microsoft/monaco-editor/issues/2530#issuecomment-861757198
|
696
|
+
triggerCharacters: ['#'],
|
697
|
+
provideCompletionItems: (model, position) => {
|
698
|
+
let suggestions = [];
|
699
|
+
// suggestions for parser keyword
|
700
|
+
suggestions = suggestions.concat(getParserKeywordSuggestions(position, model, collectParserKeywordSuggestions(editorStore)));
|
701
|
+
// suggestions for parser element snippets
|
702
|
+
suggestions = suggestions.concat(getParserElementSnippetSuggestions(position, model, (parserName) => collectParserElementSnippetSuggestions(editorStore, parserName)));
|
703
|
+
// inline code snippet suggestions
|
704
|
+
suggestions = suggestions.concat(getInlineSnippetSuggestions(position, model));
|
705
|
+
return { suggestions };
|
706
|
+
},
|
707
|
+
});
|
708
|
+
}
|
672
709
|
}
|
673
710
|
function toggleAutoFoldingElements() {
|
674
711
|
const autoFoldingElements = editorStore.pluginManager
|
@@ -676,37 +713,32 @@ export const GrammarTextEditor = observer(() => {
|
|
676
713
|
.flatMap((plugin) => plugin.getExtraGrammarTextEditorAutoFoldingElementCreatorKeywords?.() ??
|
677
714
|
[]);
|
678
715
|
const foldingClass = editor?.getContribution('editor.contrib.folding');
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
716
|
+
if (editor && foldingClass) {
|
717
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
718
|
+
foldingClass.getFoldingModel().then((foldingModel) => {
|
719
|
+
const model = editor.getModel();
|
720
|
+
const toggleFoldingLines = [];
|
721
|
+
model?.getLinesContent().forEach((line, j) => {
|
722
|
+
autoFoldingElements.forEach((elementName) => {
|
723
|
+
if (line.match(new RegExp(`^${elementName}`))) {
|
724
|
+
toggleFoldingLines.push(j + 2);
|
725
|
+
}
|
726
|
+
});
|
688
727
|
});
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
}
|
697
|
-
}
|
698
|
-
});
|
699
|
-
setFoldingElements(!allElementsFolded);
|
700
|
-
toggleFoldingLines.forEach((foldingLineRegion, i) => {
|
701
|
-
if (foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]) {
|
702
|
-
if (foldingModel._regions.isCollapsed(foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]
|
703
|
-
.regionIndex) !== elementsFolded) {
|
704
|
-
foldingModel.toggleCollapseState(foldingModel.getAllRegionsAtLine(foldingLineRegion));
|
728
|
+
const toFold = !elementsFolded;
|
729
|
+
toggleFoldingLines.forEach((foldingLineRegion, i) => {
|
730
|
+
if (foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]) {
|
731
|
+
if (foldingModel._regions.isCollapsed(foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]
|
732
|
+
.regionIndex) !== toFold) {
|
733
|
+
foldingModel.toggleCollapseState(foldingModel.getAllRegionsAtLine(foldingLineRegion));
|
734
|
+
}
|
705
735
|
}
|
706
|
-
}
|
736
|
+
});
|
737
|
+
setFoldingElements(toFold);
|
707
738
|
});
|
708
|
-
}
|
739
|
+
}
|
709
740
|
}
|
741
|
+
// below use effects watch over `forcedCursorPosition`, `wordWrapOtion`, `error`, `warnings` and reset them to the editor as needed
|
710
742
|
useEffect(() => {
|
711
743
|
if (editor && forcedCursorPosition) {
|
712
744
|
moveCursorToPosition(editor, forcedCursorPosition);
|
@@ -714,61 +746,72 @@ export const GrammarTextEditor = observer(() => {
|
|
714
746
|
}, [editor, forcedCursorPosition]);
|
715
747
|
useEffect(() => {
|
716
748
|
if (editor) {
|
717
|
-
|
718
|
-
|
719
|
-
.flatMap((plugin) => plugin.getExtraGrammarTextEditorAutoFoldingElementCreatorKeywords?.() ??
|
720
|
-
[]);
|
721
|
-
const foldingClass = editor.getContribution('editor.contrib.folding');
|
722
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
723
|
-
foldingClass.getFoldingModel().then((foldingModel) => {
|
724
|
-
foldingModel.onDidChange(() => {
|
725
|
-
const model = editor.getModel();
|
726
|
-
const toggleFoldingLines = [];
|
727
|
-
model?.getLinesContent().forEach((line, j) => {
|
728
|
-
autoFoldingElements.forEach((elementName) => {
|
729
|
-
if (line.match(new RegExp(`^${elementName}`))) {
|
730
|
-
toggleFoldingLines.push(j + 2);
|
731
|
-
}
|
732
|
-
});
|
733
|
-
});
|
734
|
-
let allElementsFolded = true;
|
735
|
-
toggleFoldingLines.forEach((foldingLineRegion, i) => {
|
736
|
-
if (foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]) {
|
737
|
-
if (foldingModel._regions.isCollapsed(foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]
|
738
|
-
.regionIndex) === false) {
|
739
|
-
allElementsFolded = false;
|
740
|
-
}
|
741
|
-
}
|
742
|
-
});
|
743
|
-
setFoldingElements(!allElementsFolded);
|
744
|
-
});
|
749
|
+
editor.updateOptions({
|
750
|
+
wordWrap: wordWrapOtion,
|
745
751
|
});
|
746
752
|
}
|
747
|
-
});
|
753
|
+
}, [editor, wordWrapOtion]);
|
754
|
+
useEffect(() => {
|
755
|
+
const editorModel = editor?.getModel();
|
756
|
+
if (editorModel && (error?.sourceInformation || warnings.length)) {
|
757
|
+
if (error?.sourceInformation) {
|
758
|
+
setErrorMarkers(editorModel, [
|
759
|
+
{
|
760
|
+
message: error.message,
|
761
|
+
startLineNumber: error.sourceInformation.startLine,
|
762
|
+
startColumn: error.sourceInformation.startColumn,
|
763
|
+
endLineNumber: error.sourceInformation.endLine,
|
764
|
+
endColumn: error.sourceInformation.endColumn,
|
765
|
+
},
|
766
|
+
]);
|
767
|
+
}
|
768
|
+
if (warnings.length) {
|
769
|
+
setWarningMarkers(editorModel, warnings
|
770
|
+
.map((warning) => {
|
771
|
+
if (!warning.sourceInformation) {
|
772
|
+
return undefined;
|
773
|
+
}
|
774
|
+
return {
|
775
|
+
message: warning.message,
|
776
|
+
startLineNumber: warning.sourceInformation.startLine,
|
777
|
+
startColumn: warning.sourceInformation.startColumn,
|
778
|
+
endLineNumber: warning.sourceInformation.endLine,
|
779
|
+
endColumn: warning.sourceInformation.endColumn,
|
780
|
+
};
|
781
|
+
})
|
782
|
+
.filter(isNonNullable));
|
783
|
+
}
|
784
|
+
}
|
785
|
+
}, [editor, error, warnings]);
|
786
|
+
// first load with grammar. auto fold element sections
|
748
787
|
useEffect(() => {
|
749
788
|
if (editor) {
|
789
|
+
const model = editor.getModel();
|
750
790
|
const autoFoldingElements = editorStore.pluginManager
|
751
791
|
.getApplicationPlugins()
|
752
792
|
.flatMap((plugin) => plugin.getExtraGrammarTextEditorAutoFoldingElementCreatorKeywords?.() ??
|
753
793
|
[]);
|
754
794
|
const foldingClass = editor.getContribution('editor.contrib.folding');
|
755
|
-
|
756
|
-
|
757
|
-
.getFoldingModel()
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
795
|
+
if (foldingClass && model) {
|
796
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
797
|
+
foldingClass.getFoldingModel().then((foldingModel) => {
|
798
|
+
if (foldingModel) {
|
799
|
+
const elementLinesToBeFolded = [];
|
800
|
+
model.getLinesContent().forEach((line, idx) => {
|
801
|
+
autoFoldingElements.forEach((elementName) => {
|
802
|
+
if (line.match(new RegExp(`^${elementName}`))) {
|
803
|
+
elementLinesToBeFolded.push(idx + 2);
|
804
|
+
}
|
805
|
+
});
|
806
|
+
});
|
807
|
+
elementLinesToBeFolded.forEach((lineToBeFolded) => {
|
808
|
+
const regionToFold = foldingModel.getAllRegionsAtLine(lineToBeFolded);
|
809
|
+
foldingModel.toggleCollapseState(regionToFold);
|
810
|
+
});
|
811
|
+
setFoldingElements(true);
|
812
|
+
}
|
770
813
|
});
|
771
|
-
}
|
814
|
+
}
|
772
815
|
}
|
773
816
|
}, [editor, editorStore.pluginManager]);
|
774
817
|
// NOTE: dispose the editor to prevent potential memory-leak
|
@@ -781,10 +824,10 @@ export const GrammarTextEditor = observer(() => {
|
|
781
824
|
suggestionProviderDisposer.current?.dispose();
|
782
825
|
}, [editor]);
|
783
826
|
useApplicationNavigationContext(LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.TEXT_MODE_EDITOR);
|
784
|
-
return (_jsxs("div", { className: "panel editor-group", children: [_jsxs("div", { className: "panel__header editor-group__header", children: [
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
827
|
+
return (_jsxs("div", { className: "panel editor-group", children: [_jsxs("div", { className: "panel__header editor-group__header", children: [_jsx("div", { className: "editor-group__header__tabs", children: _jsx(ContextMenu, { className: "editor-group__text-mode__tab editor-group__text-mode__tab--active", content: _jsx(GrammarTextEditorHeaderTabContextMenu, {}), children: _jsx("div", { className: "editor-group__text-mode__tab__label", children: grammarModeState.headerLabel }) }) }), _jsxs("div", { className: "editor-group__header__actions", children: [_jsx("div", { className: "editor-group__text-mode__action", children: _jsx("button", { title: "Compile (F9)", onClick: globalCompile, className: "editor-group__text-mode-btn btn--dark", children: "Compile" }) }), _jsx("div", { className: "editor-group__text-mode__action", children: _jsx("button", { title: "Click to exit text mode and go back to form mode (F8)", onClick: leaveTextMode, className: "editor-group__text-mode-btn btn--dark", children: "Exit Text Mode" }) }), _jsx("div", { className: "query-builder__header__actions", children: _jsxs(DropdownMenu, { className: "query-builder__header__advanced-dropdown", title: "Show Advanced Menu...", content: _jsxs(MenuContent, { children: [_jsxs(MenuContentItem, { onClick: toggleWordWrap, children: [_jsx(MenuContentItemIcon, { children: grammarTextEditorState.wrapText ? _jsx(CheckIcon, {}) : null }), _jsx(MenuContentItemLabel, { children: "Wrap Overflowing Words" })] }), _jsxs(MenuContentItem, { onClick: toggleAutoFoldingElements, children: [_jsx(MenuContentItemIcon, { children: elementsFolded ? _jsx(CheckIcon, {}) : null }), _jsx(MenuContentItemLabel, { children: "Auto Fold Elements" })] })] }), menuProps: {
|
828
|
+
anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
|
829
|
+
transformOrigin: { vertical: 'top', horizontal: 'right' },
|
830
|
+
elevation: 7,
|
831
|
+
}, children: [_jsx("div", { className: "query-builder__header__advanced-dropdown__label", children: "Advanced" }), _jsx(CaretDownIcon, { className: "query-builder__header__advanced-dropdown__icon" })] }) })] })] }), _jsxs(PanelContent, { className: "editor-group__content", children: [_jsx(PanelLoadingIndicator, { isLoading: editorStore.graphState.isRunningGlobalCompile }), _jsx("div", { className: "code-editor__container", children: _jsx("div", { className: "code-editor__body", ref: textEditorRef }) })] })] }));
|
789
832
|
});
|
790
833
|
//# sourceMappingURL=GrammarTextEditor.js.map
|