@@ -5,13 +5,7 @@ import {
55} from 'vscode-languageserver-types' ;
66import { DbSchema } from '../dbSchema.js' ;
77import { ParsedStatement } from '../cypherLanguageService.js' ;
8- import {
9- ConditionNode ,
10- isLabelLeaf ,
11- LabelLeaf ,
12- LabelOrCondition ,
13- SymbolsInfo ,
14- } from '../types.js' ;
8+ import { isLabelLeaf , LabelOrCondition , SymbolsInfo } from '../types.js' ;
159import { findParent } from '../helpers.js' ;
1610import {
1711 NodePatternContext ,
@@ -26,6 +20,11 @@ import {
2620 isNotAnyNode ,
2721 removeInnerAnys ,
2822} from '../labelTreeRewriting.js' ;
23+ import {
24+ getNodesFromRelsSet ,
25+ getRelsFromNodesSets ,
26+ walkCNFTree ,
27+ } from '../labelTreeWalking.js' ;
2928
3029export function getShortPathCompletions (
3130 lastNode : RelationshipPatternContext ,
@@ -202,120 +201,6 @@ const reltypesToCompletions = (reltypes: string[] = []) =>
202201export const allReltypeCompletions = ( dbSchema : DbSchema ) =>
203202 reltypesToCompletions ( dbSchema . relationshipTypes ) ;
204203
205- function walkCNFTree (
206- incomingLabels : Map < string , Set < string > > ,
207- outGoingLabels : Map < string , Set < string > > ,
208- labelTree : LabelOrCondition ,
209- ) : { inLabels : Set < string > ; outLabels : Set < string > } {
210- //Bail if tree is not CNF
211- if ( isLabelLeaf ( labelTree ) || labelTree . condition !== 'and' ) {
212- let inLabels = new Set < string > ( ) ;
213- let outLabels = new Set < string > ( ) ;
214- incomingLabels . values ( ) . forEach ( ( x ) => ( inLabels = inLabels . union ( x ) ) ) ;
215- outGoingLabels . values ( ) . forEach ( ( x ) => ( outLabels = outLabels . union ( x ) ) ) ;
216-
217- return { inLabels, outLabels } ;
218- }
219- const notLabels : LabelLeaf [ ] = [ ] ;
220- const literalLabels : LabelLeaf [ ] = [ ] ;
221- const orNodes : ConditionNode [ ] = [ ] ;
222-
223- labelTree . children . forEach ( ( c ) => {
224- if ( isLabelLeaf ( c ) ) {
225- literalLabels . push ( c ) ;
226- } else if (
227- c . condition === 'not' &&
228- c . children . length === 1 &&
229- isLabelLeaf ( c . children [ 0 ] )
230- ) {
231- notLabels . push ( c . children [ 0 ] ) ;
232- } else if ( c . condition === 'or' ) {
233- orNodes . push ( c ) ;
234- }
235- } ) ;
236-
237- let inLabels = new Set < string > ( ) ;
238- incomingLabels . forEach ( ( part , key ) => {
239- if ( ! notLabels . some ( ( c ) => c . value === key ) ) {
240- inLabels = inLabels . union ( part ) ;
241- }
242- } ) ;
243- let outLabels = new Set < string > ( ) ;
244- outGoingLabels . forEach ( ( part , key ) => {
245- if ( ! notLabels . some ( ( c ) => c . value === key ) ) {
246- outLabels = outLabels . union ( part ) ;
247- }
248- } ) ;
249-
250- for ( const label of literalLabels ) {
251- const incoming = incomingLabels . get ( label . value ) ?? new Set ( ) ;
252- const outgoing = outGoingLabels . get ( label . value ) ?? new Set ( ) ;
253- inLabels = inLabels . intersection ( incoming ) ;
254- outLabels = outLabels . intersection ( outgoing ) ;
255- }
256-
257- for ( const node of orNodes ) {
258- let incoming = new Set ( ) ;
259- let outGoing = new Set ( ) ;
260- for ( const c of node . children ) {
261- if ( isLabelLeaf ( c ) ) {
262- const newIncoming = incomingLabels . get ( c . value ) ?? new Set ( ) ;
263- const newOutgoing = outGoingLabels . get ( c . value ) ?? new Set ( ) ;
264- incoming = incoming . union ( newIncoming ) ;
265- outGoing = outGoing . union ( newOutgoing ) ;
266- }
267- }
268- inLabels = inLabels . intersection ( incoming ) ;
269- outLabels = outLabels . intersection ( outGoing ) ;
270- }
271- return { inLabels, outLabels } ;
272- }
273-
274- function getRelsFromNodesSets ( dbSchema : DbSchema ) : {
275- toNodes : Map < string , Set < string > > ;
276- fromNodes : Map < string , Set < string > > ;
277- } {
278- if ( dbSchema . graphSchema ) {
279- const toNodes : Map < string , Set < string > > = new Map ( ) ;
280- const fromNodes : Map < string , Set < string > > = new Map ( ) ;
281- dbSchema . graphSchema . forEach ( ( rel ) => {
282- //rels in schema defined like (from)-(relType)->(to)
283- //Means 'from' is "node going to rel", hence why we
284- //pass rel.from into toNodes
285- if ( ! toNodes . has ( rel . from ) ) {
286- toNodes . set ( rel . from , new Set ( ) ) ;
287- }
288- if ( ! fromNodes . has ( rel . to ) ) {
289- fromNodes . set ( rel . to , new Set ( ) ) ;
290- }
291- toNodes . get ( rel . from ) . add ( rel . relType ) ;
292- fromNodes . get ( rel . to ) . add ( rel . relType ) ;
293- } ) ;
294- return { toNodes, fromNodes } ;
295- }
296- return undefined ;
297- }
298-
299- function getNodesFromRelsSet ( dbSchema : DbSchema ) : {
300- toRels : Map < string , Set < string > > ;
301- fromRels : Map < string , Set < string > > ;
302- } {
303- if ( dbSchema . graphSchema ) {
304- const toRels : Map < string , Set < string > > = new Map ( ) ;
305- const fromRels : Map < string , Set < string > > = new Map ( ) ;
306- dbSchema . graphSchema . forEach ( ( rel ) => {
307- if ( ! toRels . has ( rel . relType ) ) {
308- toRels . set ( rel . relType , new Set ( ) ) ;
309- fromRels . set ( rel . relType , new Set ( ) ) ;
310- }
311- toRels . get ( rel . relType ) . add ( rel . to ) ;
312- fromRels . get ( rel . relType ) . add ( rel . from ) ;
313- } ) ;
314- return { toRels, fromRels } ;
315- }
316- return undefined ;
317- }
318-
319204function findLastVariable (
320205 lastValidElement : NodePatternContext | RelationshipPatternContext ,
321206 symbolsInfo : SymbolsInfo ,
@@ -328,7 +213,7 @@ function findLastVariable(
328213 // Because the anonymous variable created in the AST is "made up", it doesnt have a position of its own in the query.
329214 // It therefor inherits the parent-nodes (The NodePattern/RelationshipPattern = lastValidElement) position
330215 entry . definitionPosition >= lastValidElement . start . start &&
331- entry . definitionPosition <= lastValidElement . stop . stop ,
216+ entry . definitionPosition <= ( lastValidElement . stop ? .stop ?? - 1 ) ,
332217 )
333218 : symbolsInfo ?. symbolTables
334219 ?. flat ( )
@@ -350,7 +235,7 @@ export function completeNodeLabel(
350235 ( x ) => x instanceof PatternElementContext ,
351236 ) ;
352237
353- if ( callContext instanceof PatternElementContext ) {
238+ if ( callContext instanceof PatternElementContext && callContext . children ) {
354239 const lastValidElement = callContext . children . toReversed ( ) . find ( ( child ) => {
355240 if ( child instanceof RelationshipPatternContext ) {
356241 if ( child . exception === null ) {
@@ -375,11 +260,12 @@ export function completeNodeLabel(
375260 return allLabelCompletions ( dbSchema ) ;
376261 }
377262
378- const direction = lastValidElement . leftArrow ( )
379- ? 'outgoing'
380- : lastValidElement . rightArrow ( )
381- ? 'incoming'
382- : 'bidirectional' ;
263+ const direction =
264+ lastValidElement . leftArrow ( ) && ! lastValidElement . rightArrow ( )
265+ ? 'outgoing'
266+ : ! lastValidElement . leftArrow ( ) && lastValidElement . rightArrow ( )
267+ ? 'incoming'
268+ : 'bidirectional' ;
383269
384270 // limitation: not checking node label repetition
385271 const { toRels : nodesToRelsSet , fromRels : nodesFromRelsSet } =
@@ -396,14 +282,6 @@ export function completeNodeLabel(
396282 } catch {
397283 return allLabelCompletions ( dbSchema ) ;
398284 }
399- let allIncomingLabels = new Set < string > ( ) ;
400- nodesToRelsSet . forEach ( ( part ) => {
401- allIncomingLabels = allIncomingLabels . union ( part ) ;
402- } ) ;
403- let allOutGoingLabels = new Set < string > ( ) ;
404- nodesFromRelsSet . forEach ( ( part ) => {
405- allOutGoingLabels = allOutGoingLabels . union ( part ) ;
406- } ) ;
407285 const { inLabels, outLabels } = walkCNFTree (
408286 nodesToRelsSet ,
409287 nodesFromRelsSet ,
@@ -438,7 +316,10 @@ export function completeRelationshipType(
438316 ( x ) => x instanceof PatternElementContext ,
439317 ) ;
440318
441- if ( patternContext instanceof PatternElementContext ) {
319+ if (
320+ patternContext instanceof PatternElementContext &&
321+ patternContext . children
322+ ) {
442323 const lastValidElement = patternContext . children
443324 . toReversed ( )
444325 . find ( ( child ) => {
@@ -455,11 +336,12 @@ export function completeRelationshipType(
455336 ) ;
456337 let direction = 'bidirectional' ;
457338 if ( thisCtx instanceof RelationshipPatternContext ) {
458- direction = thisCtx . leftArrow ( )
459- ? 'outgoing'
460- : thisCtx . rightArrow ( )
461- ? 'incoming'
462- : 'bidirectional' ;
339+ direction =
340+ thisCtx . leftArrow ( ) && ! thisCtx . rightArrow ( )
341+ ? 'outgoing'
342+ : ! thisCtx . leftArrow ( ) && thisCtx . rightArrow ( )
343+ ? 'incoming'
344+ : 'bidirectional' ;
463345 }
464346
465347 // limitation: bailing out on quantifiers
@@ -493,14 +375,6 @@ export function completeRelationshipType(
493375 } catch {
494376 return [ ] ;
495377 }
496- let allIncomingLabels = new Set < string > ( ) ;
497- relsToNodesSet . forEach ( ( part ) => {
498- allIncomingLabels = allIncomingLabels . union ( part ) ;
499- } ) ;
500- let allOutGoingLabels = new Set < string > ( ) ;
501- relsFromNodesSet . forEach ( ( part ) => {
502- allOutGoingLabels = allOutGoingLabels . union ( part ) ;
503- } ) ;
504378 const { inLabels, outLabels } = walkCNFTree (
505379 relsToNodesSet ,
506380 relsFromNodesSet ,
0 commit comments