Rewrite interactive mode: ratatui TUI with completion, syntax highlighting, and more#11
Open
tobias-fire wants to merge 126 commits intomainfrom
Open
Rewrite interactive mode: ratatui TUI with completion, syntax highlighting, and more#11tobias-fire wants to merge 126 commits intomainfrom
tobias-fire wants to merge 126 commits intomainfrom
Conversation
Implements --format=auto option that renders JSONLines_Compact output as formatted tables with automatic content wrapping. Features: - New table_renderer module for parsing JSONLines_Compact format - Dynamic terminal-width-aware table rendering using comfy-table - Automatic cell content wrapping to fit terminal width - Support for multiple DATA messages (accumulates before rendering) - Graceful error handling with fallback to raw output - Works in both single-query and REPL modes - Compatible with existing flags (--verbose, --concise) - Can be saved as default with --update-defaults Dependencies added: - terminal_size 0.3 for terminal width detection - comfy-table 6.2 for table rendering - home version constraint to avoid edition2024 issues All tests passing (41/41). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add display_width() function to correctly calculate line width ignoring ANSI escape codes - Enable ContentArrangement::Dynamic for proper content wrapping in expanded mode - Add special handling for single-column chunks with wide content using UpperBoundary constraint - Make should_use_expanded_mode() consistent with rendering by applying same truncation logic - Add max_value_length parameter to render functions for context-specific truncation (1000 chars for expanded, 10000 for horizontal) - Add comprehensive tests for expanded mode, truncation, and ANSI width calculation Fixes alignment issues where very long truncated values (like settings_names with 1000+ chars) caused table borders to extend beyond terminal width. All chunks now align properly at the right edge. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Use LowerBoundary constraints instead of fixed boundaries for multi-column chunks to prevent unnecessary content wrapping - Skip bottom border of non-last chunks to eliminate double borders between chunks within the same row - Improves readability by ensuring column names display on single lines and reducing repetitive border lines Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Use lighter '--' borders for header separators (between column names and values) - Use heavier '==' borders for chunk separators (between different column groups) - This emphasizes the separation between chunks while keeping headers lighter Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Use '==' borders at the top of each chunk (except first) to emphasize new section - Use '--' borders for bottom, header separators, and internal structure - Creates clearer visual separation where each chunk begins Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove bottom border of non-last chunks since the next chunk's top border provides separation - Reduces visual clutter by having only one separator line (==) between chunks - Last chunk still has bottom border for proper closure Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Integrate csvlens library to provide an interactive viewer for query results in REPL mode. Users can type \view or press Ctrl+V (then Enter) to open the last query result in a full-screen viewer with vim-like navigation, search, and scrolling capabilities. Features: - Store last query result in Context for viewing - Convert query results to CSV format with proper escaping - Launch csvlens viewer with temporary CSV file - Support both \view text command and Ctrl+V keybind - Add \help command to show available commands - Handle error cases gracefully (no results, query errors, empty data) Implementation: - New viewer module (src/viewer.rs) for csvlens integration - CSV conversion functions in table_renderer with RFC 4180 escaping - Temporary file creation using process ID for uniqueness - Command detection in REPL loop for \view and \help - Ctrl+V keybind inserts \view command (user presses Enter to execute) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replaces the complex chunked expanded mode with a cleaner vertical format that displays each row as a two-column table (column name | value). This eliminates ~145 lines of chunking logic while improving readability. Changes: - Replace render_table_expanded() with render_table_vertical() - Rename all "expanded" terminology to "vertical" throughout codebase - Update format option from --format=expanded to --format=vertical - Simplify row display: "Row N:" header with simple two-column table - Column names in cyan bold, values with natural wrapping - Update all tests to match new format Benefits: Simpler code, cleaner output, easier to scan, no chunk boundaries Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replaces content-aware vertical mode detection with simple math based on available space per column. Adds two configurable parameters for control. Changes: - Add --min-col-width (default: 15) to control vertical mode threshold - Add --max-cell-length (default: 1000) for content truncation - Replace should_use_vertical_mode with simple calculation: terminal_width / num_columns < min_col_width - Update horizontal table renderer to use equal column widths - Set explicit ColumnConstraint for predictable layout - Remove all content inspection from decision logic Benefits: - Predictable behavior independent of content - User-configurable thresholds via command-line options - Simpler code with no content-aware logic - Equal column widths for consistent visual alignment Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace eprintln!("") and println!("") with eprintln!() and println!()
to fix clippy warnings about unnecessary empty string arguments.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add #[allow(dead_code)] attributes with explanatory comments to: - JsonLineMessage::Start fields (query_id, request_id, query_label) - ResultColumn.column_type field These fields are part of the Firebolt JSONLines_Compact protocol and required for deserialization but not currently used by the renderer. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add 11 new test functions validating Firebolt's JSONLines_Compact serialization format for all data types: - BIGINT (JSON string for precision) - NUMERIC/DECIMAL (JSON string for exact decimals) - INT (JSON number) - DOUBLE/REAL (JSON number) - DATE/TIMESTAMP (ISO format strings) - ARRAY (JSON array) - TEXT (JSON string with unicode) - BYTEA (hex-encoded string) - GEOGRAPHY (WKB hex string) - BOOLEAN/NULL - CSV null handling differences These tests verify that format_value() correctly handles Firebolt's type-specific serialization patterns without requiring type-aware logic. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Delete the display_width() function and its test test_display_width() as they were added but never used. The comfy_table library handles ANSI escape sequence width calculation internally with ContentArrangement::Dynamic. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add comprehensive documentation for Firebolt's type-specific JSON serialization patterns: 1. Added detailed function documentation for format_value() and format_value_csv() explaining how each Firebolt type maps to JSON 2. Added new "Data Type Handling in JSONLines_Compact Format" section to CLAUDE.md with: - Complete type mapping table (INT, BIGINT, NUMERIC, DATE, etc.) - Explanation of why BIGINT/NUMERIC use strings (precision) - How BYTEA and GEOGRAPHY are hex-encoded - Client-side rendering behavior - Note that column_type field is available for future use This documents the actual Firebolt serialization behavior validated by the comprehensive test suite added in previous commits. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Change println! to print! for raw body output to match upstream behavior. The server response already includes trailing newlines, so println! was adding an extra unwanted newline. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
SQL NULL values are now rendered in dark gray color to distinguish them from the string "NULL". This improves readability by making it immediately clear which values are actual nulls vs string data. Changes: - NULL values displayed in Color::DarkGrey in both horizontal and vertical table rendering modes - Added tests to verify NULL rendering doesn't crash - Works in both render_table() and render_table_vertical() To verify: Run a query with NULL values in a terminal: fb --core --format=auto "SELECT NULL as n, 'NULL' as s" The real NULL will appear in darker gray while the string 'NULL' will display in normal color. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add explicit client-side vs server-side rendering modes using prefix notation (client:auto, client:vertical, client:horizontal for client rendering; PSQL, JSON, CSV, etc. for server rendering). Interactive sessions default to client:auto for pretty tables, while non-interactive sessions default to PSQL for backward compatibility. Include helpful warnings when users accidentally omit the client: prefix, and clarify Ctrl+V+Enter behavior in documentation. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace verbose multi-line statistics with a single clean line showing only relevant metrics: row count with thousand separators and scanned bytes with smart KB/MB/GB formatting broken down by local (cache) and remote (storage). Statistics appear between Time and Request Id for better readability. Respects --concise flag to suppress all metadata. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Document client-side rendering with client: prefix notation, interactive result exploration via csvlens viewer, smart statistics formatting, and updated keyboard shortcuts. Include practical example using information_schema.engine_query_history. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The home crate was accidentally added but is not used anywhere in the codebase. The project uses the dirs crate for home directory operations instead. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…le-column results - Apply row (10,000) and byte (1MB) limits only in interactive TTY mode; non-interactive output and csvlens viewer always receive the full result - Single-column results get 5x the normal max cell length (5,000 vs 1,000) - Track interactive mode via Context.is_interactive set from main Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements regex-based syntax highlighting for SQL queries in the
interactive REPL mode with industry-standard color scheme.
Features:
- Keywords (SELECT, FROM, WHERE): Bright Blue
- Functions (COUNT, AVG): Bright Cyan
- Strings ('text'): Bright Yellow
- Numbers (42, 3.14): Bright Magenta
- Comments (-- text): Bright Black (gray)
- Operators: Default (subtle)
Configuration:
- Auto-enabled in interactive TTY mode
- Disabled via --no-color flag
- Respects NO_COLOR environment variable
- Auto-disabled for piped/redirected output
Implementation:
- Regex-based highlighting (no new dependencies)
- 13 comprehensive unit tests
- Graceful error handling
- Colorblind-accessible color scheme based on DuckDB, pgcli, and
accessibility research
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements regex-based syntax highlighting for SQL queries in the
interactive REPL mode with industry-standard color scheme.
Features:
- Keywords (SELECT, FROM, WHERE): Bright Blue
- Functions (COUNT, AVG): Bright Cyan
- Strings ('text'): Bright Yellow
- Numbers (42, 3.14): Bright Magenta
- Comments (-- text): Bright Black (gray)
- Operators: Default (subtle)
Configuration:
- Auto-enabled in interactive TTY mode
- Disabled via --no-color flag
- Respects NO_COLOR environment variable
- Auto-disabled for piped/redirected output
Implementation:
- Regex-based highlighting (no new dependencies)
- 13 comprehensive unit tests
- Graceful error handling
- Colorblind-accessible color scheme based on DuckDB, pgcli, and
accessibility research
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement context-aware auto-completion that suggests table names and column names from the database schema. The completion system queries information_schema on startup and caches results for fast lookups. Key features: - Auto-complete table names and column names - Async schema cache refresh (non-blocking startup) - Support for message-based query response format - Configurable via --no-completion and --completion-cache-ttl flags - Runtime control with 'set completion = on/off' - Manual refresh with \refresh command Implementation details: - New completion module with SqlCompleter, SchemaCache, and context detector - Queries information_schema.tables, columns, and routines - Thread-safe cache using Arc<RwLock<T>> - Graceful error handling and fallback Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements context-aware, frequency-based suggestion ordering with schema exploration support. Tables and columns now appear based on query context, usage frequency, and relevance, with system schemas appropriately deprioritized. Schema names can be completed directly and typing 'schema.' shows all tables in that schema. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements auto-completion for SQL functions with lowest priority (below columns, above system schemas). Functions complete with opening parenthesis for immediate argument typing. Operators are filtered out using routine_type != 'OPERATOR' from information_schema.routines. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Rewrites the interactive REPL using ratatui + tui-textarea, replacing
rustyline entirely. The headless (single-query) path is unchanged.
Layout: 3-pane vertical split — scrollable output pane (bottom-anchored),
multi-line input area with tui-textarea, 1-line status bar.
Key features:
- Multi-line SQL editing with Shift+Enter for explicit newlines
- File-backed history (same ~/.firebolt/fb_history format) with
Up/Down and Ctrl+Up/Down navigation, deduplication, 10k cap
- Kitty keyboard protocol for Shift+Enter disambiguation
- Mouse scroll routed to output pane; Shift+drag still selects text
- Ctrl+C cancels input or in-flight query via CancellationToken
- Ctrl+V / \view opens csvlens (suspends ratatui, resumes after)
- Query output routed through TuiMsg channel (Line / StyledLines)
to avoid ANSI round-trips for table rendering
- Custom TUI table renderer with Unicode box-drawing borders,
smart column-width algorithm, auto horizontal/vertical switching,
per-column header (cyan), NULL (dark gray), error (red) styling
- Control characters in cell values replaced with spaces to prevent
ratatui misalignment (fixes query_text newline rendering bug)
- SQL echo in output pane: ❯ green+bold, SQL text yellow
- Stats lines (Time:/Scanned:) rendered in dark gray
- Spinner + elapsed time shown while query is running
New files: src/tui/{mod,layout,output_pane,history}.rs, src/tui_msg.rs
Removed: src/repl_helper.rs (rustyline adapter no longer needed)
Added: src/docs/{tui,output_pipeline,table_rendering,completion}.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements readline-style incremental reverse search over history: - Ctrl+R activates search mode; subsequent presses cycle to older matches - Characters appended to the search query narrow the match in real time - Backspace removes the last search character and re-searches from most-recent - Enter accepts the current match into the textarea - Escape / Ctrl+G restores the textarea to its pre-search content - Any other key (arrows, etc.) accepts the match then re-dispatches normally The input pane is replaced by a two-line search overlay with a cyan border: (reverse-i-search)`query': <matched entry, truncated to pane width> Status bar shows contextual hint: "Enter accept Ctrl+R older Esc cancel" while search is active. Search is case-insensitive substring match walking from most-recent entry backward; HistorySearch struct lives in src/tui/history_search.rs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows live row count in the running pane while a query streams results. The query thread sends TuiMsg::Progress(n) after each data batch; the TUI updates progress_rows and renders "⠸ 1.4s 12,345 rows received". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Keywords (cyan/bold), strings (yellow), numbers (magenta), comments (gray) and functions (blue) are highlighted as the user types. Implemented by computing byte-range spans from the existing regex patterns in highlight.rs, then post-processing the ratatui buffer after tui-textarea renders — this works around tui-textarea 0.7 having no built-in multi-color highlight API. Also adds the tree-sitter + devgen-tree-sitter-sql crates and a new sql_parser.rs module (create_parser / sql_language) that Phase 6 will use for AST-based completion context detection. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
For server-side parameters (anything that modifies args.extra), send a SELECT 1 with the new setting to verify the server accepts it before persisting the change. Local-only settings (format, completion) and unset commands skip validation. On rejection, echo the set statement and show the server's error message. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove unused imports, delete dead code where safe, suppress with #[allow(dead_code)] where the code is part of an unfinished subsystem (ANSI highlighter, schema cache, context detector). Remove needless mut bindings and unused variables. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the old comfy-table-based render_table / render_table_vertical with render_table_plain, which calls the same TUI line renderer used in interactive mode and converts TuiLines to plain Unicode text. Output now uses box-drawing characters (┌─┐│╞═╡) in all modes. Remove: comfy-table dependency, render_table, render_table_vertical, should_use_vertical_mode, and the tests that covered them. Update integration tests to check for │ instead of +/|, and relax the wide-table test since vertical/horizontal layout depends on terminal width at runtime. Update README: client:auto is the default in all modes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ide format - Remove URL printing for set/unset and server-updated-URL in headless mode (only --verbose still shows URLs) - Client-side format in headless: emit Time/Scanned/Request Id to stdout right after the table, matching TUI output-pane behaviour - Server-side format: keep Time/Scanned/Request Id on stderr (scripting) - Update test to reflect the new stdout/stderr split Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Server-side formats (PSQL, JSON, CSV, etc.) now produce no timing, scan stats, or request ID in headless mode — stdout is raw server output only. Stats remain visible in the TUI for all formats. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove --concise and --no-spinner flags entirely - Spinner now shown in non-interactive mode only for client-side formats (mirroring when stats are shown); auth spinner follows same rule - Stats always shown for client-side formats, never for server-side - ^C cancellation message removed; system engine hint TUI-only - README updated: scripting examples, stdout/stderr section, flags list - Tests updated: remove all --concise usage, delete redundant test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Client-only settings (format, completion) now use a dot-prefix syntax instead of the SQL SET command, which is reserved for server parameters: .format = client:auto -- set output format .format = JSON -- switch to server-side rendering .format = -- reset to default (client:auto) .completion = off -- disable tab completion .completion = on -- re-enable tab completion .format -- show current value Behaviour changes: - `set completion = ...` in the TUI shows a clear error redirecting to the dot syntax; in pipe mode it is not intercepted (sent to server) - `unset format` now resets to `client:auto` (was `PSQL`) - `set format = ...` still works for backward compat (both client: and server-side values); only the dot syntax is documented as canonical - Dot commands work in TUI (Enter handler) and non-interactive pipe mode - Viewer flash message updated to show `.format = client:auto` Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t format list - Fix dot command errors corrupting TUI: route output through a temporary channel instead of falling back to eprintln! while in raw mode - .format now only accepts client:* formats; server-side formats require --format or set output_format=<value>; at runtime - Tab completion for .format only suggests client:auto/vertical/horizontal - Add tab completion for dot commands (both key name and value) - Add dot_command_hint() for server validation failures on known client settings - Remove unsupported CSV and JSON formats from README, args help, and completion candidates; replace with correct list: PSQL, JSON_Compact, JSON_CompactLimited, JSONLines_Compact, TabSeparatedWithNamesAndTypes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Columns were the only candidate type (tables, schemas, functions all had it) not receiving a usage frequency bonus. Frequently-used column names now rank higher within their priority class, matching the behaviour of the other types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lookup
Two bugs prevented column usage bonuses from being applied:
1. extract_column_names required FROM to appear after SELECT, so queries
written as 'FROM table SELECT col' (Firebolt allows this) never had their
columns tracked. Now extracts the SELECT list by scanning forward from
SELECT to the next major clause (FROM/WHERE/GROUP/ORDER/etc.), regardless
of what came before SELECT.
2. PriorityScorer::score passed the fully-qualified column name
(e.g. 'engine_user_query_history.query_text') to the usage tracker, but
the tracker stores bare names ('query_text'). The lookup always returned 0.
Now strips the table prefix before the get_count call for columns.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ressions - Remove apply_output_limits() from query.rs (inline limits still used) - Remove is_auto_display() and should_use_colors() from args.rs - Remove get_args() #[allow(dead_code)] annotation - Remove item_type field from FuzzyItem in fuzzy_completer.rs - Remove push_prompt() from output_pane.rs; remove allow on push_prompt_highlighted - Remove detect_context, CompletionContext, KEYWORD_PATTERN, find_last_keyword, is_inside_string_or_comment from context_detector.rs (replaced by context_analyzer) - Remove get_completions() from schema_cache.rs (unused after context_detector cleanup) All 173 unit tests and 32 integration tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the block cursor rests on '(', ')', '[', or ']', the matching
bracket is highlighted in bold LightCyan so both ends of the pair are
immediately visible.
Implementation:
- find_matching_paren(): scans forward (from '('/']') or backward (from
')'/']') tracking nesting depth; works across newlines in multiline queries
- apply_textarea_highlights(): computes the cursor's byte offset, calls
find_matching_paren, and applies the paren style on top of syntax
highlighting for the matched character
- Cursor character itself keeps its existing reversed block-cursor style;
only the non-cursor end receives the cyan highlight
Six unit tests cover forward, backward, nested, square bracket, multiline,
and no-match cases.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The matching bracket now gets Color::Indexed(234) as background — the same dark-gray used for the current-line highlight — so the highlight is consistent with the existing visual language of the TUI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
For /benchmark and /watch, only the SQL argument is passed to sqlformat; the command prefix (including any optional numeric count) is kept verbatim. For /run the argument is a file path so formatting is skipped entirely. Plain SQL (no slash prefix) is unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously Alt+F broke on /benchmark<newline>SELECT because find_slash_arg_col requires the command and SQL to be on the same line. Adds slash_cmd_sql_offset() which handles both layouts: /benchmark 5 SELECT … ← SQL same line (existing) /benchmark 5<newline>SELECT … ← SQL on next line (new) /benchmark<newline>SELECT … ← bare command, SQL on next line (new) /run is now treated the same as /benchmark and /watch (its SQL argument is formatted) rather than being skipped entirely. Five unit tests cover same-line, same-line-with-count, next-line, next-line-with-count, and no-match cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Transactions (BEGIN / COMMIT / ROLLBACK) now work correctly end-to-end:
Protocol version
Bump Firebolt-Protocol-Version header from 2.3 to 2.4, which is
required by the server to enable transaction support.
Response header handling (query.rs)
- apply_update_parameters(): new helper that correctly parses the
comma-separated "key=value,key=value" format of
Firebolt-Update-Parameters and applies each pair via set_args.
The previous code passed the whole string as a single set command,
which silently corrupted multi-value headers.
- remove_parameters(): new helper that parses the comma-separated key
list in Firebolt-Remove-Parameters and removes each from args.extra.
The previous code called unset_args with the full comma-joined string,
which only worked for single-key responses.
- Firebolt-Reset-Session: new handler — clears transaction_id and
transaction_sequence_id from the session. The server sends this header
after a successful COMMIT or ROLLBACK.
Transaction state (context.rs)
in_transaction() — returns true when transaction_id is present in
args.extra (injected by the server via Firebolt-Update-Parameters after
BEGIN, removed via Reset-Session / Remove-Parameters after COMMIT /
ROLLBACK).
TUI status bar (tui/mod.rs)
Show a bold dark-orange " TXN " badge in the status bar whenever
in_transaction() is true, so the user always knows they are inside an
open transaction.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
execute_queries clones context before spawning the query task, so transaction_id (set via Firebolt-Update-Parameters after BEGIN) was only applied to the clone and never reached self.context. Add TuiMsg::ParamUpdate(Vec<String>) which carries the updated extras list. query.rs sends it whenever any of the three transaction-related response headers are processed; drain_query_output applies it to the live self.context so in_transaction() and the TXN badge work correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unit tests (query.rs) — no server required: - apply_update_parameters: single pair, multiple pairs, empty string, trailing comma, preserves pre-existing extras - remove_parameters: single key, multiple keys, exact prefix match (doesn't touch transaction_id_extra), preserves unrelated extras, empty-string no-op - in_transaction_lifecycle: full BEGIN→mid-sequence-bump→reset→ second-transaction state machine Integration tests (tests/cli.rs): - test_transaction_begin_commit_succeeds - test_transaction_begin_rollback_succeeds - test_transaction_id_appears_in_url_after_begin (--verbose) - test_transaction_id_absent_from_url_after_commit (--verbose) - test_transaction_id_absent_from_url_after_rollback (--verbose) - test_transaction_dml_commit (CREATE/BEGIN/INSERT/COMMIT/SELECT/DROP) - test_transaction_dml_rollback (CREATE/BEGIN/INSERT/ROLLBACK/SELECT/DROP) - test_transaction_sequential_transactions (two back-to-back transactions) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. push_custom_settings: exclude transaction_id and transaction_sequence_id from the "Settings: ..." echo line. These are server-managed internal params, not user-visible settings. 2. Schema refresh: suppress the DDL-triggered schema cache refresh when a transaction is open. BEGIN returns zero columns (same DDL signal as CREATE TABLE), causing a schema query to fire inside the open transaction — which then fails. The refresh defers naturally to after COMMIT/ROLLBACK, which is correct since BEGIN doesn't change any schema. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Context::without_transaction() which returns a clone with transaction_id and transaction_sequence_id stripped from args.extra and the URL rebuilt. Use it in all three places that fire internal queries: - schema cache auto-refresh (DDL-triggered, drain_query_output) - schema cache manual refresh (/refresh command, do_refresh) - setting validation (validate_setting in execute_queries) This also removes the earlier in_transaction() guard on the DDL schema refresh, which is no longer needed since the context passed to refresh() is now always transaction-free. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The in_transaction() guard was already dropped in the previous commit (without_transaction() makes the refresh safe regardless of whether a transaction is open). Remove the now-incorrect comment that still described that old special case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tui/mod.rs:
- slash commands: call history.reset_navigation() at entry so Up/Down
navigation is consistent after /run, /benchmark, /watch, etc.
- execute_queries: fix extra_before baseline — capture it from the
already-stripped test_ctx instead of self.context so the "did this
set command add a new parameter" check isn't confused by transaction
params being present in one but absent in the other
- complete_file_paths: replace unwrap() fallback on read_dir(".") with
a graceful return of an empty Vec, preventing a panic if the working
directory becomes unreadable
- do_refresh: route schema refresh errors through context.emit_err()
instead of eprintln! so they appear in the TUI output pane rather
than corrupting the terminal
schema_cache.rs:
- do_refresh: all warning/error messages now go through context.emit_err()
so they are displayed correctly in TUI mode
viewer.rs:
- early-return conditions (no result, query errors, empty columns/rows)
now return Err() so run_viewer can show them as flash messages instead
of printing to the suspended terminal
- delete the temp CSV file after csvlens exits
- update tests to assert is_err() for the early-return cases
query.rs:
- emit "^C" to the output channel when a query is cancelled so the TUI
output pane shows a visible cancellation indicator
args.rs:
- replace format!("&output_format=JSONLines_Compact") with a plain
string (no interpolation arguments)
CLAUDE.md:
- replace stale rustyline/Ctrl+O/Ctrl+V descriptions with accurate
ratatui TUI behavior
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…connect pinger - Set context.tui_output_tx = Some(bg_tx) in TuiApp::new() so all cloned contexts (schema refresh, do_refresh) inherit a real sender instead of falling back to eprintln! and corrupting the display - Add bg_rx drain in event loop: routes Warning:/Error: lines to output pane - Add ConnectionStatus(bool) TuiMsg variant emitted by every schema refresh - On ConnectionStatus(false): show host/db in red in the status bar and spawn a background pinger (SELECT 1, once per second) until reconnected - On ConnectionStatus(true): restore green indicator, stop pinger, trigger a silent schema refresh to populate completions after reconnection - schema_cache::do_refresh now returns Err when the tables query fails so callers can distinguish "server unreachable" from partial failures Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rintln! Removed the erroneous ctx.tui_output_tx = None that was stripping the output channel from the reconnect-triggered schema refresh. When the server starts up partially (e.g. "Cluster not yet healthy"), the pinger's SELECT 1 would succeed triggering a reconnect refresh, but schema warnings would fall back to eprintln! and corrupt the TUI display. Now all warnings from reconnect refresh appear in the output pane like other background errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oter - Initialize connected=true (optimistic); avoids spurious double-refresh when the startup refresh succeeds on the first try - When pinger detects reconnection, emit "Reconnected. Refreshing schema cache..." to the output pane before spawning the schema refresh task - Footer now shows " ✗ No server connection" appended to the red host|db span so the disconnected state is unambiguous Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the pinger used query_silent("SELECT 1") which returns Ok for any
HTTP 200 response, including {"errors": [{"description": "Cluster not yet
healthy"}]}. This caused premature ConnectionStatus(true) during cluster startup.
- Add ping_server() to query.rs: sends SELECT 42 via query_silent, then scans
the response body for a top-level "errors" key — returns Err if found
- Startup schema refresh: ping_server first; only run cache.refresh() on Ok,
otherwise send ConnectionStatus(false) and let the pinger handle it
- Pinger: replace query_silent with ping_server for the same reason
The schema refresh is now only attempted once the server can actually execute
queries, not merely accept TCP connections or return HTTP 200 with error JSON.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… queries succeed The previous ping_server(SELECT 42) approach failed because SELECT 42 is a trivial constant that the server handles immediately, while information_schema queries still fail with "Cluster not yet healthy" during startup. Changes: - schema_cache::do_refresh: run tables query first; if the response body contains a top-level "errors" key (server starting up), return Err silently without emitting warnings. Only run columns/functions/signatures queries after tables succeeds, avoiding wasted requests against an unavailable cluster. - Remove ping_server() from query.rs — no longer needed. - Replace startup spawn + separate pinger with spawn_schema_retry_loop() that calls cache.refresh() directly every 1s until it returns Ok. On first failure sends ConnectionStatus(false) (red footer); on success sends ConnectionStatus(true). A /dev/null channel suppresses repeated retry warnings from the output pane. - drain_bg_output ConnectionStatus(true): schema is already populated by the retry loop, so just show "Reconnected." without spawning an additional refresh. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the startup retry loop sent ConnectionStatus(false), drain_bg_output saw ping_active=false and spawned a second retry loop. The second loop immediately hit is_refreshing()=true (set by the still-running first loop) and returned Ok(()), sending ConnectionStatus(true) without populating the schema — causing "Reconnected." to appear but completions to not work. Fix: set ping_active=true before spawning the initial retry loop in run(), so drain_bg_output's ConnectionStatus(false) handler skips the duplicate spawn. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After cache.refresh() returns Ok(()), check that functions or tables are non-empty. If both are empty the refresh completed via the is_refreshing() early-exit path without populating the cache, so retry instead of signalling a successful reconnection. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR replaces the rustyline-based REPL with a full-featured terminal UI built on ratatui + tui-textarea + crossterm.
Core TUI
Editing
$EDITOR; content loads back on saveCompletion & Search
@-prefixed file pathsSlash commands
/run @<file>|<sql>/benchmark [N] @<file>|<sql>/watch [N] @<file>|<sql>/qh [limit] [minutes]information_schema/refresh/viewDot commands
.format = client:auto|vertical|horizontal|<server-format>,.setting = value, etc.Transactions
Full Firebolt transaction support (protocol 2.4):
BEGIN/COMMIT/ROLLBACKwith a TXN badge in the status bar. Server-driven parameter updates (firebolt-update-parameters,firebolt-remove-parameters) are propagated back to the TUI context.Connection monitoring
information_schemaqueries succeed (handles "Cluster not yet healthy")Output & rendering
client:auto,client:vertical,client:horizontal) replacing comfy-tableclient:autois now the default in interactive modeCtrl+H help overlay
Floating help window listing all keybindings and slash commands.
Test plan
cargo testpasses$EDITOR; edited content loads back/benchmark 3 SELECT 1;runs warmup + 3 timed runs with live stats/watch 2 SELECT now();re-runs every 2s; Ctrl+C stops itBEGIN; ... COMMIT;shows TXN badge; server param updates are reflectedcargo run -- --core SELECT 1;works headlessly with correct exit code🤖 Generated with Claude Code