From ebf8297664570f8c324c55fdec05e134c789bcf8 Mon Sep 17 00:00:00 2001 From: Daniel Chevalier Date: Tue, 31 Oct 2023 16:55:05 -0400 Subject: [PATCH] New VSCode fallback behaviour for copyToClipboard (#6598) ![](https://media.giphy.com/media/4URfklUToxk6A/giphy.gif) Adds a fallback behaviour for copying which will allow copy buttons to actually work in VSCode. NOTE: success toasts will no longer show in VSCode though. I've also added some logging just in case a user is looking at their console when a copy fails. Hopefully this provides enough information that the unhide-able error doesn't look suspicious. ![Screenshot 2023-10-26 at 10 53 39 AM](https://github.com/flutter/devtools/assets/1386322/b4ebd139-f3f5-4dd6-8c62-794f7d6df80d) Related to https://github.com/Dart-Code/Dart-Code/pull/4814 Fixes https://github.com/flutter/devtools/issues/5775 --- .../lib/src/framework/settings_dialog.dart | 2 +- .../src/screens/network/network_screen.dart | 1 + .../lib/src/shared/common_widgets.dart | 1 + .../_copy_to_clipboard_desktop.dart | 7 +++ .../_copy_to_clipboard_web.dart | 19 +++++++ .../copy_to_clipboard/copy_to_clipboard.dart | 52 +++++++++++++++++++ .../devtools_app/lib/src/shared/dialogs.dart | 2 +- .../lib/src/shared/editable_list.dart | 2 +- .../devtools_app/lib/src/shared/utils.dart | 19 ------- .../release_notes/NEXT_RELEASE_NOTES.md | 5 -- 10 files changed, 83 insertions(+), 27 deletions(-) create mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart create mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart create mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart diff --git a/packages/devtools_app/lib/src/framework/settings_dialog.dart b/packages/devtools_app/lib/src/framework/settings_dialog.dart index 3705255a32bf..0e850ef7082e 100644 --- a/packages/devtools_app/lib/src/framework/settings_dialog.dart +++ b/packages/devtools_app/lib/src/framework/settings_dialog.dart @@ -11,10 +11,10 @@ import 'package:provider/provider.dart'; import '../shared/analytics/analytics_controller.dart'; import '../shared/analytics/constants.dart' as gac; import '../shared/common_widgets.dart'; +import '../shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import '../shared/config_specific/server/server.dart'; import '../shared/globals.dart'; import '../shared/log_storage.dart'; -import '../shared/utils.dart'; class OpenSettingsAction extends ScaffoldAction { OpenSettingsAction({super.key, Color? color}) diff --git a/packages/devtools_app/lib/src/screens/network/network_screen.dart b/packages/devtools_app/lib/src/screens/network/network_screen.dart index f84ff588f6f0..d72610d5c49c 100644 --- a/packages/devtools_app/lib/src/screens/network/network_screen.dart +++ b/packages/devtools_app/lib/src/screens/network/network_screen.dart @@ -13,6 +13,7 @@ import 'package:provider/provider.dart'; import '../../shared/analytics/analytics.dart' as ga; import '../../shared/analytics/constants.dart' as gac; import '../../shared/common_widgets.dart'; +import '../../shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import '../../shared/globals.dart'; import '../../shared/http/curl_command.dart'; import '../../shared/http/http_request_data.dart'; diff --git a/packages/devtools_app/lib/src/shared/common_widgets.dart b/packages/devtools_app/lib/src/shared/common_widgets.dart index bf4f20e493e6..82d23c7a2654 100644 --- a/packages/devtools_app/lib/src/shared/common_widgets.dart +++ b/packages/devtools_app/lib/src/shared/common_widgets.dart @@ -17,6 +17,7 @@ import '../screens/debugger/debugger_controller.dart'; import '../screens/inspector/layout_explorer/ui/theme.dart'; import 'analytics/analytics.dart' as ga; import 'analytics/constants.dart' as gac; +import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import 'config_specific/launch_url/launch_url.dart'; import 'console/widgets/expandable_variable.dart'; import 'diagnostics/dart_object_node.dart'; diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart new file mode 100644 index 000000000000..04bef782f203 --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart @@ -0,0 +1,7 @@ +// Copyright 2023 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +void copyToClipboardVSCode(String _) { + // Do nothing +} diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart new file mode 100644 index 000000000000..e00b987428eb --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart @@ -0,0 +1,19 @@ +// Copyright 2023 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +import 'dart:js_interop'; +import 'package:web/web.dart'; + +void copyToClipboardVSCode(String data) { + // This post message is only relevant in VSCode. If the parent frame is + // listening for this command, then it will attempt to copy the contents + // to the clipboard in the context of the parent frame. + window.parent?.postMessage( + { + 'command': 'clipboard-write', + 'data': data, + }.jsify(), + '*'.toJS, + ); +} diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart new file mode 100644 index 000000000000..455414e7898f --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart @@ -0,0 +1,52 @@ +// Copyright 2023 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. + +import 'package:devtools_app_shared/ui.dart'; +import 'package:flutter/services.dart'; +import 'package:logging/logging.dart'; + +import '../../globals.dart'; +import '_copy_to_clipboard_desktop.dart' + if (dart.library.js_interop) '_copy_to_clipboard_web.dart'; + +final _log = Logger('copy_to_clipboard'); + +/// Attempts to copy a String of `data` to the clipboard. +/// +/// Shows a `successMessage` [Notification] on the passed in `context`, if the +/// copy is successfully done using the [Clipboard.setData] api. Otherwise it +/// attempts to post the [data] to the parent frame where the parent frame will +/// try to complete the copy (this fallback will only work in VSCode). +Future copyToClipboard( + String data, + String? successMessage, +) async { + try { + await Clipboard.setData( + ClipboardData( + text: data, + ), + ); + + if (successMessage != null) notificationService.push(successMessage); + } catch (e) { + if (ideTheme.embed) { + _log.warning( + 'DevTools copy failed. This may be as a result of a known bug in VSCode. ' + 'See https://github.com/Dart-Code/Dart-Code/issues/4540 for more ' + 'information. DevTools will now attempt to use a fallback method of ' + 'copying the contents.', + ); + // Trying to use Clipboard.setData to copy in vscode will not work as a + // result of a bug. So we should fallback to `copyToClipboardVSCode` which + // delegates the copy to the frame above DevTools. + // Sending this message in other environments will just have no effect. + // See https://github.com/Dart-Code/Dart-Code/issues/4540 for more + // information. + copyToClipboardVSCode(data); + } else { + rethrow; + } + } +} diff --git a/packages/devtools_app/lib/src/shared/dialogs.dart b/packages/devtools_app/lib/src/shared/dialogs.dart index 6b4abd22e66c..f510379aabfc 100644 --- a/packages/devtools_app/lib/src/shared/dialogs.dart +++ b/packages/devtools_app/lib/src/shared/dialogs.dart @@ -8,8 +8,8 @@ import 'package:devtools_app_shared/ui.dart'; import 'package:flutter/material.dart'; import '../shared/config_specific/launch_url/launch_url.dart'; +import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import 'globals.dart'; -import 'utils.dart'; /// A dialog, that reports unexpected error and allows to copy details and create issue. class UnexpectedErrorDialog extends StatelessWidget { diff --git a/packages/devtools_app/lib/src/shared/editable_list.dart b/packages/devtools_app/lib/src/shared/editable_list.dart index c6c08ba4a409..6d013120a67e 100644 --- a/packages/devtools_app/lib/src/shared/editable_list.dart +++ b/packages/devtools_app/lib/src/shared/editable_list.dart @@ -10,7 +10,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'common_widgets.dart'; -import 'utils.dart'; +import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart'; /// A widget that displays the contents of [entries]. /// diff --git a/packages/devtools_app/lib/src/shared/utils.dart b/packages/devtools_app/lib/src/shared/utils.dart index 3c691b9c3610..29cd8d3d73bf 100644 --- a/packages/devtools_app/lib/src/shared/utils.dart +++ b/packages/devtools_app/lib/src/shared/utils.dart @@ -6,13 +6,10 @@ // other libraries in this package. // Utils, that do not have dependencies, should go to primitives/utils.dart. -import 'dart:async'; - import 'package:devtools_app_shared/service.dart'; import 'package:devtools_app_shared/ui.dart'; import 'package:devtools_app_shared/utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; @@ -25,22 +22,6 @@ import 'globals.dart'; final _log = Logger('lib/src/shared/utils'); -/// Attempts to copy a String of `data` to the clipboard. -/// -/// Shows a `successMessage` [Notification] on the passed in `context`. -Future copyToClipboard( - String data, - String? successMessage, -) async { - await Clipboard.setData( - ClipboardData( - text: data, - ), - ); - - if (successMessage != null) notificationService.push(successMessage); -} - /// Logging to debug console only in debug runs. void debugLogger(String message) { assert( diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index d48505b77f2c..2f7bb8c242e6 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -10,11 +10,6 @@ To learn more about DevTools, check out the ## General updates -* Enabled DevTools extensions when debugging a Dart entry point that is not -under `lib` (e.g. a unit test or integration test). Thanks to -[@bartekpacia](https://github.com/bartekpacia) for this change! - -[#6644](https://github.com/flutter/devtools/pull/6644) - ## Inspector updates TODO: Remove this section if there are not any general updates.