@@ -10,8 +10,8 @@ import {
1010 NodePatternContext ,
1111 PatternElementContext ,
1212 QuantifierContext ,
13+ RelationshipPatternContext ,
1314} from '../generated-parser/CypherCmdParser' ;
14- import { ParserRuleContext } from 'antlr4' ;
1515import { backtickIfNeeded } from './autocompletionHelpers' ;
1616import { _internalFeatureFlags } from '../featureFlags' ;
1717
@@ -52,45 +52,45 @@ export const allReltypeCompletions = (dbSchema: DbSchema) =>
5252 reltypesToCompletions ( dbSchema . relationshipTypes ) ;
5353
5454function intersectChildren (
55- relsFromLabels : Map < string , Set < string > > ,
55+ connectedLabels : Map < string , Set < string > > ,
5656 children : LabelOrCondition [ ] ,
5757) : Set < string > {
5858 let intersection : Set < string > = undefined ;
5959 children . forEach ( ( c ) => {
6060 intersection = intersection
6161 ? ( intersection = intersection . intersection (
62- walkLabelTree ( relsFromLabels , c ) ,
62+ walkLabelTree ( connectedLabels , c ) ,
6363 ) )
64- : walkLabelTree ( relsFromLabels , c ) ;
64+ : walkLabelTree ( connectedLabels , c ) ;
6565 } ) ;
6666 return intersection ?? new Set ( ) ;
6767}
6868
6969function uniteChildren (
70- relsFromLabels : Map < string , Set < string > > ,
70+ connectedLabels : Map < string , Set < string > > ,
7171 children : LabelOrCondition [ ] ,
7272) : Set < string > {
7373 let union : Set < string > = new Set ( ) ;
7474 children . forEach (
75- ( c ) => ( union = union . union ( walkLabelTree ( relsFromLabels , c ) ) ) ,
75+ ( c ) => ( union = union . union ( walkLabelTree ( connectedLabels , c ) ) ) ,
7676 ) ;
7777 return union ;
7878}
7979
8080function walkLabelTree (
81- relsFromLabels : Map < string , Set < string > > ,
81+ connectedLabels : Map < string , Set < string > > ,
8282 labelTree : LabelOrCondition ,
8383) : Set < string > {
8484 if ( isLabelLeaf ( labelTree ) ) {
85- return relsFromLabels . get ( labelTree . value ) ;
85+ return connectedLabels . get ( labelTree . value ) ;
8686 } else if ( labelTree . andOr == 'and' ) {
87- return intersectChildren ( relsFromLabels , labelTree . children ) ;
87+ return intersectChildren ( connectedLabels , labelTree . children ) ;
8888 } else {
89- return uniteChildren ( relsFromLabels , labelTree . children ) ;
89+ return uniteChildren ( connectedLabels , labelTree . children ) ;
9090 }
9191}
9292
93- function getRelsFromLabelsSet ( dbSchema : DbSchema ) : Map < string , Set < string > > {
93+ function getRelsFromNodesSet ( dbSchema : DbSchema ) : Map < string , Set < string > > {
9494 if ( dbSchema . graphSchema ) {
9595 const relsFromLabelsSet : Map < string , Set < string > > = new Map ( ) ;
9696 dbSchema . graphSchema . forEach ( ( rel ) => {
@@ -112,35 +112,114 @@ function getRelsFromLabelsSet(dbSchema: DbSchema): Map<string, Set<string>> {
112112 return undefined ;
113113}
114114
115- export function completeRelationshipType (
115+ function getNodesFromRelsSet ( dbSchema : DbSchema ) : Map < string , Set < string > > {
116+ if ( dbSchema . graphSchema ) {
117+ const nodesFromRelsSet : Map < string , Set < string > > = new Map ( ) ;
118+ dbSchema . graphSchema . forEach ( ( rel ) => {
119+ if ( ! nodesFromRelsSet . has ( rel . relType ) ) {
120+ nodesFromRelsSet . set ( rel . relType , new Set ( ) ) ;
121+ }
122+ const currentRelEntry = nodesFromRelsSet . get ( rel . relType ) ;
123+ currentRelEntry . add ( rel . to ) ;
124+ currentRelEntry . add ( rel . from ) ;
125+ } ) ;
126+ return nodesFromRelsSet ;
127+ }
128+ return undefined ;
129+ }
130+
131+ export function completeNodeLabel (
116132 dbSchema : DbSchema ,
117133 parsingResult : ParsedStatement ,
118134 symbolsInfo : SymbolsInfo ,
119135) : CompletionItem [ ] {
120- if ( ! _internalFeatureFlags . schemaBasedPatternCompletions ) {
121- return allReltypeCompletions ( dbSchema ) ;
136+ if (
137+ ! _internalFeatureFlags . schemaBasedPatternCompletions ||
138+ dbSchema . graphSchema === undefined
139+ ) {
140+ return allLabelCompletions ( dbSchema ) ;
122141 }
123142
124- if ( dbSchema . graphSchema === undefined ) {
125- return allReltypeCompletions ( dbSchema ) ;
126- }
127-
128- // limitation: not checking PathPatternNonEmptyContext
129- // limitation: not handling parenthesized paths
130143 const callContext = findParent (
131144 parsingResult . stopNode . parentCtx ,
132145 ( x ) => x instanceof PatternElementContext ,
133146 ) ;
134147
135148 if ( callContext instanceof PatternElementContext ) {
136149 const lastValidElement = callContext . children . toReversed ( ) . find ( ( child ) => {
137- if ( child instanceof ParserRuleContext ) {
150+ if ( child instanceof RelationshipPatternContext ) {
138151 if ( child . exception === null ) {
139152 return true ;
140153 }
141154 }
142155 } ) ;
143156
157+ // limitation: bailing out on quantifiers
158+ if ( lastValidElement instanceof QuantifierContext ) {
159+ return allLabelCompletions ( dbSchema ) ;
160+ }
161+
162+ if ( lastValidElement instanceof RelationshipPatternContext ) {
163+ // limitation: not checking anonymous variables
164+ const variable = lastValidElement . variable ( ) ;
165+ if ( variable === null ) {
166+ return allLabelCompletions ( dbSchema ) ;
167+ }
168+
169+ const foundVariable = symbolsInfo ?. symbolTables
170+ ?. flat ( )
171+ . find ( ( entry ) => entry . references . includes ( variable . start . start ) ) ;
172+
173+ if (
174+ foundVariable === undefined ||
175+ ( 'children' in foundVariable . labels &&
176+ foundVariable . labels . children . length == 0 )
177+ ) {
178+ return allLabelCompletions ( dbSchema ) ;
179+ }
180+
181+ // limitation: not direction-aware (ignores <- vs ->)
182+ // limitation: not checking node label repetition
183+ const nodesFromRelsSet = getNodesFromRelsSet ( dbSchema ) ;
184+ const rels = walkLabelTree ( nodesFromRelsSet , foundVariable . labels ) ;
185+
186+ return labelsToCompletions ( Array . from ( rels ) ) ;
187+ }
188+ }
189+
190+ return allLabelCompletions ( dbSchema ) ;
191+ }
192+
193+ export function completeRelationshipType (
194+ dbSchema : DbSchema ,
195+ parsingResult : ParsedStatement ,
196+ symbolsInfo : SymbolsInfo ,
197+ ) : CompletionItem [ ] {
198+ if (
199+ ! _internalFeatureFlags . schemaBasedPatternCompletions ||
200+ dbSchema . graphSchema === undefined
201+ ) {
202+ return allReltypeCompletions ( dbSchema ) ;
203+ }
204+
205+ // limitation: not checking PathPatternNonEmptyContext
206+ // limitation: not handling parenthesized paths
207+ const patternContext = findParent (
208+ parsingResult . stopNode . parentCtx ,
209+ ( x ) => x instanceof PatternElementContext ,
210+ ) ;
211+
212+ if ( patternContext instanceof PatternElementContext ) {
213+ const lastValidElement = patternContext . children
214+ . toReversed ( )
215+ . find ( ( child ) => {
216+ if ( child instanceof NodePatternContext ) {
217+ if ( child . exception === null ) {
218+ return true ;
219+ }
220+ }
221+ } ) ;
222+
144223 // limitation: bailing out on quantifiers
145224 if ( lastValidElement instanceof QuantifierContext ) {
146225 return allReltypeCompletions ( dbSchema ) ;
@@ -166,8 +245,8 @@ export function completeRelationshipType(
166245 }
167246
168247 // limitation: not direction-aware (ignores <- vs ->)
169- // limitation: not checking relationship variable reuse
170- const relsFromLabelsSet = getRelsFromLabelsSet ( dbSchema ) ;
248+ // limitation: not checking relationship type repetition
249+ const relsFromLabelsSet = getRelsFromNodesSet ( dbSchema ) ;
171250 const rels = walkLabelTree ( relsFromLabelsSet , foundVariable . labels ) ;
172251
173252 return reltypesToCompletions ( Array . from ( rels ) ) ;
0 commit comments