Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Format extension types. #1387

Merged
merged 4 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions lib/src/front_end/ast_node_visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
node.sealedKeyword,
node.macroKeyword,
node.mixinKeyword,
node.classKeyword,
],
node.classKeyword,
node.name,
typeParameters: node.typeParameters,
extendsClause: node.extendsClause,
Expand All @@ -281,8 +281,8 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
node.finalKeyword,
node.sealedKeyword,
node.mixinKeyword,
node.typedefKeyword,
],
node.typedefKeyword,
node.name,
equals: node.equals,
superclass: node.superclass,
Expand Down Expand Up @@ -607,7 +607,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {

@override
Piece visitExtensionDeclaration(ExtensionDeclaration node) {
return createType(node.metadata, const [], node.extensionKeyword, node.name,
return createType(node.metadata, [node.extensionKeyword], node.name,
typeParameters: node.typeParameters,
onType: (node.onKeyword, node.extendedType),
body: (
Expand All @@ -617,6 +617,26 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
));
}

@override
Piece visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
return createType(
node.metadata,
[
node.extensionKeyword,
node.typeKeyword,
if (node.constKeyword case var keyword?) keyword
],
node.name,
typeParameters: node.typeParameters,
representation: node.representation,
implementsClause: node.implementsClause,
body: (
leftBracket: node.leftBracket,
members: node.members,
rightBracket: node.rightBracket
));
}

@override
Piece visitFieldDeclaration(FieldDeclaration node) {
return buildPiece((b) {
Expand Down Expand Up @@ -1226,7 +1246,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
@override
Piece visitMixinDeclaration(MixinDeclaration node) {
return createType(
node.metadata, [node.baseKeyword], node.mixinKeyword, node.name,
node.metadata, [node.baseKeyword, node.mixinKeyword], node.name,
typeParameters: node.typeParameters,
onClause: node.onClause,
implementsClause: node.implementsClause,
Expand Down Expand Up @@ -1531,6 +1551,28 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
throw UnimplementedError();
}

@override
Piece visitRepresentationConstructorName(RepresentationConstructorName node) {
return buildPiece((b) {
b.token(node.period);
b.token(node.name);
});
}

@override
Piece visitRepresentationDeclaration(RepresentationDeclaration node) {
return buildPiece((b) {
b.visit(node.constructorName);

var builder = DelimitedListBuilder(this);
builder.leftBracket(node.leftParenthesis);
builder.add(createParameter(
metadata: node.fieldMetadata, node.fieldType, node.fieldName));
builder.rightBracket(node.rightParenthesis);
b.add(builder.build());
});
}

@override
Piece visitRethrowExpression(RethrowExpression node) {
return tokenPiece(node.rethrowKeyword);
Expand Down
47 changes: 31 additions & 16 deletions lib/src/front_end/piece_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ mixin PieceFactory {
Piece createFormalParameter(
NormalFormalParameter node, TypeAnnotation? type, Token? name,
{Token? mutableKeyword, Token? fieldKeyword, Token? period}) {
return _createParameter(
return createParameter(
metadata: node.metadata,
modifiers: [
node.requiredKeyword,
Expand Down Expand Up @@ -752,7 +752,7 @@ mixin PieceFactory {

/// Creates an [AdjacentPiece] for a given record type field.
Piece createRecordTypeField(RecordTypeAnnotationField node) {
return _createParameter(metadata: node.metadata, node.type, node.name);
return createParameter(metadata: node.metadata, node.type, node.name);
}

/// Creates a [ListPiece] for a record literal or pattern.
Expand Down Expand Up @@ -802,23 +802,28 @@ mixin PieceFactory {
);
}

/// Creates a class, enum, extension, mixin, or mixin application class
/// declaration.
/// Creates a class, enum, extension, extension type, mixin, or mixin
/// application class declaration.
///
/// The [keywords] list is the ordered list of modifiers and keywords at the
/// beginning of the declaration.
///
/// For all but a mixin application class, [body] should a record containing
/// the bracket delimiters and the list of member declarations for the type's
/// body.
///
/// For mixin application classes, [body] is `null` and instead [equals],
/// [superclass], and [semicolon] are provided.
/// body. For mixin application classes, [body] is `null` and instead
/// [equals], [superclass], and [semicolon] are provided.
///
/// If the type is an extension, then [onType] is a record containing the
/// `on` keyword and the on type.
Piece createType(NodeList<Annotation> metadata, List<Token?> modifiers,
Token keyword, Token? name,
///
/// If the type is an extension type, then [representation] is the primary
/// constructor for it.
Piece createType(
NodeList<Annotation> metadata, List<Token?> keywords, Token? name,
{TypeParameterList? typeParameters,
Token? equals,
NamedType? superclass,
RepresentationDeclaration? representation,
ExtendsClause? extendsClause,
OnClause? onClause,
WithClause? withClause,
Expand All @@ -831,8 +836,13 @@ mixin PieceFactory {
metadataBuilder.metadata(metadata);

var header = buildPiece((b) {
modifiers.forEach(b.modifier);
b.token(keyword);
var space = false;
for (var keyword in keywords) {
if (space) b.space();
b.token(keyword);
if (keyword != null) space = true;
}

b.token(name, spaceBefore: true);

if (typeParameters != null) {
Expand All @@ -847,6 +857,11 @@ mixin PieceFactory {
b.space();
b.visit(superclass!);
}

// Extension types have a representation type.
if (representation != null) {
b.visit(representation);
}
});

var clauses = <ClausePiece>[];
Expand Down Expand Up @@ -989,11 +1004,14 @@ mixin PieceFactory {
/// Creates a piece for a parameter-like constructor: Either a simple formal
/// parameter or a record type field, which is syntactically similar to a
/// parameter.
Piece _createParameter(TypeAnnotation? type, Token? name,
Piece createParameter(TypeAnnotation? type, Token? name,
{List<Annotation> metadata = const [],
List<Token?> modifiers = const [],
Token? fieldKeyword,
Token? period}) {
var builder = AdjacentBuilder(this);
builder.metadata(metadata, inline: true);

Piece? typePiece;
if (type != null) {
typePiece = buildPiece((b) {
Expand Down Expand Up @@ -1022,9 +1040,6 @@ mixin PieceFactory {
});
}

var builder = AdjacentBuilder(this);
builder.metadata(metadata, inline: true);

if (typePiece != null && namePiece != null) {
// We have both a type and name, allow splitting between them.
builder.add(VariablePiece(typePiece, [namePiece], hasType: true));
Expand Down
7 changes: 7 additions & 0 deletions lib/src/piece/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -487,3 +487,10 @@ class ListStyle {
this.splitListIfBeforeSplits = false,
this.allowBlockElement = false});
}

munificent marked this conversation as resolved.
Show resolved Hide resolved
enum E {
a,

b,
c,
}
129 changes: 129 additions & 0 deletions test/declaration/extension_type.unit
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
40 columns |
>>> Empty body.
extension type A(int a) {
}
<<<
extension type A(int a) {}
>>> Const and named constructor.
extension type const A . name ( int a ) {}
<<<
extension type const A.name(int a) {}
>>> Type parameters.
extension type A<T extends int, R>(int a) {}
<<<
extension type A<T extends int, R>(
int a,
) {}
>>> Indentation in body.
extension type A(int a) {
inc(int x) => ++x;
foo(int x) {
if (x == 0) {
return true;
}}}
<<<
extension type A(int a) {
inc(int x) => ++x;
foo(int x) {
if (x == 0) {
return true;
}
}
}
>>> Don't split clauses if they fit.
extension type E(T i)
implements I, J {}
<<<
extension type E(T i) implements I, J {}
>>> Split in representation parameter list.
extension type LongExtensionType(LongTypeName a) {}
<<<
extension type LongExtensionType(
LongTypeName a,
) {}
>>> Split in representation with implements clause.
extension type LongExtensionType(LongTypeName a) implements Something {
method() {;}
}
<<<
extension type LongExtensionType(
LongTypeName a,
) implements Something {
method() {
;
}
}
>>> Simple body.
extension type A(int a) { int meaningOfLife() => 42; }
<<<
extension type A(int a) {
int meaningOfLife() => 42;
}
>>> Discard extra newlines.
extension

type

const

A

<

T

>

.

name

(

int

a

)

{

}
<<<
extension type const A<T>.name(int a) {}
>>> All kinds of members (except instance fields).
extension type const A<T>.name(int a) {
static const int c = 1;
static final int f = 1;
static late final int l;
static var v;
static int get g => c;
static set g(int i) {}
static int m<X>(X x) => c;
const A(int a) : this.a = a;
const A.r(int a) : this(a);
const factory A.rf(int a) = A;
factory A.f(int a) => A(a);
int get pr => 0;
set pr(int x) {}
int me(int x) => x;
int operator+(int x) => x;
}
<<<
extension type const A<T>.name(int a) {
static const int c = 1;
static final int f = 1;
static late final int l;
static var v;
static int get g => c;
static set g(int i) {}
static int m<X>(X x) => c;
const A(int a) : this.a = a;
const A.r(int a) : this(a);
const factory A.rf(int a) = A;
factory A.f(int a) => A(a);
int get pr => 0;
set pr(int x) {}
int me(int x) => x;
int operator +(int x) => x;
}
Loading