Skip to content

Commit c7a8619

Browse files
committed
Implement command history navigation in console: add up/down arrow key support and limit history size
1 parent aedf01e commit c7a8619

1 file changed

Lines changed: 61 additions & 1 deletion

File tree

lib/features/console/console_screen.dart

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'dart:async';
44
import 'dart:convert';
55

66
import 'package:flutter/material.dart';
7+
import 'package:flutter/services.dart';
78
import 'package:flutter_riverpod/flutter_riverpod.dart';
89
import 'package:intl/intl.dart';
910

@@ -43,10 +44,27 @@ class _ConsoleScreenState extends ConsumerState<ConsoleScreen> {
4344
bool _busy = false;
4445
bool _suppressRawLines = false;
4546
bool _showCommands = true;
47+
final _history = <String>[];
48+
int _historyIndex = -1;
49+
String _savedInput = '';
4650

4751
@override
4852
void initState() {
4953
super.initState();
54+
_commandFocus.onKeyEvent = (node, event) {
55+
if (event is! KeyDownEvent && event is! KeyRepeatEvent) {
56+
return KeyEventResult.ignored;
57+
}
58+
if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
59+
_historyUp();
60+
return KeyEventResult.handled;
61+
}
62+
if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
63+
_historyDown();
64+
return KeyEventResult.handled;
65+
}
66+
return KeyEventResult.ignored;
67+
};
5068
_eventSub = ref.listenManual(latestEventsProvider, (_, next) {
5169
if (!mounted) return;
5270
if (_mode == ConsoleMode.directSerialRepeater) return;
@@ -87,6 +105,12 @@ class _ConsoleScreenState extends ConsumerState<ConsoleScreen> {
87105
if (_busy) return;
88106
if (text.isEmpty && _mode != ConsoleMode.directSerialRepeater) return;
89107
if (!mounted) return;
108+
if (text.isNotEmpty && (_history.isEmpty || _history.last != text)) {
109+
_history.add(text);
110+
if (_history.length > 100) _history.removeAt(0);
111+
}
112+
_historyIndex = -1;
113+
_savedInput = '';
90114
setState(() => _busy = true);
91115
final txLabel =
92116
_mode == ConsoleMode.directSerialRepeater && _serialDeviceName != null
@@ -148,7 +172,12 @@ class _ConsoleScreenState extends ConsumerState<ConsoleScreen> {
148172
if (!mounted) return;
149173
_append('ERR', '$error');
150174
} finally {
151-
if (mounted) setState(() => _busy = false);
175+
if (mounted) {
176+
setState(() => _busy = false);
177+
WidgetsBinding.instance.addPostFrameCallback((_) {
178+
if (mounted) _commandFocus.requestFocus();
179+
});
180+
}
152181
}
153182
}
154183

@@ -624,6 +653,37 @@ class _ConsoleScreenState extends ConsumerState<ConsoleScreen> {
624653
_commandFocus.requestFocus();
625654
}
626655

656+
void _historyUp() {
657+
if (_history.isEmpty) return;
658+
if (_historyIndex == -1) {
659+
_savedInput = _commandController.text;
660+
_historyIndex = _history.length - 1;
661+
} else if (_historyIndex > 0) {
662+
_historyIndex--;
663+
} else {
664+
return;
665+
}
666+
final cmd = _history[_historyIndex];
667+
_commandController.text = cmd;
668+
_commandController.selection = TextSelection.collapsed(offset: cmd.length);
669+
}
670+
671+
void _historyDown() {
672+
if (_historyIndex == -1) return;
673+
if (_historyIndex < _history.length - 1) {
674+
_historyIndex++;
675+
final cmd = _history[_historyIndex];
676+
_commandController.text = cmd;
677+
_commandController.selection =
678+
TextSelection.collapsed(offset: cmd.length);
679+
} else {
680+
_historyIndex = -1;
681+
_commandController.text = _savedInput;
682+
_commandController.selection =
683+
TextSelection.collapsed(offset: _savedInput.length);
684+
}
685+
}
686+
627687
Future<void> _openSerialConfig() async {
628688
if (_serialDeviceName == null) return;
629689
final console = ref.read(rawSerialConsoleProvider);

0 commit comments

Comments
 (0)