Skip to content

Commit bea262b

Browse files
committed
fix graph visualization from Neo4jApi.js
1 parent 9aff5e1 commit bea262b

File tree

3 files changed

+281
-217
lines changed

3 files changed

+281
-217
lines changed

src/components/InputPanel.vue

Lines changed: 197 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)