diff --git a/lib/core/ui/querya_shell_status.dart b/lib/core/ui/querya_shell_status.dart index 9979ceca..0afdccad 100644 --- a/lib/core/ui/querya_shell_status.dart +++ b/lib/core/ui/querya_shell_status.dart @@ -85,6 +85,25 @@ class QueryaShellStatus extends ChangeNotifier { notifyListeners(); } + /// Replaces the shown message and query metrics, for example with those of + /// the tab that has just been selected, or with a notice such as `Nothing to + /// run`. Nulls clear the field. While a query is running the busy message + /// owns the bar and this does nothing. + void setQueryMetrics({ + String? message, + int? rowCount, + int? columnCount, + Duration? duration, + }) { + if (_isBusy) return; + _statusMessage = message; + _messageIsBusy = false; + _rowCount = rowCount; + _columnCount = columnCount; + _lastQueryDuration = duration; + notifyListeners(); + } + /// Clears busy + metrics (e.g. connection switch). void clear() { _isBusy = false; diff --git a/lib/features/workspace/generic_sql_workspace.dart b/lib/features/workspace/generic_sql_workspace.dart index 8b85d884..aab422a1 100644 --- a/lib/features/workspace/generic_sql_workspace.dart +++ b/lib/features/workspace/generic_sql_workspace.dart @@ -533,6 +533,31 @@ class GenericSqlWorkspaceState extends material.State { }); } + /// The status bar already shows the row and column counts on its right and + /// the result bar the row count: a plain `N row(s).` is not repeated there. + static String? _shellMessage(String? statusLine) => + statusLine != null && RegExp(r'^\d+ row\(s\)\.$').hasMatch(statusLine) + ? null + : statusLine; + + /// The tab whose numbers the status bar shows. + SqlQueryTabSession? _statusSession; + + /// Shows the active tab's own numbers in the status bar (a tab that has not + /// run shows none). + void _publishActiveStatus() { + if (!mounted) return; + final session = _activeSession; + if (session.running) return; + final hasResult = session.columns.isNotEmpty || session.rows.isNotEmpty; + QueryaShellStatus.instance.setQueryMetrics( + message: _shellMessage(session.statusLine), + rowCount: hasResult ? session.rows.length : null, + columnCount: hasResult ? session.columns.length : null, + duration: session.elapsed, + ); + } + /// Runs the statement at the caret ([statementAtCursor]: the selection, or /// the one statement under the caret), or else the script: the selection, or /// the whole text. A script runs one statement at a time and stops at the @@ -554,7 +579,11 @@ class GenericSqlWorkspaceState extends material.State { selected: selected, caret: selection.isValid ? selection.baseOffset : scope.length, ); - if (spans.isEmpty) return; + if (spans.isEmpty) { + // Say so: a silent Run on an empty editor reads as a broken button. + QueryaShellStatus.instance.setQueryMetrics(message: 'Nothing to run'); + return; + } if (!mounted) return; final scopeStart = selected ? selection.start : 0; final texts = [for (final s in spans) s.textIn(scope)]; @@ -731,7 +760,7 @@ class GenericSqlWorkspaceState extends material.State { duration: duration, rowCount: outRows.length, columnCount: cols.length, - message: session.statusLine, + message: _shellMessage(session.statusLine), ); // One history entry per run, with the text that ran. @@ -836,7 +865,10 @@ class GenericSqlWorkspaceState extends material.State { ? selection.textInside(session.controller.text) : session.controller.text) .trim(); - if (sql.isEmpty) return; + if (sql.isEmpty) { + QueryaShellStatus.instance.setQueryMetrics(message: 'Nothing to explain'); + return; + } invalidatePane(session); setState(() { @@ -1072,6 +1104,11 @@ class GenericSqlWorkspaceState extends material.State { @override material.Widget build(material.BuildContext context) { + if (!identical(_statusSession, _activeSession)) { + _statusSession = _activeSession; + material.WidgetsBinding.instance + .addPostFrameCallback((_) => _publishActiveStatus()); + } return Actions( actions: >{ NewSqlIntent: CallbackAction( diff --git a/test/core/ui/querya_shell_status_test.dart b/test/core/ui/querya_shell_status_test.dart index 85318a55..710af6c9 100644 --- a/test/core/ui/querya_shell_status_test.dart +++ b/test/core/ui/querya_shell_status_test.dart @@ -55,4 +55,38 @@ void main() { expect(status.statusMessage, '1 row(s).'); }); + + test('setQueryMetrics replaces the numbers of the last run (#1479)', () { + status.reportQueryResult( + duration: const Duration(milliseconds: 48), + rowCount: 8, + columnCount: 6, + message: 'OK', + ); + + status.setQueryMetrics(); + + expect(status.statusMessage, isNull); + expect(status.rowCount, isNull); + expect(status.columnCount, isNull); + expect(status.lastQueryDuration, isNull); + }); + + test('setQueryMetrics shows a notice and drops the old counts (#1478)', () { + status.reportQueryResult(rowCount: 1, columnCount: 1, message: '1 row(s).'); + + status.setQueryMetrics(message: 'Nothing to run'); + + expect(status.statusMessage, 'Nothing to run'); + expect(status.rowCount, isNull); + }); + + test('setQueryMetrics leaves a running query\'s busy message alone', () { + status.beginBusy(message: 'Running query…'); + + status.setQueryMetrics(message: 'Nothing to run'); + + expect(status.isBusy, isTrue); + expect(status.statusMessage, 'Running query…'); + }); }