applying config in app

This commit is contained in:
pedrocarlo 2025-05-02 01:58:02 -03:00
parent c73927729d
commit b3b52f7f2f
4 changed files with 91 additions and 14 deletions

View file

@ -4,12 +4,13 @@ use crate::{
import::ImportFile, import::ImportFile,
Command, CommandParser, Command, CommandParser,
}, },
config::Config,
helper::LimboHelper, helper::LimboHelper,
input::{get_io, get_writer, DbLocation, OutputMode, Settings}, input::{get_io, get_writer, DbLocation, OutputMode, Settings},
opcodes_dictionary::OPCODE_DESCRIPTIONS, opcodes_dictionary::OPCODE_DESCRIPTIONS,
HISTORY_FILE, HISTORY_FILE,
}; };
use comfy_table::{Attribute, Cell, CellAlignment, Color, ContentArrangement, Row, Table}; use comfy_table::{Attribute, Cell, CellAlignment, ContentArrangement, Row, Table};
use limbo_core::{Database, LimboError, Statement, StepResult, Value}; use limbo_core::{Database, LimboError, Statement, StepResult, Value};
use tracing_appender::non_blocking::WorkerGuard; use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
@ -72,6 +73,7 @@ pub struct Limbo {
input_buff: String, input_buff: String,
opts: Settings, opts: Settings,
pub rl: Option<Editor<LimboHelper, DefaultHistory>>, pub rl: Option<Editor<LimboHelper, DefaultHistory>>,
config: Option<Config>,
} }
struct QueryStatistics { struct QueryStatistics {
@ -104,8 +106,6 @@ macro_rules! query_internal {
}}; }};
} }
static COLORS: &[Color] = &[Color::Green, Color::Black, Color::Grey];
impl Limbo { impl Limbo {
pub fn new() -> anyhow::Result<Self> { pub fn new() -> anyhow::Result<Self> {
let opts = Opts::parse(); let opts = Opts::parse();
@ -154,13 +154,23 @@ impl Limbo {
input_buff: String::new(), input_buff: String::new(),
opts: Settings::from(opts), opts: Settings::from(opts),
rl: None, rl: None,
config: None,
}; };
app.first_run(sql, quiet)?; app.first_run(sql, quiet)?;
Ok(app) Ok(app)
} }
pub fn with_config(mut self, config: Config) -> Self {
self.config = Some(config);
self
}
pub fn with_readline(mut self, mut rl: Editor<LimboHelper, DefaultHistory>) -> Self { pub fn with_readline(mut self, mut rl: Editor<LimboHelper, DefaultHistory>) -> Self {
let h = LimboHelper::new(self.conn.clone(), self.io.clone()); let h = LimboHelper::new(
self.conn.clone(),
self.io.clone(),
self.config.as_ref().map(|c| c.highlight.clone()),
);
rl.set_helper(Some(h)); rl.set_helper(Some(h));
self.rl = Some(rl); self.rl = Some(rl);
self self
@ -727,6 +737,7 @@ impl Limbo {
println!("Query interrupted."); println!("Query interrupted.");
return Ok(()); return Ok(());
} }
let config = self.config.as_ref().unwrap();
let mut table = Table::new(); let mut table = Table::new();
table table
.set_content_arrangement(ContentArrangement::Dynamic) .set_content_arrangement(ContentArrangement::Dynamic)
@ -738,7 +749,7 @@ impl Limbo {
let name = rows.get_column_name(i); let name = rows.get_column_name(i);
Cell::new(name) Cell::new(name)
.add_attribute(Attribute::Bold) .add_attribute(Attribute::Bold)
.fg(Color::White) .fg(config.table.header_color.into_comfy_table_color())
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
table.set_header(header); table.set_header(header);
@ -774,7 +785,9 @@ impl Limbo {
row.add_cell( row.add_cell(
Cell::new(content) Cell::new(content)
.set_alignment(alignment) .set_alignment(alignment)
.fg(COLORS[idx % COLORS.len()]), .fg(config.table.column_colors
[idx % config.table.column_colors.len()]
.into_comfy_table_color()),
); );
} }
table.add_row(row); table.add_row(row);

View file

@ -258,4 +258,4 @@ impl LimboColor {
Color::Fixed(ansi_color_num) => comfy_table::Color::AnsiValue(ansi_color_num), Color::Fixed(ansi_color_num) => comfy_table::Color::AnsiValue(ansi_color_num),
} }
} }
} }

View file

