@@ -8,6 +8,7 @@ import CypherParser, {
88 CallClauseContext ,
99 Expression2Context ,
1010 NodePatternContext ,
11+ PatternElementContext ,
1112 RelationshipPatternContext ,
1213} from '../generated-parser/CypherCmdParser' ;
1314import {
@@ -499,6 +500,7 @@ export function completionCoreCompletion(
499500 caretIndex -- ;
500501 }
501502
503+ //Rules that should be returned as candidates instead of their constituent tokens
502504 codeCompletion . preferredRules = new Set < number > ( [
503505 CypherParser . RULE_functionName ,
504506 CypherParser . RULE_procedureName ,
@@ -507,16 +509,13 @@ export function completionCoreCompletion(
507509 CypherParser . RULE_parameter ,
508510 CypherParser . RULE_propertyKeyName ,
509511 CypherParser . RULE_variable ,
510- CypherParser . RULE_leftArrow ,
511- CypherParser . RULE_arrowLine ,
512512 // this rule is used for usernames and roles.
513513 CypherParser . RULE_commandNameExpression ,
514514 CypherParser . RULE_procedureResultItem ,
515515 CypherParser . RULE_cypherVersion ,
516516 CypherParser . RULE_cypher ,
517517 CypherParser . RULE_labelType ,
518518 CypherParser . RULE_relType ,
519-
520519 // Either enable the helper rules for lexer clashes,
521520 // or collect all console commands like below with symbolicNameString
522521 ...( _internalFeatureFlags . consoleCommands
@@ -765,74 +764,180 @@ export function completionCoreCompletion(
765764 return [ { label : 'write' , kind : CompletionItemKind . Event } ] ;
766765 }
767766
768- if ( ruleNumber === CypherParser . RULE_arrowLine ) {
769- const possiblePatternElementParent =
770- parsingResult . stopNode ?. parentCtx ?. parentCtx ;
771- const lastNode : RelationshipPatternContext | NodePatternContext =
772- possiblePatternElementParent ?. children . findLast (
773- ( x ) =>
774- ( x instanceof RelationshipPatternContext ||
775- x instanceof NodePatternContext ) &&
776- x . exception === null ,
777- ) as RelationshipPatternContext | NodePatternContext ;
778-
779- const shortPathCompletions : CompletionItem [ ] =
780- lastNode instanceof RelationshipPatternContext
781- ? getShortPathCompletions ( lastNode , symbolsInfo , dbSchema )
782- : [ ] ;
783- return shortPathCompletions ;
784- }
785-
786- if ( ruleNumber === CypherParser . RULE_leftArrow ) {
787- const possiblePatternElementParent =
788- parsingResult ?. stopNode ?. parentCtx ?. parentCtx ;
789- if ( possiblePatternElementParent ) {
790- const lastNodePattern = possiblePatternElementParent . children
791- . toReversed ( )
792- . find ( ( child ) => {
793- if ( child instanceof NodePatternContext ) {
794- if ( child . exception === null ) {
795- return true ;
796- }
797- }
798- } ) ;
799- const availablePaths =
800- lastNodePattern instanceof NodePatternContext
801- ? getPathCompletions ( lastNodePattern , symbolsInfo , dbSchema )
802- : [ ] ;
803- return availablePaths ;
804- }
805- }
806-
807767 return [ ] ;
808768 } ,
809769 ) ;
810770
771+ const snippetCompletions : CompletionItem [ ] = getSnippetCompletions (
772+ parsingResult ,
773+ dbSchema ,
774+ tokens ,
775+ candidates ,
776+ symbolsInfo ,
777+ ) ;
778+ const snippetTriggers = [
779+ CypherParser . RPAREN ,
780+ CypherParser . RBRACKET ,
781+ CypherParser . ARROW_LINE ,
782+ CypherParser . ARROW_LEFT_HEAD ,
783+ ] ;
784+
811785 // if the completion was automatically triggered by a snippet trigger character
812786 // we should only return snippet completions
813- if (
814- ( CypherLexer . RPAREN === previousToken ?. type ||
815- CypherLexer . RBRACKET === previousToken ?. type ) &&
816- ! manualTrigger
817- ) {
818- return ruleCompletions . filter (
819- ( completion ) => completion . kind === CompletionItemKind . Snippet ,
820- ) ;
787+ if ( snippetTriggers . includes ( previousToken ?. type ) && ! manualTrigger ) {
788+ return snippetCompletions ;
821789 }
822790
823791 return [
824792 ...ruleCompletions ,
825793 ...getTokenCompletions ( candidates , ignoredTokens ) ,
794+ ...snippetCompletions ,
826795 ] ;
827796}
828797
798+ function getSnippetCompletions (
799+ parsingResult : ParsedStatement ,
800+ dbSchema : DbSchema ,
801+ tokens : Token [ ] ,
802+ candidates : CandidatesCollection ,
803+ symbolsInfo : SymbolsInfo ,
804+ ) {
805+ let snippetCompletions : CompletionItem [ ] = [ ] ;
806+ if (
807+ tokens . length > 4 &&
808+ candidates . tokens
809+ . keys ( )
810+ . toArray ( )
811+ . some ( ( x ) =>
812+ [
813+ CypherParser . ARROW_LINE ,
814+ CypherParser . LPAREN ,
815+ CypherParser . ARROW_RIGHT_HEAD ,
816+ CypherParser . LBRACKET ,
817+ ] . includes ( x ) ,
818+ )
819+ ) {
820+ const parent = findParent (
821+ parsingResult . stopNode ,
822+ ( x ) => x . parentCtx instanceof PatternElementContext ,
823+ ) ?. parentCtx ;
824+
825+ const lastNode : RelationshipPatternContext | NodePatternContext =
826+ parent ?. children . findLast (
827+ ( x ) =>
828+ ( x instanceof RelationshipPatternContext ||
829+ x instanceof NodePatternContext ) &&
830+ x . exception === null ,
831+ ) as RelationshipPatternContext | NodePatternContext ;
832+ const finalNonEofToken = tokens . at ( - 2 ) ;
833+
834+ if (
835+ lastNode &&
836+ [
837+ CypherParser . LT ,
838+ CypherParser . ARROW_LEFT_HEAD ,
839+ CypherParser . ARROW_LINE ,
840+ CypherParser . MINUS ,
841+ ] . includes ( finalNonEofToken . type )
842+ ) {
843+ const secondLastToken = tokens . at ( - 3 ) ;
844+ // case: ]-
845+ if (
846+ secondLastToken . type === CypherParser . RBRACKET &&
847+ lastNode instanceof RelationshipPatternContext
848+ ) {
849+ const shortPathCompletions : CompletionItem [ ] = getShortPathCompletions (
850+ lastNode ,
851+ symbolsInfo ,
852+ dbSchema ,
853+ ) ;
854+ const cutSnippets = shortPathCompletions
855+ . filter ( ( x ) => x . label [ 0 ] === finalNonEofToken . text )
856+ . map ( ( x ) => {
857+ return {
858+ ...x ,
859+ insertText : x ?. insertText . slice ( 1 , x . insertText . length ) ,
860+ label : x . label . slice ( 1 , x . label . length ) ,
861+ } ;
862+ } ) ;
863+ snippetCompletions = cutSnippets ;
864+ // cases:
865+ // )<
866+ // )-
867+ } else if (
868+ secondLastToken . type === CypherParser . RPAREN &&
869+ lastNode instanceof NodePatternContext
870+ ) {
871+ const pathCompletions : CompletionItem [ ] = getPathCompletions (
872+ lastNode ,
873+ symbolsInfo ,
874+ dbSchema ,
875+ ) ;
876+ const cutSnippets = pathCompletions
877+ . filter ( ( x ) => x . label [ 0 ] === finalNonEofToken . text )
878+ . map ( ( x ) => {
879+ return {
880+ ...x ,
881+ insertText : x ?. insertText . slice ( 1 , x . insertText . length ) ,
882+ label : x . label . slice ( 1 , x . label . length ) ,
883+ } ;
884+ } ) ;
885+ snippetCompletions = cutSnippets ;
886+ // case: )<-
887+ // Needed because the "-" will re-trigger completions
888+ } else if (
889+ tokens . at ( - 4 ) . type === CypherParser . RPAREN &&
890+ [ CypherParser . ARROW_LEFT_HEAD , CypherParser . LT ] . includes (
891+ secondLastToken . type ,
892+ ) &&
893+ [ CypherParser . ARROW_LINE , CypherParser . MINUS ] . includes (
894+ finalNonEofToken . type ,
895+ ) &&
896+ lastNode instanceof NodePatternContext
897+ ) {
898+ const pathCompletions : CompletionItem [ ] = getPathCompletions (
899+ lastNode ,
900+ symbolsInfo ,
901+ dbSchema ,
902+ ) ;
903+ const cutSnippets = pathCompletions
904+ . filter ( ( x ) => x . label [ 0 ] === '<' )
905+ . map ( ( x ) => {
906+ return {
907+ ...x ,
908+ insertText : x ?. insertText . slice ( 2 , x . insertText . length ) ,
909+ label : x . label . slice ( 2 , x . label . length ) ,
910+ } ;
911+ } ) ;
912+ snippetCompletions = cutSnippets ;
913+ }
914+ } else if (
915+ lastNode instanceof RelationshipPatternContext &&
916+ finalNonEofToken . text === ']'
917+ ) {
918+ snippetCompletions = getShortPathCompletions (
919+ lastNode ,
920+ symbolsInfo ,
921+ dbSchema ,
922+ ) ;
923+ } else if (
924+ lastNode instanceof NodePatternContext &&
925+ finalNonEofToken . text === ')'
926+ ) {
927+ snippetCompletions = getPathCompletions ( lastNode , symbolsInfo , dbSchema ) ;
928+ }
929+ }
930+ return snippetCompletions ;
931+ }
932+
829933type CompletionHelperArgs = {
830934 parsingResult : ParsedStatement ;
831935 dbSchema : DbSchema ;
832936 previousToken ?: Token ;
833937 tokens : Token [ ] ;
834938 candidateRule : CandidateRule ;
835939} ;
940+
836941function completeAliasName ( {
837942 candidateRule,
838943 dbSchema,
0 commit comments