[analysis_server] Suppress snippet support for Command CodeActions

We support a non-standard mechanism (https://github.com/rust-lang/rust-analyzer/blob/b35559a2460e7f0b2b79a7029db0c5d4e0acdb44/docs/dev/lsp-extensions.md#snippet-textedit) for having snippets on edits for Code Actions. This is only specified for edits returned from `codeAction` requests (and only implemented in Dart-Code for the same), so we should not return snippets when applying edits via `workspace/applyEdit` (eg. when code actions are returned as commands).

This prevents snippet markup being inserted literally into the editor if codeActions are executed over DTD (eg. from property editor).

Change-Id: I7882bffb8038671459e7487dc2e944020395f128
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/429340
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/abstract_code_actions_producer.dart b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/abstract_code_actions_producer.dart
index 50377bf..497671c 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/abstract_code_actions_producer.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/abstract_code_actions_producer.dart
@@ -39,6 +39,14 @@
   final int length;
   final bool Function(CodeActionKind?) shouldIncludeKind;
 
+  /// Whether non-standard LSP snippets are allowed in edits produced.
+  ///
+  /// This is usually true for the `textDocument/codeAction` request (because we
+  /// support it for [CodeActionLiteral]s) but `false` for the
+  /// [Commands.applyCodeAction] handler because it's not supported for
+  /// `workspace/applyEdit` reverse requests.
+  final bool allowSnippets;
+
   /// The capabilities of the caller making the request for [CodeAction]s.
   final LspClientCapabilities callerCapabilities;
 
@@ -78,6 +86,7 @@
     required this.editorCapabilities,
     required this.allowCommands,
     required this.allowCodeActionLiterals,
+    required this.allowSnippets,
     required this.analysisOptions,
   });
 
@@ -139,7 +148,7 @@
         server,
         editorCapabilities,
         change,
-        allowSnippets: true,
+        allowSnippets: allowSnippets,
         filePath: path,
         lineInfo: lineInfo,
       ),
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/analysis_options.dart b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/analysis_options.dart
index 17e2dc7..597c040 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/analysis_options.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/analysis_options.dart
@@ -31,6 +31,7 @@
     required super.allowCodeActionLiterals,
     required super.allowCommands,
     required super.analysisOptions,
+    required super.allowSnippets,
   });
 
   @override
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/code_action_computer.dart b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/code_action_computer.dart
index 8368f1d..01126a3 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/code_action_computer.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/code_action_computer.dart
@@ -46,6 +46,14 @@
   /// How the request was triggered.
   final CodeActionTriggerKind? triggerKind;
 
+  /// Whether non-standard LSP snippets are allowed in edits produced.
+  ///
+  /// This is usually true for the `textDocument/codeAction` request (because we
+  /// support it for [CodeActionLiteral]s) but `false` for the
+  /// [Commands.applyCodeAction] handler because it's not supported for
+  /// `workspace/applyEdit` reverse requests.
+  final bool allowSnippets;
+
   /// Whether [CodeAction]s can be [Command]s or not.
   ///
   /// This is usually true (because there is no capability for this), however
@@ -92,6 +100,7 @@
     required this.triggerKind,
     required this.allowCommands,
     required this.allowCodeActionLiterals,
+    required this.allowSnippets,
     required this.performance,
   });
 
@@ -237,8 +246,9 @@
           callerCapabilities: callerCapabilities,
           allowCodeActionLiterals: allowCodeActionLiterals,
           allowCommands: allowCommands,
-          triggerKind: triggerKind,
+          allowSnippets: allowSnippets,
           analysisOptions: analysisOptions,
+          triggerKind: triggerKind,
           willBeDeduplicated: true,
         ),
       if (isPubspec)
@@ -254,6 +264,7 @@
           callerCapabilities: callerCapabilities,
           allowCodeActionLiterals: allowCodeActionLiterals,
           allowCommands: allowCommands,
+          allowSnippets: allowSnippets,
           analysisOptions: analysisOptions,
         ),
       if (isAnalysisOptions)
@@ -269,6 +280,7 @@
           callerCapabilities: callerCapabilities,
           allowCodeActionLiterals: allowCodeActionLiterals,
           allowCommands: allowCommands,
+          allowSnippets: allowSnippets,
           analysisOptions: analysisOptions,
         ),
       PluginCodeActionsProducer(
@@ -283,6 +295,7 @@
         callerCapabilities: callerCapabilities,
         allowCodeActionLiterals: allowCodeActionLiterals,
         allowCommands: allowCommands,
+        allowSnippets: allowSnippets,
         analysisOptions: analysisOptions,
       ),
     ];
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/dart.dart b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/dart.dart
index 3e743a4..a502b23 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/dart.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/dart.dart
@@ -55,6 +55,7 @@
     required super.callerCapabilities,
     required super.allowCodeActionLiterals,
     required super.allowCommands,
