Rewrite interactive mode: ratatui TUI with completion, syntax highlighting, and more#11
Open
tobias-fire wants to merge 144 commits intomainfrom
Open
Rewrite interactive mode: ratatui TUI with completion, syntax highlighting, and more#11tobias-fire wants to merge 144 commits intomainfrom
tobias-fire wants to merge 144 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>
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>
- Add src/lib.rs with all module declarations, run(), run_query(), exit_code_for(), and a #[no_mangle] fb_cli_main(argc, argv) C FFI entry point so fb-cli can be embedded in a C++ binary - Add get_args_from(raw: &[String]) to args.rs so the caller can inject an explicit argv (e.g. ["fb", "--core", ...]) instead of reading std::env::args(); get_args() delegates to it - Simplify main.rs to a thin shim that calls fb_cli_main via FFI - Remove devgen-tree-sitter-sql and tree-sitter dependencies (unused); delete src/sql_parser.rs - Change crate-type to ["staticlib", "rlib"] so both the C staticlib and the Rust binary can use the library Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add transport.rs module abstracting HTTP over both TCP (reqwest) and Unix domain sockets (hyper). The --unix-socket flag routes all queries through the given socket path, removing the need for TCP on port 3473. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When --unix-socket is set, the status bar was still displaying the HTTP host (e.g. localhost:3473). Now it shows the socket path instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
set_textarea_content creates a fresh TextArea whose viewport starts at (0,0), but ta_col_top/ta_row_top retained the old scroll offset. next_scroll_top never resets, so a stale col_top caused apply_textarea_highlights to map characters to wrong screen columns. Reset both mirrors to 0 whenever the textarea content is replaced. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Passes positional parameters as Firebolt's query_parameters URL parameter. First -p value becomes $1, second becomes $2, etc. Values are auto-typed: integers, floats, booleans, and NULL are sent as their native JSON types; everything else is sent as a JSON string. Also fix pre-existing test_params_escaping failure: alias param() columns so the column name is stable across server versions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously, fmt_cell replaced all control characters (including \n) with spaces, collapsing multi-line values into a single line and losing structure. Changes: - fmt_cell: preserve \n; only replace other control chars with spaces - wrap_cell: split on \n first (hard line breaks), then wrap each segment by column width independently - decide_col_widths: use cell_display_width() (max line width) instead of chars().count() for natural/min width calculation, and rows_needed_for_cell() for the too-tall check, so multi-line cells don't inflate estimates - any_wrap / any_wrap_in_row: also trigger on embedded newlines Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two independent fixes: 1. Enable bracketed paste mode (EnableBracketedPaste / DisableBracketedPaste). The terminal now bundles the entire pasted string into a single Event::Paste(String) instead of sending one Event::Key per character. handle_paste() inserts the whole string before the next render. Bracketed paste is also disabled/re-enabled around suspend_tui/resume_tui so external programs (editor, csvlens) are not affected. 2. Drain all pending events before calling terminal.draw(). Previously the loop rendered after every single event; now it consumes all immediately- available events in a tight inner loop and renders once at the end. Pasting N characters without bracketed paste support now costs 1 render instead of N. update_signature_hint() is also called once per drain cycle rather than once per keystroke. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ture hints
Firebolt Core now returns routine_parameters as an array of structs
({type, name, default_value, comment}) instead of plain type strings,
and adds an is_variadic boolean column.
- Try new query (with is_variadic) first; fall back to old format on error
- Parse new struct format: show "name type" (or just "type" when unnamed)
and append "..." to the last param when is_variadic is true
- Old format (plain string array) still detected and handled at parse time
- Fix != → <> in routines queries (new Firebolt parser rejects !=)
- Add unit tests for both old and new formats
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Type names now displayed in UPPERCASE (TEXT, INTEGER, TIMESTAMP, ...) - Default values shown with => separator (e.g. `unit TEXT => 'day'`) - Variadic marker (...) placed after the type, before any default - Popup wraps to multiple lines when signature is wider than the terminal: each param on its own line, continuation lines aligned under the opening `(` - Per-token coloring: function name=cyan, param names=white, types=yellow, `=>`=gray, default values=light-gray, parens=dim-white Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The popup height was not clamped, so with many overloads it could exceed the terminal dimensions, causing a ratatui buffer bounds panic. - Cap popup_h to input_area.y (space above the input pane, the most the popup can occupy without going off-screen) - Also cap against total.height as a hard safety bound - Truncate displayed lines to match the clamped height - Return early when there are fewer than 3 rows available above the input Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of one-param-per-line, pack as many params as fit on each line
(greedy fill). Continuation lines align under the opening '('. This
produces fewer display lines per overload, so more overloads fit within
the vertical space before the height clamp kicks in.
Example with max_w=50:
generate_series(start INTEGER, stop INTEGER,
step INTEGER)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the cursor is inside a function call with known signatures, Tab completion now suggests named parameters (e.g. `unit => `, `value => `) with priority just below in-query column suggestions. - Param names are extracted from all overloads of the current function - Unique names across overloads, in declaration order - Filtered by the current partial word (prefix match, case-insensitive) - Completing inserts `<name> => ` (with trailing space) - Inserted after column items, before tables/functions in the popup - Only appears when signatures are known (popup is visible) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- completion_popup::popup_area gains bottom_offset param so the popup positions itself above the signature hint when both are visible - render_signature_hint now returns Option<Rect> so its height can be passed as bottom_offset to the completion popup - Render order flipped: signature hint drawn first (lower z), then completion popup drawn on top (higher z) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tive mode gumdrop sets help=true when --help is passed but does not auto-exit when the struct declares a help field. Add an explicit check after the version check to print the usage string and return 0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add fb_cli_usage_string() that returns the gumdrop options block as a malloc'd C string (caller frees with free()); used by embedding binaries to compose their own help output - Fix --help handler to use raw_args[0] as the program name instead of hardcoded "fb" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The spinner character was being erased after the table was already written to stdout. Since both stderr (spinner) and stdout (table) share the terminal cursor, the backspace sequence landed in the wrong place, leaving the spinner character visible at the start of the first table line. Cancel and await the spinner at the top of the response arm, before any output is produced. The post-select cancel+await is still present to handle the ^C arm where take() was never called. 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 modeParameterized queries (
-p/--param)Positional query parameters for non-interactive and pipe mode. Values are auto-typed (int, float, bool, NULL, or string) and sent as Firebolt's
query_parametersURL parameter.Ctrl+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 reflectedfb --core -p 42 -p hello "SELECT \$1, \$2"returns42 hellocargo run -- --core SELECT 1;works headlessly with correct exit code🤖 Generated with Claude Code