Skip to content

Commit

Permalink
Implementing align doc type + using it for classes
Browse files Browse the repository at this point in the history
closes #276
closes #275
  • Loading branch information
belav committed Jun 10, 2021
1 parent 783110b commit f8ce264
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 29 deletions.
35 changes: 31 additions & 4 deletions Src/CSharpier.Tests/DocPrinterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,26 @@ public void Conditional_Group_Does_Not_Propagate_Breaks_To_Parent()
PrintedDocShouldBe(doc, "1 2", 10);
}

[Test]
public void Align_Should_Print_Basic_Case()
{
var doc = Doc.Concat("+ ", Doc.Align(2, Doc.Group("1", Doc.HardLine, "2")));
PrintedDocShouldBe(doc, $"+ 1{NewLine} 2");
}

[Test]
public void Align_Should_Convert_Spaces_To_Tabs()
{
var doc = Doc.Concat(
"+ ",
Doc.Align(
2,
Doc.Indent(Doc.Concat("+ ", Doc.Align(2, Doc.Group("1", Doc.HardLine, "2"))))
)
);
PrintedDocShouldBe(doc, $"+ + 1{NewLine}\t\t 2", useTabs: true);
}

[Test]
public void Scratch()
{
Expand All @@ -627,21 +647,28 @@ private static void PrintedDocShouldBe(
Doc doc,
string expected,
int width = PrinterOptions.WidthUsedByTests,
bool trimInitialLines = false
bool trimInitialLines = false,
bool useTabs = false
) {
var result = Print(doc, width, trimInitialLines);
var result = Print(doc, width, trimInitialLines, useTabs);

result.Should().Be(expected);
}

private static string Print(
Doc doc,
int width = PrinterOptions.WidthUsedByTests,
bool trimInitialLines = false
bool trimInitialLines = false,
bool useTabs = false
) {
return DocPrinter.DocPrinter.Print(
doc,
new PrinterOptions { Width = width, TrimInitialLines = trimInitialLines, },
new PrinterOptions
{
Width = width,
TrimInitialLines = trimInitialLines,
UseTabs = useTabs
},
Environment.NewLine
)
.TrimEnd('\r', '\n');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ class NoModifiers { }

public class WithInterface : IInterface { }

public class WithReallyLongNameInterface :
IReallyLongNameLetsMakeThisBreak___________________________ { }
public class WithReallyLongNameInterface
: IReallyLongNameLetsMakeThisBreak___________________________ { }

public class ThisIsSomeLongNameAndItShouldFormatWell1 :
AnotherLongClassName,
AndYetAnotherLongClassName,
AndStillOneMore { }
public class ThisIsSomeLongNameAndItShouldFormatWell1
: AnotherLongClassName,
AndYetAnotherLongClassName,
AndStillOneMore { }

public class SimpleGeneric<T>
where T : new() { }
Expand All @@ -36,9 +36,24 @@ public class LongerClassNameWithLotsOfGenerics<
public class SimpleGeneric<T> : BaseClass<T>
where T : new() { }

public class ThisIsSomeLongNameAndItShouldFormatWell2<T, T2, T3> :
AnotherLongClassName<T>,
AnotherClassName
public class ThisIsSomeLongNameAndItShouldFormatWell2<T, T2, T3>
: AnotherLongClassName<T>,
AnotherClassName
where T : new(), AnotherTypeConstraint
where T2 : new()
where T3 : new() { }

public class IdentityDbContext<TUser, TRole, TKey>
: IdentityDbContext<
TUser,
TRole,
TKey,
IdentityUserClaim<TKey>,
IdentityUserRole<TKey>,
IdentityUserLogin<TKey>,
IdentityRoleClaim<TKey>,
IdentityUserToken<TKey>
>
where TUser : IdentityUser<TKey>
where TRole : IdentityRole<TKey>
where TKey : IEquatable<TKey> { }
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ record PC(string x) : PrimaryConstructor(x) { }

record RecordWithoutBody(string property);

record LongerRecordNameWhatHappens_________________________________________(string x) :
R4<string>(x) { }
record LongerRecordNameWhatHappens_________________________________________(string x)
: R4<string>(x) { }

record GenericRecord<T>(T Result);
13 changes: 12 additions & 1 deletion Src/CSharpier/DocPrinter/DocFitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ void Push(Doc doc, PrintMode printMode, Indent indent)
Push(
indent.Contents,
currentMode,
IndentBuilder.Make(currentIndent, printerOptions)
IndentBuilder.MakeIndent(currentIndent, printerOptions)
);
break;
case Trim:
Expand Down Expand Up @@ -146,6 +146,17 @@ is ConditionalGroup conditionalGroup
break;
case BreakParent:
break;
case Align align:
Push(
align.Contents,
currentMode,
IndentBuilder.MakeAlign(
currentIndent,
align.Alignment,
printerOptions
)
);
break;
default:
throw new Exception("Can't handle " + currentDoc.GetType());
}
Expand Down
13 changes: 12 additions & 1 deletion Src/CSharpier/DocPrinter/DocPrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ private void ProcessNextCommand()
break;
}
case IndentDoc indentDoc:
Push(indentDoc.Contents, mode, IndentBuilder.Make(indent, PrinterOptions));
Push(
indentDoc.Contents,
mode,
IndentBuilder.MakeIndent(indent, PrinterOptions)
);
break;
case Trim:
CurrentWidth -= Output.TrimTrailingWhitespace();
Expand Down Expand Up @@ -128,6 +132,13 @@ private void ProcessNextCommand()
case ForceFlat forceFlat:
Push(forceFlat.Contents, PrintMode.Flat, indent);
break;
case Align align:
Push(
align.Contents,
mode,
IndentBuilder.MakeAlign(indent, align.Alignment, PrinterOptions)
);
break;
default:
throw new Exception("didn't handle " + doc);
}
Expand Down
113 changes: 105 additions & 8 deletions Src/CSharpier/DocPrinter/Indent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,130 @@

namespace CSharpier.DocPrinter
{
public record Indent(string Value, int Length);
public class Indent
{
public string Value = string.Empty;
public int Length;
public IList<IndentType>? TypesForTabs;
}

public class IndentType
{
public bool IsAlign { get; set; }
public int AlignWidth { get; set; }
}

public static class IndentBuilder
{
public static Indent MakeRoot()
{
return new Indent(string.Empty, 0);
return new();
}

public static Indent Make(Indent indent, PrinterOptions printerOptions)
public static Indent MakeIndent(Indent indent, PrinterOptions printerOptions)
{
return GenerateIndent(indent, printerOptions);
}

private static Indent GenerateIndent(Indent indent, PrinterOptions printerOptions)
{
if (!printerOptions.UseTabs)
{
return new Indent
{
Value = indent.Value + new string(' ', printerOptions.TabWidth),
Length = indent.Length + printerOptions.TabWidth
};
}

if (indent.TypesForTabs != null)
{
return MakeIndentWithTypesForTabs(indent, new IndentType(), printerOptions);
}

return new Indent
{
Value = indent.Value + "\t",
Length = indent.Length + printerOptions.TabWidth
};
}

public static Indent MakeAlign(Indent indent, int alignment, PrinterOptions printerOptions)
{
if (printerOptions.UseTabs)
{
return new Indent(indent.Value + "\t", indent.Length + printerOptions.TabWidth);
return MakeIndentWithTypesForTabs(
indent,
new IndentType { IsAlign = true, AlignWidth = alignment },
printerOptions
);
}
else
{
return new Indent(
indent.Value + new string(' ', printerOptions.TabWidth),
indent.Length + printerOptions.TabWidth
);
return new Indent
{
Value = indent.Value + new string(' ', alignment),
Length = indent.Length + alignment
};
}
}

// when using tabs we need to sometimes replace the spaces from align with tabs
// trailing aligns stay as spaces, but any aligns before a tab get converted to a single tab
// see https://github.com/prettier/prettier/blob/main/commands.md#align
private static Indent MakeIndentWithTypesForTabs(
Indent indent,
IndentType nextIndentType,
PrinterOptions printerOptions
) {
List<IndentType> types;

// if it doesn't exist yet, then all values on it are regular indents, not aligns
if (indent.TypesForTabs == null)
{
types = new List<IndentType>();
for (var x = 0; x < indent.Value.Length; x++)
{
types.Add(new IndentType());
}
}
else
{
var placeTab = false;
types = new List<IndentType>(indent.TypesForTabs);
for (var x = types.Count - 1; x >= 0; x--)
{
if (!types[x].IsAlign)
{
placeTab = true;
}

if (placeTab)
{
types[x] = new IndentType();
}
}
}

types.Add(nextIndentType);

var length = 0;
var value = new StringBuilder();
foreach (var indentType in types)
{
if (indentType.IsAlign)
{
value.Append(' ', indentType.AlignWidth);
length += indentType.AlignWidth;
}
else
{
value.Append('\t');
length += printerOptions.TabWidth;
}
}

return new Indent { Length = length, Value = value.ToString(), TypesForTabs = types };
}
}
}
1 change: 1 addition & 0 deletions Src/CSharpier/DocSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ string PrintConcat(Concat concatToPrint)
LineDoc lineDoc => indent
+ (lineDoc.Type == LineDoc.LineType.Normal ? "Doc.Line" : "Doc.SoftLine"),
BreakParent => "",
Align align => $"{indent}Doc.Align({align.Alignment}, {PrintIndentedDocTree(align.Contents)})",
Trim => $"{indent}Doc.Trim",
ForceFlat forceFlat => $"{indent}Doc.ForceFlat({newLine}{PrintIndentedDocTree(forceFlat.Contents)})",
IndentDoc indentDoc => $"{indent}Doc.Indent({newLine}{PrintIndentedDocTree(indentDoc.Contents)}{newLine}{indent})",
Expand Down
21 changes: 21 additions & 0 deletions Src/CSharpier/DocTypes/Align.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace CSharpier.DocTypes
{
public class Align : Doc
{
public int Alignment { get; }
public Doc Contents { get; }

public Align(int alignment, Doc contents)
{
if (alignment < 1)
{
throw new Exception($"{nameof(alignment)} must be >= 1");
}

this.Alignment = alignment;
this.Contents = contents;
}
}
}
2 changes: 2 additions & 0 deletions Src/CSharpier/DocTypes/Doc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ public static IndentIfBreak IndentIfBreak(Doc contents, string groupId) =>
public static Doc Directive(string value) => new StringDoc(value, true);

public static ConditionalGroup ConditionalGroup(params Doc[] options) => new(options);

public static Align Align(int alignment, Doc contents) => new(alignment, contents);
}

public enum CommentType
Expand Down
12 changes: 8 additions & 4 deletions Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ public static class BaseList
{
public static Doc Print(BaseListSyntax node)
{
return Doc.Concat(
" ",
Token.Print(node.ColonToken),
return Doc.Group(
Doc.Indent(
Doc.Group(Doc.Line, SeparatedSyntaxList.Print(node.Types, Node.Print, Doc.Line))
Doc.Line,
Token.Print(node.ColonToken),
" ",
Doc.Align(
2,
Doc.Concat(SeparatedSyntaxList.Print(node.Types, Node.Print, Doc.Line))
)
)
);
}
Expand Down

0 comments on commit f8ce264

Please sign in to comment.