+    required super.allowSnippets,
     required super.analysisOptions,
     required this.triggerKind,
     required this.willBeDeduplicated,
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/plugins.dart b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/plugins.dart
index 54c51a9..8d83dd5 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/plugins.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/plugins.dart
@@ -30,6 +30,7 @@
     required super.allowCodeActionLiterals,
     required super.allowCommands,
     required super.analysisOptions,
+    required super.allowSnippets,
   }) : driver = server.getAnalysisDriver(file.path);
 
   @override
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/pubspec.dart b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/pubspec.dart
index 5596c18..1c24c88 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/pubspec.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/pubspec.dart
@@ -28,6 +28,7 @@
     required super.allowCodeActionLiterals,
     required super.allowCommands,
     required super.analysisOptions,
+    required super.allowSnippets,
   });
 
   @override
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/apply_code_action.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/apply_code_action.dart
index d57ba3a..d0b3d59 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/apply_code_action.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/apply_code_action.dart
@@ -147,6 +147,10 @@
       // ability to applyEdit has been checked at the top of this method.
       allowCommands: false,
       allowCodeActionLiterals: true,
+
+      // We don't support non-standard snippets for `workspace/applyEdit`, only
+      // CodeActionLiterals.
+      allowSnippets: false,
     );
     var actions = await computer.compute();
 
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 0832908..e8b1295 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
@@ -57,6 +57,7 @@
       triggerKind: params.context.triggerKind,
       allowCommands: true,
       allowCodeActionLiterals: supportsLiterals,
+      allowSnippets: true, // We allow snippets from code actions requests.
       performance: performance,
     );
 
diff --git a/pkg/analysis_server/test/shared/shared_code_actions_assists_tests.dart b/pkg/analysis_server/test/shared/shared_code_actions_assists_tests.dart
index 965fca4..dd235e0 100644
--- a/pkg/analysis_server/test/shared/shared_code_actions_assists_tests.dart
+++ b/pkg/analysis_server/test/shared/shared_code_actions_assists_tests.dart
@@ -126,7 +126,7 @@
 >>>>>>>>>> lib/test.dart
 import 'package:flutter/widgets.dart';
 Widget build() {
-  return Center($0child: Text(''));
+  return Center(child: Text(''));
 }
 ''';
 
diff --git a/pkg/analysis_server/test/shared/shared_code_actions_fixes_tests.dart b/pkg/analysis_server/test/shared/shared_code_actions_fixes_tests.dart
index a3b0e3a..380dbdb 100644
--- a/pkg/analysis_server/test/shared/shared_code_actions_fixes_tests.dart
+++ b/pkg/analysis_server/test/shared/shared_code_actions_fixes_tests.dart
@@ -701,6 +701,38 @@
     );
   }
 
+  Future<void> test_snippets() async {
+    setSnippetTextEditSupport();
+
+    const content = '''
+abstract class A {
+  void m();
+}
+
+class ^B extends A {}
+''';
+
+    const expectedContent = r'''
+abstract class A {
+  void m();
+}
+
+class B extends A {
+  @override
+  void m() {
+    // TODO: implement m$0
+  }
+}
+''';
+
+    await verifyCodeActionLiteralEdits(
+      content,
+      expectedContent,
+      kind: CodeActionKind('quickfix.create.missingOverrides'),
+      title: 'Create 1 missing override',
+    );
+  }
+
   Future<void> test_snippets_createMethod_functionTypeNestedParameters() async {
     const content = '''
 class A {
@@ -788,6 +820,43 @@
     );
   }
 
+  /// The non-standard snippets we supported are only supported for
+  /// [CodeActionLiteral]s and not for [Command]s (which go via
+  /// workspace/applyEdit) so even if enabled, they should not be returned.
+  Future<void> test_snippets_unsupportedForCommands() async {
+    setSupportedCodeActionKinds(null); // no codeActionLiteralSupport
+    setSnippetTextEditSupport(); // will be ignored
+
+    const content = '''
+abstract class A {
+  void m();
+}
+
+class ^B extends A {}
+''';
+
+    // No $0 placeholder in this content (unlike in `test_snippets`).
+    const expectedContent = r'''
+abstract class A {
+  void m();
+}
+
+class B extends A {
+  @override
+  void m() {
+    // TODO: implement m
+  }
+}
+''';
+
+    await verifyCommandCodeActionEdits(
+      content,
+      expectedContent,
+      command: Commands.applyCodeAction,
+      title: 'Create 1 missing override',
+    );
+  }
+
   void _enableLints(List<String> lintNames) {
     registerLintRules();
     var lintsYaml = lintNames.map((name) => '    - $name\n').join();