@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
@@ -20,17 +20,20 @@ import {
|
|
20
20
|
type IDisposable,
|
21
21
|
editor as monacoEditorAPI,
|
22
22
|
languages as monacoLanguagesAPI,
|
23
|
+
KeyCode,
|
24
|
+
KeyMod,
|
23
25
|
} from 'monaco-editor';
|
24
26
|
import {
|
25
27
|
ContextMenu,
|
26
|
-
clsx,
|
27
|
-
WordWrapIcon,
|
28
|
-
MoreHorizontalIcon,
|
29
|
-
FoldIcon,
|
30
|
-
UnfoldIcon,
|
31
28
|
PanelContent,
|
32
29
|
MenuContent,
|
33
30
|
MenuContentItem,
|
31
|
+
PanelLoadingIndicator,
|
32
|
+
CaretDownIcon,
|
33
|
+
DropdownMenu,
|
34
|
+
MenuContentItemIcon,
|
35
|
+
CheckIcon,
|
36
|
+
MenuContentItemLabel,
|
34
37
|
} from '@finos/legend-art';
|
35
38
|
import {
|
36
39
|
DEFAULT_TAB_SIZE,
|
@@ -64,12 +67,20 @@ import { useDrop } from 'react-dnd';
|
|
64
67
|
import type { DSL_LegendStudioApplicationPlugin_Extension } from '../../../stores/LegendStudioApplicationPlugin.js';
|
65
68
|
import { flowResult } from 'mobx';
|
66
69
|
import { useEditorStore } from '../EditorStoreProvider.js';
|
67
|
-
import { hasWhiteSpace, isNonNullable } from '@finos/legend-shared';
|
68
70
|
import {
|
71
|
+
LogEvent,
|
72
|
+
assertErrorThrown,
|
73
|
+
assertTrue,
|
74
|
+
hasWhiteSpace,
|
75
|
+
isNonNullable,
|
76
|
+
} from '@finos/legend-shared';
|
77
|
+
import {
|
78
|
+
ELEMENT_PATH_DELIMITER,
|
69
79
|
PARSER_SECTION_MARKER,
|
70
80
|
PURE_CONNECTION_NAME,
|
71
81
|
PURE_ELEMENT_NAME,
|
72
82
|
PURE_PARSER,
|
83
|
+
isValidFullPath,
|
73
84
|
} from '@finos/legend-graph';
|
74
85
|
import type { EditorStore } from '../../../stores/editor/EditorStore.js';
|
75
86
|
import { LEGEND_STUDIO_DOCUMENTATION_KEY } from '../../../__lib__/LegendStudioDocumentation.js';
|
@@ -109,7 +120,11 @@ import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../__lib
|
|
109
120
|
import type { DSL_Mapping_LegendStudioApplicationPlugin_Extension } from '../../../stores/extensions/DSL_Mapping_LegendStudioApplicationPlugin_Extension.js';
|
110
121
|
import type { STO_Relational_LegendStudioApplicationPlugin_Extension } from '../../../stores/extensions/STO_Relational_LegendStudioApplicationPlugin_Extension.js';
|
111
122
|
import { LEGEND_STUDIO_SETTING_KEY } from '../../../__lib__/LegendStudioSetting.js';
|
112
|
-
import {
|
123
|
+
import {
|
124
|
+
GRAMMAR_MODE_EDITOR_ACTION,
|
125
|
+
GraphEditGrammarModeState,
|
126
|
+
} from '../../../stores/editor/GraphEditGrammarModeState.js';
|
127
|
+
import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
|
113
128
|
|
114
129
|
export const GrammarTextEditorHeaderTabContextMenu = observer(
|
115
130
|
forwardRef<HTMLDivElement, { children?: React.ReactNode }>(
|
@@ -678,6 +693,81 @@ const collectParserElementSnippetSuggestions = (
|
|
678
693
|
return [];
|
679
694
|
};
|
680
695
|
|
696
|
+
const resolveElementPathFromCurrentPosition = (
|
697
|
+
_editor: monacoEditorAPI.ICodeEditor,
|
698
|
+
grammarModeState: GraphEditGrammarModeState,
|
699
|
+
): string | undefined => {
|
700
|
+
let elementPath = '';
|
701
|
+
try {
|
702
|
+
const model = _editor.getModel();
|
703
|
+
let position = _editor.getPosition();
|
704
|
+
let maxWords = 0;
|
705
|
+
if (model) {
|
706
|
+
while (position && maxWords < 30) {
|
707
|
+
const currentWord = model.getWordAtPosition(position);
|
708
|
+
const lineNumber = position.lineNumber;
|
709
|
+
position = null;
|
710
|
+
maxWords += 1;
|
711
|
+
if (currentWord) {
|
712
|
+
const wordStartPost = {
|
713
|
+
lineNumber: lineNumber,
|
714
|
+
column: currentWord.startColumn,
|
715
|
+
};
|
716
|
+
const startPost = model.modifyPosition(wordStartPost, -2);
|
717
|
+
elementPath = currentWord.word + elementPath;
|
718
|
+
const pathDelimiterRange = {
|
719
|
+
startLineNumber: startPost.lineNumber,
|
720
|
+
startColumn: startPost.column,
|
721
|
+
endLineNumber: wordStartPost.lineNumber,
|
722
|
+
endColumn: wordStartPost.column,
|
723
|
+
};
|
724
|
+
const packageRange = model.getValueInRange(
|
725
|
+
model.validateRange(pathDelimiterRange),
|
726
|
+
);
|
727
|
+
if (packageRange === ELEMENT_PATH_DELIMITER) {
|
728
|
+
elementPath = packageRange + elementPath;
|
729
|
+
position = model.modifyPosition(startPost, -1);
|
730
|
+
}
|
731
|
+
}
|
732
|
+
}
|
733
|
+
}
|
734
|
+
assertTrue(
|
735
|
+
isValidFullPath(elementPath),
|
736
|
+
`Unable to go to element definition. Not valid element path: ${elementPath}`,
|
737
|
+
);
|
738
|
+
return elementPath;
|
739
|
+
} catch (error) {
|
740
|
+
assertErrorThrown(error);
|
741
|
+
grammarModeState.editorStore.applicationStore.logService.error(
|
742
|
+
LogEvent.create(
|
743
|
+
LEGEND_STUDIO_APP_EVENT.TEXT_MODE_ACTION_KEYBOARD_SHORTCUT_GO_TO_DEFINITION__ERROR,
|
744
|
+
),
|
745
|
+
error,
|
746
|
+
);
|
747
|
+
}
|
748
|
+
return undefined;
|
749
|
+
};
|
750
|
+
|
751
|
+
const goToElement = (
|
752
|
+
_editor: monacoEditorAPI.ICodeEditor,
|
753
|
+
grammarModeState: GraphEditGrammarModeState,
|
754
|
+
): void => {
|
755
|
+
grammarModeState.editorStore.applicationStore.logService.info(
|
756
|
+
LogEvent.create(
|
757
|
+
LEGEND_STUDIO_APP_EVENT.TEXT_MODE_ACTION_KEYBOARD_SHORTCUT_GO_TO_DEFINITION__LAUNCH,
|
758
|
+
),
|
759
|
+
);
|
760
|
+
const elementPath = resolveElementPathFromCurrentPosition(
|
761
|
+
_editor,
|
762
|
+
grammarModeState,
|
763
|
+
);
|
764
|
+
if (elementPath) {
|
765
|
+
flowResult(grammarModeState.goToElement(elementPath)).catch(
|
766
|
+
grammarModeState.editorStore.applicationStore.alertUnhandledError,
|
767
|
+
);
|
768
|
+
}
|
769
|
+
};
|
770
|
+
|
681
771
|
export const GrammarTextEditor = observer(() => {
|
682
772
|
const [editor, setEditor] = useState<
|
683
773
|
monacoEditorAPI.IStandaloneCodeEditor | undefined
|
@@ -689,9 +779,11 @@ export const GrammarTextEditor = observer(() => {
|
|
689
779
|
);
|
690
780
|
const grammarTextEditorState = grammarModeState.grammarTextEditorState;
|
691
781
|
const error = editorStore.graphState.error;
|
782
|
+
const warnings = editorStore.graphState.warnings;
|
692
783
|
const [elementsFolded, setFoldingElements] = useState(false);
|
693
784
|
|
694
785
|
const forcedCursorPosition = grammarTextEditorState.forcedCursorPosition;
|
786
|
+
const wordWrapOtion = grammarTextEditorState.wordWrapOtion;
|
695
787
|
const value = normalizeLineEnding(grammarTextEditorState.graphGrammarText);
|
696
788
|
const textEditorRef = useRef<HTMLDivElement>(null);
|
697
789
|
const hoverProviderDisposer = useRef<IDisposable | undefined>(undefined);
|
@@ -701,6 +793,10 @@ export const GrammarTextEditor = observer(() => {
|
|
701
793
|
flowResult(editorStore.toggleTextMode()),
|
702
794
|
);
|
703
795
|
|
796
|
+
const globalCompile = applicationStore.guardUnhandledError(() =>
|
797
|
+
flowResult(grammarModeState.globalCompile()),
|
798
|
+
);
|
799
|
+
|
704
800
|
const toggleWordWrap = (): void => {
|
705
801
|
grammarTextEditorState.setWrapText(!grammarTextEditorState.wrapText);
|
706
802
|
editorStore.applicationStore.settingService.persistValue(
|
@@ -717,18 +813,36 @@ export const GrammarTextEditor = observer(() => {
|
|
717
813
|
language: CODE_EDITOR_LANGUAGE.PURE,
|
718
814
|
theme: CODE_EDITOR_THEME.DEFAULT_DARK,
|
719
815
|
renderValidationDecorations: 'on',
|
816
|
+
wordWrap: grammarTextEditorState.wordWrapOtion,
|
817
|
+
readOnly: editorStore.editorMode.disableEditing,
|
720
818
|
});
|
721
819
|
_editor.onDidChangeModelContent(() => {
|
722
820
|
grammarTextEditorState.setGraphGrammarText(getCodeEditorValue(_editor));
|
723
821
|
clearMarkers();
|
724
822
|
// NOTE: we can technically can reset the current element label regex string here
|
725
823
|
// but if we do that on first load, the cursor will not jump to the current element
|
726
|
-
// also, it's better to place that logic in an effect that watches for the regex string
|
824
|
+
// also, it's better to place that logic in an effect that watches for the regex string.
|
825
|
+
// this is done by watching `forcedCursorPosition` in the useEffect
|
727
826
|
});
|
728
827
|
_editor.focus(); // focus on the editor initially
|
828
|
+
_editor.getModel()?.updateOptions({ tabSize: DEFAULT_TAB_SIZE });
|
829
|
+
_editor.addAction({
|
830
|
+
id: GRAMMAR_MODE_EDITOR_ACTION.GO_TO_ELEMENT_DEFINITION,
|
831
|
+
label: 'Go To Element',
|
832
|
+
keybindings: [KeyMod.CtrlCmd | KeyCode.KeyB],
|
833
|
+
run: (ed: monacoEditorAPI.ICodeEditor): void => {
|
834
|
+
goToElement(ed, grammarModeState);
|
835
|
+
},
|
836
|
+
});
|
729
837
|
setEditor(_editor);
|
730
838
|
}
|
731
|
-
}, [
|
839
|
+
}, [
|
840
|
+
editorStore,
|
841
|
+
applicationStore,
|
842
|
+
editor,
|
843
|
+
grammarTextEditorState,
|
844
|
+
grammarModeState,
|
845
|
+
]);
|
732
846
|
|
733
847
|
// Drag and Drop
|
734
848
|
const extraElementDragTypes = editorStore.pluginManager
|
@@ -776,206 +890,163 @@ export const GrammarTextEditor = observer(() => {
|
|
776
890
|
[extraElementDragTypes, handleDrop],
|
777
891
|
);
|
778
892
|
dropConnector(textEditorRef);
|
779
|
-
|
780
893
|
if (editor) {
|
781
894
|
// Set the value of the editor
|
782
895
|
const currentValue = getCodeEditorValue(editor);
|
783
896
|
if (currentValue !== value) {
|
784
897
|
editor.setValue(value);
|
785
898
|
}
|
786
|
-
editor.updateOptions({
|
787
|
-
wordWrap: grammarTextEditorState.wrapText ? 'on' : 'off',
|
788
|
-
});
|
789
899
|
resetLineNumberGutterWidth(editor);
|
790
900
|
const editorModel = editor.getModel();
|
791
901
|
if (editorModel) {
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
endLineNumber: error.sourceInformation.endLine,
|
803
|
-
endColumn: error.sourceInformation.endColumn,
|
804
|
-
},
|
805
|
-
]);
|
806
|
-
}
|
807
|
-
|
808
|
-
if (
|
809
|
-
!editorStore.graphState.areProblemsStale &&
|
810
|
-
editorStore.graphState.warnings.length
|
811
|
-
) {
|
812
|
-
setWarningMarkers(
|
813
|
-
editorModel,
|
814
|
-
editorStore.graphState.warnings
|
815
|
-
.map((warning) => {
|
816
|
-
if (!warning.sourceInformation) {
|
817
|
-
return undefined;
|
818
|
-
}
|
819
|
-
return {
|
820
|
-
message: warning.message,
|
821
|
-
startLineNumber: warning.sourceInformation.startLine,
|
822
|
-
startColumn: warning.sourceInformation.startColumn,
|
823
|
-
endLineNumber: warning.sourceInformation.endLine,
|
824
|
-
endColumn: warning.sourceInformation.endColumn,
|
825
|
-
};
|
826
|
-
})
|
827
|
-
.filter(isNonNullable),
|
828
|
-
);
|
829
|
-
}
|
830
|
-
}
|
831
|
-
// Disable editing if user is in viewer mode
|
832
|
-
editor.updateOptions({ readOnly: editorStore.editorMode.disableEditing });
|
833
|
-
|
834
|
-
// hover
|
835
|
-
hoverProviderDisposer.current?.dispose();
|
836
|
-
hoverProviderDisposer.current = monacoLanguagesAPI.registerHoverProvider(
|
837
|
-
CODE_EDITOR_LANGUAGE.PURE,
|
838
|
-
{
|
839
|
-
provideHover: (model, position) => {
|
840
|
-
const currentWord = model.getWordAtPosition(position);
|
841
|
-
if (!currentWord) {
|
842
|
-
return { contents: [] };
|
843
|
-
}
|
902
|
+
// hover
|
903
|
+
hoverProviderDisposer.current?.dispose();
|
904
|
+
hoverProviderDisposer.current = monacoLanguagesAPI.registerHoverProvider(
|
905
|
+
CODE_EDITOR_LANGUAGE.PURE,
|
906
|
+
{
|
907
|
+
provideHover: (model, position) => {
|
908
|
+
const currentWord = model.getWordAtPosition(position);
|
909
|
+
if (!currentWord) {
|
910
|
+
return { contents: [] };
|
911
|
+
}
|
844
912
|
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
);
|
855
|
-
// NOTE: we don't need to trim here since the leading whitespace in front of
|
856
|
-
// the section header is considered invalid syntax in the grammar
|
857
|
-
if (
|
858
|
-
!hasWhiteSpace(lineTextIncludingWord) &&
|
859
|
-
lineTextIncludingWord.startsWith(PARSER_SECTION_MARKER)
|
860
|
-
) {
|
861
|
-
const parserKeyword = lineTextIncludingWord.substring(
|
862
|
-
PARSER_SECTION_MARKER.length,
|
913
|
+
// show documention for parser section
|
914
|
+
const lineTextIncludingWordRange = {
|
915
|
+
startLineNumber: position.lineNumber,
|
916
|
+
startColumn: 1,
|
917
|
+
endLineNumber: position.lineNumber,
|
918
|
+
endColumn: currentWord.endColumn,
|
919
|
+
};
|
920
|
+
const lineTextIncludingWord = model.getValueInRange(
|
921
|
+
lineTextIncludingWordRange,
|
863
922
|
);
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
923
|
+
// NOTE: we don't need to trim here since the leading whitespace in front of
|
924
|
+
// the section header is considered invalid syntax in the grammar
|
925
|
+
if (
|
926
|
+
!hasWhiteSpace(lineTextIncludingWord) &&
|
927
|
+
lineTextIncludingWord.startsWith(PARSER_SECTION_MARKER)
|
928
|
+
) {
|
929
|
+
const parserKeyword = lineTextIncludingWord.substring(
|
930
|
+
PARSER_SECTION_MARKER.length,
|
931
|
+
);
|
932
|
+
const doc = getParserDocumetation(editorStore, parserKeyword);
|
933
|
+
if (doc) {
|
934
|
+
return {
|
935
|
+
range: lineTextIncludingWordRange,
|
936
|
+
contents: [
|
937
|
+
doc.markdownText
|
938
|
+
? {
|
939
|
+
value: doc.markdownText.value,
|
940
|
+
}
|
941
|
+
: undefined,
|
942
|
+
doc.url
|
943
|
+
? {
|
944
|
+
value: `[See documentation](${doc.url})`,
|
945
|
+
}
|
946
|
+
: undefined,
|
947
|
+
].filter(isNonNullable),
|
948
|
+
};
|
949
|
+
}
|
881
950
|
}
|
882
|
-
}
|
883
951
|
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
952
|
+
// show documentation for parser element
|
953
|
+
const textUntilPosition = model.getValueInRange({
|
954
|
+
startLineNumber: 1,
|
955
|
+
startColumn: 1,
|
956
|
+
endLineNumber: position.lineNumber,
|
957
|
+
endColumn: position.column,
|
958
|
+
});
|
959
|
+
const allParserSectionHeaders =
|
960
|
+
// NOTE: since `###Pure` is implicitly considered as the first section, we prepend it to the text
|
961
|
+
`${PARSER_SECTION_MARKER}${PURE_PARSER.PURE}\n${textUntilPosition}`
|
962
|
+
.split('\n')
|
963
|
+
.filter((line) => line.startsWith(PARSER_SECTION_MARKER));
|
964
|
+
const currentSectionParserKeyword =
|
965
|
+
getSectionParserNameFromLineText(
|
966
|
+
allParserSectionHeaders[allParserSectionHeaders.length - 1] ??
|
967
|
+
'',
|
968
|
+
);
|
969
|
+
if (currentSectionParserKeyword) {
|
970
|
+
const doc = getParserElementDocumentation(
|
971
|
+
editorStore,
|
972
|
+
currentSectionParserKeyword,
|
973
|
+
currentWord.word,
|
974
|
+
);
|
975
|
+
if (doc) {
|
976
|
+
return {
|
977
|
+
range: {
|
978
|
+
startLineNumber: position.lineNumber,
|
979
|
+
startColumn: currentWord.startColumn,
|
980
|
+
endLineNumber: position.lineNumber,
|
981
|
+
endColumn: currentWord.endColumn,
|
982
|
+
},
|
983
|
+
contents: [
|
984
|
+
doc.markdownText
|
985
|
+
? {
|
986
|
+
value: doc.markdownText.value,
|
987
|
+
}
|
988
|
+
: undefined,
|
989
|
+
doc.url
|
990
|
+
? {
|
991
|
+
value: `[See documentation](${doc.url})`,
|
992
|
+
}
|
993
|
+
: undefined,
|
994
|
+
].filter(isNonNullable),
|
995
|
+
};
|
996
|
+
}
|
926
997
|
}
|
927
|
-
}
|
928
998
|
|
929
|
-
|
999
|
+
return { contents: [] };
|
1000
|
+
},
|
930
1001
|
},
|
931
|
-
|
932
|
-
);
|
1002
|
+
);
|
933
1003
|
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
1004
|
+
// suggestion
|
1005
|
+
suggestionProviderDisposer.current?.dispose();
|
1006
|
+
suggestionProviderDisposer.current =
|
1007
|
+
monacoLanguagesAPI.registerCompletionItemProvider(
|
1008
|
+
CODE_EDITOR_LANGUAGE.PURE,
|
1009
|
+
{
|
1010
|
+
// NOTE: we need to specify this to show suggestions for section
|
1011
|
+
// because by default, only alphanumeric characters trigger completion item provider
|
1012
|
+
// See https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.CompletionContext.html#triggerCharacter
|
1013
|
+
// See https://github.com/microsoft/monaco-editor/issues/2530#issuecomment-861757198
|
1014
|
+
triggerCharacters: ['#'],
|
1015
|
+
provideCompletionItems: (model, position) => {
|
1016
|
+
let suggestions: monacoLanguagesAPI.CompletionItem[] = [];
|
947
1017
|
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
1018
|
+
// suggestions for parser keyword
|
1019
|
+
suggestions = suggestions.concat(
|
1020
|
+
getParserKeywordSuggestions(
|
1021
|
+
position,
|
1022
|
+
model,
|
1023
|
+
collectParserKeywordSuggestions(editorStore),
|
1024
|
+
),
|
1025
|
+
);
|
956
1026
|
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
1027
|
+
// suggestions for parser element snippets
|
1028
|
+
suggestions = suggestions.concat(
|
1029
|
+
getParserElementSnippetSuggestions(
|
1030
|
+
position,
|
1031
|
+
model,
|
1032
|
+
(parserName: string) =>
|
1033
|
+
collectParserElementSnippetSuggestions(
|
1034
|
+
editorStore,
|
1035
|
+
parserName,
|
1036
|
+
),
|
1037
|
+
),
|
1038
|
+
);
|
969
1039
|
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
1040
|
+
// inline code snippet suggestions
|
1041
|
+
suggestions = suggestions.concat(
|
1042
|
+
getInlineSnippetSuggestions(position, model),
|
1043
|
+
);
|
974
1044
|
|
975
|
-
|
1045
|
+
return { suggestions };
|
1046
|
+
},
|
976
1047
|
},
|
977
|
-
|
978
|
-
|
1048
|
+
);
|
1049
|
+
}
|
979
1050
|
}
|
980
1051
|
|
981
1052
|
function toggleAutoFoldingElements(): void {
|
@@ -989,80 +1060,7 @@ export const GrammarTextEditor = observer(() => {
|
|
989
1060
|
[],
|
990
1061
|
);
|
991
1062
|
const foldingClass = editor?.getContribution('editor.contrib.folding');
|
992
|
-
|
993
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
994
|
-
(foldingClass as any).getFoldingModel().then(
|
995
|
-
(foldingModel: {
|
996
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
997
|
-
_regions: any;
|
998
|
-
onDidChange: (arg0: () => void) => void;
|
999
|
-
getRegionAtLine: (arg0: unknown) => unknown;
|
1000
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
1001
|
-
getAllRegionsAtLine(arg0: unknown): any;
|
1002
|
-
toggleCollapseState: (arg0: unknown) => unknown;
|
1003
|
-
}) => {
|
1004
|
-
const model = editor?.getModel();
|
1005
|
-
const toggleFoldingLines: number[] = [];
|
1006
|
-
model?.getLinesContent().forEach((line, j) => {
|
1007
|
-
autoFoldingElements.forEach((elementName) => {
|
1008
|
-
if (line.match(new RegExp(`^${elementName}`))) {
|
1009
|
-
toggleFoldingLines.push(j + 2);
|
1010
|
-
}
|
1011
|
-
});
|
1012
|
-
});
|
1013
|
-
let allElementsFolded = true;
|
1014
|
-
toggleFoldingLines.forEach((foldingLineRegion, i) => {
|
1015
|
-
if (foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]) {
|
1016
|
-
if (
|
1017
|
-
foldingModel._regions.isCollapsed(
|
1018
|
-
foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]
|
1019
|
-
.regionIndex,
|
1020
|
-
) === false
|
1021
|
-
) {
|
1022
|
-
allElementsFolded = false;
|
1023
|
-
}
|
1024
|
-
}
|
1025
|
-
});
|
1026
|
-
|
1027
|
-
setFoldingElements(!allElementsFolded);
|
1028
|
-
|
1029
|
-
toggleFoldingLines.forEach((foldingLineRegion, i) => {
|
1030
|
-
if (foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]) {
|
1031
|
-
if (
|
1032
|
-
foldingModel._regions.isCollapsed(
|
1033
|
-
foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]
|
1034
|
-
.regionIndex,
|
1035
|
-
) !== elementsFolded
|
1036
|
-
) {
|
1037
|
-
foldingModel.toggleCollapseState(
|
1038
|
-
foldingModel.getAllRegionsAtLine(foldingLineRegion),
|
1039
|
-
);
|
1040
|
-
}
|
1041
|
-
}
|
1042
|
-
});
|
1043
|
-
},
|
1044
|
-
);
|
1045
|
-
}
|
1046
|
-
|
1047
|
-
useEffect(() => {
|
1048
|
-
if (editor && forcedCursorPosition) {
|
1049
|
-
moveCursorToPosition(editor, forcedCursorPosition);
|
1050
|
-
}
|
1051
|
-
}, [editor, forcedCursorPosition]);
|
1052
|
-
|
1053
|
-
useEffect(() => {
|
1054
|
-
if (editor) {
|
1055
|
-
const autoFoldingElements = editorStore.pluginManager
|
1056
|
-
.getApplicationPlugins()
|
1057
|
-
.flatMap(
|
1058
|
-
(plugin) =>
|
1059
|
-
(
|
1060
|
-
plugin as DSL_LegendStudioApplicationPlugin_Extension
|
1061
|
-
).getExtraGrammarTextEditorAutoFoldingElementCreatorKeywords?.() ??
|
1062
|
-
[],
|
1063
|
-
);
|
1064
|
-
const foldingClass = editor.getContribution('editor.contrib.folding');
|
1065
|
-
|
1063
|
+
if (editor && foldingClass) {
|
1066
1064
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
1067
1065
|
(foldingClass as any).getFoldingModel().then(
|
1068
1066
|
(foldingModel: {
|
@@ -1074,39 +1072,89 @@ export const GrammarTextEditor = observer(() => {
|
|
1074
1072
|
getAllRegionsAtLine(arg0: unknown): any;
|
1075
1073
|
toggleCollapseState: (arg0: unknown) => unknown;
|
1076
1074
|
}) => {
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
toggleFoldingLines.push(j + 2);
|
1084
|
-
}
|
1085
|
-
});
|
1086
|
-
});
|
1087
|
-
let allElementsFolded = true;
|
1088
|
-
toggleFoldingLines.forEach((foldingLineRegion, i) => {
|
1089
|
-
if (foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]) {
|
1090
|
-
if (
|
1091
|
-
foldingModel._regions.isCollapsed(
|
1092
|
-
foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]
|
1093
|
-
.regionIndex,
|
1094
|
-
) === false
|
1095
|
-
) {
|
1096
|
-
allElementsFolded = false;
|
1097
|
-
}
|
1075
|
+
const model = editor.getModel();
|
1076
|
+
const toggleFoldingLines: number[] = [];
|
1077
|
+
model?.getLinesContent().forEach((line, j) => {
|
1078
|
+
autoFoldingElements.forEach((elementName) => {
|
1079
|
+
if (line.match(new RegExp(`^${elementName}`))) {
|
1080
|
+
toggleFoldingLines.push(j + 2);
|
1098
1081
|
}
|
1099
1082
|
});
|
1100
|
-
|
1101
|
-
setFoldingElements(!allElementsFolded);
|
1102
1083
|
});
|
1084
|
+
const toFold = !elementsFolded;
|
1085
|
+
toggleFoldingLines.forEach((foldingLineRegion, i) => {
|
1086
|
+
if (foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]) {
|
1087
|
+
if (
|
1088
|
+
foldingModel._regions.isCollapsed(
|
1089
|
+
foldingModel.getAllRegionsAtLine(foldingLineRegion)[0]
|
1090
|
+
.regionIndex,
|
1091
|
+
) !== toFold
|
1092
|
+
) {
|
1093
|
+
foldingModel.toggleCollapseState(
|
1094
|
+
foldingModel.getAllRegionsAtLine(foldingLineRegion),
|
1095
|
+
);
|
1096
|
+
}
|
1097
|
+
}
|
1098
|
+
});
|
1099
|
+
setFoldingElements(toFold);
|
1103
1100
|
},
|
1104
1101
|
);
|
1105
1102
|
}
|
1106
|
-
}
|
1103
|
+
}
|
1104
|
+
|
1105
|
+
// below use effects watch over `forcedCursorPosition`, `wordWrapOtion`, `error`, `warnings` and reset them to the editor as needed
|
1106
|
+
useEffect(() => {
|
1107
|
+
if (editor && forcedCursorPosition) {
|
1108
|
+
moveCursorToPosition(editor, forcedCursorPosition);
|
1109
|
+
}
|
1110
|
+
}, [editor, forcedCursorPosition]);
|
1111
|
+
useEffect(() => {
|
1112
|
+
if (editor) {
|
1113
|
+
editor.updateOptions({
|
1114
|
+
wordWrap: wordWrapOtion,
|
1115
|
+
});
|
1116
|
+
}
|
1117
|
+
}, [editor, wordWrapOtion]);
|
1118
|
+
useEffect(() => {
|
1119
|
+
const editorModel = editor?.getModel();
|
1120
|
+
if (editorModel && (error?.sourceInformation || warnings.length)) {
|
1121
|
+
if (error?.sourceInformation) {
|
1122
|
+
setErrorMarkers(editorModel, [
|
1123
|
+
{
|
1124
|
+
message: error.message,
|
1125
|
+
startLineNumber: error.sourceInformation.startLine,
|
1126
|
+
startColumn: error.sourceInformation.startColumn,
|
1127
|
+
endLineNumber: error.sourceInformation.endLine,
|
1128
|
+
endColumn: error.sourceInformation.endColumn,
|
1129
|
+
},
|
1130
|
+
]);
|
1131
|
+
}
|
1132
|
+
if (warnings.length) {
|
1133
|
+
setWarningMarkers(
|
1134
|
+
editorModel,
|
1135
|
+
warnings
|
1136
|
+
.map((warning) => {
|
1137
|
+
if (!warning.sourceInformation) {
|
1138
|
+
return undefined;
|
1139
|
+
}
|
1140
|
+
return {
|
1141
|
+
message: warning.message,
|
1142
|
+
startLineNumber: warning.sourceInformation.startLine,
|
1143
|
+
startColumn: warning.sourceInformation.startColumn,
|
1144
|
+
endLineNumber: warning.sourceInformation.endLine,
|
1145
|
+
endColumn: warning.sourceInformation.endColumn,
|
1146
|
+
};
|
1147
|
+
})
|
1148
|
+
.filter(isNonNullable),
|
1149
|
+
);
|
1150
|
+
}
|
1151
|
+
}
|
1152
|
+
}, [editor, error, warnings]);
|
1107
1153
|
|
1154
|
+
// first load with grammar. auto fold element sections
|
1108
1155
|
useEffect(() => {
|
1109
1156
|
if (editor) {
|
1157
|
+
const model = editor.getModel();
|
1110
1158
|
const autoFoldingElements = editorStore.pluginManager
|
1111
1159
|
.getApplicationPlugins()
|
1112
1160
|
.flatMap(
|
@@ -1117,33 +1165,38 @@ export const GrammarTextEditor = observer(() => {
|
|
1117
1165
|
[],
|
1118
1166
|
);
|
1119
1167
|
const foldingClass = editor.getContribution('editor.contrib.folding');
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
}) => {
|
1131
|
-
const model = editor.getModel();
|
1132
|
-
const foldingLines: number[] = [];
|
1133
|
-
model?.getLinesContent().forEach((line, j) => {
|
1134
|
-
autoFoldingElements.forEach((elementName) => {
|
1135
|
-
if (line.match(new RegExp(`^${elementName}`))) {
|
1136
|
-
foldingLines.push(j + 2);
|
1168
|
+
if (foldingClass && model) {
|
1169
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
1170
|
+
(foldingClass as any).getFoldingModel().then(
|
1171
|
+
(
|
1172
|
+
foldingModel:
|
1173
|
+
| {
|
1174
|
+
onDidChange: (arg0: () => void) => void;
|
1175
|
+
getRegionAtLine: (arg0: unknown) => unknown;
|
1176
|
+
getAllRegionsAtLine(arg0: unknown): unknown;
|
1177
|
+
toggleCollapseState: (arg0: unknown) => unknown;
|
1137
1178
|
}
|
1179
|
+
| undefined,
|
1180
|
+
) => {
|
1181
|
+
if (foldingModel) {
|
1182
|
+
const elementLinesToBeFolded: number[] = [];
|
1183
|
+
model.getLinesContent().forEach((line, idx) => {
|
1184
|
+
autoFoldingElements.forEach((elementName) => {
|
1185
|
+
if (line.match(new RegExp(`^${elementName}`))) {
|
1186
|
+
elementLinesToBeFolded.push(idx + 2);
|
1187
|
+
}
|
1188
|
+
});
|
1138
1189
|
});
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
foldingModel.
|
1143
|
-
);
|
1144
|
-
|
1190
|
+
elementLinesToBeFolded.forEach((lineToBeFolded) => {
|
1191
|
+
const regionToFold =
|
1192
|
+
foldingModel.getAllRegionsAtLine(lineToBeFolded);
|
1193
|
+
foldingModel.toggleCollapseState(regionToFold);
|
1194
|
+
});
|
1195
|
+
setFoldingElements(true);
|
1196
|
+
}
|
1145
1197
|
},
|
1146
1198
|
);
|
1199
|
+
}
|
1147
1200
|
}
|
1148
1201
|
}, [editor, editorStore.pluginManager]);
|
1149
1202
|
|
@@ -1169,19 +1222,6 @@ export const GrammarTextEditor = observer(() => {
|
|
1169
1222
|
<div className="panel editor-group">
|
1170
1223
|
<div className="panel__header editor-group__header">
|
1171
1224
|
<div className="editor-group__header__tabs">
|
1172
|
-
<div className="editor-group__text-mode__tab">
|
1173
|
-
<button
|
1174
|
-
className="editor-group__text-mode__tab__label"
|
1175
|
-
disabled={
|
1176
|
-
editorStore.graphState.isApplicationLeavingGraphEditMode
|
1177
|
-
}
|
1178
|
-
onClick={leaveTextMode}
|
1179
|
-
tabIndex={-1}
|
1180
|
-
title="Click to exit text mode and go back to form mode"
|
1181
|
-
>
|
1182
|
-
<MoreHorizontalIcon />
|
1183
|
-
</button>
|
1184
|
-
</div>
|
1185
1225
|
<ContextMenu
|
1186
1226
|
className="editor-group__text-mode__tab editor-group__text-mode__tab--active"
|
1187
1227
|
content={<GrammarTextEditorHeaderTabContextMenu />}
|
@@ -1192,39 +1232,66 @@ export const GrammarTextEditor = observer(() => {
|
|
1192
1232
|
</ContextMenu>
|
1193
1233
|
</div>
|
1194
1234
|
<div className="editor-group__header__actions">
|
1195
|
-
<
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1235
|
+
<div className="editor-group__text-mode__action">
|
1236
|
+
<button
|
1237
|
+
title="Compile (F9)"
|
1238
|
+
onClick={globalCompile}
|
1239
|
+
className="editor-group__text-mode-btn btn--dark"
|
1240
|
+
>
|
1241
|
+
Compile
|
1242
|
+
</button>
|
1243
|
+
</div>
|
1244
|
+
<div className="editor-group__text-mode__action">
|
1245
|
+
<button
|
1246
|
+
title="Click to exit text mode and go back to form mode (F8)"
|
1247
|
+
onClick={leaveTextMode}
|
1248
|
+
className="editor-group__text-mode-btn btn--dark"
|
1249
|
+
>
|
1250
|
+
Exit Text Mode
|
1251
|
+
</button>
|
1252
|
+
</div>
|
1253
|
+
<div className="query-builder__header__actions">
|
1254
|
+
<DropdownMenu
|
1255
|
+
className="query-builder__header__advanced-dropdown"
|
1256
|
+
title="Show Advanced Menu..."
|
1257
|
+
content={
|
1258
|
+
<MenuContent>
|
1259
|
+
<MenuContentItem onClick={toggleWordWrap}>
|
1260
|
+
<MenuContentItemIcon>
|
1261
|
+
{grammarTextEditorState.wrapText ? <CheckIcon /> : null}
|
1262
|
+
</MenuContentItemIcon>
|
1263
|
+
<MenuContentItemLabel>
|
1264
|
+
Wrap Overflowing Words
|
1265
|
+
</MenuContentItemLabel>
|
1266
|
+
</MenuContentItem>
|
1267
|
+
<MenuContentItem onClick={toggleAutoFoldingElements}>
|
1268
|
+
<MenuContentItemIcon>
|
1269
|
+
{elementsFolded ? <CheckIcon /> : null}
|
1270
|
+
</MenuContentItemIcon>
|
1271
|
+
<MenuContentItemLabel>
|
1272
|
+
Auto Fold Elements
|
1273
|
+
</MenuContentItemLabel>
|
1274
|
+
</MenuContentItem>
|
1275
|
+
</MenuContent>
|
1276
|
+
}
|
1277
|
+
menuProps={{
|
1278
|
+
anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
|
1279
|
+
transformOrigin: { vertical: 'top', horizontal: 'right' },
|
1280
|
+
elevation: 7,
|
1281
|
+
}}
|
1282
|
+
>
|
1283
|
+
<div className="query-builder__header__advanced-dropdown__label">
|
1284
|
+
Advanced
|
1285
|
+
</div>
|
1286
|
+
<CaretDownIcon className="query-builder__header__advanced-dropdown__icon" />
|
1287
|
+
</DropdownMenu>
|
1288
|
+
</div>
|
1225
1289
|
</div>
|
1226
1290
|
</div>
|
1227
1291
|
<PanelContent className="editor-group__content">
|
1292
|
+
<PanelLoadingIndicator
|
1293
|
+
isLoading={editorStore.graphState.isRunningGlobalCompile}
|
1294
|
+
/>
|
1228
1295
|
<div className="code-editor__container">
|
1229
1296
|
<div className="code-editor__body" ref={textEditorRef} />
|
1230
1297
|
</div>
|