Add some special cases for types we can't/don't need to parse

Includes a fabricated base type for file operations to avoid a List of a Union.

Change-Id: I0ddc7c6428cc8f1535c3a8d7d0b042b1725969f3
Reviewed-on: https://dart-review.googlesource.com/c/79323
Commit-Queue: Danny Tuppeny <dantup@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Danny Tuppeny 2018-10-11 17:51:10 +00:00 committed by commit-bot@chromium.org
parent 49b121f20d
commit 2989116219
5 changed files with 97 additions and 26 deletions

View file

@ -6,6 +6,8 @@
// To regenerate the file, use the script
// "pkg/analysis_server/tool/lsp_spec/generate_all.dart".
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
class ApplyWorkspaceEditParams {
/// The edits to apply.
WorkspaceEdit edit;
@ -177,7 +179,7 @@ class CodeLensParams {
TextDocumentIdentifier textDocument;
}
class CodeLensRegistrationOptions {
class CodeLensRegistrationOptions extends TextDocumentRegistrationOptions {
/// Code lens has a resolve provider as well.
bool resolveProvider;
}
@ -410,14 +412,14 @@ class CompletionOptions {
List<String> triggerCharacters;
}
class CompletionParams {
class CompletionParams extends TextDocumentPositionParams {
/// The completion context. This is only available if the client specifies to
/// send this using `ClientCapabilities.textDocument.completion.contextSupport
/// === true`
CompletionContext context;
}
class CompletionRegistrationOptions {
class CompletionRegistrationOptions extends TextDocumentRegistrationOptions {
/// The server provides support to resolve additional information for a
/// completion item.
bool resolveProvider;
@ -462,7 +464,7 @@ class ConfigurationParams {
}
/// Create file operation
class CreateFile {
class CreateFile extends FileOperation {
/// Additional options
CreateFileOptions options;
@ -480,7 +482,7 @@ class CreateFileOptions {
}
/// Delete file operation
class DeleteFile {
class DeleteFile extends FileOperation {
/// Delete options.
DeleteFileOptions options;
@ -664,7 +666,7 @@ class DocumentLinkParams {
TextDocumentIdentifier textDocument;
}
class DocumentLinkRegistrationOptions {
class DocumentLinkRegistrationOptions extends TextDocumentRegistrationOptions {
/// Document links have a resolve provider as well.
bool resolveProvider;
}
@ -692,7 +694,8 @@ class DocumentOnTypeFormattingParams {
TextDocumentIdentifier textDocument;
}
class DocumentOnTypeFormattingRegistrationOptions {
class DocumentOnTypeFormattingRegistrationOptions
extends TextDocumentRegistrationOptions {
/// A character on which formatting should be triggered, like `}`.
String firstTriggerCharacter;
@ -1003,7 +1006,7 @@ abstract class MessageType {
static const Warning = 2;
}
class NotificationMessage {
class NotificationMessage extends Message {
/// The method to be invoked.
String method;
}
@ -1053,7 +1056,7 @@ class ReferenceContext {
bool includeDeclaration;
}
class ReferenceParams {
class ReferenceParams extends TextDocumentPositionParams {
ReferenceContext context;
}
@ -1075,7 +1078,7 @@ class RegistrationParams {
}
/// Rename file operation
class RenameFile {
class RenameFile extends FileOperation {
/// The new location.
String newUri;
@ -1113,12 +1116,12 @@ class RenameParams {
TextDocumentIdentifier textDocument;
}
class RenameRegistrationOptions {
class RenameRegistrationOptions extends TextDocumentRegistrationOptions {
/// Renames should be checked and tested for validity before being executed.
bool prepareProvider;
}
class RequestMessage {
class RequestMessage extends Message {
/// The request id.
Object /*Either<num, String>*/ id;
@ -1137,7 +1140,7 @@ abstract class ResourceOperationKind {
static const Rename = 'rename';
}
class ResponseMessage {
class ResponseMessage extends Message {
/// The request id.
Object /*Either<num, String>*/ id;
@ -1270,7 +1273,7 @@ class SignatureHelpOptions {
List<String> triggerCharacters;
}
class SignatureHelpRegistrationOptions {
class SignatureHelpRegistrationOptions extends TextDocumentRegistrationOptions {
/// The characters that trigger signature help automatically.
List<String> triggerCharacters;
}
@ -1383,7 +1386,8 @@ abstract class SymbolKind {
/// Describe options to be used when registering for text document change
/// events.
class TextDocumentChangeRegistrationOptions {
class TextDocumentChangeRegistrationOptions
extends TextDocumentRegistrationOptions {
/// How documents are synced to the server. See TextDocumentSyncKind.Full and
/// TextDocumentSyncKind.Incremental.
num syncKind;
@ -1420,7 +1424,7 @@ class TextDocumentContentChangeEvent {
String text;
}
class TextDocumentEdit {
class TextDocumentEdit extends FileOperation {
/// The edits to be applied.
List<TextEdit> edits;
@ -1475,7 +1479,8 @@ abstract class TextDocumentSaveReason {
static const Manual = 1;
}
class TextDocumentSaveRegistrationOptions {
class TextDocumentSaveRegistrationOptions
extends TextDocumentRegistrationOptions {
/// The client is supposed to include the content on save.
bool includeText;
}
@ -1537,7 +1542,7 @@ class UnregistrationParams {
List<Unregistration> unregisterations;
}
class VersionedTextDocumentIdentifier {
class VersionedTextDocumentIdentifier extends TextDocumentIdentifier {
/// The version number of this document. If a versioned text document
/// identifier is sent from the server to the client and the file is not open
/// in the editor (the server has not received an open notification before)
@ -1587,7 +1592,25 @@ class WorkspaceClientCapabilities {
List<ResourceOperationKind> resourceOperations;
}
class WorkspaceEdit {}
class WorkspaceEdit {
/// Holds changes to existing resources.
Map<String, List<TextEdit>> changes;
/// Depending on the client capability
/// `workspace.workspaceEdit.resourceOperations` document changes are either
/// an array of `TextDocumentEdit`s to express changes to n different text
/// documents where each text document edit addresses a specific version of a
/// text document. Or it can contain above `TextDocumentEdit`s mixed with
/// create, rename and delete file / folder operations.
///
/// Whether a client supports versioned document edits is expressed via
/// `workspace.workspaceEdit.documentChanges` client capability.
///
/// If a client neither supports `documentChanges` nor
/// `workspace.workspaceEdit.resourceOperations` then only plain `TextEdit`s
/// using the `changes` property are supported.
List<FileOperation> documentChanges;
}
class WorkspaceFolder {
/// The name of the workspace folder. Defaults to the uri's basename.

View file

@ -0,0 +1,5 @@
// Copyright (c) 2018, 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.
class FileOperation {}

View file

@ -32,6 +32,7 @@ String _mapType(String type) {
'string': 'String',
'number': 'num',
'any': 'Object',
'{ [uri: string]: TextEdit[]; }': 'Map<String, List<TextEdit>>',
};
return types[type] ?? type;
}
@ -114,8 +115,12 @@ void _writeInterface(IndentableStringBuffer buffer, Interface interface) {
return;
}
buffer.writeIndented('class ${interface.name} ');
if (interface.baseTypes.isNotEmpty) {
buffer.writeIndented('extends ${interface.baseTypes.join(', ')} ');
}
buffer
..writeln('class ${interface.name} {')
..writeln('{')
..indent();
// TODO(dantup): Generate constructors (inc. type checks for unions)
_writeMembers(buffer, interface.members);
@ -123,7 +128,7 @@ void _writeInterface(IndentableStringBuffer buffer, Interface interface) {
// TODO(dantup): Generate fromJson()
buffer
..outdent()
..writeln('}')
..writeIndentedLn('}')
..writeln();
}

View file

@ -21,6 +21,8 @@ const _generatedFileHeader = '''
// To regenerate the file, use the script
// "pkg/analysis_server/tool/lsp_spec/generate_all.dart".
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
''';
main() async {

View file

@ -5,7 +5,7 @@
// TODO(dantup): Regex seemed like a good choice when parsing the first few...
// maybe it's not so great now. We should parse this properly if it turns out
// there are issues with what we have here.
const String _blockBody = r'\{([\s\S]*?)\s+\}';
const String _blockBody = r'\{([\s\S]*?)\s*\n\s*\}';
const String _comment = r'(?:\/\*\*((?:[\S\s](?!\*\/))+?)\s\*\/)?\s*';
List<ApiItem> extractAllTypes(List<String> code) {
@ -60,6 +60,11 @@ List<ApiItem> _mergeTypes(List<ApiItem> items) {
}
List<String> _parseTypes(String baseTypes, String sep) {
// Special case for a single complicated type we can't parse easily...
if (baseTypes ==
'(TextDocumentEdit[] | (TextDocumentEdit | CreateFile | RenameFile | DeleteFile)[])') {
return ['FileOperation[]'];
}
return baseTypes?.split(sep)?.map((t) => t.trim())?.toList() ?? [];
}
@ -115,13 +120,26 @@ class Field extends Member {
: super(name, comment);
static List<Field> extractFrom(String code) {
final RegExp _fieldPattern =
new RegExp(_comment + r'(\w+\??)\s*:\s*([\w\[\]\s|]+)\s*(?:;|$)');
final RegExp _fieldPattern = new RegExp(
_comment + r'([\w\[\]]+\??)\s*:\s*([\w\[\] \|\{\}\(\):;]+)\s*(?:;|$)');
final fields = _fieldPattern.allMatches(code).map((m) {
final fields = _fieldPattern.allMatches(code).where((m) {
// Skip over the indexer in FormattingOptions since we don't need this
// (for now) and it's complicated to represent.
if (m.group(0).contains('[key: string]: boolean | number | string;')) {
return false;
}
return true;
}).map((m) {
final String comment = m.group(1);
String name = m.group(2);
final List<String> types = _parseTypes(m.group(3), '|');
String typesString = m.group(3).trim();
// Our regex may result in semicolons on the end...
// TODO(dantup): Fix this, or make a simple parser.
if (typesString.endsWith(';')) {
typesString = typesString.substring(0, typesString.length - 1);
}
final List<String> types = _parseTypes(typesString, '|');
final bool allowsNull = types.contains('null');
if (allowsNull) {
types.remove('null');
@ -155,6 +173,10 @@ class Interface extends ApiItem {
final List<String> baseTypes = _parseTypes(match.group(3), ',');
final String body = match.group(4);
final List<Member> members = Member.extractFrom(body);
// Add any special base classes we've added to simplify types.
baseTypes.addAll(_getSpecialBaseClasses(name));
return new Interface(name, comment, baseTypes, members);
}).toList();
_sort(interfaces);
@ -162,6 +184,20 @@ class Interface extends ApiItem {
}
}
List<String> _getSpecialBaseClasses(String name) {
const fileOperationTypes = [
'TextDocumentEdit',
'CreateFile',
'RenameFile',
'DeleteFile'
];
if (fileOperationTypes.contains(name)) {
return ['FileOperation'];
} else {
return [];
}
}
/// A Field or Constant parsed from the LSP type.
abstract class Member extends ApiItem {
Member(String name, String comment) : super(name, comment);