mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 18:18:03 +00:00
first version of vtable with keyword autocomplete
This commit is contained in:
parent
ca574651d9
commit
99d979eb80
8 changed files with 89 additions and 18 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1647,6 +1647,7 @@ dependencies = [
|
|||
"julian_day_converter",
|
||||
"libc",
|
||||
"libloading",
|
||||
"limbo_completion",
|
||||
"limbo_crypto",
|
||||
"limbo_ext",
|
||||
"limbo_ipaddr",
|
||||
|
|
|
@ -44,6 +44,7 @@ limbo_regexp = { path = "extensions/regexp", version = "0.0.15" }
|
|||
limbo_series = { path = "extensions/series", version = "0.0.15" }
|
||||
limbo_time = { path = "extensions/time", version = "0.0.15" }
|
||||
limbo_uuid = { path = "extensions/uuid", version = "0.0.15" }
|
||||
limbo_completion = { path = "extensions/completion", version = "0.0.15" }
|
||||
limbo_sqlite3_parser = { path = "vendored/sqlite3-parser", version = "0.0.15" }
|
||||
limbo_ipaddr = { path = "extensions/ipaddr", version = "0.0.15" }
|
||||
# Config for 'cargo dist'
|
||||
|
|
|
@ -24,7 +24,7 @@ clap = { version = "4.5", features = ["derive"] }
|
|||
comfy-table = "7.1.4"
|
||||
dirs = "5.0.1"
|
||||
env_logger = "0.10.1"
|
||||
limbo_core = { path = "../core" }
|
||||
limbo_core = { path = "../core", default-features = true, features = ["completion"]}
|
||||
rustyline = "12.0.0"
|
||||
ctrlc = "3.4.4"
|
||||
csv = "1.3.1"
|
||||
|
|
|
@ -25,6 +25,7 @@ time = ["limbo_time/static"]
|
|||
crypto = ["limbo_crypto/static"]
|
||||
series = ["limbo_series/static"]
|
||||
ipaddr = ["limbo_ipaddr/static"]
|
||||
completion = ["limbo_completion/static"]
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
io-uring = { version = "0.6.1", optional = true }
|
||||
|
@ -64,6 +65,7 @@ limbo_time = { workspace = true, optional = true, features = ["static"] }
|
|||
limbo_crypto = { workspace = true, optional = true, features = ["static"] }
|
||||
limbo_series = { workspace = true, optional = true, features = ["static"] }
|
||||
limbo_ipaddr = { workspace = true, optional = true, features = ["static"] }
|
||||
limbo_completion = { workspace = true, optional = true, features = ["static"] }
|
||||
miette = "7.4.0"
|
||||
strum = "0.26"
|
||||
parking_lot = "0.12.3"
|
||||
|
|
|
@ -154,6 +154,10 @@ impl Database {
|
|||
if unsafe { !limbo_ipaddr::register_extension_static(&ext_api).is_ok() } {
|
||||
return Err("Failed to register ipaddr extension".to_string());
|
||||
}
|
||||
#[cfg(feature = "completion")]
|
||||
if unsafe { !limbo_completion::register_extension_static(&ext_api).is_ok() } {
|
||||
return Err("Failed to register completion extension".to_string());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ repository.workspace = true
|
|||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
limbo_ext = { path = "../core", features = ["static"] }
|
||||
limbo_ext = { workspace = true, features = ["static"] }
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||
mimalloc = { version = "0.1", default-features = false }
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
pub(crate) static KEYWORDS: [&str; 136] = [
|
||||
pub(crate) static KEYWORDS: [&str; 147] = [
|
||||
"ABORT",
|
||||
"ACTION",
|
||||
"ADD",
|
||||
"AFTER",
|
||||
"ALL",
|
||||
"ALTER",
|
||||
"ALWAYS",
|
||||
"ANALYZE",
|
||||
"AND",
|
||||
"AS",
|
||||
|
@ -45,18 +46,22 @@ pub(crate) static KEYWORDS: [&str; 136] = [
|
|||
"END",
|
||||
"ESCAPE",
|
||||
"EXCEPT",
|
||||
"EXCLUDE",
|
||||
"EXCLUSIVE",
|
||||
"EXISTS",
|
||||
"EXPLAIN",
|
||||
"FAIL",
|
||||
"FILTER",
|
||||
"FIRST",
|
||||
"FOLLOWING",
|
||||
"FOR",
|
||||
"FOREIGN",
|
||||
"FROM",
|
||||
"FULL",
|
||||
"GLO",
|
||||
"GENERATED",
|
||||
"GLOB",
|
||||
"GROUP",
|
||||
"GROUPS",
|
||||
"HAVING",
|
||||
"IF",
|
||||
"IGNORE",
|
||||
|
@ -74,21 +79,25 @@ pub(crate) static KEYWORDS: [&str; 136] = [
|
|||
"ISNULL",
|
||||
"JOIN",
|
||||
"KEY",
|
||||
"LAST",
|
||||
"LEFT",
|
||||
"LIKE",
|
||||
"LIMIT",
|
||||
"MATCH",
|
||||
"MATERIALIZED",
|
||||
"NATURAL",
|
||||
"NO",
|
||||
"NOT",
|
||||
"NOTHING",
|
||||
"NOTNULL",
|
||||
"NULL",
|
||||
"NULLS",
|
||||
"OF",
|
||||
"OFFSET",
|
||||
"ON",
|
||||
"OR",
|
||||
"ORDER",
|
||||
"OTHERS",
|
||||
"OUTER",
|
||||
"OVER",
|
||||
"PARTITION",
|
||||
|
@ -107,6 +116,7 @@ pub(crate) static KEYWORDS: [&str; 136] = [
|
|||
"RENAME",
|
||||
"REPLACE",
|
||||
"RESTRICT",
|
||||
"RETURNING",
|
||||
"RIGHT",
|
||||
"ROLLBACK",
|
||||
"ROW",
|
||||
|
@ -118,6 +128,7 @@ pub(crate) static KEYWORDS: [&str; 136] = [
|
|||
"TEMP",
|
||||
"TEMPORARY",
|
||||
"THEN",
|
||||
"TIES",
|
||||
"TO",
|
||||
"TRANSACTION",
|
||||
"TRIGGER",
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
//! Reference for implementation
|
||||
//! https://github.com/sqlite/sqlite/blob/a80089c5167856f0aadc9c878bd65843df724c06/ext/misc/completion.c
|
||||
|
||||
mod keywords;
|
||||
|
||||
use keywords::KEYWORDS;
|
||||
|
@ -21,7 +24,6 @@ macro_rules! try_option {
|
|||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
enum CompletionPhase {
|
||||
#[default]
|
||||
FirstPhase = 0,
|
||||
Keywords = 1,
|
||||
Pragmas = 2,
|
||||
Functions = 3,
|
||||
|
@ -39,7 +41,6 @@ impl Into<i64> for CompletionPhase {
|
|||
fn into(self) -> i64 {
|
||||
use self::CompletionPhase::*;
|
||||
match self {
|
||||
FirstPhase => 0,
|
||||
Keywords => 1,
|
||||
Pragmas => 2,
|
||||
Functions => 3,
|
||||
|
@ -91,7 +92,37 @@ impl VTabModule for CompletionVTab {
|
|||
}
|
||||
|
||||
fn filter(cursor: &mut Self::VCursor, arg_count: i32, args: &[Value]) -> ResultCode {
|
||||
todo!()
|
||||
if arg_count == 0 || arg_count > 2 {
|
||||
return ResultCode::InvalidArgs;
|
||||
}
|
||||
cursor.reset();
|
||||
let prefix = try_option!(args[0].to_text(), ResultCode::InvalidArgs);
|
||||
|
||||
let wholeline = args.get(1).map(|v| v.to_text().unwrap_or("")).unwrap_or("");
|
||||
|
||||
cursor.line = wholeline.to_string();
|
||||
cursor.prefix = prefix.to_string();
|
||||
|
||||
// Currently best index is not implemented so the correct arg parsing is not done here
|
||||
if !cursor.line.is_empty() && cursor.prefix.is_empty() {
|
||||
let mut i = cursor.line.len();
|
||||
while let Some(ch) = cursor.line.chars().next() {
|
||||
if i > 0 && (ch.is_alphanumeric() || ch == '_') {
|
||||
i -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if cursor.line.len() - i > 0 {
|
||||
// TODO see if need to inclusive range
|
||||
cursor.prefix = cursor.line[..i].to_string();
|
||||
}
|
||||
}
|
||||
|
||||
cursor.rowid = 0;
|
||||
cursor.phase = CompletionPhase::Keywords;
|
||||
|
||||
Self::next(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,35 +139,56 @@ struct CompletionCursor {
|
|||
// conn: Connection
|
||||
}
|
||||
|
||||
impl CompletionCursor {}
|
||||
impl CompletionCursor {
|
||||
fn reset(&mut self) {
|
||||
self.line.clear();
|
||||
self.prefix.clear();
|
||||
self.inter_phase_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl VTabCursor for CompletionCursor {
|
||||
type Error = ResultCode;
|
||||
|
||||
fn next(&mut self) -> ResultCode {
|
||||
let mut curr_col = -1 as isize;
|
||||
self.rowid += 1;
|
||||
let mut next_phase = CompletionPhase::FirstPhase;
|
||||
|
||||
while self.phase != CompletionPhase::Eof {
|
||||
// dbg!(&self.phase, &self.prefix, &self.curr_row);
|
||||
match self.phase {
|
||||
CompletionPhase::Keywords => {
|
||||
if self.inter_phase_counter >= KEYWORDS.len() {
|
||||
self.curr_row.clear();
|
||||
self.phase = CompletionPhase::Databases;
|
||||
self.phase = CompletionPhase::Eof;
|
||||
} else {
|
||||
self.inter_phase_counter += 1;
|
||||
self.curr_row.clear();
|
||||
self.curr_row.push_str(KEYWORDS[self.inter_phase_counter]);
|
||||
self.inter_phase_counter += 1;
|
||||
}
|
||||
}
|
||||
CompletionPhase::Databases => {
|
||||
// TODO implement this when
|
||||
// self.stmt = self.conn.prepare("PRAGMA database_list")
|
||||
curr_col = 1;
|
||||
next_phase = CompletionPhase::Tables;
|
||||
|
||||
// CompletionPhase::Databases => {
|
||||
// // TODO implement this when db conn is available
|
||||
// // self.stmt = self.conn.prepare("PRAGMA database_list")
|
||||
// curr_col = 1;
|
||||
// next_phase = CompletionPhase::Tables;
|
||||
// self.phase = CompletionPhase::Eof; // for now skip other phases
|
||||
// }
|
||||
_ => {
|
||||
return ResultCode::EOF;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
if self.prefix.is_empty() {
|
||||
break;
|
||||
}
|
||||
if self.prefix.len() <= self.curr_row.len()
|
||||
&& self.prefix == self.curr_row.to_lowercase()[..self.prefix.len()]
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if self.phase == CompletionPhase::Eof {
|
||||
return ResultCode::EOF;
|
||||
}
|
||||
ResultCode::OK
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue