diff --git a/packages/language-support/src/formatting/formatting.ts b/packages/language-support/src/formatting/formatting.ts index 269bcdd0..57dbfd14 100644 --- a/packages/language-support/src/formatting/formatting.ts +++ b/packages/language-support/src/formatting/formatting.ts @@ -38,6 +38,7 @@ import { NumberLiteralContext, ParameterContext, ParenthesizedExpressionContext, + ParenthesizedPathContext, PathLengthContext, PatternContext, PatternListContext, @@ -821,6 +822,20 @@ export class TreePrintVisitor extends CypherCmdParserVisitor { } }; + visitParenthesizedPath = (ctx: ParenthesizedPathContext) => { + this.visit(ctx.LPAREN()); + this.avoidBreakBetween(); + const parenthesizedPathGrp = this.startGroup(); + this.visit(ctx.pattern()); + if (ctx.WHERE()) { + this.visit(ctx.WHERE()); + this.visit(ctx.expression()); + } + this.endGroup(parenthesizedPathGrp); + this.visit(ctx.RPAREN()); + this.visitIfNotNull(ctx.quantifier()); + }; + visitArrowLine = (ctx: ArrowLineContext) => { this.visitRawIfNotNull(ctx.MINUS()); this.visitRawIfNotNull(ctx.ARROW_LINE()); @@ -891,10 +906,16 @@ export class TreePrintVisitor extends CypherCmdParserVisitor { this.visit(ctx.EQ()); this.avoidBreakBetween(); } - this.visitIfNotNull(ctx.selector()); + const selectorAnonymousPatternGrp = this.startGroup(); + if (ctx.selector()) { + const selectorGroup = this.startGroup(); + this.visitIfNotNull(ctx.selector()); + this.endGroup(selectorGroup); + } const anonymousPatternGrp = this.startGroup(); this.visit(ctx.anonymousPattern()); this.endGroup(anonymousPatternGrp); + this.endGroup(selectorAnonymousPatternGrp); }; visitPatternList = (ctx: PatternListContext) => { @@ -1283,6 +1304,7 @@ export class TreePrintVisitor extends CypherCmdParserVisitor { }; visitListItemsPredicate = (ctx: ListItemsPredicateContext) => { + const wholeListItemGrp = this.startGroup(); this.visitRawIfNotNull(ctx.ALL()); this.visitRawIfNotNull(ctx.ANY()); this.visitRawIfNotNull(ctx.NONE()); @@ -1300,6 +1322,7 @@ export class TreePrintVisitor extends CypherCmdParserVisitor { } this.endGroup(listGrp); this.visit(ctx.RPAREN()); + this.endGroup(wholeListItemGrp); }; visitListLiteral = (ctx: ListLiteralContext) => { diff --git a/packages/language-support/src/tests/formatting/linebreaks.test.ts b/packages/language-support/src/tests/formatting/linebreaks.test.ts index 05546561..e334edd9 100644 --- a/packages/language-support/src/tests/formatting/linebreaks.test.ts +++ b/packages/language-support/src/tests/formatting/linebreaks.test.ts @@ -413,6 +413,58 @@ RETURN DISTINCT abcde.qwertyuiopa, abcde.zxcvbnmasdfgh, abcde.zxcvbnml, ORDER BY lm.lkjhgfdswert ASC`; verifyFormatting(query, expected); }); + + test('simple selector example', () => { + const query = `MATCH SHORTEST 1 + (:Station {name: 'Hartlebury'}) + (()--(n))+ + (:Station {name: 'Cheltenham Spa'}) +RETURN [stop in n[..-1] | stop.name] AS stops`; + const expected = ` +MATCH SHORTEST 1 (:Station {name: 'Hartlebury'}) (()--(n))+ + (:Station {name: 'Cheltenham Spa'}) +RETURN [stop IN n[.. -1] | stop.name] AS stops`.trimStart(); + verifyFormatting(query, expected); + }); + + test('complex selector example', () => { + const query = `MATCH SHORTEST 1 ((:Station {name: 'Hartlebury'}) (()--(n:Station))+ + (:Station {name: 'Cheltenham Spa'}) WHERE none( + stop IN n[.. -1] WHERE stop.name = 'Bromsgrove')) +RETURN [stop IN n[.. -1] | stop.name] AS stops`; + const expected = ` +MATCH SHORTEST 1 ((:Station {name: 'Hartlebury'}) (()--(n:Station))+ + (:Station {name: 'Cheltenham Spa'}) WHERE + none(stop IN n[.. -1] WHERE stop.name = 'Bromsgrove')) +RETURN [stop IN n[.. -1] | stop.name] AS stops`.trimStart(); + verifyFormatting(query, expected); + }); + + test('selector example with path', () => { + const query = `MATCH p = SHORTEST 1 ((:Station {name: 'Thisisanabsurdlylongnametomakeitawkward'}) + (()--(n:Station))+(:Station {name: 'Cheltenham Spa'}) WHERE + none(stop IN n[.. -1] WHERE stop.name = 'Bromsgrove')) +RETURN [stop IN n[.. -1] | stop.name] AS stops`; + const expected = ` +MATCH p = SHORTEST 1 + ((:Station {name: 'Thisisanabsurdlylongnametomakeitawkward'}) + (()--(n:Station))+(:Station {name: 'Cheltenham Spa'}) WHERE + none(stop IN n[.. -1] WHERE stop.name = 'Bromsgrove')) +RETURN [stop IN n[.. -1] | stop.name] AS stops`.trimStart(); + verifyFormatting(query, expected); + }); + + test('selector and quantifier example', () => { + const query = `MATCH path = ANY + (:Station {name: 'Pershore'})-[l:LINK WHERE l.distance < 10]-+(b:Station {name: 'Bromsgrove'}) +RETURN [r IN relationships(path) | r.distance] AS distances`; + const expected = ` +MATCH path = ANY (:Station {name: 'Pershore'})-[l:LINK WHERE l.distance < 10]-+ + (b:Station {name: 'Bromsgrove'}) +RETURN [r IN relationships(path) | r.distance] AS distances`.trimStart(); + verifyFormatting(query, expected); + }); + test('test for nested exists cases', () => { const query = `MATCH (user:Actor {actor_type:"EFXxFHob"}) WHERE(