Add ability to generate custom classes for custom LSP methods

Change-Id: Ie613a828fd6d92133e3d5cb53673c37734fb6eec
Reviewed-on: https://dart-review.googlesource.com/c/90011
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
new file mode 100644
index 0000000..1f65f46
--- /dev/null
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file has been automatically generated. Please do not edit it manually.
+// To regenerate the file, use the script
+// "pkg/analysis_server/tool/lsp_spec/generate_all.dart".
+
+// ignore_for_file: deprecated_member_use
+// ignore_for_file: unnecessary_brace_in_string_interps
+// ignore_for_file: unused_import
+
+import 'dart:core' hide deprecated;
+import 'dart:core' as core show deprecated;
+import 'dart:convert' show JsonEncoder;
+import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/protocol/protocol_internal.dart'
+    show listEqual, mapEqual;
+import 'package:analyzer/src/generated/utilities_general.dart';
+
+const jsonEncoder = const JsonEncoder.withIndent('    ');
+
+class DartDiagnosticServer implements ToJsonable {
+  DartDiagnosticServer(this.port) {
+    if (port == null) {
+      throw 'port is required but was not provided';
+    }
+  }
+  static DartDiagnosticServer fromJson(Map<String, dynamic> json) {
+    final port = json['port'];
+    return new DartDiagnosticServer(port);
+  }
+
+  final num port;
+
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> __result = {};
+    __result['port'] = port ?? (throw 'port is required but was not set');
+    return __result;
+  }
+
+  static bool canParse(Object obj) {
+    return obj is Map<String, dynamic> &&
+        obj.containsKey('port') &&
+        obj['port'] is num;
+  }
+
+  @override
+  bool operator ==(other) {
+    if (other is DartDiagnosticServer) {
+      return port == other.port && true;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, port.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+
+  @override
+  String toString() => jsonEncoder.convert(toJson());
+}
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
index 8991e2f..b3f4fe6 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
@@ -8,6 +8,7 @@
 
 // ignore_for_file: deprecated_member_use
 // ignore_for_file: unnecessary_brace_in_string_interps
+// ignore_for_file: unused_import
 
 import 'dart:core' hide deprecated;
 import 'dart:core' as core show deprecated;
diff --git a/pkg/analysis_server/tool/lsp_spec/generate_all.dart b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
index b6d9cc2..aa2ffbb 100644
--- a/pkg/analysis_server/tool/lsp_spec/generate_all.dart
+++ b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
@@ -40,6 +40,11 @@
   final String outFolder = path.join(packageFolder, 'lib', 'lsp_protocol');
   new Directory(outFolder).createSync();
 
+  await writeSpecClasses(args, outFolder);
+  await writeCustomClasses(args, outFolder);
+}
+
+Future writeSpecClasses(ArgResults args, String outFolder) async {
   if (args[argDownload]) {
     await downloadSpec();
   }
@@ -62,7 +67,28 @@
   final String output = generateDartForTypes(types);
 
   new File(path.join(outFolder, 'protocol_generated.dart'))
-      .writeAsStringSync(_generatedFileHeader + output);
+      .writeAsStringSync(generatedFileHeader(2018) + output);
+}
+
+/// Writes classes used by Dart's custom LSP methods.
+Future writeCustomClasses(ArgResults args, String outFolder) async {
+  interface(String name, List<Member> fields) {
+    return new Interface(null, Token.identifier(name), [], [], fields);
+  }
+
+  field(String name, {String type, canBeNull: false, canBeUndefined: false}) {
+    return new Field(null, Token.identifier(name), Type.identifier(type),
+        canBeNull, canBeUndefined);
+  }
+
+  final List<AstNode> customTypes = [
+    interface('DartDiagnosticServer', [field('port', type: 'number')]),
+  ];
+
+  final String output = generateDartForTypes(customTypes);
+
+  new File(path.join(outFolder, 'protocol_custom_generated.dart'))
+      .writeAsStringSync(generatedFileHeader(2019) + output);
 }
 
 Namespace extractMethodsEnum(String spec) {
@@ -91,8 +117,8 @@
       comment, new Token.identifier('Method'), methodConstants);
 }
 
-const _generatedFileHeader = '''
-// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+String generatedFileHeader(int year) => '''
+// Copyright (c) $year, the Dart project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
@@ -102,6 +128,7 @@
 
 // ignore_for_file: deprecated_member_use
 // ignore_for_file: unnecessary_brace_in_string_interps
+// ignore_for_file: unused_import
 
 import 'dart:core' hide deprecated;
 import 'dart:core' as core show deprecated;