Format extensions. (#1318)

This was trivial thanks to `createType()`.
diff --git a/lib/src/front_end/ast_node_visitor.dart b/lib/src/front_end/ast_node_visitor.dart
index d942085..5fa2060 100644
--- a/lib/src/front_end/ast_node_visitor.dart
+++ b/lib/src/front_end/ast_node_visitor.dart
@@ -433,7 +433,14 @@
 
   @override
   void visitExtensionDeclaration(ExtensionDeclaration node) {
-    throw UnimplementedError();
+    createType(node.metadata, const [], node.extensionKeyword, node.name,
+        typeParameters: node.typeParameters,
+        onType: (node.onKeyword, node.extendedType),
+        body: (
+          leftBracket: node.leftBracket,
+          members: node.members,
+          rightBracket: node.rightBracket
+        ));
   }
 
   @override
diff --git a/lib/src/front_end/piece_factory.dart b/lib/src/front_end/piece_factory.dart
index c9a554a..c0f42b0 100644
--- a/lib/src/front_end/piece_factory.dart
+++ b/lib/src/front_end/piece_factory.dart
@@ -450,8 +450,11 @@
   ///
   /// 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.
   void createType(NodeList<Annotation> metadata, List<Token?> modifiers,
-      Token keyword, Token name,
+      Token keyword, Token? name,
       {TypeParameterList? typeParameters,
       Token? equals,
       NamedType? superclass,
@@ -460,14 +463,14 @@
       WithClause? withClause,
       ImplementsClause? implementsClause,
       NativeClause? nativeClause,
+      (Token, TypeAnnotation)? onType,
       ({Token leftBracket, List<AstNode> members, Token rightBracket})? body,
       Token? semicolon}) {
     if (metadata.isNotEmpty) throw UnimplementedError('Type metadata.');
 
     modifiers.forEach(modifier);
     token(keyword);
-    space();
-    token(name);
+    token(name, before: space);
     visit(typeParameters);
 
     // Mixin application classes have ` = Superclass` after the declaration
@@ -514,6 +517,10 @@
           implementsClause.implementsKeyword, implementsClause.interfaces);
     }
 
+    if (onType case (var onKeyword, var onType)?) {
+      typeClause(onKeyword, [onType]);
+    }
+
     ClausesPiece? clausesPiece;
     if (clauses.isNotEmpty) {
       clausesPiece = ClausesPiece(clauses,
diff --git a/test/declaration/extension.unit b/test/declaration/extension.unit
new file mode 100644
index 0000000..bf59c84
--- /dev/null
+++ b/test/declaration/extension.unit
@@ -0,0 +1,69 @@
+40 columns                              |
+>>> Empty body.
+extension  A  on  B  {
+
+
+  }
+<<<
+extension A on B {}
+>>> Members.
+extension A on B{z() => 0;f() { body; }}
+<<<
+extension A on B {
+  z() => 0;
+  f() {
+    body;
+  }
+}
+>>> Insert blank line before and after extension.
+var x = 1;
+extension A on B {}
+var y = 2;
+<<<
+var x = 1;
+
+extension A on B {}
+
+var y = 2;
+>>> Unsplit type parameters.
+extension  A  <  T  ,  S  >  on  B  {}
+<<<
+extension A<T, S> on B {}
+>>> Split type parameters.
+extension Extension<LongTypeParameter, Another> on BaseClass {}
+<<<
+extension Extension<
+  LongTypeParameter,
+  Another
+> on BaseClass {}
+>>> Unnamed.
+extension on String {}
+<<<
+extension on String {}
+>>> Unnamed with type parameters.
+extension  <  T  ,  S  >  on  B {}
+<<<
+extension<T, S> on B {}
+>>> Split at `on`.
+extension SomeExtension on VeryLongClass {}
+<<<
+extension SomeExtension
+    on VeryLongClass {}
+>>> Unsplit generic on type.
+extension SomeExtension on C<int> {}
+<<<
+extension SomeExtension on C<int> {}
+>>> Split before `on` on generic on type.
+extension SomeExtension on C<SomeLongClass> {}
+<<<
+extension SomeExtension
+    on C<SomeLongClass> {}
+>>> Split in generic on type.
+extension SomeExtension on C<VeryLongType, AnotherLongType> {}
+<<<
+extension SomeExtension
+    on
+        C<
+          VeryLongType,
+          AnotherLongType
+        > {}
\ No newline at end of file