diff --git a/pkg/analysis_server/lib/plugin/edit/assist/assist_core.dart b/pkg/analysis_server/lib/plugin/edit/assist/assist_core.dart index c4469eaf42b..c56e634ac00 100644 --- a/pkg/analysis_server/lib/plugin/edit/assist/assist_core.dart +++ b/pkg/analysis_server/lib/plugin/edit/assist/assist_core.dart @@ -15,7 +15,9 @@ class Assist { /// Assists with the same relevance are sorted alphabetically. static final Comparator SORT_BY_RELEVANCE = (Assist a, Assist b) { if (a.kind.priority != b.kind.priority) { - return a.kind.priority - b.kind.priority; + // A higher priority indicates a higher relevance + // and should be sorted before a lower priority. + return b.kind.priority - a.kind.priority; } return a.change.message.compareTo(b.change.message); }; diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart index 9f7152c05e8..ecc14cc5a1b 100644 --- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart +++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart @@ -36,10 +36,11 @@ class CodeActionHandler extends MessageHandler(); - /// A comparator that can be used to sort [CodeActions]s using priorties - /// in [codeActionPriorities]. The highest number priority will be sorted - /// before lower number priorityies. Items with the same relevance are sorted - /// alphabetically by their title. + /// A comparator that can be used to sort [CodeActions]s using priorities + /// in [codeActionPriorities]. + /// + /// The highest number priority will be sorted before lower number priorities. + /// Items with the same priority are sorted alphabetically by their title. late final Comparator _codeActionComparator = (CodeAction a, CodeAction b) { // We should never be sorting actions without priorities. diff --git a/pkg/analysis_server/lib/src/services/correction/assist.dart b/pkg/analysis_server/lib/src/services/correction/assist.dart index 47624e2b980..c7233ba240d 100644 --- a/pkg/analysis_server/lib/src/services/correction/assist.dart +++ b/pkg/analysis_server/lib/src/services/correction/assist.dart @@ -73,7 +73,7 @@ class DartAssistKind { ); static const CONVERT_INTO_ASYNC_BODY = AssistKind( 'dart.assist.convert.bodyToAsync', - 29, + 31, 'Convert to async function body', ); static const CONVERT_INTO_BLOCK_BODY = AssistKind( @@ -222,77 +222,77 @@ class DartAssistKind { // Flutter wrap specific assists static const FLUTTER_WRAP_GENERIC = AssistKind( 'dart.assist.flutter.wrap.generic', - 31, + 29, 'Wrap with widget...', ); static const FLUTTER_WRAP_BUILDER = AssistKind( 'dart.assist.flutter.wrap.builder', - 32, + 28, 'Wrap with Builder', ); static const FLUTTER_WRAP_CENTER = AssistKind( 'dart.assist.flutter.wrap.center', - 32, + 28, 'Wrap with Center', ); static const FLUTTER_WRAP_COLUMN = AssistKind( 'dart.assist.flutter.wrap.column', - 32, + 28, 'Wrap with Column', ); static const FLUTTER_WRAP_CONTAINER = AssistKind( 'dart.assist.flutter.wrap.container', - 32, + 28, 'Wrap with Container', ); static const FLUTTER_WRAP_PADDING = AssistKind( 'dart.assist.flutter.wrap.padding', - 32, + 28, 'Wrap with Padding', ); static const FLUTTER_WRAP_ROW = AssistKind( 'dart.assist.flutter.wrap.row', - 32, + 28, 'Wrap with Row', ); static const FLUTTER_WRAP_SIZED_BOX = AssistKind( 'dart.assist.flutter.wrap.sizedBox', - 32, + 28, 'Wrap with SizedBox', ); static const FLUTTER_WRAP_STREAM_BUILDER = AssistKind( 'dart.assist.flutter.wrap.streamBuilder', - 32, + 28, 'Wrap with StreamBuilder', ); // Flutter re-order assists static const FLUTTER_SWAP_WITH_CHILD = AssistKind( 'dart.assist.flutter.swap.withChild', - 33, + 27, 'Swap with child', ); static const FLUTTER_SWAP_WITH_PARENT = AssistKind( 'dart.assist.flutter.swap.withParent', - 33, + 27, 'Swap with parent', ); static const FLUTTER_MOVE_DOWN = AssistKind( 'dart.assist.flutter.move.down', - 34, + 26, 'Move widget down', ); static const FLUTTER_MOVE_UP = AssistKind( 'dart.assist.flutter.move.up', - 34, + 26, 'Move widget up', ); // Flutter remove assist static const FLUTTER_REMOVE_WIDGET = AssistKind( 'dart.assist.flutter.removeWidget', - 35, + 25, 'Remove this widget', ); @@ -334,7 +334,7 @@ class DartAssistKind { static const REMOVE_TYPE_ANNOTATION = AssistKind( // todo (pq): unify w/ fix 'dart.assist.remove.typeAnnotation', - 29, + 31, 'Remove type annotation', ); static const REPLACE_CONDITIONAL_WITH_IF_ELSE = AssistKind( @@ -374,47 +374,47 @@ class DartAssistKind { ); static const SURROUND_WITH_BLOCK = AssistKind( 'dart.assist.surround.block', - 22, + 38, 'Surround with block', ); static const SURROUND_WITH_DO_WHILE = AssistKind( 'dart.assist.surround.doWhile', - 27, + 33, "Surround with 'do-while'", ); static const SURROUND_WITH_FOR = AssistKind( 'dart.assist.surround.forEach', - 26, + 34, "Surround with 'for'", ); static const SURROUND_WITH_FOR_IN = AssistKind( 'dart.assist.surround.forIn', - 25, + 35, "Surround with 'for-in'", ); static const SURROUND_WITH_IF = AssistKind( 'dart.assist.surround.if', - 23, + 37, "Surround with 'if'", ); static const SURROUND_WITH_SET_STATE = AssistKind( 'dart.assist.surround.setState', - 27, + 33, "Surround with 'setState'", ); static const SURROUND_WITH_TRY_CATCH = AssistKind( 'dart.assist.surround.tryCatch', - 28, + 32, "Surround with 'try-catch'", ); static const SURROUND_WITH_TRY_FINALLY = AssistKind( 'dart.assist.surround.tryFinally', - 29, + 31, "Surround with 'try-finally'", ); static const SURROUND_WITH_WHILE = AssistKind( 'dart.assist.surround.while', - 24, + 36, "Surround with 'while'", ); static const USE_CURLY_BRACES = AssistKind( diff --git a/pkg/analysis_server/test/lsp/code_actions_assists_test.dart b/pkg/analysis_server/test/lsp/code_actions_assists_test.dart index 27971099add..d814a750d25 100644 --- a/pkg/analysis_server/test/lsp/code_actions_assists_test.dart +++ b/pkg/analysis_server/test/lsp/code_actions_assists_test.dart @@ -393,6 +393,39 @@ class AssistsCodeActionsTest extends AbstractCodeActionsTest { } } + Future test_sort() async { + const content = ''' + import 'package:flutter/widgets.dart'; + + build() => Contai^ner(child: Container()); + '''; + + newFile(mainFilePath, content: withoutMarkers(content)); + await initialize( + textDocumentCapabilities: withCodeActionKinds( + emptyTextDocumentClientCapabilities, [CodeActionKind.Refactor]), + workspaceCapabilities: + withDocumentChangesSupport(emptyWorkspaceClientCapabilities), + ); + + final codeActions = await getCodeActions(mainFileUri.toString(), + position: positionFromMarker(content)); + final names = codeActions.map( + (e) => e.map((command) => command.title, (action) => action.title), + ); + + expect( + names, + containsAllInOrder([ + // Check the ordering for two well-known assists that should always be + // sorted this way. + // https://github.com/Dart-Code/Dart-Code/issues/3646 + 'Wrap with widget...', + 'Remove this widget', + ]), + ); + } + List _extractTextDocumentEdits( Either2< List, diff --git a/pkg/analyzer_plugin/lib/utilities/assist/assist.dart b/pkg/analyzer_plugin/lib/utilities/assist/assist.dart index 3e6290b0fb7..58791ae3a08 100644 --- a/pkg/analyzer_plugin/lib/utilities/assist/assist.dart +++ b/pkg/analyzer_plugin/lib/utilities/assist/assist.dart @@ -69,7 +69,8 @@ class AssistKind { /// for example to allow key-binding specific fixes (or groups of). final String id; - /// The priority of this kind of assist for the kind of error being addressed. + /// The priority of this kind of assist for the kind of error being addressed + /// where a higher integer value indicates a higher priority and relevance. final int priority; /// A human-readable description of the changes that will be applied by this