chore: adopt Dart 3.9 toolchain — honest floor + tall-style reformat (T-353)
Raise the declared minimums in pubspec.yaml to what our deps already require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist 0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is the binding floor. Pin the exact build toolchain in .fvmrc (Flutter 3.44.1). Moving to the Dart 3.9 language level switches `dart format` to the new "tall" style and enables two new lints. This commit is the resulting mechanical churn, isolated from any behaviour change: - whole-tree `dart format` reformat (tall style) - `dart fix` for unnecessary_underscores + use_null_aware_elements No runtime behaviour change; `make test` green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -99,12 +99,7 @@ class EditorController extends ChangeNotifier {
|
||||
if (raw is! List) return;
|
||||
_buffers = [
|
||||
for (final b in raw)
|
||||
if (b is Map)
|
||||
(
|
||||
id: b['id']! as String,
|
||||
path: b['path']! as String,
|
||||
dirty: (b['dirty'] as bool?) ?? false,
|
||||
),
|
||||
if (b is Map) (id: b['id']! as String, path: b['path']! as String, dirty: (b['dirty'] as bool?) ?? false),
|
||||
];
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -143,10 +138,7 @@ class EditorController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
/// Called by the widget on every local text edit.
|
||||
void pushLocalEdit({
|
||||
required String newContent,
|
||||
required Selection newSelection,
|
||||
}) {
|
||||
void pushLocalEdit({required String newContent, required Selection newSelection}) {
|
||||
final id = _activeId;
|
||||
if (id == null) return;
|
||||
|
||||
@@ -162,11 +154,7 @@ class EditorController extends ChangeNotifier {
|
||||
// large buffers, so event broadcasts stay small.
|
||||
_pendingLocalEdits++;
|
||||
_suppressNextRemoteEdit = true;
|
||||
ipc.request('editor.set-content', args: {
|
||||
'id': id,
|
||||
'text': newContent,
|
||||
'selection': newSelection.toJson(),
|
||||
}).whenComplete(() => _pendingLocalEdits--);
|
||||
ipc.request('editor.set-content', args: {'id': id, 'text': newContent, 'selection': newSelection.toJson()}).whenComplete(() => _pendingLocalEdits--);
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
|
||||
@@ -65,10 +65,7 @@ class _EditorViewState extends State<EditorView> {
|
||||
final kernel = ClideKernel.of(context);
|
||||
_controller = EditorController(ipc: kernel.ipc, events: kernel.events)..addListener(_onControllerChanged);
|
||||
_keymap = kernel.keymap;
|
||||
_matcher = SequenceMatcher(
|
||||
keymap: () => kernel.keymap.keymap ?? Keymap(const []),
|
||||
context: () => kernel.keymap.scope,
|
||||
);
|
||||
_matcher = SequenceMatcher(keymap: () => kernel.keymap.keymap ?? Keymap(const []), context: () => kernel.keymap.scope);
|
||||
// Rebuild when the Vim mode flips so the editor toggles read-only.
|
||||
kernel.keymap.addListener(_onModeChanged);
|
||||
unawaited(_controller!.hydrate());
|
||||
@@ -105,10 +102,7 @@ class _EditorViewState extends State<EditorView> {
|
||||
_text.updatePath(c.activePath);
|
||||
if (c.content != _lastRemoteContent) {
|
||||
_lastRemoteContent = c.content;
|
||||
final sel = TextSelection(
|
||||
baseOffset: c.selection.start.clamp(0, c.content.length),
|
||||
extentOffset: c.selection.end.clamp(0, c.content.length),
|
||||
);
|
||||
final sel = TextSelection(baseOffset: c.selection.start.clamp(0, c.content.length), extentOffset: c.selection.end.clamp(0, c.content.length));
|
||||
_text.removeListener(_onTextChanged);
|
||||
_text.value = TextEditingValue(text: c.content, selection: sel);
|
||||
_text.addListener(_onTextChanged);
|
||||
@@ -306,9 +300,7 @@ class _EditorViewState extends State<EditorView> {
|
||||
return ClidePaneChrome(
|
||||
title: 'editor',
|
||||
subtitle: 'no buffer · use `clide open <path>` or pick a file in the tree',
|
||||
child: const Center(
|
||||
child: ClideText('Open a file to begin editing.', muted: true),
|
||||
),
|
||||
child: const Center(child: ClideText('Open a file to begin editing.', muted: true)),
|
||||
);
|
||||
}
|
||||
return MultitabPane<String>(
|
||||
@@ -357,12 +349,7 @@ class _TextBody extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final style = TextStyle(
|
||||
color: foreground,
|
||||
fontSize: clideFontMono,
|
||||
fontFamily: clideMonoFamily,
|
||||
fontFamilyFallback: clideMonoFamilyFallback,
|
||||
);
|
||||
final style = TextStyle(color: foreground, fontSize: clideFontMono, fontFamily: clideMonoFamily, fontFamilyFallback: clideMonoFamilyFallback);
|
||||
final editable = EditableText(
|
||||
controller: controller,
|
||||
focusNode: focus,
|
||||
@@ -398,7 +385,10 @@ class _TextBody extends StatelessWidget {
|
||||
|
||||
/// Advance width of one monospace glyph in [style].
|
||||
static double _charWidth(TextStyle style) {
|
||||
final tp = TextPainter(text: TextSpan(text: '0', style: style), textDirection: TextDirection.ltr)..layout();
|
||||
final tp = TextPainter(
|
||||
text: TextSpan(text: '0', style: style),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
return tp.width;
|
||||
}
|
||||
}
|
||||
@@ -413,11 +403,12 @@ class _RulerPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
canvas.drawLine(
|
||||
Offset(x, 0),
|
||||
Offset(x, size.height),
|
||||
Paint()
|
||||
..color = color
|
||||
..strokeWidth = 1);
|
||||
Offset(x, 0),
|
||||
Offset(x, size.height),
|
||||
Paint()
|
||||
..color = color
|
||||
..strokeWidth = 1,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -50,14 +50,14 @@ class EditorExtension extends ClideExtension {
|
||||
|
||||
@override
|
||||
List<ContributionPoint> get contributions => [
|
||||
TabContribution(
|
||||
id: 'editor.active',
|
||||
slot: Slots.workspace,
|
||||
title: 'Editor',
|
||||
titleKey: 'tab.title',
|
||||
i18nNamespace: id,
|
||||
priority: 80, // between Claude (90) and welcome (-100)
|
||||
build: (_) => const EditorView(),
|
||||
),
|
||||
];
|
||||
TabContribution(
|
||||
id: 'editor.active',
|
||||
slot: Slots.workspace,
|
||||
title: 'Editor',
|
||||
titleKey: 'tab.title',
|
||||
i18nNamespace: id,
|
||||
priority: 80, // between Claude (90) and welcome (-100)
|
||||
build: (_) => const EditorView(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -36,18 +36,23 @@ class SyntaxTextController extends TextEditingController {
|
||||
if (source == _highlightedText) return;
|
||||
|
||||
_highlighting = true;
|
||||
_syntax.highlight(path, source).then((result) {
|
||||
_highlighting = false;
|
||||
if (text != source) {
|
||||
_requestHighlight();
|
||||
return;
|
||||
}
|
||||
_highlightedText = source;
|
||||
_spans = result.spans;
|
||||
notifyListeners();
|
||||
}, onError: (_) {
|
||||
_highlighting = false;
|
||||
});
|
||||
_syntax
|
||||
.highlight(path, source)
|
||||
.then(
|
||||
(result) {
|
||||
_highlighting = false;
|
||||
if (text != source) {
|
||||
_requestHighlight();
|
||||
return;
|
||||
}
|
||||
_highlightedText = source;
|
||||
_spans = result.spans;
|
||||
notifyListeners();
|
||||
},
|
||||
onError: (_) {
|
||||
_highlighting = false;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -57,11 +62,7 @@ class SyntaxTextController extends TextEditingController {
|
||||
}
|
||||
|
||||
@override
|
||||
TextSpan buildTextSpan({
|
||||
required BuildContext context,
|
||||
TextStyle? style,
|
||||
required bool withComposing,
|
||||
}) {
|
||||
TextSpan buildTextSpan({required BuildContext context, TextStyle? style, required bool withComposing}) {
|
||||
final tokens = _tokens;
|
||||
if (_spans.isEmpty || tokens == null || text.isEmpty) {
|
||||
return TextSpan(text: text, style: style);
|
||||
@@ -125,20 +126,17 @@ class SyntaxTextController extends TextEditingController {
|
||||
|
||||
// Gap before this span — plain text.
|
||||
if (spanCharStart > charPos) {
|
||||
children.add(TextSpan(
|
||||
text: source.substring(charPos, spanCharStart),
|
||||
style: style,
|
||||
));
|
||||
children.add(TextSpan(text: source.substring(charPos, spanCharStart), style: style));
|
||||
}
|
||||
|
||||
// The highlighted span.
|
||||
if (spanCharEnd > spanCharStart) {
|
||||
children.add(TextSpan(
|
||||
text: source.substring(spanCharStart, spanCharEnd),
|
||||
style: style?.copyWith(
|
||||
color: TreeSitterService.colorForRole(span.role, tokens),
|
||||
children.add(
|
||||
TextSpan(
|
||||
text: source.substring(spanCharStart, spanCharEnd),
|
||||
style: style?.copyWith(color: TreeSitterService.colorForRole(span.role, tokens)),
|
||||
),
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
charPos = spanCharEnd;
|
||||
@@ -146,10 +144,7 @@ class SyntaxTextController extends TextEditingController {
|
||||
|
||||
// Trailing plain text.
|
||||
if (charPos < source.length) {
|
||||
children.add(TextSpan(
|
||||
text: source.substring(charPos),
|
||||
style: style,
|
||||
));
|
||||
children.add(TextSpan(text: source.substring(charPos), style: style));
|
||||
}
|
||||
|
||||
return TextSpan(style: style, children: children);
|
||||
|
||||
@@ -79,13 +79,7 @@ class VimResult {
|
||||
/// Apply [action] to [v]. [count] repeats motions/line-edits; [visual]
|
||||
/// selects between collapse-to-caret (normal) and extend-from-anchor
|
||||
/// (visual) for motions, and enables the `visual*` range ops.
|
||||
VimResult applyVim(
|
||||
String action,
|
||||
TextEditingValue v, {
|
||||
VimRegister register = VimRegister.empty,
|
||||
bool visual = false,
|
||||
int count = 1,
|
||||
}) {
|
||||
VimResult applyVim(String action, TextEditingValue v, {VimRegister register = VimRegister.empty, bool visual = false, int count = 1}) {
|
||||
final t = v.text;
|
||||
final caret = v.selection.extentOffset.clamp(0, t.length);
|
||||
final anchor = v.selection.baseOffset.clamp(0, t.length);
|
||||
@@ -226,7 +220,8 @@ int _up(String t, int off) {
|
||||
int _cls(String ch) {
|
||||
if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') return 0;
|
||||
final c = ch.codeUnitAt(0);
|
||||
final isWord = (c >= 0x30 && c <= 0x39) || // 0-9
|
||||
final isWord =
|
||||
(c >= 0x30 && c <= 0x39) || // 0-9
|
||||
(c >= 0x41 && c <= 0x5A) || // A-Z
|
||||
(c >= 0x61 && c <= 0x7A) || // a-z
|
||||
c == 0x5F; // _
|
||||
@@ -279,13 +274,19 @@ int _wordEnd(String t, int off) {
|
||||
// -- Edit helpers -----------------------------------------------------------
|
||||
|
||||
VimResult _collapsed(String text, int caret) => VimResult(
|
||||
TextEditingValue(text: text, selection: TextSelection.collapsed(offset: _clamp(caret, 0, text.length))),
|
||||
);
|
||||
TextEditingValue(
|
||||
text: text,
|
||||
selection: TextSelection.collapsed(offset: _clamp(caret, 0, text.length)),
|
||||
),
|
||||
);
|
||||
|
||||
VimResult _insertAt(String t, int caret) => VimResult(
|
||||
TextEditingValue(text: t, selection: TextSelection.collapsed(offset: _clamp(caret, 0, t.length))),
|
||||
enterInsert: true,
|
||||
);
|
||||
TextEditingValue(
|
||||
text: t,
|
||||
selection: TextSelection.collapsed(offset: _clamp(caret, 0, t.length)),
|
||||
),
|
||||
enterInsert: true,
|
||||
);
|
||||
|
||||
VimResult _deleteChar(String t, int caret, int count) {
|
||||
final le = _lineEnd(t, caret);
|
||||
@@ -298,7 +299,10 @@ VimResult _deleteChar(String t, int caret, int count) {
|
||||
final nle = _lineEnd(nt, caret);
|
||||
final ncaret = _clamp(caret, nls, nle > nls ? nle - 1 : nls);
|
||||
return VimResult(
|
||||
TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: ncaret)),
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: ncaret),
|
||||
),
|
||||
register: VimRegister(removed),
|
||||
);
|
||||
}
|
||||
@@ -324,7 +328,10 @@ VimResult _deleteLines(String t, int caret, int count) {
|
||||
caretLineStart = ls;
|
||||
}
|
||||
return VimResult(
|
||||
TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: _firstNonBlank(nt, caretLineStart))),
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: _firstNonBlank(nt, caretLineStart)),
|
||||
),
|
||||
register: reg,
|
||||
);
|
||||
}
|
||||
@@ -337,7 +344,10 @@ VimResult _deleteToEnd(String t, int caret) {
|
||||
final ls = _lineStart(nt, caret);
|
||||
final nle = _lineEnd(nt, caret);
|
||||
return VimResult(
|
||||
TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: _clamp(caret, ls, nle > ls ? nle - 1 : ls))),
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: _clamp(caret, ls, nle > ls ? nle - 1 : ls)),
|
||||
),
|
||||
register: VimRegister(removed),
|
||||
);
|
||||
}
|
||||
@@ -349,7 +359,10 @@ VimResult _deleteToOffset(String t, int caret, int target, {bool insert = false}
|
||||
final removed = t.substring(lo, hi);
|
||||
final nt = t.replaceRange(lo, hi, '');
|
||||
return VimResult(
|
||||
TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: lo)),
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: lo),
|
||||
),
|
||||
register: VimRegister(removed),
|
||||
enterInsert: insert,
|
||||
);
|
||||
@@ -366,7 +379,10 @@ VimResult _changeLine(String t, int caret, int count) {
|
||||
final removed = t.substring(ls, end);
|
||||
final nt = t.replaceRange(ls, end, '');
|
||||
return VimResult(
|
||||
TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: ls)),
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: ls),
|
||||
),
|
||||
register: VimRegister(removed.endsWith('\n') ? removed : '$removed\n', linewise: true),
|
||||
enterInsert: true,
|
||||
);
|
||||
@@ -382,7 +398,10 @@ VimResult _yankLines(String t, int caret, int count) {
|
||||
}
|
||||
final yanked = t.substring(ls, end);
|
||||
return VimResult(
|
||||
TextEditingValue(text: t, selection: TextSelection.collapsed(offset: caret)),
|
||||
TextEditingValue(
|
||||
text: t,
|
||||
selection: TextSelection.collapsed(offset: caret),
|
||||
),
|
||||
register: VimRegister(yanked.endsWith('\n') ? yanked : '$yanked\n', linewise: true),
|
||||
);
|
||||
}
|
||||
@@ -394,7 +413,12 @@ VimResult _paste(String t, int caret, VimRegister reg, {required bool before}) {
|
||||
if (before) {
|
||||
final ls = _lineStart(t, caret);
|
||||
final nt = t.replaceRange(ls, ls, body);
|
||||
return VimResult(TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: _firstNonBlank(nt, ls))));
|
||||
return VimResult(
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: _firstNonBlank(nt, ls)),
|
||||
),
|
||||
);
|
||||
}
|
||||
final le = _lineEnd(t, caret);
|
||||
final insertAt = le < t.length ? le + 1 : t.length;
|
||||
@@ -402,12 +426,22 @@ VimResult _paste(String t, int caret, VimRegister reg, {required bool before}) {
|
||||
final chunk = le < t.length ? body : '\n${body.substring(0, body.length - 1)}';
|
||||
final nt = t.replaceRange(insertAt, insertAt, chunk);
|
||||
final caretLine = le < t.length ? insertAt : insertAt + 1;
|
||||
return VimResult(TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: _firstNonBlank(nt, caretLine))));
|
||||
return VimResult(
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: _firstNonBlank(nt, caretLine)),
|
||||
),
|
||||
);
|
||||
}
|
||||
// Charwise: p pastes after the caret, P at the caret.
|
||||
final at = before ? caret : _clamp(caret + 1, 0, t.length);
|
||||
final nt = t.replaceRange(at, at, reg.text);
|
||||
return VimResult(TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: at + reg.text.length - 1)));
|
||||
return VimResult(
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: at + reg.text.length - 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
VimResult _openLine(String t, int caret, {required bool below}) {
|
||||
@@ -415,14 +449,20 @@ VimResult _openLine(String t, int caret, {required bool below}) {
|
||||
final le = _lineEnd(t, caret);
|
||||
final nt = t.replaceRange(le, le, '\n');
|
||||
return VimResult(
|
||||
TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: le + 1)),
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: le + 1),
|
||||
),
|
||||
enterInsert: true,
|
||||
);
|
||||
}
|
||||
final ls = _lineStart(t, caret);
|
||||
final nt = t.replaceRange(ls, ls, '\n');
|
||||
return VimResult(
|
||||
TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: ls)),
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: ls),
|
||||
),
|
||||
enterInsert: true,
|
||||
);
|
||||
}
|
||||
@@ -435,7 +475,10 @@ VimResult _deleteRange(String t, int anchor, int caret, {required bool insert})
|
||||
final removed = t.substring(lo, hi);
|
||||
final nt = t.replaceRange(lo, hi, '');
|
||||
return VimResult(
|
||||
TextEditingValue(text: nt, selection: TextSelection.collapsed(offset: lo)),
|
||||
TextEditingValue(
|
||||
text: nt,
|
||||
selection: TextSelection.collapsed(offset: lo),
|
||||
),
|
||||
register: VimRegister(removed),
|
||||
enterInsert: insert,
|
||||
);
|
||||
@@ -445,7 +488,10 @@ VimResult _yankRange(String t, int anchor, int caret) {
|
||||
final lo = anchor < caret ? anchor : caret;
|
||||
final hi = _clamp((anchor < caret ? caret : anchor) + 1, 0, t.length);
|
||||
return VimResult(
|
||||
TextEditingValue(text: t, selection: TextSelection.collapsed(offset: lo)),
|
||||
TextEditingValue(
|
||||
text: t,
|
||||
selection: TextSelection.collapsed(offset: lo),
|
||||
),
|
||||
register: VimRegister(t.substring(lo, hi)),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user