@@ -135,161 +135,223 @@ onMounted(() => {
135135 extensions: [" .cypher" ],
136136 aliases: [" Cypher" , " cypher" ],
137137 });
138- if (! window [PROVIDER_FLAG ]){
139- providerDisposable = monaco .languages .registerCompletionItemProvider (" cypher" , {
140- triggerCharacters: [" :" , " )" , " -" , " [" , " ]" , " >" , " <" , " {" , " }" , " ." ],
141- provideCompletionItems : (model , position ) => {
142- const textUtilPosition = model .getValueInRange ({
143- startLineNumber: 1 ,
144- startColumn: 1 ,
145- endLineNumber: position .lineNumber ,
146- endColumn: position .column ,
147- });
138+ if (! window [PROVIDER_FLAG ]) {
139+ providerDisposable = monaco .languages .registerCompletionItemProvider (
140+ " cypher" ,
141+ {
142+ triggerCharacters: [" :" , " )" , " -" , " [" , " ]" , " >" , " <" , " {" , " }" , " ." ],
143+ provideCompletionItems : (model , position ) => {
144+ const textUtilPosition = model .getValueInRange ({
145+ startLineNumber: 1 ,
146+ startColumn: 1 ,
147+ endLineNumber: position .lineNumber ,
148+ endColumn: position .column ,
149+ });
148150
149- const split = textUtilPosition .split (/ (?=MATCH| OPTIONAL MATCH| MERGE)/ gi );
150- const lastMatchClause = split .length ? split[split .length - 1 ] : textUtilPosition;
151+ const split = textUtilPosition .split (
152+ / (?=MATCH| OPTIONAL MATCH| MERGE)/ gi ,
153+ );
154+ const lastMatchClause = split .length
155+ ? split[split .length - 1 ]
156+ : textUtilPosition;
151157
152- const matchNodes = [... lastMatchClause .matchAll (/ \( \s * \w * \s * :\s * ([A-Za-z0-9 _] + )/ g )];
153- const matchRels = [... lastMatchClause .matchAll (/ \[ \s * \w * \s * :\s * ([A-Za-z0-9 _] + )(?=[\s \]\- ] | $ )/ g )];
158+ const matchNodes = [
159+ ... lastMatchClause .matchAll (/ \( \s * \w * \s * :\s * ([A-Za-z0-9 _] + )/ g ),
160+ ];
161+ const matchRels = [
162+ ... lastMatchClause .matchAll (
163+ / \[ \s * \w * \s * :\s * ([A-Za-z0-9 _] + )(?=[\s \]\- ] | $ )/ g ,
164+ ),
165+ ];
154166
155- const trimmedBeforeCursor = textUtilPosition .trimRight ();
156- const justOpenedBlock = / \b (?:WHERE\s + NOT\s + EXISTS| CALL| EXISTS| FOREACH\s * \( [^ )] + \) )\s * \{ $ / .test (trimmedBeforeCursor);
157- const genericNodeMatch = textUtilPosition .match (/ MATCH\s * \( \s * \{ \s * $ / );
158-
159- const nodePropMatch = textUtilPosition .match (
160- / (?:\( \s * \w * \s * :\s * [A-Za-z0-9 _] + \s * \{ \s * ([A-Za-z0-9 _] * ))| \b ([A-Za-z ][A-Za-z0-9 _] * )\. \s * ([A-Za-z0-9 _] * )$ /
161- );
162- const relPropMatch = textUtilPosition .match (
163- / \[ \s * \w * \s * :\s * [A-Za-z0-9 _] + \s * \{ \s * ([A-Za-z0-9 _] * )$ /
164- );
165- const aliasLabelRE = / \( \s * (\w * )\s * :\s * ([A-Za-z0-9 _] + )/ g ;
166- const relAliasTypeRE = / \[ \s * (\w * )\s * :\s * ([A-Za-z0-9 _] + )/ g ;
167- const dotPropMatch = textUtilPosition .match (
168- / \b ([A-Za-z ][A-Za-z0-9 _] * )\. \s * ([A-Za-z0-9 _] * )$ /
169- );
170- const chainedNodeMatch = textUtilPosition .match (/ \) \s * --\s * \( \s * (\w * \s * :)? \s * [\w ] + . * $ / );
171- const pathAliasMatch = [... textUtilPosition .matchAll (/ MATCH\s + (\w + )\s * =\s * \( . *? \) / g )];
172- const pathAliases = pathAliasMatch .map (m => m[1 ]);
173- const relWithoutTypePropMatch = textUtilPosition .match (/ \[ \s * \{ \s * ([A-Za-z0-9 _] * )$ / );
167+ const trimmedBeforeCursor = textUtilPosition .trimRight ();
168+ const justOpenedBlock =
169+ / \b (?:WHERE\s + NOT\s + EXISTS| CALL| EXISTS| FOREACH\s * \( [^ )] + \) )\s * \{ $ / .test (
170+ trimmedBeforeCursor,
171+ );
172+ const genericNodeMatch =
173+ textUtilPosition .match (/ MATCH\s * \( \s * \{ \s * $ / );
174174
175- const relInPathMatch = textUtilPosition .match (/ \b (\w + )\s + IN\s + relationships\( \s * (\w + )\s * \) / );
176- const nodeInPathMatch = textUtilPosition .match (/ \b (\w + )\s + IN\s + nodes\( \s * (\w + )\s * \) / );
175+ const nodePropMatch = textUtilPosition .match (
176+ / (?:\( \s * \w * \s * :\s * [A-Za-z0-9 _] + \s * \{ \s * ([A-Za-z0-9 _] * ))| \b ([A-Za-z ][A-Za-z0-9 _] * )\. \s * ([A-Za-z0-9 _] * )$ / ,
177+ );
178+ const relPropMatch = textUtilPosition .match (
179+ / \[ \s * \w * \s * :\s * [A-Za-z0-9 _] + \s * \{ \s * ([A-Za-z0-9 _] * )$ / ,
180+ );
181+ const aliasLabelRE = / \( \s * (\w * )\s * :\s * ([A-Za-z0-9 _] + )/ g ;
182+ const relAliasTypeRE = / \[ \s * (\w * )\s * :\s * ([A-Za-z0-9 _] + )/ g ;
183+ const dotPropMatch = textUtilPosition .match (
184+ / \b ([A-Za-z ][A-Za-z0-9 _] * )\. \s * ([A-Za-z0-9 _] * )$ / ,
185+ );
186+ const chainedNodeMatch = textUtilPosition .match (
187+ / \) \s * --\s * \( \s * (\w * \s * :)? \s * [\w ] + . * $ / ,
188+ );
189+ const pathAliasMatch = [
190+ ... textUtilPosition .matchAll (/ MATCH\s + (\w + )\s * =\s * \( . *? \) / g ),
191+ ];
192+ const pathAliases = pathAliasMatch .map ((m ) => m[1 ]);
193+ const relWithoutTypePropMatch = textUtilPosition .match (
194+ / \[ \s * \{ \s * ([A-Za-z0-9 _] * )$ / ,
195+ );
177196
178- const activeNodeLabel = matchNodes .length
179- ? matchNodes[matchNodes .length - 1 ][1 ]
180- : null ;
197+ const relInPathMatch = textUtilPosition .match (
198+ / \b (\w + )\s + IN\s + relationships\( \s * (\w + )\s * \) / ,
199+ );
200+ const nodeInPathMatch = textUtilPosition .match (
201+ / \b (\w + )\s + IN\s + nodes\( \s * (\w + )\s * \) / ,
202+ );
181203
182- const activeRelationship = matchRels .length && ! chainedNodeMatch
183- ? matchRels[ matchRels .length - 1 ][1 ]
184- : null ;
204+ const activeNodeLabel = matchNodes .length
205+ ? matchNodes[ matchNodes .length - 1 ][1 ]
206+ : null ;
185207
186- const aliasMap = {};
208+ const activeRelationship =
209+ matchRels .length && ! chainedNodeMatch
210+ ? matchRels[matchRels .length - 1 ][1 ]
211+ : null ;
187212
188- for (const m of textUtilPosition .matchAll (aliasLabelRE)) {
189- const alias = m[1 ] || null ;
190- const label = m[2 ];
191- if (alias) aliasMap[alias] = label;
192- }
213+ const aliasMap = {};
193214
194- for (const m of textUtilPosition .matchAll (relAliasTypeRE)) {
195- const alias = m[1 ] || null ;
196- const type = m[2 ];
197- if (alias) aliasMap[alias] = type;
198- }
215+ for (const m of textUtilPosition .matchAll (aliasLabelRE)) {
216+ const alias = m[1 ] || null ;
217+ const label = m[2 ];
218+ if (alias) aliasMap[alias] = label;
219+ }
220+
221+ for (const m of textUtilPosition .matchAll (relAliasTypeRE)) {
222+ const alias = m[1 ] || null ;
223+ const type = m[2 ];
224+ if (alias) aliasMap[alias] = type;
225+ }
199226
200- let relationships = [];
201- let targetNodes = [];
202- let properties = [];
227+ let relationships = [];
228+ let targetNodes = [];
229+ let properties = [];
203230
204- if (activeNodeLabel){
205- relationships = Object .keys (schema .schema [activeNodeLabel] || {} );
206- const connectedVia = schema .schema [activeNodeLabel] || {};
207- targetNodes = Object .values (connectedVia).flat ();
208- targetNodes = [... new Set (targetNodes)];
209- }
210- if (justOpenedBlock) {
211- return { suggestions: [] };
212- }
213- if (genericNodeMatch) {
214- const allProps = Object .values (schema .node_properties || {}).flat ();
215- properties = [... new Set (allProps)];
216- }
217- if (activeRelationship && activeNodeLabel){
218- targetNodes = schema .schema [activeNodeLabel]? .[activeRelationship] || [];
219- }
220- if (activeNodeLabel && nodePropMatch){
221- properties = [... new Set (schema .node_properties [activeNodeLabel] || [])];
222- }
223- if (activeRelationship && relPropMatch){
224- properties = [... new Set (schema .relationship_properties [activeRelationship] || [])];
225- }
226- if (activeNodeLabel && relWithoutTypePropMatch){
227- const relTypes = Object .keys (schema .schema [activeNodeLabel] || {});
228- properties = relTypes .flatMap (type => schema .relationship_properties [type] || []);
229- properties = [... new Set (properties)];
230- }
231- if (dotPropMatch) {
232- const alias = dotPropMatch[1 ];
233- if (relInPathMatch && alias === relInPathMatch[1 ]) {
234- const pathVar = relInPathMatch[2 ];
235- let relTypes = [];
236- for (const m of textUtilPosition .matchAll (/ MATCH\s + (\w + )\s * =\s * . *? \[ :([A-Za-z0-9 _] + )/ g )) {
237- if (m[1 ] === pathVar) {
231+ if (activeNodeLabel) {
232+ relationships = Object .keys (schema .schema [activeNodeLabel] || {});
233+ const connectedVia = schema .schema [activeNodeLabel] || {};
234+ targetNodes = Object .values (connectedVia).flat ();
235+ targetNodes = [... new Set (targetNodes)];
236+ }
237+ if (justOpenedBlock) {
238+ return { suggestions: [] };
239+ }
240+ if (genericNodeMatch) {
241+ const allProps = Object .values (schema .node_properties || {}).flat ();
242+ properties = [... new Set (allProps)];
243+ }
244+ if (activeRelationship && activeNodeLabel) {
245+ targetNodes =
246+ schema .schema [activeNodeLabel]? .[activeRelationship] || [];
247+ }
248+ if (activeNodeLabel && nodePropMatch) {
249+ properties = [
250+ ... new Set (schema .node_properties [activeNodeLabel] || []),
251+ ];
252+ }
253+ if (activeRelationship && relPropMatch) {
254+ properties = [
255+ ... new Set (
256+ schema .relationship_properties [activeRelationship] || [],
257+ ),
258+ ];
259+ }
260+ if (activeNodeLabel && relWithoutTypePropMatch) {
261+ const relTypes = Object .keys (schema .schema [activeNodeLabel] || {});
262+ properties = relTypes .flatMap (
263+ (type ) => schema .relationship_properties [type] || [],
264+ );
265+ properties = [... new Set (properties)];
266+ }
267+ if (dotPropMatch) {
268+ const alias = dotPropMatch[1 ];
269+ if (relInPathMatch && alias === relInPathMatch[1 ]) {
270+ const pathVar = relInPathMatch[2 ];
271+ let relTypes = [];
272+ for (const m of textUtilPosition .matchAll (
273+ / MATCH\s + (\w + )\s * =\s * . *? \[ :([A-Za-z0-9 _] + )/ g ,
274+ )) {
275+ if (m[1 ] === pathVar) {
238276 relTypes .push (m[2 ]);
277+ }
239278 }
240- }
241- properties = relTypes .flatMap (type => schema .relationship_properties [type] || []);
242- properties = [... new Set (properties)];
243- } else if (nodeInPathMatch && alias === nodeInPathMatch[1 ]) {
244- const pathVar = nodeInPathMatch[2 ];
245- let nodeLabels = [];
246- for (const m of textUtilPosition .matchAll (/ MATCH\s + (\w + )\s * =\s * \( (?:\w * ):([A-Za-z0-9 _] + )/ g )) {
247- if (m[1 ] === pathVar) {
279+ properties = relTypes .flatMap (
280+ (type ) => schema .relationship_properties [type] || [],
281+ );
282+ properties = [... new Set (properties)];
283+ } else if (nodeInPathMatch && alias === nodeInPathMatch[1 ]) {
284+ const pathVar = nodeInPathMatch[2 ];
285+ let nodeLabels = [];
286+ for (const m of textUtilPosition .matchAll (
287+ / MATCH\s + (\w + )\s * =\s * \( (?:\w * ):([A-Za-z0-9 _] + )/ g ,
288+ )) {
289+ if (m[1 ] === pathVar) {
248290 nodeLabels .push (m[2 ]);
291+ }
292+ }
293+ nodeLabels = [... new Set (nodeLabels)];
294+ properties = [
295+ ... new Set (
296+ nodeLabels .flatMap (
297+ (label ) => schema .node_properties [label] || [],
298+ ),
299+ ),
300+ ];
301+ } else {
302+ if (pathAliases .includes (alias)) {
303+ return { suggestions: [] };
304+ }
305+ const labelOrType = aliasMap[alias];
306+ if (schema .node_properties [labelOrType]) {
307+ properties = [
308+ ... new Set (schema .node_properties [labelOrType] || []),
309+ ];
310+ } else if (schema .relationship_properties [labelOrType]) {
311+ properties = [
312+ ... new Set (schema .relationship_properties [labelOrType] || []),
313+ ];
249314 }
250- }
251- nodeLabels = [... new Set (nodeLabels)];
252- properties = [... new Set (nodeLabels .flatMap (label => schema .node_properties [label] || []))];
253- }
254- else {
255- if (pathAliases .includes (alias)) {
256- return { suggestions: [] };
257- }
258- const labelOrType = aliasMap[alias];
259- if (schema .node_properties [labelOrType]) {
260- properties = [... new Set (schema .node_properties [labelOrType] || [])];
261- }
262- else if (schema .relationship_properties [labelOrType]) {
263- properties = [... new Set (schema .relationship_properties [labelOrType] || [])];
264315 }
265316 }
266- }
267317
268- const autocompleteSchema = {
269- labels: targetNodes .length > 0 ? targetNodes : Object .keys (schema .node_properties ),
270- relationshipTypes: relationships .length > 0 ? relationships : Object .keys (schema .relationship_properties ),
271- propertyKeys: properties .length > 0 ? properties : [... new Set ([
272- ... Object .values (schema .node_properties ).flat (),
273- ... Object .values (schema .relationship_properties ).flat (),
274- ])],
275- };
318+ const autocompleteSchema = {
319+ labels:
320+ targetNodes .length > 0
321+ ? targetNodes
322+ : Object .keys (schema .node_properties ),
323+ relationshipTypes:
324+ relationships .length > 0
325+ ? relationships
326+ : Object .keys (schema .relationship_properties ),
327+ propertyKeys:
328+ properties .length > 0
329+ ? properties
330+ : [
331+ ... new Set ([
332+ ... Object .values (schema .node_properties ).flat (),
333+ ... Object .values (schema .relationship_properties ).flat (),
334+ ]),
335+ ],
336+ };
276337
277- const completionItems = autocomplete (
278- textUtilPosition,
279- autocompleteSchema,
280- );
281- return {
282- suggestions: completionItems .map ((item ) => ({
283- label: item .label ,
284- kind: monaco .languages .CompletionItemKind .Text ,
285- insertText: item .label ,
286- range: item .range ,
287- })),
288- };
338+ const completionItems = autocomplete (
339+ textUtilPosition,
340+ autocompleteSchema,
341+ );
342+ return {
343+ suggestions: completionItems .map ((item ) => ({
344+ label: item .label ,
345+ kind: monaco .languages .CompletionItemKind .Text ,
346+ insertText: item .label ,
347+ range: item .range ,
348+ })),
349+ };
350+ },
289351 },
290- } );
291- window [PROVIDER_FLAG ] = true ;
292- };
352+ );
353+ window [PROVIDER_FLAG ] = true ;
354+ }
293355 editor .addAction ({
294356 id: " run" ,
295357 label: " Run" ,
0 commit comments