@finos/legend-code-editor 1.2.73

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.
Files changed (56) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +3 -0
  3. package/lib/CodeEditorTheme.d.ts +41 -0
  4. package/lib/CodeEditorTheme.d.ts.map +1 -0
  5. package/lib/CodeEditorTheme.js +99 -0
  6. package/lib/CodeEditorTheme.js.map +1 -0
  7. package/lib/CodeEditorUtils.d.ts +66 -0
  8. package/lib/CodeEditorUtils.d.ts.map +1 -0
  9. package/lib/CodeEditorUtils.js +139 -0
  10. package/lib/CodeEditorUtils.js.map +1 -0
  11. package/lib/PureLanguage.d.ts +38 -0
  12. package/lib/PureLanguage.d.ts.map +1 -0
  13. package/lib/PureLanguage.js +39 -0
  14. package/lib/PureLanguage.js.map +1 -0
  15. package/lib/PureLanguageCodeEditorSupport.d.ts +71 -0
  16. package/lib/PureLanguageCodeEditorSupport.d.ts.map +1 -0
  17. package/lib/PureLanguageCodeEditorSupport.js +238 -0
  18. package/lib/PureLanguageCodeEditorSupport.js.map +1 -0
  19. package/lib/PureLanguageService.d.ts +19 -0
  20. package/lib/PureLanguageService.d.ts.map +1 -0
  21. package/lib/PureLanguageService.js +373 -0
  22. package/lib/PureLanguageService.js.map +1 -0
  23. package/lib/index.d.ts +21 -0
  24. package/lib/index.d.ts.map +1 -0
  25. package/lib/index.js +21 -0
  26. package/lib/index.js.map +1 -0
  27. package/lib/themes/Github-Theme-dark-dimmed.json +613 -0
  28. package/lib/themes/Github-Theme-dark.json +513 -0
  29. package/lib/themes/Github-Theme-light.json +598 -0
  30. package/lib/themes/Material-Theme-Darker.json +816 -0
  31. package/lib/themes/Material-Theme-Default.json +816 -0
  32. package/lib/themes/MonacoEditorThemeUtils.d.ts +31 -0
  33. package/lib/themes/MonacoEditorThemeUtils.d.ts.map +1 -0
  34. package/lib/themes/MonacoEditorThemeUtils.js +88 -0
  35. package/lib/themes/MonacoEditorThemeUtils.js.map +1 -0
  36. package/lib/themes/OneDark-Pro-darker.json +2061 -0
  37. package/lib/themes/OneDark-Pro.json +2090 -0
  38. package/lib/themes/solarized-dark-color-theme.json +398 -0
  39. package/package.json +66 -0
  40. package/src/CodeEditorTheme.ts +151 -0
  41. package/src/CodeEditorUtils.ts +241 -0
  42. package/src/PureLanguage.ts +42 -0
  43. package/src/PureLanguageCodeEditorSupport.ts +341 -0
  44. package/src/PureLanguageService.ts +416 -0
  45. package/src/index.ts +22 -0
  46. package/src/themes/Github-Theme-dark-dimmed.json +613 -0
  47. package/src/themes/Github-Theme-dark.json +513 -0
  48. package/src/themes/Github-Theme-light.json +598 -0
  49. package/src/themes/Material-Theme-Darker.json +816 -0
  50. package/src/themes/Material-Theme-Default.json +816 -0
  51. package/src/themes/MonacoEditorThemeUtils.ts +128 -0
  52. package/src/themes/OneDark-Pro-darker.json +2061 -0
  53. package/src/themes/OneDark-Pro.json +2090 -0
  54. package/src/themes/README.md +8 -0
  55. package/src/themes/solarized-dark-color-theme.json +423 -0
  56. package/tsconfig.json +72 -0
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { editor as monacoEditorAPI, MarkerSeverity } from 'monaco-editor';
18
+ import {
19
+ CODE_EDITOR_THEME,
20
+ DEFAULT_DARK_THEME,
21
+ GITHUB_DARK_DIMMED_THEME,
22
+ GITHUB_DARK_THEME,
23
+ GITHUB_LIGHT_THEME,
24
+ MATERIAL_DARKER_THEME,
25
+ MATERIAL_DEFAULT_THEME,
26
+ ONE_DARK_PRO_DARKER_THEME,
27
+ ONE_DARK_PRO_THEME,
28
+ SOLARIZED_DARK_THEME,
29
+ } from './CodeEditorTheme.js';
30
+
31
+ export type CodeEditorPosition = {
32
+ lineNumber: number;
33
+ column: number;
34
+ };
35
+
36
+ /**
37
+ * Get the text value with LF line ending.
38
+ * This is needed since if there are CR `\r` characters in the text input
39
+ * (e.g. users of Windows doing copy/paste)
40
+ * the default mode of `monaco-editor` is `TextDefined` which means if the text
41
+ * contains CR character(s), it will automatically be treated as CRLF. As such, we want
42
+ * an utility method to extract the text value with line ending option LF
43
+ * to force omission of CR characters
44
+ * See https://github.com/finos/legend-studio/issues/608
45
+ */
46
+ export const getCodeEditorValue = (
47
+ editor: monacoEditorAPI.IStandaloneCodeEditor,
48
+ ): string =>
49
+ editor.getModel()?.getValue(monacoEditorAPI.EndOfLinePreference.LF) ?? '';
50
+
51
+ export const getBaseCodeEditorOptions =
52
+ (): monacoEditorAPI.IStandaloneEditorConstructionOptions =>
53
+ ({
54
+ contextmenu: false,
55
+ copyWithSyntaxHighlighting: false,
56
+ // NOTE: These following font options are needed (and CSS font-size option `.monaco-editor * { font-size: ... }` as well)
57
+ // in order to make the editor appear properly on multiple platform, the ligatures option is needed for Mac to display properly
58
+ // otherwise the cursor position relatively to text would be off
59
+ // Another potential cause for this misaligment is that the fonts are being lazy-loaded and made available after `monaco-editor`
60
+ // calculated the font-width, for this, we can use `remeasureFonts`, but our case here, `fontLigatures: true` seems
61
+ // to do the trick
62
+ // See https://github.com/microsoft/monaco-editor/issues/392
63
+ fontSize: 14,
64
+ // Enforce a fixed font-family to make cross platform display consistent (i.e. Mac defaults to use `Menlo` which is bigger than
65
+ // `Consolas` on Windows, etc.)
66
+ fontFamily: 'Roboto Mono',
67
+ // Enable font ligature: glyphs which combine the shapes of certain sequences of characters into a new form that makes for
68
+ // a more harmonious reading experience.
69
+ fontLigatures: true,
70
+ // Make sure hover or widget shown near boundary are not truncated by setting their position to `fixed`
71
+ fixedOverflowWidgets: true,
72
+ detectIndentation: false, // i.e. so we can force tab-size
73
+ tabSize: 2,
74
+ // The typing is currently not correct for `bracketPairColorization`, until this is fixed, we will remove the cast
75
+ // See https://github.com/microsoft/monaco-editor/issues/3013
76
+ 'bracketPairColorization.enabled': false,
77
+ automaticLayout: true,
78
+ }) as monacoEditorAPI.IStandaloneEditorConstructionOptions;
79
+
80
+ export const moveCursorToPosition = (
81
+ editor: monacoEditorAPI.ICodeEditor,
82
+ position: CodeEditorPosition,
83
+ ): void => {
84
+ if (!editor.hasTextFocus()) {
85
+ editor.focus();
86
+ } // focus the editor first so that it can shows the cursor
87
+ editor.revealPositionInCenter(position, 0);
88
+ editor.setPosition(position);
89
+ };
90
+
91
+ const INTERNAL__DUMMY_PROBLEM_MARKER_OWNER = 'dummy_problem_marker_owner';
92
+
93
+ export const setErrorMarkers = (
94
+ editorModel: monacoEditorAPI.ITextModel,
95
+ errors: {
96
+ message: string;
97
+ startLineNumber: number;
98
+ startColumn: number;
99
+ endLineNumber: number;
100
+ endColumn: number;
101
+ }[],
102
+ ownerId?: string,
103
+ ): void => {
104
+ monacoEditorAPI.setModelMarkers(
105
+ editorModel,
106
+ ownerId ?? INTERNAL__DUMMY_PROBLEM_MARKER_OWNER,
107
+ errors.map((error) => ({
108
+ startLineNumber: error.startLineNumber,
109
+ startColumn: error.startColumn,
110
+ endLineNumber: error.endLineNumber,
111
+ endColumn: error.endColumn + 1, // add a 1 to endColumn as monaco editor range is not inclusive
112
+ // NOTE: when the message is empty, no error tooltip is shown, we want to avoid this
113
+ message: error.message === '' ? '(no error message)' : error.message,
114
+ severity: MarkerSeverity.Error,
115
+ })),
116
+ );
117
+ };
118
+
119
+ export const setWarningMarkers = (
120
+ editorModel: monacoEditorAPI.ITextModel,
121
+ warnings: {
122
+ message: string;
123
+ startLineNumber: number;
124
+ startColumn: number;
125
+ endLineNumber: number;
126
+ endColumn: number;
127
+ }[],
128
+ ownerId?: string,
129
+ ): void => {
130
+ monacoEditorAPI.setModelMarkers(
131
+ editorModel,
132
+ ownerId ?? INTERNAL__DUMMY_PROBLEM_MARKER_OWNER,
133
+ warnings.map((warning) => ({
134
+ startLineNumber: warning.startLineNumber,
135
+ startColumn: warning.startColumn,
136
+ endColumn: warning.endColumn,
137
+ endLineNumber: warning.endLineNumber,
138
+ message:
139
+ warning.message === '' ? '(no warning message)' : warning.message,
140
+ severity: MarkerSeverity.Warning,
141
+ })),
142
+ );
143
+ };
144
+
145
+ export const clearMarkers = (ownerId?: string): void => {
146
+ monacoEditorAPI.removeAllMarkers(
147
+ ownerId ?? INTERNAL__DUMMY_PROBLEM_MARKER_OWNER,
148
+ );
149
+ };
150
+
151
+ /**
152
+ * This method eliminates CR '\r' character(s) in the provided text value.
153
+ */
154
+ export const normalizeLineEnding = (val: string): string =>
155
+ val.replace(/\r/g, '');
156
+
157
+ // We need to dynamically adjust the width of the line number gutter, otherwise as the document gets
158
+ // larger, the left margin will start to shrink
159
+ // See https://github.com/microsoft/monaco-editor/issues/2206
160
+ export const resetLineNumberGutterWidth = (
161
+ editor: monacoEditorAPI.ICodeEditor,
162
+ ): void => {
163
+ const currentValue = editor.getValue();
164
+ editor.updateOptions({
165
+ lineNumbersMinChars: Math.max(
166
+ Math.floor(Math.log10(currentValue.split(/\r\n|\r|\n/g).length)) + 3,
167
+ 5,
168
+ ),
169
+ });
170
+ };
171
+
172
+ export const configureCodeEditor = async (
173
+ fontFamily: string,
174
+ onError: (error: Error) => void,
175
+ ): Promise<void> => {
176
+ // themes
177
+ monacoEditorAPI.defineTheme(
178
+ CODE_EDITOR_THEME.DEFAULT_DARK,
179
+ DEFAULT_DARK_THEME,
180
+ );
181
+ monacoEditorAPI.defineTheme(
182
+ CODE_EDITOR_THEME.SOLARIZED_DARK,
183
+ SOLARIZED_DARK_THEME,
184
+ );
185
+ monacoEditorAPI.defineTheme(CODE_EDITOR_THEME.GITHUB_DARK, GITHUB_DARK_THEME);
186
+ monacoEditorAPI.defineTheme(
187
+ CODE_EDITOR_THEME.GITHUB_DARK_DIMMED,
188
+ GITHUB_DARK_DIMMED_THEME,
189
+ );
190
+ monacoEditorAPI.defineTheme(
191
+ CODE_EDITOR_THEME.GITHUB_LIGHT,
192
+ GITHUB_LIGHT_THEME,
193
+ );
194
+ monacoEditorAPI.defineTheme(
195
+ CODE_EDITOR_THEME.MATERIAL_DEFAULT,
196
+ MATERIAL_DEFAULT_THEME,
197
+ );
198
+ monacoEditorAPI.defineTheme(
199
+ CODE_EDITOR_THEME.MATERIAL_DARKER,
200
+ MATERIAL_DARKER_THEME,
201
+ );
202
+ monacoEditorAPI.defineTheme(
203
+ CODE_EDITOR_THEME.ONE_DARK_PRO,
204
+ ONE_DARK_PRO_THEME,
205
+ );
206
+ monacoEditorAPI.defineTheme(
207
+ CODE_EDITOR_THEME.ONE_DARK_PRO_DARKER,
208
+ ONE_DARK_PRO_DARKER_THEME,
209
+ );
210
+
211
+ /**
212
+ * Since we use a custom fonts for text-editor, we want to make sure the font is loaded before any text-editor is opened
213
+ * this is to ensure
214
+ */
215
+ const fontLoadFailureErrorMessage = `Monospaced font '${fontFamily}' has not been loaded properly, code editor might not display properly`;
216
+ await Promise.all(
217
+ [400, 700].map((weight) =>
218
+ document.fonts.load(`${weight} 1em ${fontFamily}`),
219
+ ),
220
+ )
221
+ .then(() => {
222
+ if (document.fonts.check(`1em ${fontFamily}`)) {
223
+ monacoEditorAPI.remeasureFonts();
224
+ } else {
225
+ onError(new Error(fontLoadFailureErrorMessage));
226
+ }
227
+ })
228
+ .catch(() => onError(new Error(fontLoadFailureErrorMessage)));
229
+ };
230
+
231
+ export enum CODE_EDITOR_LANGUAGE {
232
+ TEXT = 'plaintext',
233
+ PURE = 'pure',
234
+ JSON = 'json',
235
+ JAVA = 'java',
236
+ MARKDOWN = 'markdown',
237
+ SQL = 'sql',
238
+ XML = 'xml',
239
+ YAML = 'yaml',
240
+ GRAPHQL = 'graphql',
241
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ export enum PURE_GRAMMAR_TOKEN {
18
+ WHITESPACE = '',
19
+
20
+ KEYWORD = 'keyword',
21
+ IDENTIFIER = 'identifier',
22
+ OPERATOR = 'operator',
23
+ DELIMITER = 'delimiter',
24
+
25
+ PARSER = 'parser',
26
+ NUMBER = 'number',
27
+ DATE = 'date',
28
+ COLOR = 'color',
29
+ PACKAGE = 'package',
30
+ STRING = 'string',
31
+ COMMENT = 'comment',
32
+
33
+ LANGUAGE_STRUCT = 'language-struct',
34
+ MULTIPLICITY = 'multiplicity',
35
+ GENERICS = 'generics',
36
+ PROPERTY = 'property',
37
+ PARAMETER = 'parameter',
38
+ VARIABLE = 'variable',
39
+ TYPE = 'type',
40
+
41
+ INVALID = 'invalid',
42
+ }
@@ -0,0 +1,341 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { PARSER_SECTION_MARKER, PURE_PARSER } from '@finos/legend-graph';
18
+ import {
19
+ getNullableFirstEntry,
20
+ guaranteeNonNullable,
21
+ hasWhiteSpace,
22
+ type DocumentationEntry,
23
+ } from '@finos/legend-shared';
24
+ import {
25
+ type editor as monacoEditorAPI,
26
+ languages as monacoLanguagesAPI,
27
+ type IPosition,
28
+ } from 'monaco-editor';
29
+
30
+ /**
31
+ * This snippet suggestion is meant for an embedded content of an element
32
+ * In other words, it is used to construct element snippet suggestions
33
+ *
34
+ * Because of that, it is expected that there are text content wrapping around
35
+ * this snippet, so the first suggestion might not start from index 1.
36
+ */
37
+ export interface ElementEmbeddedContentSnippetSuggestion {
38
+ /**
39
+ * Brief description about the suggestion item to enable the users to quickly
40
+ * differentiate between one suggestions from another
41
+ */
42
+ description?: string | undefined;
43
+ /**
44
+ * The snippet text to be embedded in the full snippet suggestion text for the element
45
+ *
46
+ * NOTE: The snippet syntax follows that of `monaco-editor`
47
+ * See https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets
48
+ */
49
+ text: string;
50
+ }
51
+
52
+ /**
53
+ * This mirrors `monaco-editor` completion item structure
54
+ * See https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.CompletionItem.html
55
+ */
56
+ export interface PureGrammarTextSuggestion {
57
+ /**
58
+ * The text label of the suggestion.
59
+ */
60
+ text: string;
61
+ /**
62
+ * Brief description about the suggestion item to enable the users to quickly
63
+ * differentiate between one suggestions from another
64
+ */
65
+ description?: string | undefined;
66
+ /**
67
+ * Detailed documentation that explains/elaborates the suggestion item.
68
+ */
69
+ documentation?: DocumentationEntry | undefined;
70
+ /**
71
+ * A string or snippet that should be inserted when selecting this suggestion.
72
+ *
73
+ * NOTE: The snippet syntax follows that of `monaco-editor`
74
+ * See https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets
75
+ */
76
+ insertText: string;
77
+ }
78
+
79
+ export const getParserKeywordSuggestions = (
80
+ position: IPosition,
81
+ model: monacoEditorAPI.ITextModel,
82
+ suggestions: PureGrammarTextSuggestion[],
83
+ ): monacoLanguagesAPI.CompletionItem[] => {
84
+ const results: monacoLanguagesAPI.CompletionItem[] = [];
85
+ const currentWord = model.getWordUntilPosition(position);
86
+
87
+ // suggestions for parser keyword
88
+ const lineTextIncludingWordRange = {
89
+ startLineNumber: position.lineNumber,
90
+ startColumn: 1,
91
+ endLineNumber: position.lineNumber,
92
+ endColumn: currentWord.endColumn,
93
+ };
94
+ const lineTextIncludingWord = model.getValueInRange(
95
+ lineTextIncludingWordRange,
96
+ );
97
+
98
+ // NOTE: make sure parser keyword suggestions only show up when the current word is the
99
+ // the first word of the line since parser section header must not be preceded by anything
100
+ if (!hasWhiteSpace(lineTextIncludingWord.trim())) {
101
+ suggestions.forEach((suggestion) => {
102
+ results.push({
103
+ label: {
104
+ label: `${PARSER_SECTION_MARKER}${suggestion.text}`,
105
+ description: suggestion.description,
106
+ },
107
+ kind: monacoLanguagesAPI.CompletionItemKind.Keyword,
108
+ insertText: `${PARSER_SECTION_MARKER}${suggestion.insertText}\n`,
109
+ range: lineTextIncludingWordRange,
110
+ documentation: suggestion.documentation
111
+ ? suggestion.documentation.markdownText
112
+ ? {
113
+ value: suggestion.documentation.markdownText.value,
114
+ }
115
+ : suggestion.documentation.text
116
+ : undefined,
117
+ } as monacoLanguagesAPI.CompletionItem);
118
+ });
119
+ }
120
+
121
+ return results;
122
+ };
123
+
124
+ export const getSectionParserNameFromLineText = (
125
+ lineText: string,
126
+ ): string | undefined => {
127
+ if (lineText.startsWith(PARSER_SECTION_MARKER)) {
128
+ return lineText.substring(PARSER_SECTION_MARKER.length).split(' ')[0];
129
+ }
130
+ // NOTE: since leading whitespace to parser name is considered invalid, we will return `undefined`
131
+ return undefined;
132
+ };
133
+
134
+ export const getParserElementSnippetSuggestions = (
135
+ position: IPosition,
136
+ model: monacoEditorAPI.ITextModel,
137
+ suggestionsGetter: (parserName: string) => PureGrammarTextSuggestion[],
138
+ ): monacoLanguagesAPI.CompletionItem[] => {
139
+ const results: monacoLanguagesAPI.CompletionItem[] = [];
140
+ const currentWord = model.getWordUntilPosition(position);
141
+
142
+ // suggestions for parser element snippets
143
+ const textUntilPosition = model.getValueInRange({
144
+ startLineNumber: 1,
145
+ startColumn: 1,
146
+ endLineNumber: position.lineNumber,
147
+ endColumn: position.column,
148
+ });
149
+ const allParserSectionHeaders =
150
+ // NOTE: since `###Pure` is implicitly considered as the first section, we prepend it to the text
151
+ `${PARSER_SECTION_MARKER}${PURE_PARSER.PURE}\n${textUntilPosition}`
152
+ .split('\n')
153
+ .filter((line) => line.startsWith(PARSER_SECTION_MARKER));
154
+ const currentParserName = getSectionParserNameFromLineText(
155
+ allParserSectionHeaders[allParserSectionHeaders.length - 1] ?? '',
156
+ );
157
+
158
+ if (currentParserName) {
159
+ suggestionsGetter(currentParserName).forEach((snippetSuggestion) => {
160
+ results.push({
161
+ label: {
162
+ label: snippetSuggestion.text,
163
+ description: snippetSuggestion.description,
164
+ },
165
+ kind: monacoLanguagesAPI.CompletionItemKind.Snippet,
166
+ insertTextRules:
167
+ monacoLanguagesAPI.CompletionItemInsertTextRule.InsertAsSnippet,
168
+ insertText: `${snippetSuggestion.insertText}\n`,
169
+ range: {
170
+ startLineNumber: position.lineNumber,
171
+ startColumn: currentWord.startColumn,
172
+ endLineNumber: position.lineNumber,
173
+ endColumn: currentWord.endColumn,
174
+ },
175
+ documentation: snippetSuggestion.documentation
176
+ ? snippetSuggestion.documentation.markdownText
177
+ ? {
178
+ value: snippetSuggestion.documentation.markdownText.value,
179
+ }
180
+ : snippetSuggestion.documentation.text
181
+ : undefined,
182
+ } as monacoLanguagesAPI.CompletionItem);
183
+ });
184
+ }
185
+
186
+ return results;
187
+ };
188
+
189
+ export const getInlineSnippetSuggestions = (
190
+ position: IPosition,
191
+ model: monacoEditorAPI.ITextModel,
192
+ extraSnippetSuggestions: PureGrammarTextSuggestion[] = [],
193
+ ): monacoLanguagesAPI.CompletionItem[] => {
194
+ const currentWord = model.getWordUntilPosition(position);
195
+
196
+ return (
197
+ [
198
+ {
199
+ text: 'let',
200
+ description: 'new variable',
201
+ insertText: `let \${1:} = \${2:};`,
202
+ },
203
+ {
204
+ text: 'let',
205
+ description: 'new collection',
206
+ insertText: `let \${1:} = [\${2:}];`,
207
+ },
208
+ {
209
+ text: 'cast',
210
+ description: 'type casting',
211
+ insertText: `cast(@\${1:model::SomeClass})`,
212
+ },
213
+ // conditionals
214
+ {
215
+ text: 'if',
216
+ description: '(conditional)',
217
+ insertText: `if(\${1:'true'}, | \${2:/* if true do this */}, | \${3:/* if false do this */})`,
218
+ },
219
+ {
220
+ text: 'case',
221
+ description: '(conditional)',
222
+ insertText: `case(\${1:}, \${2:'true'}, \${3:'false'})`,
223
+ },
224
+ {
225
+ text: 'match',
226
+ description: '(conditional)',
227
+ insertText: `match([x:\${1:String[1]}, \${2:''}])`,
228
+ },
229
+ // collection
230
+ {
231
+ text: 'map',
232
+ description: '(collection)',
233
+ insertText: `map(x|\${1:})`,
234
+ },
235
+ {
236
+ text: 'filter',
237
+ description: '(collection)',
238
+ insertText: `filter(x|\${1:})`,
239
+ },
240
+ {
241
+ text: 'fold',
242
+ description: '(collection)',
243
+ insertText: `fold({a, b| \${1:$a + $b}}, \${2:0})`,
244
+ },
245
+ {
246
+ text: 'filter',
247
+ description: '(collection)',
248
+ insertText: `filter(x|\${1:})`,
249
+ },
250
+ {
251
+ text: 'sort',
252
+ description: '(collection)',
253
+ insertText: `sort()`,
254
+ },
255
+ {
256
+ text: 'in',
257
+ description: '(collection)',
258
+ insertText: `in()`,
259
+ },
260
+ {
261
+ text: 'slice',
262
+ description: '(collection)',
263
+ insertText: `slice(\${1:1},$\{2:2})`,
264
+ },
265
+ {
266
+ text: 'removeDuplicates',
267
+ description: '(collection)',
268
+ insertText: `removeDuplicates()`,
269
+ },
270
+ {
271
+ text: 'toOne',
272
+ description: '(collection)',
273
+ insertText: `toOne(\${1:})`,
274
+ },
275
+ {
276
+ text: 'toOneMany',
277
+ description: '(collection)',
278
+ insertText: `toOneMany(\${1:})`,
279
+ },
280
+ {
281
+ text: 'isEmpty',
282
+ description: '(collection)',
283
+ insertText: `isEmpty()`,
284
+ },
285
+ // string
286
+ {
287
+ text: 'endsWith',
288
+ description: '(string)',
289
+ insertText: `endsWith()`,
290
+ },
291
+ {
292
+ text: 'startsWith',
293
+ description: '(string)',
294
+ insertText: `startsWith()`,
295
+ },
296
+ ...extraSnippetSuggestions,
297
+ ] as PureGrammarTextSuggestion[]
298
+ ).map(
299
+ (snippetSuggestion) =>
300
+ ({
301
+ label: {
302
+ label: snippetSuggestion.text,
303
+ description: snippetSuggestion.description,
304
+ },
305
+ kind: monacoLanguagesAPI.CompletionItemKind.Snippet,
306
+ insertTextRules:
307
+ monacoLanguagesAPI.CompletionItemInsertTextRule.InsertAsSnippet,
308
+ insertText: snippetSuggestion.insertText,
309
+ range: {
310
+ startLineNumber: position.lineNumber,
311
+ startColumn: currentWord.startColumn,
312
+ endLineNumber: position.lineNumber,
313
+ endColumn: currentWord.endColumn,
314
+ },
315
+ documentation: snippetSuggestion.documentation
316
+ ? snippetSuggestion.documentation.markdownText
317
+ ? {
318
+ value: snippetSuggestion.documentation.markdownText.value,
319
+ }
320
+ : snippetSuggestion.documentation.text
321
+ : undefined,
322
+ }) as monacoLanguagesAPI.CompletionItem,
323
+ );
324
+ };
325
+
326
+ export const isTokenOneOf = (
327
+ token: string,
328
+ baseTokens: string[],
329
+ exact = false,
330
+ ): boolean => {
331
+ if (exact) {
332
+ return baseTokens.map((baseToken) => `${baseToken}.pure`).includes(token);
333
+ }
334
+ const baseToken = guaranteeNonNullable(
335
+ getNullableFirstEntry(token.split('.')),
336
+ );
337
+ return baseTokens.includes(baseToken);
338
+ };
339
+
340
+ export const PURE_CODE_EDITOR_WORD_SEPARATORS =
341
+ '`~!@#%^&*()-=+[{]}\\|;:\'",.<>/?'; // omit $ from default word separators