@ -11,8 +11,14 @@ use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use std::{ffi::OsString, path::PathBuf, str::FromStr as _}; use std::{ffi::OsString, path::PathBuf, str::FromStr as _};
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};
use crate::commands::CommandParser; use crate::commands::CommandParser;
use crate::config::{HighlightConfig, CONFIG_DIR};
macro_rules! try_result { macro_rules! try_result {
($expr:expr, $err:expr) => { ($expr:expr, $err:expr) => {
@ -27,14 +33,37 @@ macro_rules! try_result {
pub struct LimboHelper { pub struct LimboHelper {
#[rustyline(Completer)] #[rustyline(Completer)]
completer: SqlCompleter<CommandParser>, completer: SqlCompleter<CommandParser>,
syntax_set: SyntaxSet,
theme_set: ThemeSet,
syntax_config: HighlightConfig,
#[rustyline(Hinter)] #[rustyline(Hinter)]
hinter: HistoryHinter, hinter: HistoryHinter,
} }
impl LimboHelper { impl LimboHelper {
pub fn new(conn: Rc<Connection>, io: Arc<dyn limbo_core::IO>) -> Self { pub fn new(
conn: Rc<Connection>,
io: Arc<dyn limbo_core::IO>,
syntax_config: Option<HighlightConfig>,
) -> Self {
// Load only predefined syntax
let ps = from_uncompressed_data(include_bytes!(concat!(
env!("OUT_DIR"),
"/SQL_syntax_set_dump.packdump"
)))
.unwrap();
let mut ts = ThemeSet::load_defaults();
let theme_dir = CONFIG_DIR.join("themes");
if theme_dir.exists() {
if let Err(err) = ts.add_from_folder(theme_dir) {
tracing::error!("{err}");
}
}
LimboHelper { LimboHelper {
completer: SqlCompleter::new(conn, io), completer: SqlCompleter::new(conn, io),
syntax_set: ps,
theme_set: ts,
syntax_config: syntax_config.unwrap_or_default(),
hinter: HistoryHinter::new(), hinter: HistoryHinter::new(),
} }
} }
@ -43,9 +72,37 @@ impl LimboHelper {
impl Highlighter for LimboHelper { impl Highlighter for LimboHelper {
fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> { fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> {
let _ = pos; let _ = pos;
let style = Style::new().fg(Color::White); // Standard shell text color if self.syntax_config.enable {
let styled_str = style.paint(line); // TODO use lifetimes to store highlight lines
std::borrow::Cow::Owned(styled_str.to_string()) let syntax = self
.syntax_set
.find_syntax_by_scope(Scope::new("source.sql").unwrap())
.unwrap();
let theme = self
.theme_set
.themes
.get(&self.syntax_config.theme)
.unwrap_or(&self.theme_set.themes["base16-ocean.dark"]);
let mut h = HighlightLines::new(syntax, theme);
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 terminal color modes at the end of the string
ret_line.push_str("\x1b[0m");
std::borrow::Cow::Owned(ret_line)
} else {
// Appease Pekka in syntax highlighting
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())
}
} }
fn highlight_prompt<'b, 's: 'b, 'p: 'b>( fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
@ -55,13 +112,13 @@ impl Highlighter for LimboHelper {
) -> std::borrow::Cow<'b, str> { ) -> std::borrow::Cow<'b, str> {
let _ = default; let _ = default;
// Dark emerald green for prompt // Dark emerald green for prompt
let style = Style::new().bold().fg(Color::Rgb(34u8, 197u8, 94u8)); let style = Style::new().bold().fg(self.syntax_config.prompt.0);
let styled_str = style.paint(prompt); let styled_str = style.paint(prompt);
std::borrow::Cow::Owned(styled_str.to_string()) std::borrow::Cow::Owned(styled_str.to_string())
} }
fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> { 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().fg(self.syntax_config.hint.0); // Brighter dark grey for hints
let styled_str = style.paint(hint); let styled_str = style.paint(hint);
std::borrow::Cow::Owned(styled_str.to_string()) std::borrow::Cow::Owned(styled_str.to_string())
} }
@ -72,7 +129,7 @@ impl Highlighter for LimboHelper {
completion: rustyline::CompletionType, completion: rustyline::CompletionType,
) -> std::borrow::Cow<'c, str> { ) -> std::borrow::Cow<'c, str> {
let _ = completion; let _ = completion;
let style = Style::new().fg(Color::Green); let style = Style::new().fg(self.syntax_config.candidate.0);
let styled_str = style.paint(candidate); let styled_str = style.paint(candidate);
std::borrow::Cow::Owned(styled_str.to_string()) std::borrow::Cow::Owned(styled_str.to_string())
} }

View file

@ -6,6 +6,7 @@ mod helper;
mod input; mod input;
mod opcodes_dictionary; mod opcodes_dictionary;
use config::CONFIG_DIR;
use rustyline::{error::ReadlineError, Config, Editor}; use rustyline::{error::ReadlineError, Config, Editor};
use std::{ use std::{
path::PathBuf, path::PathBuf,
@ -33,6 +34,12 @@ fn main() -> anyhow::Result<()> {
if HISTORY_FILE.exists() { if HISTORY_FILE.exists() {
rl.load_history(HISTORY_FILE.as_path())?; rl.load_history(HISTORY_FILE.as_path())?;
} }
let config_file = CONFIG_DIR.join("limbo.toml");
let config = config::Config::from_config_file(config_file);
tracing::info!("Configuration: {:?}", config);
app = app.with_config(config);
app = app.with_readline(rl); app = app.with_readline(rl);
} else { } else {
tracing::debug!("not in tty"); tracing::debug!("not in tty");