feat(editor): copyable diagnostics + hover tooltip + quick fixes
Three operator-UX gaps closed in one pass — the diagnostic
strip stayed plain Text (no copy), the wavy underline gave no
hover tooltip (had to read the strip), and there was no way to
act on an issue without leaving the editor.
- **Selectable + copyable strip**. Both the header summary
and the per-row issue message are now SelectableText. Per-
row Copy button (compact icon) lives next to the message;
header carries a 'Copy all' that copies every issue as
'L7: <message>' lines. Trackpad-only operators no longer
have to use the system selection gesture.
- **Hover tooltip on the wavy underline**. The controller
attaches TextSpan.onEnter / onExit handlers to every
issue-overlapping leaf and publishes IssueHoverRequest via
a ValueNotifier. FlowEditorPage listens and inserts an
OverlayEntry tooltip card near the cursor. Card carries
its own MouseRegion that cancels the dismiss timer so the
operator can slide INTO it to click the action buttons.
- **Quick fixes for the two most common issues**:
· 'Unknown capability X' → InstallCapabilityFix (delegated
to host via the new onInstallCapability callback). On
success, controller.setAvailableCapabilities + reanalyze
clear the issue automatically.
· 'Unknown type Y' with a Levenshtein-distance-≤2 match
→ ReplaceLineValueFix. Applied by the editor itself:
line is mutated, key + indent preserved, comment
preserved, then reanalyze.
Distance > 2 stays unfixed — pushing 'zonglefax' to 'bytes'
would be worse than no suggestion.
New public surface (exported from the package):
- QuickFix sealed base + InstallCapabilityFix + ReplaceLineValueFix
- InstallCapabilityCallback typedef
- IssueHoverRequest + IssueHoverSeverity
- FlowEditorPage.onInstallCapability prop
Tests:
- FlowAnalyzer attaches InstallCapabilityFix to capability
issues
- FlowAnalyzer suggests the closest valid type for typos
- FlowAnalyzer emits no fix when the typo is too far
All 33 editor tests green. Bumped to 0.17.0.
Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
parent
1b50924e16
commit
f43c1ac6cf
7 changed files with 884 additions and 198 deletions
112
lib/src/quick_fix.dart
Normal file
112
lib/src/quick_fix.dart
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// Quick-fix model for the flow editor's diagnostics.
|
||||
//
|
||||
// Each analyzer issue can carry one or more `QuickFix` records
|
||||
// describing a one-click remediation. The editor renders these
|
||||
// as action buttons on the hover tooltip and inside the bottom
|
||||
// diagnostic strip. Two categories exist today:
|
||||
//
|
||||
// * `ReplaceLineValueFix` — purely textual; the editor
|
||||
// applies it directly by mutating the controller's
|
||||
// buffer.
|
||||
// * `InstallCapabilityFix` — needs the host (Studio) to talk
|
||||
// to the Hub. The editor invokes the host-provided
|
||||
// callback and reanalyzes once it returns.
|
||||
//
|
||||
// New fix kinds can be added by extending the sealed base; both
|
||||
// surfaces (tooltip + strip) switch on the runtime type, so
|
||||
// adding a kind also means adding a case there.
|
||||
|
||||
import 'dart:ui' show Offset;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
@immutable
|
||||
sealed class QuickFix {
|
||||
/// One-line button label rendered in the UI (verb-first,
|
||||
/// imperative). Kept short so the strip layout doesn't reflow
|
||||
/// when multiple fixes are present.
|
||||
final String label;
|
||||
const QuickFix(this.label);
|
||||
}
|
||||
|
||||
/// Replace the trailing value of `[line]` with [replacement].
|
||||
/// Used by the unknown-type warning to offer the closest
|
||||
/// matching valid type token (e.g. `byes` → `bytes`).
|
||||
@immutable
|
||||
class ReplaceLineValueFix extends QuickFix {
|
||||
/// Zero-based line index inside the document.
|
||||
final int line;
|
||||
|
||||
/// The token to substitute for the existing value (the
|
||||
/// substring after the colon, with surrounding whitespace
|
||||
/// preserved by the applier).
|
||||
final String replacement;
|
||||
|
||||
const ReplaceLineValueFix({
|
||||
required this.line,
|
||||
required this.replacement,
|
||||
required String label,
|
||||
}) : super(label);
|
||||
}
|
||||
|
||||
/// Ask the host (Studio) to install the named capability via
|
||||
/// the Hub. The editor delegates to the
|
||||
/// [InstallCapabilityCallback] passed into `FlowEditorPage`
|
||||
/// and reanalyzes the document once the host returns.
|
||||
@immutable
|
||||
class InstallCapabilityFix extends QuickFix {
|
||||
/// The capability spec the operator wrote (with or without an
|
||||
/// `@version` tail). The host is responsible for parsing the
|
||||
/// version part if it matters; the editor never strips it.
|
||||
final String capability;
|
||||
|
||||
const InstallCapabilityFix({
|
||||
required this.capability,
|
||||
required String label,
|
||||
}) : super(label);
|
||||
}
|
||||
|
||||
/// Host-side install handler signature. Returns the new
|
||||
/// installed-capability list after the install completes (used
|
||||
/// by the editor to refresh its analyzer without round-tripping
|
||||
/// through the parent widget tree). Returns `null` on failure;
|
||||
/// in that case the editor keeps the prior list and the issue
|
||||
/// stays visible.
|
||||
typedef InstallCapabilityCallback =
|
||||
Future<List<String>?> Function(String capability);
|
||||
|
||||
/// Hover-tooltip request — the controller emits one of these
|
||||
/// via a ValueNotifier when the pointer enters an issue range,
|
||||
/// and clears it when the pointer leaves both the range and the
|
||||
/// tooltip. The editor host listens and renders an overlay.
|
||||
@immutable
|
||||
class IssueHoverRequest {
|
||||
/// Zero-based line of the issue the pointer is over.
|
||||
final int line;
|
||||
|
||||
/// Human-readable issue message (lifted from
|
||||
/// `Issue.message`).
|
||||
final String message;
|
||||
|
||||
/// Issue severity. Drives the dot colour in the tooltip.
|
||||
final IssueHoverSeverity severity;
|
||||
|
||||
/// Pre-computed quick fixes the tooltip should render as
|
||||
/// action buttons. Empty when the editor has nothing
|
||||
/// actionable to offer.
|
||||
final List<QuickFix> fixes;
|
||||
|
||||
/// Global cursor position at hover time — the host overlay
|
||||
/// uses it to anchor the tooltip near the pointer.
|
||||
final Offset globalPosition;
|
||||
|
||||
const IssueHoverRequest({
|
||||
required this.line,
|
||||
required this.message,
|
||||
required this.severity,
|
||||
required this.fixes,
|
||||
required this.globalPosition,
|
||||
});
|
||||
}
|
||||
|
||||
enum IssueHoverSeverity { error, warning, info }
|
||||
Loading…
Add table
Add a link
Reference in a new issue