feat(editor): type-token coloring + analyzer diagnostics
The text tab now colours `type: text|json|bytes|file|number`
values in the same hues the graph canvas uses for the wire of
that type — a glance at the YAML confirms what a glance at the
graph shows. Promoted the wire-colour palette to a single
source of truth in `wire_colors.dart` so the graph, the
properties panel and the text tab can't drift.
Adds `FlowAnalyzer` (extends `AbstractAnalyzer`) so the gutter
shows error pins and broken lines get wavy red underlines for:
- YAML parse errors (source-span pinned to the offending line)
- `use:` referencing a capability the operator hasn't installed
(bare provider/name match — `text.echo@^1` and `text.echo`
compare equal)
Editor host passes a closure into `setAvailableCapabilities` so
the analyser always sees the current installed list without
re-creating the controller on every Studio rebuild. Bumps the
package version to 0.15.0.
Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
parent
73736769a0
commit
885d2db4e1
8 changed files with 400 additions and 52 deletions
98
lib/src/flow_analyzer.dart
Normal file
98
lib/src/flow_analyzer.dart
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// FlowAnalyzer — feeds AbstractAnalyzer-shaped issues into the
|
||||
// flow editor's CodeController so the gutter can show error
|
||||
// pins and the text tab can underline broken lines.
|
||||
//
|
||||
// Two kinds of issues are reported today:
|
||||
//
|
||||
// - YAML parse errors. The exception's source span pins the
|
||||
// line; the message goes straight through.
|
||||
// - `use:` lines pointing at a capability the operator hasn't
|
||||
// installed. We compare the bare `provider/name` (no
|
||||
// version) against the host-supplied capability list, so
|
||||
// `text.echo@^1` matches an installed `text.echo` even when
|
||||
// the constraints differ.
|
||||
//
|
||||
// `$step.field` reference resolution and per-field type checks
|
||||
// will land here next — they need the module spec map and are
|
||||
// staged in the editor → controller plumbing in editor_controller.dart.
|
||||
|
||||
import 'package:flutter_code_editor/flutter_code_editor.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
class FlowAnalyzer extends AbstractAnalyzer {
|
||||
/// Returns the names of capabilities the operator has installed.
|
||||
/// A closure so the analyzer always sees the current list
|
||||
/// without needing a re-create on every Studio rebuild.
|
||||
final List<String> Function() availableCapabilities;
|
||||
|
||||
const FlowAnalyzer({required this.availableCapabilities});
|
||||
|
||||
@override
|
||||
Future<AnalysisResult> analyze(Code code) async {
|
||||
final issues = <Issue>[];
|
||||
final text = code.text;
|
||||
if (text.trim().isEmpty) {
|
||||
return const AnalysisResult(issues: []);
|
||||
}
|
||||
|
||||
final caps = availableCapabilities();
|
||||
final knownBareCaps = caps.map(_bareCap).toSet();
|
||||
|
||||
YamlNode? doc;
|
||||
try {
|
||||
doc = loadYamlNode(text);
|
||||
} on YamlException catch (e) {
|
||||
issues.add(
|
||||
Issue(
|
||||
line: e.span?.start.line ?? 0,
|
||||
message: 'YAML: ${e.message}',
|
||||
type: IssueType.error,
|
||||
),
|
||||
);
|
||||
return AnalysisResult(issues: issues);
|
||||
} catch (e) {
|
||||
issues.add(
|
||||
Issue(
|
||||
line: 0,
|
||||
message: 'YAML: $e',
|
||||
type: IssueType.error,
|
||||
),
|
||||
);
|
||||
return AnalysisResult(issues: issues);
|
||||
}
|
||||
|
||||
if (doc is YamlMap) {
|
||||
final steps = doc['steps'];
|
||||
if (steps is YamlList) {
|
||||
for (final step in steps.nodes) {
|
||||
if (step is! YamlMap) continue;
|
||||
final useNode = step.nodes['use'];
|
||||
if (useNode == null) continue;
|
||||
final useValue = useNode.value;
|
||||
if (useValue is! String) continue;
|
||||
if (knownBareCaps.isEmpty) continue;
|
||||
if (!knownBareCaps.contains(_bareCap(useValue))) {
|
||||
issues.add(
|
||||
Issue(
|
||||
line: useNode.span.start.line,
|
||||
message:
|
||||
'Unknown capability "$useValue". '
|
||||
'Install the module that provides it, or check the spelling.',
|
||||
type: IssueType.error,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AnalysisResult(issues: issues);
|
||||
}
|
||||
}
|
||||
|
||||
/// Strip the `@<version>` tail from a capability spec so
|
||||
/// `text.echo@^1` and `text.echo` compare equal.
|
||||
String _bareCap(String full) {
|
||||
final at = full.indexOf('@');
|
||||
return at < 0 ? full : full.substring(0, at);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue