-
Notifications
You must be signed in to change notification settings - Fork 345
[Property Editor] Add APIs to support refactors from the Property Editor #9202
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
Changes from 7 commits
0100376
2c88aa8
6d1667b
8740d15
13d9754
36f6e60
ee0fe55
abe353c
a77c314
64e19bd
d891ad8
2a7df21
d125614
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
48f87a5fe76aa65f89f37cc716e50b34933e78e9 | ||
dd671fae53d37eb15e0f8fc94cd52c2f2ff147ee |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,14 +31,36 @@ enum EditorMethod { | |
/// TODO(https://github.com/flutter/devtools/issues/8824): Add tests that these | ||
/// are in-sync with analysis_server. | ||
enum LspMethod { | ||
codeAction(methodName: 'textDocument/codeAction'), | ||
editableArguments(methodName: 'dart/textDocument/editableArguments'), | ||
editArgument(methodName: 'dart/textDocument/editArgument'); | ||
editArgument(methodName: 'dart/textDocument/editArgument'), | ||
executeCommand(methodName: 'workspace/executeCommand'); | ||
|
||
const LspMethod({required this.methodName}); | ||
|
||
/// Returns the [LspMethod] for the given [methodName]. | ||
/// | ||
/// If the [methodName] does not exist, returns null. | ||
static LspMethod? fromMethodName(String methodName) { | ||
for (final method in LspMethod.values) { | ||
if (method.methodName == methodName) return method; | ||
} | ||
return null; | ||
} | ||
|
||
final String methodName; | ||
|
||
String get experimentalMethodName => 'experimental/$methodName'; | ||
static final _registrationStatus = <LspMethod, bool>{ | ||
for (final method in LspMethod.values) method: false, | ||
}; | ||
|
||
/// Sets the registration status for this LSP method. | ||
set isRegistered(bool isRegistered) { | ||
_registrationStatus[this] = isRegistered; | ||
} | ||
|
||
/// Gets the current registration status of this LSP method. | ||
bool get isRegistered => _registrationStatus[this] ?? false; | ||
} | ||
|
||
/// Known kinds of events that may come from the editor. | ||
|
@@ -82,12 +104,14 @@ enum EditorEventKind { | |
/// Constants for all fields used in JSON maps to avoid literal strings that | ||
/// may have typos sprinkled throughout the API classes. | ||
abstract class Field { | ||
static const actions = 'actions'; | ||
static const active = 'active'; | ||
static const anchor = 'anchor'; | ||
static const arguments = 'arguments'; | ||
static const backgroundColor = 'backgroundColor'; | ||
static const category = 'category'; | ||
static const character = 'character'; | ||
static const command = 'command'; | ||
static const debuggerType = 'debuggerType'; | ||
static const debugSession = 'debugSession'; | ||
static const debugSessionId = 'debugSessionId'; | ||
|
@@ -115,7 +139,9 @@ abstract class Field { | |
static const isEditable = 'isEditable'; | ||
static const isNullable = 'isNullable'; | ||
static const isRequired = 'isRequired'; | ||
static const kind = 'kind'; | ||
static const line = 'line'; | ||
static const loggedAction = 'loggedAction'; | ||
DanTup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
static const name = 'name'; | ||
static const options = 'options'; | ||
static const page = 'page'; | ||
|
@@ -133,6 +159,7 @@ abstract class Field { | |
static const supportsForceExternal = 'supportsForceExternal'; | ||
static const textDocument = 'textDocument'; | ||
static const theme = 'theme'; | ||
static const title = 'title'; | ||
static const type = 'type'; | ||
static const uri = 'uri'; | ||
static const value = 'value'; | ||
|
@@ -501,6 +528,105 @@ class EditableArgumentsResult with Serializable { | |
}; | ||
} | ||
|
||
/// Constants for [CodeAction] prefixes used to filter the results returned by | ||
/// an [LspMethod.codeAction] request. | ||
abstract class CodeActionPrefixes { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add unit tests for all of the new logic in this file? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, added |
||
static const flutterWrap = 'refactor.flutter.wrap'; | ||
} | ||
|
||
/// The result of an [LspMethod.codeAction] request to the Analysis Server. | ||
/// | ||
/// Contains a list of [CodeAction]s that can be performed. | ||
class CodeActionResult with Serializable { | ||
CodeActionResult({required this.actions}); | ||
|
||
CodeActionResult.fromJson(List<Map<String, Object?>> list) | ||
: this(actions: list.map(CodeAction.fromJson).toList()); | ||
|
||
final List<CodeAction> actions; | ||
|
||
@override | ||
Map<String, Object?> toJson() => {Field.actions: actions}; | ||
} | ||
|
||
/// A code action (also known as a "Refactor" or "Quick Fix") that can be called | ||
/// via an [LspMethod.executeCommand] request. | ||
/// | ||
/// For example, "Wrap with Center" or "Wrap with Container". | ||
class CodeAction with Serializable { | ||
DanTup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
CodeAction({required this.command, required this.title, required this.args}); | ||
|
||
CodeAction.fromJson(Map<String, Object?> map) | ||
: this( | ||
command: map[Field.command] as String, | ||
title: map[Field.title] as String, | ||
args: (map[Field.arguments] as List<Object?>? ?? <Object?>[]) | ||
.cast<Map<String, Object?>>() | ||
.map(CodeActionArgument.fromJson) | ||
.toList(), | ||
DanTup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
); | ||
|
||
/// The command identifier to send to [LspMethod.executeCommand]. | ||
final String? command; | ||
|
||
/// The human-readable title of the command, e.g., "Wrap with Center". | ||
final String? title; | ||
|
||
/// Arguments that should be passed to [LspMethod.executeCommand] when | ||
/// invoking this action. | ||
final List<CodeActionArgument> args; | ||
|
||
@override | ||
Map<String, Object?> toJson() => { | ||
Field.command: command, | ||
Field.title: title, | ||
Field.arguments: args, | ||
}; | ||
} | ||
|
||
/// An argument for a [CodeAction]. | ||
/// | ||
/// This includes information about the document and range to which the | ||
/// [CodeAction] should be applied. | ||
class CodeActionArgument with Serializable { | ||
CodeActionArgument({ | ||
required this.textDocument, | ||
required this.range, | ||
required this.kind, | ||
required this.loggedAction, | ||
}); | ||
|
||
CodeActionArgument.fromJson(Map<String, Object?> map) | ||
: this( | ||
textDocument: TextDocument.fromJson( | ||
map[Field.textDocument] as Map<String, Object?>, | ||
), | ||
range: EditorRange.fromJson(map[Field.range] as Map<String, Object?>), | ||
kind: map[Field.kind] as String?, | ||
loggedAction: map[Field.loggedAction] as String?, | ||
); | ||
|
||
/// The document to which the [CodeAction] applies. | ||
final TextDocument textDocument; | ||
|
||
/// The range within the [textDocument] to which the [CodeAction] applies. | ||
final EditorRange range; | ||
|
||
/// The kind of action, often a string like "refactor.flutter.wrap.container". | ||
final String? kind; | ||
|
||
/// An identifier used for logging or analytics purposes related to this action. | ||
final String? loggedAction; | ||
|
||
@override | ||
Map<String, Object?> toJson() => { | ||
Field.textDocument: textDocument.toJson(), | ||
Field.range: range.toJson(), | ||
Field.kind: kind, | ||
Field.loggedAction: loggedAction, | ||
}; | ||
} | ||
|
||
/// Errors that the Analysis Server returns for failed argument edits. | ||
/// | ||
/// These should be kept in sync with the error coes defined at | ||
|
@@ -554,16 +680,17 @@ enum EditArgumentError { | |
} | ||
} | ||
|
||
/// Response to an edit argument request. | ||
class EditArgumentResponse { | ||
EditArgumentResponse({required this.success, this.errorMessage, errorCode}) | ||
: _errorCode = errorCode; | ||
/// Generic response representing whether a reqeust was a [success]. | ||
DanTup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
class GenericApiResponse { | ||
GenericApiResponse({ | ||
required this.success, | ||
this.errorMessage, | ||
this.errorCode, | ||
}); | ||
|
||
final bool success; | ||
final String? errorMessage; | ||
final int? _errorCode; | ||
|
||
EditArgumentError? get errorType => EditArgumentError.fromCode(_errorCode); | ||
final int? errorCode; | ||
} | ||
|
||
/// Information about a single editable argument of a widget. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be
return LspMethod.values.firstWhereOrNull((method) => method.methodName == methodName)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a minor perf increase I think you could also add something like:
And then look it up as:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! Though we need the method name map instead of the name map, so I've added a static map for method name -> method to use for the look-up.