mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-22 01:24:18 +01:00
Merge 'Cli config 2' from Pedro Muniz
As there were many merge conflicts for the other PR, I rewrote the code and condensed it here. ORIGINAL PR TEXT: Provides the code to almost close https://github.com/tursodatabase/limbo/issues/1251 . The JsonSchema is derived, but I am still not sure how to automate the distribution to SchemaStore for autocomplete. I added some docs for that want to see the config file description. I still am not sure how to automate this documentation. Maybe some macro magic? Reviewed-by: Preston Thorpe (@PThorpe92) Closes #1430
This commit is contained in:
230
Cargo.lock
generated
230
Cargo.lock
generated
@@ -697,6 +697,41 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.8.0"
|
||||
@@ -786,6 +821,12 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
@@ -1134,6 +1175,12 @@ dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
@@ -1151,7 +1198,7 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1335,6 +1382,12 @@ dependencies = [
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
@@ -1356,6 +1409,17 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.8.0"
|
||||
@@ -1363,7 +1427,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1379,7 +1443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"indexmap",
|
||||
"indexmap 2.8.0",
|
||||
"is-terminal",
|
||||
"itoa",
|
||||
"log",
|
||||
@@ -1708,11 +1772,16 @@ dependencies = [
|
||||
"miette",
|
||||
"nu-ansi-term 0.50.1",
|
||||
"rustyline",
|
||||
"schemars",
|
||||
"serde",
|
||||
"shlex",
|
||||
"syntect",
|
||||
"toml",
|
||||
"toml_edit",
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
"tracing-subscriber",
|
||||
"validator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1904,7 +1973,7 @@ dependencies = [
|
||||
"cc",
|
||||
"env_logger 0.11.7",
|
||||
"fallible-iterator",
|
||||
"indexmap",
|
||||
"indexmap 2.8.0",
|
||||
"log",
|
||||
"memchr",
|
||||
"miette",
|
||||
@@ -2018,7 +2087,7 @@ version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2259,6 +2328,7 @@ version = "0.50.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
@@ -2441,7 +2511,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"indexmap",
|
||||
"indexmap 2.8.0",
|
||||
"quick-xml 0.32.0",
|
||||
"serde",
|
||||
"time",
|
||||
@@ -2570,6 +2640,28 @@ dependencies = [
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
@@ -3065,6 +3157,31 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"indexmap 1.9.3",
|
||||
"schemars_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
@@ -3097,6 +3214,17 @@ dependencies = [
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
@@ -3109,6 +3237,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
@@ -3518,6 +3655,48 @@ dependencies = [
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
|
||||
dependencies = [
|
||||
"indexmap 2.8.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
|
||||
dependencies = [
|
||||
"indexmap 2.8.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_write",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
@@ -3686,6 +3865,36 @@ dependencies = [
|
||||
"getrandom 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43fb22e1a008ece370ce08a3e9e4447a910e92621bb49b85d6e48a45397e7cfa"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"url",
|
||||
"validator_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator_derive"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"once_cell",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
@@ -4079,6 +4288,15 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
|
||||
@@ -33,7 +33,7 @@ limbo_core = { path = "../core", default-features = true, features = [
|
||||
"completion",
|
||||
] }
|
||||
miette = { version = "7.4.0", features = ["fancy"] }
|
||||
nu-ansi-term = "0.50.1"
|
||||
nu-ansi-term = {version = "0.50.1", features = ["serde", "derive_serde_style"]}
|
||||
rustyline = { version = "15.0.0", default-features = true, features = [
|
||||
"derive",
|
||||
] }
|
||||
@@ -42,6 +42,11 @@ syntect = "5.2.0"
|
||||
tracing = "0.1.41"
|
||||
tracing-appender = "0.2.3"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
toml = {version = "0.8.20", features = ["preserve_order"]}
|
||||
schemars = {version = "0.8.22", features = ["preserve_order"]}
|
||||
serde = {version = "1.0.218", features = ["derive"]}
|
||||
validator = {version = "0.20.0", features = ["derive"]}
|
||||
toml_edit = {version = "0.22.24", features = ["serde"]}
|
||||
|
||||
[features]
|
||||
default = ["io_uring"]
|
||||
|
||||
25
cli/app.rs
25
cli/app.rs
@@ -4,12 +4,13 @@ use crate::{
|
||||
import::ImportFile,
|
||||
Command, CommandParser,
|
||||
},
|
||||
config::Config,
|
||||
helper::LimboHelper,
|
||||
input::{get_io, get_writer, DbLocation, OutputMode, Settings},
|
||||
opcodes_dictionary::OPCODE_DESCRIPTIONS,
|
||||
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 tracing_appender::non_blocking::WorkerGuard;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||
@@ -72,6 +73,7 @@ pub struct Limbo {
|
||||
input_buff: String,
|
||||
opts: Settings,
|
||||
pub rl: Option<Editor<LimboHelper, DefaultHistory>>,
|
||||
config: Option<Config>,
|
||||
}
|
||||
|
||||
struct QueryStatistics {
|
||||
@@ -104,8 +106,6 @@ macro_rules! query_internal {
|
||||
}};
|
||||
}
|
||||
|
||||
static COLORS: &[Color] = &[Color::Green, Color::Black, Color::Grey];
|
||||
|
||||
impl Limbo {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
let opts = Opts::parse();
|
||||
@@ -154,13 +154,23 @@ impl Limbo {
|
||||
input_buff: String::new(),
|
||||
opts: Settings::from(opts),
|
||||
rl: None,
|
||||
config: None,
|
||||
};
|
||||
app.first_run(sql, quiet)?;
|
||||
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 {
|
||||
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));
|
||||
self.rl = Some(rl);
|
||||
self
|
||||
@@ -727,6 +737,7 @@ impl Limbo {
|
||||
println!("Query interrupted.");
|
||||
return Ok(());
|
||||
}
|
||||
let config = self.config.as_ref().unwrap();
|
||||
let mut table = Table::new();
|
||||
table
|
||||
.set_content_arrangement(ContentArrangement::Dynamic)
|
||||
@@ -738,7 +749,7 @@ impl Limbo {
|
||||
let name = rows.get_column_name(i);
|
||||
Cell::new(name)
|
||||
.add_attribute(Attribute::Bold)
|
||||
.fg(Color::White)
|
||||
.fg(config.table.header_color.into_comfy_table_color())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
table.set_header(header);
|
||||
@@ -774,7 +785,9 @@ impl Limbo {
|
||||
row.add_cell(
|
||||
Cell::new(content)
|
||||
.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);
|
||||
|
||||
137
cli/config/mod.rs
Normal file
137
cli/config/mod.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
mod palette;
|
||||
|
||||
use crate::HOME_DIR;
|
||||
use nu_ansi_term::Color;
|
||||
use palette::LimboColor;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::fmt::Debug;
|
||||
use std::fs::read_to_string;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::LazyLock;
|
||||
use validator::Validate;
|
||||
|
||||
pub static CONFIG_DIR: LazyLock<PathBuf> = LazyLock::new(|| HOME_DIR.join(".config/limbo"));
|
||||
|
||||
fn ok_or_default<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
T: Deserialize<'de> + Default + Validate + Debug,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let v: toml::Value = Deserialize::deserialize(deserializer)?;
|
||||
let x = T::deserialize(v)
|
||||
.map(|v| {
|
||||
let validate = v.validate();
|
||||
if validate.is_err() {
|
||||
tracing::error!(
|
||||
"Invalid value for {}.\n Original config value: {:?}",
|
||||
validate.unwrap_err(),
|
||||
v
|
||||
);
|
||||
T::default()
|
||||
} else {
|
||||
v
|
||||
}
|
||||
})
|
||||
.unwrap_or_default();
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default, JsonSchema)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
#[serde(deserialize_with = "ok_or_default")]
|
||||
pub table: TableConfig,
|
||||
pub highlight: HighlightConfig,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_config_file(path: PathBuf) -> Self {
|
||||
if let Some(config) = Self::read_config_str(path) {
|
||||
Self::from_config_str(&config)
|
||||
} else {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_config_str(config: &str) -> Self {
|
||||
toml::from_str(config)
|
||||
.inspect_err(|err| tracing::error!("{}", err))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn read_config_str(path: PathBuf) -> Option<String> {
|
||||
if path.exists() {
|
||||
tracing::trace!("Trying to read from {:?}", path);
|
||||
|
||||
let result = read_to_string(path);
|
||||
|
||||
if result.is_err() {
|
||||
tracing::debug!("Error reading file: {:?}", result);
|
||||
} else {
|
||||
tracing::trace!("File read successfully");
|
||||
};
|
||||
|
||||
result.ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, JsonSchema, Validate)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct TableConfig {
|
||||
#[serde(default = "TableConfig::default_header_color")]
|
||||
pub header_color: LimboColor,
|
||||
#[serde(default = "TableConfig::default_column_colors")]
|
||||
#[validate(length(min = 1))]
|
||||
pub column_colors: Vec<LimboColor>,
|
||||
}
|
||||
|
||||
impl Default for TableConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
header_color: TableConfig::default_header_color(),
|
||||
column_colors: TableConfig::default_column_colors(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TableConfig {
|
||||
// Default colors for Pekka's tastes
|
||||
fn default_header_color() -> LimboColor {
|
||||
LimboColor(Color::White)
|
||||
}
|
||||
|
||||
fn default_column_colors() -> Vec<LimboColor> {
|
||||
vec![
|
||||
LimboColor(Color::Green),
|
||||
LimboColor(Color::Black),
|
||||
// Comfy Table Color::Grey
|
||||
LimboColor(Color::Fixed(7)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, JsonSchema)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct HighlightConfig {
|
||||
pub enable: bool,
|
||||
pub theme: String,
|
||||
pub prompt: LimboColor,
|
||||
pub hint: LimboColor,
|
||||
pub candidate: LimboColor,
|
||||
}
|
||||
|
||||
impl Default for HighlightConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enable: true,
|
||||
theme: "base16-ocean.dark".to_string(),
|
||||
prompt: LimboColor(Color::Rgb(34u8, 197u8, 94u8)),
|
||||
hint: LimboColor(Color::Rgb(107u8, 114u8, 128u8)),
|
||||
candidate: LimboColor(Color::Green),
|
||||
}
|
||||
}
|
||||
}
|
||||
261
cli/config/palette.rs
Normal file
261
cli/config/palette.rs
Normal file
@@ -0,0 +1,261 @@
|
||||
use core::fmt;
|
||||
use std::{
|
||||
fmt::Display,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use nu_ansi_term::Color;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{
|
||||
de::{self, Visitor},
|
||||
Deserialize, Deserializer, Serialize,
|
||||
};
|
||||
use tracing::trace;
|
||||
use validator::Validate;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct LimboColor(pub Color);
|
||||
|
||||
impl TryFrom<&str> for LimboColor {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
// Parse RGB hex values
|
||||
trace!("Parsing color_string: {}", value);
|
||||
|
||||
let color = match value.chars().collect::<Vec<_>>()[..] {
|
||||
// #rrggbb hex color
|
||||
['#', r1, r2, g1, g2, b1, b2] => {
|
||||
let r = u8::from_str_radix(&format!("{r1}{r2}"), 16).map_err(|e| e.to_string())?;
|
||||
let g = u8::from_str_radix(&format!("{g1}{g2}"), 16).map_err(|e| e.to_string())?;
|
||||
let b = u8::from_str_radix(&format!("{b1}{b2}"), 16).map_err(|e| e.to_string())?;
|
||||
Some(Color::Rgb(r, g, b))
|
||||
}
|
||||
// #rgb shorthand hex color
|
||||
['#', r, g, b] => {
|
||||
let r = u8::from_str_radix(&format!("{r}{r}"), 16).map_err(|e| e.to_string())?;
|
||||
let g = u8::from_str_radix(&format!("{g}{g}"), 16).map_err(|e| e.to_string())?;
|
||||
let b = u8::from_str_radix(&format!("{b}{b}"), 16).map_err(|e| e.to_string())?;
|
||||
Some(Color::Rgb(r, g, b))
|
||||
}
|
||||
// 0-255 color code
|
||||
[c1, c2, c3] => {
|
||||
if let Ok(ansi_color_num) = str::parse::<u8>(&format!("{c1}{c2}{c3}")) {
|
||||
Some(Color::Fixed(ansi_color_num))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
[c1, c2] => {
|
||||
if let Ok(ansi_color_num) = str::parse::<u8>(&format!("{c1}{c2}")) {
|
||||
Some(Color::Fixed(ansi_color_num))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
[c1] => {
|
||||
if let Ok(ansi_color_num) = str::parse::<u8>(&format!("{c1}")) {
|
||||
Some(Color::Fixed(ansi_color_num))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// unknown format
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(color) = color {
|
||||
return Ok(LimboColor(color));
|
||||
}
|
||||
|
||||
// Check for any predefined color strings
|
||||
// There are no predefined enums for bright colors, so we use Color::Fixed
|
||||
let predefined_color = match value.to_lowercase().as_str() {
|
||||
"black" => Color::Black,
|
||||
"red" => Color::Red,
|
||||
"green" => Color::Green,
|
||||
"yellow" => Color::Yellow,
|
||||
"blue" => Color::Blue,
|
||||
"purple" => Color::Purple,
|
||||
"cyan" => Color::Cyan,
|
||||
"magenta" => Color::Magenta,
|
||||
"white" => Color::White,
|
||||
"bright-black" => Color::DarkGray, // "bright-black" is dark grey
|
||||
"bright-red" => Color::LightRed,
|
||||
"bright-green" => Color::LightGreen,
|
||||
"bright-yellow" => Color::LightYellow,
|
||||
"bright-blue" => Color::LightBlue,
|
||||
"bright-cyan" => Color::LightCyan,
|
||||
"birght-magenta" => Color::LightMagenta,
|
||||
"bright-white" => Color::LightGray,
|
||||
"dark-red" => Color::Fixed(1),
|
||||
"dark-green" => Color::Fixed(2),
|
||||
"dark-yellow" => Color::Fixed(3),
|
||||
"dark-blue" => Color::Fixed(4),
|
||||
"dark-magenta" => Color::Fixed(5),
|
||||
"dark-cyan" => Color::Fixed(6),
|
||||
"grey" => Color::Fixed(7),
|
||||
"dark-grey" => Color::Fixed(8),
|
||||
_ => return Err(format!("Could not parse color in string: {}", value)),
|
||||
};
|
||||
|
||||
trace!("Read predefined color: {}", value);
|
||||
Ok(LimboColor(predefined_color))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LimboColor {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let val = match self.0 {
|
||||
Color::Black => "black".to_string(),
|
||||
Color::Red => "red".to_string(),
|
||||
Color::Green => "green".to_string(),
|
||||
Color::Yellow => "yellow".to_string(),
|
||||
Color::Blue => "blue".to_string(),
|
||||
Color::Purple => "purple".to_string(),
|
||||
Color::Cyan => "cyan".to_string(),
|
||||
Color::Magenta => "magenta".to_string(),
|
||||
Color::White => "white".to_string(),
|
||||
Color::DarkGray => "bright-black".to_string(), // "bright-black" is dark grey
|
||||
Color::LightRed => "bright-red".to_string(),
|
||||
Color::LightGreen => "bright-green".to_string(),
|
||||
Color::LightYellow => "bright-yellow".to_string(),
|
||||
Color::LightBlue => "bright-blue".to_string(),
|
||||
Color::LightCyan => "bright-cyan".to_string(),
|
||||
Color::LightMagenta | Color::LightPurple => "bright-magenta".to_string(),
|
||||
Color::LightGray => "bright-white".to_string(),
|
||||
Color::Fixed(1) => "dark-red".to_string(),
|
||||
Color::Fixed(2) => "dark-green".to_string(),
|
||||
Color::Fixed(3) => "dark-yellow".to_string(),
|
||||
Color::Fixed(4) => "dark-blue".to_string(),
|
||||
Color::Fixed(5) => "dark-magenta".to_string(),
|
||||
Color::Fixed(6) => "dark-cyan".to_string(),
|
||||
Color::Fixed(7) => "grey".to_string(),
|
||||
Color::Fixed(8) => "dark-grey".to_string(),
|
||||
Color::Rgb(r, g, b) => format!("#{r:x}{g:x}{b:X}"),
|
||||
Color::Fixed(ansi_color_num) => format!("{ansi_color_num}"),
|
||||
Color::Default => unreachable!(),
|
||||
};
|
||||
write!(f, "{val}")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<comfy_table::Color> for LimboColor {
|
||||
fn from(value: comfy_table::Color) -> Self {
|
||||
let color = match value {
|
||||
comfy_table::Color::Rgb { r, g, b } => Color::Rgb(r, g, b),
|
||||
comfy_table::Color::AnsiValue(ansi_color_num) => Color::Fixed(ansi_color_num),
|
||||
comfy_table::Color::Black => Color::Black,
|
||||
comfy_table::Color::Red => Color::Red,
|
||||
comfy_table::Color::Green => Color::Green,
|
||||
comfy_table::Color::Yellow => Color::Yellow,
|
||||
comfy_table::Color::Blue => Color::Blue,
|
||||
comfy_table::Color::Cyan => Color::Cyan,
|
||||
comfy_table::Color::Magenta => Color::Magenta,
|
||||
comfy_table::Color::White => Color::White,
|
||||
comfy_table::Color::DarkRed => Color::Fixed(1),
|
||||
comfy_table::Color::DarkGreen => Color::Fixed(2),
|
||||
comfy_table::Color::DarkYellow => Color::Fixed(3),
|
||||
comfy_table::Color::DarkBlue => Color::Fixed(4),
|
||||
comfy_table::Color::DarkMagenta => Color::Fixed(5),
|
||||
comfy_table::Color::DarkCyan => Color::Fixed(6),
|
||||
comfy_table::Color::Grey => Color::Fixed(7),
|
||||
comfy_table::Color::DarkGrey => Color::Fixed(8),
|
||||
comfy_table::Color::Reset => unreachable!(), // Should never have Reset Color here
|
||||
};
|
||||
LimboColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for LimboColor {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct LimboColorVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for LimboColorVisitor {
|
||||
type Value = LimboColor;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("struct LimboColor")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
LimboColor::try_from(v).map_err(de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(LimboColorVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonSchema for LimboColor {
|
||||
fn schema_name() -> String {
|
||||
"LimboColor".into()
|
||||
}
|
||||
|
||||
fn schema_id() -> std::borrow::Cow<'static, str> {
|
||||
// Include the module, in case a type with the same name is in another module/crate
|
||||
std::borrow::Cow::Borrowed(concat!(module_path!(), "::LimboColor"))
|
||||
}
|
||||
|
||||
fn json_schema(generator: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
||||
generator.subschema_for::<LimboColor>()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for LimboColor {
|
||||
type Target = Color;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for LimboColor {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Validate for LimboColor {
|
||||
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl LimboColor {
|
||||
pub fn into_comfy_table_color(&self) -> comfy_table::Color {
|
||||
match self.0 {
|
||||
Color::Black => comfy_table::Color::Black,
|
||||
Color::Red => comfy_table::Color::Red,
|
||||
Color::Green => comfy_table::Color::Green,
|
||||
Color::Yellow => comfy_table::Color::Yellow,
|
||||
Color::Blue => comfy_table::Color::Blue,
|
||||
Color::Magenta | Color::Purple => comfy_table::Color::Magenta,
|
||||
Color::Cyan => comfy_table::Color::Cyan,
|
||||
Color::White | Color::Default => comfy_table::Color::White,
|
||||
Color::Fixed(1) => comfy_table::Color::DarkRed,
|
||||
Color::Fixed(2) => comfy_table::Color::DarkGreen,
|
||||
Color::Fixed(3) => comfy_table::Color::DarkYellow,
|
||||
Color::Fixed(4) => comfy_table::Color::DarkBlue,
|
||||
Color::Fixed(5) => comfy_table::Color::DarkMagenta,
|
||||
Color::Fixed(6) => comfy_table::Color::DarkCyan,
|
||||
Color::Fixed(7) => comfy_table::Color::Grey,
|
||||
Color::Fixed(8) => comfy_table::Color::DarkGrey,
|
||||
Color::DarkGray => comfy_table::Color::AnsiValue(241),
|
||||
Color::LightRed => comfy_table::Color::AnsiValue(09),
|
||||
Color::LightGreen => comfy_table::Color::AnsiValue(10),
|
||||
Color::LightYellow => comfy_table::Color::AnsiValue(11),
|
||||
Color::LightBlue => comfy_table::Color::AnsiValue(12),
|
||||
Color::LightMagenta | Color::LightPurple => comfy_table::Color::AnsiValue(13),
|
||||
Color::LightCyan => comfy_table::Color::AnsiValue(14),
|
||||
Color::LightGray => comfy_table::Color::AnsiValue(15),
|
||||
Color::Rgb(r, g, b) => comfy_table::Color::Rgb { r, g, b },
|
||||
Color::Fixed(ansi_color_num) => comfy_table::Color::AnsiValue(ansi_color_num),
|
||||
}
|
||||
}
|
||||
}
|
||||
110
cli/docs/config.md
Normal file
110
cli/docs/config.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Config
|
||||
|
||||
Config folder should be located at `$HOME/.config/limbo`. The config file inside should be named `limbo.toml`. Optionally you can have a `themes` folder whithin to store `.tmTheme` files to be discovered by the CLI on startup.
|
||||
|
||||
Describes the Limbo Config file for the CLI\
|
||||
|
||||
**Note**: Colors can be inputted as
|
||||
- Rrggbb string -> `"#010101"`
|
||||
- Rgb string -> `"#A3F"`
|
||||
- 256 Ansi Color -> `"100"`
|
||||
- Predefined Color Names:
|
||||
- `"black"`
|
||||
- `"red"`
|
||||
- `"green"`
|
||||
- `"yellow"`
|
||||
- `"blue"`
|
||||
- `"purple"`
|
||||
- `"cyan"`
|
||||
- `"magenta"`
|
||||
- `"white"`
|
||||
- `"grey"`
|
||||
- `"bright-black"`
|
||||
- `"bright-red"`
|
||||
- `"bright-green"`
|
||||
- `"bright-yellow"`
|
||||
- `"bright-blue"`
|
||||
- `"bright-cyan"`
|
||||
- `"bright-magenta"`
|
||||
- `"bright-white"`
|
||||
- `"dark-red"`
|
||||
- `"dark-green"`
|
||||
- `"dark-yellow"`
|
||||
- `"dark-blue"`
|
||||
- `"dark-magenta"`
|
||||
- `"dark-cyan"`
|
||||
- `"dark-grey"`
|
||||
|
||||
## `table`
|
||||
|
||||
### `column_colors`
|
||||
**Type**: `List[Color]`\
|
||||
*Example*: `["cyan"]`
|
||||
|
||||
### `header_color`
|
||||
**Type**: `Color`\
|
||||
*Example*: `"red"`
|
||||
|
||||
## `highlight`
|
||||
|
||||
### `enable`
|
||||
**Type**: `bool`\
|
||||
*Example*: `true`
|
||||
|
||||
### `theme`
|
||||
**Type**: `String`\
|
||||
*Example*: `"base16-ocean.dark"`
|
||||
|
||||
Preloaded themes:
|
||||
- `base16-ocean.dark`
|
||||
- `base16-eighties.dark`
|
||||
- `base16-mocha.dark`
|
||||
- `base16-ocean.light`
|
||||
|
||||
You can reference a custom theme in your `themes` folder directly by name from the config file.
|
||||
|
||||
*Example*:
|
||||
|
||||
Folder structure
|
||||
|
||||
```
|
||||
limbo
|
||||
├── limbo.toml
|
||||
└── themes
|
||||
└── Amy.tmTheme
|
||||
```
|
||||
|
||||
`limbo.toml`
|
||||
|
||||
```toml
|
||||
[highlight]
|
||||
theme = "Amy"
|
||||
```
|
||||
|
||||
### `prompt`
|
||||
**Type**: `Color`\
|
||||
*Example*: `"green"`
|
||||
|
||||
### `hint`
|
||||
**Type**: `Color`\
|
||||
*Example*: `"grey"`
|
||||
|
||||
### `candidate`
|
||||
**Type**: `Color`\
|
||||
*Example*: `"yellow"`
|
||||
|
||||
## Example `limbo.toml`
|
||||
|
||||
```toml
|
||||
[table]
|
||||
column_colors = ["cyan", "black", "#010101"]
|
||||
header_color = "red"
|
||||
|
||||
[highlight]
|
||||
enable = true
|
||||
prompt = "bright-blue"
|
||||
theme = "base16-ocean.light"
|
||||
hint = "123"
|
||||
candidate = "dark-yellow"
|
||||
```
|
||||
|
||||
@@ -11,8 +11,14 @@ use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
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::config::{HighlightConfig, CONFIG_DIR};
|
||||
|
||||
macro_rules! try_result {
|
||||
($expr:expr, $err:expr) => {
|
||||
@@ -27,14 +33,37 @@ macro_rules! try_result {
|
||||
pub struct LimboHelper {
|
||||
#[rustyline(Completer)]
|
||||
completer: SqlCompleter<CommandParser>,
|
||||
syntax_set: SyntaxSet,
|
||||
theme_set: ThemeSet,
|
||||
syntax_config: HighlightConfig,
|
||||
#[rustyline(Hinter)]
|
||||
hinter: HistoryHinter,
|
||||
}
|
||||
|
||||
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 {
|
||||
completer: SqlCompleter::new(conn, io),
|
||||
syntax_set: ps,
|
||||
theme_set: ts,
|
||||
syntax_config: syntax_config.unwrap_or_default(),
|
||||
hinter: HistoryHinter::new(),
|
||||
}
|
||||
}
|
||||
@@ -43,9 +72,37 @@ 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())
|
||||
if self.syntax_config.enable {
|
||||
// TODO use lifetimes to store highlight lines
|
||||
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>(
|
||||
@@ -55,13 +112,13 @@ impl Highlighter for LimboHelper {
|
||||
) -> std::borrow::Cow<'b, str> {
|
||||
let _ = default;
|
||||
// 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);
|
||||
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().fg(self.syntax_config.hint.0); // Brighter dark grey for hints
|
||||
let styled_str = style.paint(hint);
|
||||
std::borrow::Cow::Owned(styled_str.to_string())
|
||||
}
|
||||
@@ -72,7 +129,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(self.syntax_config.candidate.0);
|
||||
let styled_str = style.paint(candidate);
|
||||
std::borrow::Cow::Owned(styled_str.to_string())
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#![allow(clippy::arc_with_non_send_sync)]
|
||||
mod app;
|
||||
mod commands;
|
||||
mod config;
|
||||
mod helper;
|
||||
mod input;
|
||||
mod opcodes_dictionary;
|
||||
|
||||
use config::CONFIG_DIR;
|
||||
use rustyline::{error::ReadlineError, Config, Editor};
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
@@ -32,6 +34,12 @@ fn main() -> anyhow::Result<()> {
|
||||
if HISTORY_FILE.exists() {
|
||||
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);
|
||||
} else {
|
||||
tracing::debug!("not in tty");
|
||||
|
||||
Reference in New Issue
Block a user