Repository: tursodatabase/limbo (rust)

Issue:
Default to pretty mode if there's TTY
Pretty mode is pretty. Let's consider making it the default if there's a TTY.

Pull request description:
Enable pretty mode in shell by default
Fixes #929

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 5da5d99b..15820264 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -505,7 +505,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
 [[package]]
 name = "core_tester"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "anyhow",
  "assert_cmd",
@@ -1622,7 +1622,7 @@ dependencies = [
 
 [[package]]
 name = "limbo"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "limbo_core",
  "thiserror 2.0.12",
@@ -1631,14 +1631,14 @@ dependencies = [
 
 [[package]]
 name = "limbo-go"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "limbo_core",
 ]
 
 [[package]]
 name = "limbo-java"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "jni",
  "limbo_core",
@@ -1647,7 +1647,7 @@ dependencies = [
 
 [[package]]
 name = "limbo-wasm"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "console_error_panic_hook",
  "getrandom 0.2.15",
@@ -1660,7 +1660,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_cli"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "anyhow",
  "cfg-if",
@@ -1681,7 +1681,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_completion"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "limbo_ext",
  "mimalloc",
@@ -1689,7 +1689,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_core"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "built",
  "cfg_block",
@@ -1746,7 +1746,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_crypto"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "blake3",
  "data-encoding",
@@ -1759,7 +1759,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_ext"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "chrono",
  "getrandom 0.3.1",
@@ -1768,7 +1768,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_ext_tests"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "env_logger 0.11.6",
  "lazy_static",
@@ -1779,7 +1779,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_ipaddr"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "ipnetwork",
  "limbo_ext",
@@ -1788,7 +1788,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_macros"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1797,7 +1797,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_percentile"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "limbo_ext",
  "mimalloc",
@@ -1805,7 +1805,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_regexp"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "limbo_ext",
  "mimalloc",
@@ -1814,7 +1814,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_series"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "limbo_ext",
  "mimalloc",
@@ -1824,7 +1824,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_sim"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "anarchist-readable-name-generator-lib",
  "clap",
@@ -1844,7 +1844,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_sqlite3"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "env_logger 0.11.6",
  "libc",
@@ -1854,7 +1854,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_sqlite3_parser"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "bitflags 2.9.0",
  "cc",
@@ -1874,7 +1874,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_stress"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "antithesis_sdk",
  "clap",
@@ -1885,7 +1885,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_time"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "chrono",
  "limbo_ext",
@@ -1897,7 +1897,7 @@ dependencies = [
 
 [[package]]
 name = "limbo_uuid"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "limbo_ext",
  "mimalloc",
@@ -2534,7 +2534,7 @@ dependencies = [
 
 [[package]]
 name = "py-limbo"
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 dependencies = [
  "anyhow",
  "limbo_core",
diff --git a/Cargo.toml b/Cargo.toml
index aae6669a..2cd0849c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,26 +28,26 @@ members = [
 exclude = ["perf/latency/limbo"]
 
 [workspace.package]
-version = "0.0.18-pre.3"
+version = "0.0.18-pre.2"
 authors = ["the Limbo authors"]
 edition = "2021"
 license = "MIT"
 repository = "https://github.com/tursodatabase/limbo"
 
 [workspace.dependencies]
-limbo_completion = { path = "extensions/completion", version = "0.0.18-pre.3" }
-limbo_core = { path = "core", version = "0.0.18-pre.3" }
-limbo_crypto = { path = "extensions/crypto", version = "0.0.18-pre.3" }
-limbo_ext = { path = "extensions/core", version = "0.0.18-pre.3" }
-limbo_ext_tests = { path = "extensions/tests", version = "0.0.18-pre.3" }
-limbo_ipaddr = { path = "extensions/ipaddr", version = "0.0.18-pre.3" }
-limbo_macros = { path = "macros", version = "0.0.18-pre.3" }
-limbo_percentile = { path = "extensions/percentile", version = "0.0.18-pre.3" }
-limbo_regexp = { path = "extensions/regexp", version = "0.0.18-pre.3" }
-limbo_series = { path = "extensions/series", version = "0.0.18-pre.3" }
-limbo_sqlite3_parser = { path = "vendored/sqlite3-parser", version = "0.0.18-pre.3" }
-limbo_time = { path = "extensions/time", version = "0.0.18-pre.3" }
-limbo_uuid = { path = "extensions/uuid", version = "0.0.18-pre.3" }
+limbo_completion = { path = "extensions/completion", version = "0.0.18-pre.2" }
+limbo_core = { path = "core", version = "0.0.18-pre.2" }
+limbo_crypto = { path = "extensions/crypto", version = "0.0.18-pre.2" }
+limbo_ext = { path = "extensions/core", version = "0.0.18-pre.2" }
+limbo_ext_tests = { path = "extensions/tests", version = "0.0.18-pre.2" }
+limbo_ipaddr = { path = "extensions/ipaddr", version = "0.0.18-pre.2" }
+limbo_macros = { path = "macros", version = "0.0.18-pre.2" }
+limbo_percentile = { path = "extensions/percentile", version = "0.0.18-pre.2" }
+limbo_regexp = { path = "extensions/regexp", version = "0.0.18-pre.2" }
+limbo_series = { path = "extensions/series", version = "0.0.18-pre.2" }
+limbo_sqlite3_parser = { path = "vendored/sqlite3-parser", version = "0.0.18-pre.2" }
+limbo_time = { path = "extensions/time", version = "0.0.18-pre.2" }
+limbo_uuid = { path = "extensions/uuid", version = "0.0.18-pre.2" }
 
 # Config for 'cargo dist'
 [workspace.metadata.dist]
diff --git a/Makefile b/Makefile
index 01aa6fb7..5202112d 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ RUSTUP := $(shell command -v rustup 2> /dev/null)
 UNAME_S := $(shell uname -s)
 
 # Executable used to execute the compatibility tests.
-SQLITE_EXEC ?= ./target/debug/limbo
+SQLITE_EXEC ?= scripts/limbo-sqlite3
 
 # Static library to use for SQLite C API compatibility tests.
 BASE_SQLITE_LIB = ./target/$(CURRENT_RUST_TARGET)/debug/liblimbo_sqlite3.a
diff --git a/cli/app.rs b/cli/app.rs
index 0144a365..7102fa78 100644
--- a/cli/app.rs
+++ b/cli/app.rs
@@ -29,7 +29,7 @@ pub struct Opts {
     pub database: Option<PathBuf>,
     #[clap(index = 2, help = "Optional SQL command to execute")]
     pub sql: Option<String>,
-    #[clap(short = 'm', long, default_value_t = OutputMode::List)]
+    #[clap(short = 'm', long, default_value_t = OutputMode::Pretty)]
     pub output_mode: OutputMode,
     #[clap(short, long, default_value = "")]
     pub output: String,
@@ -200,7 +200,7 @@ macro_rules! query_internal {
     }};
 }
 
-static COLORS: &[Color] = &[Color::Green, Color::Black, Color::Grey];
+static COLORS: &[Color] = &[Color::DarkRed, Color::DarkGreen, Color::DarkBlue];
 
 impl<'a> Limbo<'a> {
     pub fn new(rl: &'a mut rustyline::Editor<LimboHelper, DefaultHistory>) -> anyhow::Result<Self> {
@@ -757,7 +757,7 @@ impl<'a> Limbo<'a> {
                                 let name = rows.get_column_name(i);
                                 Cell::new(name)
                                     .add_attribute(Attribute::Bold)
-                                    .fg(Color::White)
+                                    .fg(Color::AnsiValue(49)) // Green color for headers
                             })
                             .collect::<Vec<_>>();
                         table.set_header(header);
diff --git a/cli/helper.rs b/cli/helper.rs
index 90549dd1..37cd462b 100644
--- a/cli/helper.rs
+++ b/cli/helper.rs
@@ -7,6 +7,11 @@ use rustyline::completion::{extract_word, Completer, Pair};
 use rustyline::highlight::Highlighter;
 use rustyline::hint::HistoryHinter;
 use rustyline::{Completer, Helper, Hinter, Validator};
+use syntect::dumps::from_uncompressed_data;
+use syntect::easy::HighlightLines;
+use syntect::highlighting::ThemeSet;
+use syntect::parsing::{Scope, SyntaxSet};
+use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};
 
 macro_rules! try_result {
     ($expr:expr, $err:expr) => {
@@ -21,14 +26,25 @@ macro_rules! try_result {
 pub struct LimboHelper {
     #[rustyline(Completer)]
     completer: SqlCompleter,
+    syntax_set: SyntaxSet,
+    theme_set: ThemeSet,
     #[rustyline(Hinter)]
     hinter: HistoryHinter,
 }
 
 impl LimboHelper {
     pub fn new(conn: Rc<Connection>, io: Arc<dyn limbo_core::IO>) -> Self {
+        // Load only predefined syntax
+        let ps = from_uncompressed_data(include_bytes!(concat!(
+            env!("OUT_DIR"),
+            "/SQL_syntax_set_dump.packdump"
+        )))
+        .unwrap();
+        let ts = ThemeSet::load_defaults();
         LimboHelper {
             completer: SqlCompleter::new(conn, io),
+            syntax_set: ps,
+            theme_set: ts,
             hinter: HistoryHinter::new(),
         }
     }
@@ -37,9 +53,25 @@ impl LimboHelper {
 impl Highlighter for LimboHelper {
     fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> {
         let _ = pos;
-        let style = Style::new().fg(Color::White); // Standard shell text color
-        let styled_str = style.paint(line);
-        std::borrow::Cow::Owned(styled_str.to_string())
+        // TODO use lifetimes to store highlight lines
+        let syntax = self
+            .syntax_set
+            .find_syntax_by_scope(Scope::new("source.sql").unwrap())
+            .unwrap();
+        let mut h = HighlightLines::new(syntax, &self.theme_set.themes["base16-ocean.dark"]);
+        let ranges = {
+            let mut ret_ranges = Vec::new();
+            for new_line in LinesWithEndings::from(line) {
+                let ranges: Vec<(syntect::highlighting::Style, &str)> =
+                    h.highlight_line(new_line, &self.syntax_set).unwrap();
+                ret_ranges.extend(ranges);
+            }
+            ret_ranges
+        };
+        let mut ret_line = as_24_bit_terminal_escaped(&ranges[..], false);
+        // Push this escape sequence to reset color modes at the end of the string
+        ret_line.push_str("\x1b[0m");
+        std::borrow::Cow::Owned(ret_line)
     }
 
     fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
@@ -48,16 +80,20 @@ impl Highlighter for LimboHelper {
         default: bool,
     ) -> std::borrow::Cow<'b, str> {
         let _ = default;
-        // Dark emerald green for prompt
-        let style = Style::new().bold().fg(Color::Rgb(34u8, 197u8, 94u8));
+        // Make prompt bold
+        let style = Style::new().bold().fg(Color::Green);
         let styled_str = style.paint(prompt);
         std::borrow::Cow::Owned(styled_str.to_string())
     }
 
     fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> {
-        let style = Style::new().bold().fg(Color::Rgb(107u8, 114u8, 128u8)); // Brighter dark grey for hints
+        let style = Style::new()
+            .bold()
+            .dimmed()
+            .underline()
+            .fg(Color::Fixed(246));
         let styled_str = style.paint(hint);
-        std::borrow::Cow::Owned(styled_str.to_string())
+        std::borrow::Cow::Owned(styled_str.to_string()) // Bold dim grey underline
     }
 
     fn highlight_candidate<'c>(
@@ -66,7 +102,7 @@ impl Highlighter for LimboHelper {
         completion: rustyline::CompletionType,
     ) -> std::borrow::Cow<'c, str> {
         let _ = completion;
-        let style = Style::new().fg(Color::Green);
+        let style = Style::new().fg(Color::Fixed(69));
         let styled_str = style.paint(candidate);
         std::borrow::Cow::Owned(styled_str.to_string())
     }
diff --git a/scripts/limbo-sqlite3 b/scripts/limbo-sqlite3
new file mode 100755
index 00000000..8e9f0389
--- /dev/null
+++ b/scripts/limbo-sqlite3
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+target/debug/limbo -m list "$@"


Test patch:
diff --git a/testing/cli_tests/extensions.py b/testing/cli_tests/extensions.py
index d53e75b2..56784b28 100755
--- a/testing/cli_tests/extensions.py
+++ b/testing/cli_tests/extensions.py
@@ -2,7 +2,7 @@
 import os
 from test_limbo_cli import TestLimboShell
 
-sqlite_exec = "./target/debug/limbo"
+sqlite_exec = "./scripts/limbo-sqlite3"
 sqlite_flags = os.getenv("SQLITE_FLAGS", "-q").split(" ")
 
 test_data = """CREATE TABLE numbers ( id INTEGER PRIMARY KEY, value FLOAT NOT NULL);
diff --git a/testing/cli_tests/test_limbo_cli.py b/testing/cli_tests/test_limbo_cli.py
index 38186bf4..10e87869 100755
--- a/testing/cli_tests/test_limbo_cli.py
+++ b/testing/cli_tests/test_limbo_cli.py
@@ -104,7 +104,7 @@ class TestLimboShell:
         flags="",
     ):
         if exec_name is None:
-            exec_name = "./target/debug/limbo"
+            exec_name = "./scripts/limbo-sqlite3"
             if flags == "":
                 flags = "-q"
         self.config = ShellConfig(exe_name=exec_name, flags=flags)