Skip to content

Commit a3d3b80

Browse files
authored
Fix inconsistent formatting of arguments (#358)
1 parent a2876f8 commit a3d3b80

File tree

5 files changed

+173
-29
lines changed

5 files changed

+173
-29
lines changed

src/GraphQLParser.ApiTests/GraphQLParser.approved.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,12 @@ namespace GraphQLParser.Visitors
907907
public System.IO.TextWriter Writer { get; }
908908
}
909909
}
910+
public enum SDLPrinterArgumentsMode
911+
{
912+
None = 0,
913+
PreferNewLine = 1,
914+
ForceNewLine = 2,
915+
}
910916
public static class SDLPrinterExtensions
911917
{
912918
public static string Print(this GraphQLParser.Visitors.SDLPrinter printer, GraphQLParser.AST.ASTNode node) { }
@@ -916,6 +922,7 @@ namespace GraphQLParser.Visitors
916922
public class SDLPrinterOptions
917923
{
918924
public SDLPrinterOptions() { }
925+
public GraphQLParser.Visitors.SDLPrinterArgumentsMode ArgumentsPrintMode { get; set; }
919926
public bool EachDirectiveLocationOnNewLine { get; set; }
920927
public bool EachUnionMemberOnNewLine { get; set; }
921928
public int IndentSize { get; set; }

src/GraphQLParser.Tests/Visitors/SDLPrinterFromParsedTextTests.cs

Lines changed: 94 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -557,16 +557,27 @@ type Query {
557557
@"directive @skip(""Skipped when true."" if: Boolean!, x: Some) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT",
558558
@"directive @skip(
559559
""Skipped when true.""
560-
if: Boolean!, x: Some) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT")]
560+
if: Boolean!, x: Some) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT", true, true, false, false, 2, SDLPrinterArgumentsMode.None)]
561561
[InlineData(43,
562+
@"directive @skip(if: Boolean!, x: Some) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT",
563+
@"directive @skip(
564+
if: Boolean!,
565+
x: Some) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT", true, true, false, false, 2, SDLPrinterArgumentsMode.ForceNewLine)]
566+
[InlineData(44,
567+
@"directive @skip(""Skipped when true."" if: Boolean!, x: Some) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT",
568+
@"directive @skip(
569+
""Skipped when true.""
570+
if: Boolean!,
571+
x: Some) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT")]
572+
[InlineData(45,
562573
"""directive @skip("Skipped when true." if: Boolean!, "Second argument" x: Some) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT""", """
563574
directive @skip(
564575
"Skipped when true."
565576
if: Boolean!,
566577
"Second argument"
567578
x: Some) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
568579
""")]
569-
[InlineData(44,
580+
[InlineData(46,
570581
"schema { query: Q mutation: M subscription: S }",
571582
"""
572583
schema {
@@ -575,7 +586,7 @@ directive @skip(
575586
subscription: S
576587
}
577588
""", true, true, false, false, 5)]
578-
[InlineData(45,
589+
[InlineData(47,
579590
"""
580591
"A component contains the parametric details of a PCB part."
581592
input DesComponentFilterInput {
@@ -606,7 +617,7 @@ input DesComponentFilterInput {
606617
revision: DesRevisionFilterInput
607618
}
608619
""")]
609-
[InlineData(46,
620+
[InlineData(48,
610621
"""
611622
# comment
612623
directive @my on FIELD
@@ -615,7 +626,7 @@ directive @my on FIELD
615626
# comment
616627
directive @my on FIELD
617628
""")]
618-
[InlineData(47,
629+
[InlineData(49,
619630
"""
620631
query q
621632
# comment
@@ -628,7 +639,7 @@ query q
628639
x
629640
}
630641
""")]
631-
[InlineData(48,
642+
[InlineData(50,
632643
"""
633644
query q
634645
(
@@ -642,7 +653,7 @@ query q(
642653
x
643654
}
644655
""")]
645-
[InlineData(49,
656+
[InlineData(51,
646657
"""
647658
"description"
648659
schema {
@@ -655,7 +666,7 @@ query q(
655666
query: Query
656667
}
657668
""")]
658-
[InlineData(50,
669+
[InlineData(52,
659670
"""
660671
type Query {
661672
"Fetches an object given its ID."
@@ -691,7 +702,7 @@ type Query {
691702
id: ID!): DesWorkspace
692703
}
693704
""")]
694-
[InlineData(51,
705+
[InlineData(53,
695706
"""
696707
type Query {
697708
user
@@ -710,8 +721,50 @@ type Query {
710721
# comment 2
711722
id: ID!, name: Name!): Node
712723
}
724+
""", true, true, false, false, 2, SDLPrinterArgumentsMode.None)]
725+
[InlineData(54,
726+
"""
727+
type Query {
728+
user
729+
# comment 1
730+
(
731+
# comment 2
732+
id: ID!
733+
name: Name!): Node
734+
}
735+
""",
736+
"""
737+
type Query {
738+
user
739+
# comment 1
740+
(
741+
# comment 2
742+
id: ID!,
743+
name: Name!): Node
744+
}
745+
""", true, true, false, false, 2, SDLPrinterArgumentsMode.ForceNewLine)]
746+
[InlineData(55,
747+
"""
748+
type Query {
749+
user
750+
# comment 1
751+
(
752+
# comment 2
753+
id: ID!
754+
name: Name!): Node
755+
}
756+
""",
757+
"""
758+
type Query {
759+
user
760+
# comment 1
761+
(
762+
# comment 2
763+
id: ID!,
764+
name: Name!): Node
765+
}
713766
""")]
714-
[InlineData(52,
767+
[InlineData(56,
715768
"""
716769
directive @my
717770
# comment 1
@@ -726,7 +779,7 @@ directive @my
726779
# comment 2
727780
arg: Boolean!) on FIELD
728781
""")]
729-
[InlineData(53,
782+
[InlineData(57,
730783
"""
731784
query Q {
732785
field1(arg1: 1) {
@@ -745,7 +798,7 @@ query Q {
745798
}
746799
}
747800
""")]
748-
[InlineData(54,
801+
[InlineData(58,
749802
"""
750803
query Q {
751804
field1
@@ -791,7 +844,7 @@ query Q {
791844
}
792845
}
793846
""")]
794-
[InlineData(55,
847+
[InlineData(59,
795848
"""
796849
fragment f
797850
#comment
@@ -804,7 +857,7 @@ on Person {
804857
name
805858
}
806859
""")]
807-
[InlineData(56,
860+
[InlineData(60,
808861
"""
809862
type Person
810863
#comment
@@ -817,7 +870,7 @@ implements Entity {
817870
name: String
818871
}
819872
""")]
820-
[InlineData(57,
873+
[InlineData(61,
821874
"""
822875
type Person
823876
#comment
@@ -834,7 +887,7 @@ implements Entity &
834887
name: String
835888
}
836889
""")]
837-
[InlineData(58,
890+
[InlineData(62,
838891
""""
839892
"description"
840893
type Person {
@@ -846,6 +899,28 @@ type Person {
846899
name: String
847900
}
848901
""", false, false)]
902+
[InlineData(63, // https://github.com/graphql-dotnet/parser/issues/330
903+
""""
904+
type DesPcb {
905+
designItems("An optional array of designators to search." designators: [String!] "Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: DesDesignItemFilterInput): DesDesignItemConnection
906+
}
907+
"""",
908+
"""
909+
type DesPcb {
910+
designItems(
911+
"An optional array of designators to search."
912+
designators: [String!],
913+
"Returns the first _n_ elements from the list."
914+
first: Int,
915+
"Returns the elements in the list that come after the specified cursor."
916+
after: String,
917+
"Returns the last _n_ elements from the list."
918+
last: Int,
919+
"Returns the elements in the list that come before the specified cursor."
920+
before: String,
921+
where: DesDesignItemFilterInput): DesDesignItemConnection
922+
}
923+
""")]
849924
public async Task SDLPrinter_Should_Print_Document(
850925
int number,
851926
string text,
@@ -854,7 +929,8 @@ public async Task SDLPrinter_Should_Print_Document(
854929
bool writeDescriptions = true,
855930
bool eachDirectiveLocationOnNewLine = false,
856931
bool eachUnionMemberOnNewLine = false,
857-
int indentSize = 2)
932+
int indentSize = 2,
933+
SDLPrinterArgumentsMode mode = SDLPrinterArgumentsMode.PreferNewLine)
858934
{
859935
var printer = new SDLPrinter(new SDLPrinterOptions
860936
{
@@ -863,6 +939,7 @@ public async Task SDLPrinter_Should_Print_Document(
863939
EachDirectiveLocationOnNewLine = eachDirectiveLocationOnNewLine,
864940
EachUnionMemberOnNewLine = eachUnionMemberOnNewLine,
865941
IndentSize = indentSize,
942+
ArgumentsPrintMode = mode,
866943
});
867944
var writer = new StringWriter();
868945
var document = text.Parse();

src/GraphQLParser/AST/Definitions/GraphQLInputValueDefinition.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
using System.Diagnostics;
2+
13
namespace GraphQLParser.AST;
24

35
/// <summary>
46
/// AST node for <see cref="ASTNodeKind.InputValueDefinition"/>.
57
/// </summary>
8+
[DebuggerDisplay("GraphQLInputValueDefinition: {Name}: {Type}")]
69
public class GraphQLInputValueDefinition : GraphQLTypeDefinition, IHasDirectivesNode, IHasDefaultValueNode
710
{
811
internal GraphQLInputValueDefinition()

src/GraphQLParser/Visitors/SDLPrinter.cs

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -480,13 +480,6 @@ protected override async ValueTask VisitInputObjectTypeExtensionAsync(GraphQLInp
480480
/// <inheritdoc/>
481481
protected override async ValueTask VisitInputValueDefinitionAsync(GraphQLInputValueDefinition inputValueDefinition, TContext context)
482482
{
483-
bool hasParent = TryPeekParent(context, out var parent);
484-
485-
if (hasParent && parent is GraphQLArgumentsDefinition argsDef && argsDef.Items.IndexOf(inputValueDefinition) > 0)
486-
{
487-
await context.WriteAsync(inputValueDefinition.Description == null ? ", " : ",").ConfigureAwait(false);
488-
}
489-
490483
await VisitAsync(inputValueDefinition.Comments, context).ConfigureAwait(false);
491484
await VisitAsync(inputValueDefinition.Description, context).ConfigureAwait(false);
492485
await VisitAsync(inputValueDefinition.Name, context).ConfigureAwait(false);
@@ -714,10 +707,12 @@ protected override async ValueTask VisitArgumentsDefinitionAsync(GraphQLArgument
714707
{
715708
await VisitAsync(argumentsDefinition.Comments, context).ConfigureAwait(false);
716709
await VisitAsync(LiteralNode.Wrap("("), context).ConfigureAwait(false);
717-
718-
foreach (var argumentDefinition in argumentsDefinition.Items)
719-
await VisitAsync(argumentDefinition, context).ConfigureAwait(false);
720-
710+
for (int i = 0; i < argumentsDefinition.Items.Count; ++i)
711+
{
712+
await VisitAsync(argumentsDefinition.Items[i], context).ConfigureAwait(false);
713+
if (i < argumentsDefinition.Items.Count - 1)
714+
await context.WriteAsync(",").ConfigureAwait(false);
715+
}
721716
await context.WriteAsync(")").ConfigureAwait(false);
722717
}
723718

@@ -905,9 +900,39 @@ node is GraphQLInputValueDefinition
905900
if ((context.LastVisitedNode is GraphQLFragmentSpread || context.LastVisitedNode is GraphQLSelectionSet) && !context.IndentPrinted)
906901
await context.WriteLineAsync().ConfigureAwait(false);
907902

903+
// ensure NewLine before printing argument if previous node did not print NewLine
904+
// https://github.com/graphql-dotnet/parser/issues/330
905+
_ = TryPeekParent(context, out var parent);
906+
if (!context.IndentPrinted && node is GraphQLInputValueDefinition && parent is GraphQLArgumentsDefinition arguments)
907+
{
908+
switch (Options.ArgumentsPrintMode)
909+
{
910+
case SDLPrinterArgumentsMode.ForceNewLine:
911+
await context.WriteLineAsync().ConfigureAwait(false);
912+
break;
913+
914+
case SDLPrinterArgumentsMode.PreferNewLine:
915+
foreach (var arg in arguments.Items)
916+
{
917+
if (HasPrintableComments(arg) || HasPrintableDescription(arg))
918+
{
919+
await context.WriteLineAsync().ConfigureAwait(false);
920+
break;
921+
}
922+
}
923+
break;
924+
925+
default:
926+
break;
927+
}
928+
}
929+
908930
// ensure proper indentation on the current line before printing new node
909931
if (context.NewLinePrinted)
910932
await WriteIndentAsync(context).ConfigureAwait(false);
933+
// otherwise ensure single whitespace indentation for all arguments in list except the first one
934+
else if (parent is GraphQLArgumentsDefinition argsDef && node is GraphQLInputValueDefinition input && argsDef.Items.IndexOf(input) != 0)
935+
await context.WriteAsync(" ").ConfigureAwait(false);
911936

912937
if (node is LiteralNode literalNode) // base.VisitAsync will throw on unknown node
913938
await context.WriteAsync(literalNode.Literal).ConfigureAwait(false);
@@ -969,6 +994,8 @@ private async ValueTask WriteIndentAsync(TContext context)
969994

970995
private bool HasPrintableComments(ASTNode node) => node.Comments?.Count > 0 && Options.PrintComments;
971996

997+
private bool HasPrintableDescription(IHasDescriptionNode node) => node.Description != null && Options.PrintDescriptions;
998+
972999
// Returns parent if called inside VisitXXX i.e. after context.Parents.Push(node);
9731000
// Returns grand-parent if called inside VisitAsync i.e. before context.Parents.Push(node);
9741001
private static bool TryPeekParent(TContext context, [NotNullWhen(true)] out ASTNode? node)
@@ -1136,6 +1163,12 @@ public class SDLPrinterOptions
11361163
/// </summary>
11371164
public bool EachUnionMemberOnNewLine { get; set; }
11381165

1166+
/// <summary>
1167+
/// How to print each argument definition.
1168+
/// By default <see cref="SDLPrinterArgumentsMode.PreferNewLine"/>.
1169+
/// </summary>
1170+
public SDLPrinterArgumentsMode ArgumentsPrintMode { get; set; } = SDLPrinterArgumentsMode.PreferNewLine;
1171+
11391172
/// <summary>
11401173
/// The size of the horizontal indentation in spaces.
11411174
/// By default 2.
@@ -1144,7 +1177,7 @@ public class SDLPrinterOptions
11441177
}
11451178

11461179
/// <summary>
1147-
/// Preudo AST node to allow calls to <see cref="SDLPrinter{TContext}.VisitAsync(ASTNode?, TContext)"/>
1180+
/// Pseudo AST node to allow calls to <see cref="SDLPrinter{TContext}.VisitAsync(ASTNode?, TContext)"/>
11481181
/// for indentation purposes. Any literal printed first after optional comment or description nodes in
11491182
/// any VisitXXX method should be wrapped into <see cref="LiteralNode"/> for proper indentation.
11501183
/// </summary>

0 commit comments

Comments
 (0)