From ca574651d99bb85be4507cf340f0b227e807314d Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Fri, 21 Feb 2025 01:45:45 -0300 Subject: [PATCH] wip --- Cargo.lock | 8 ++ Cargo.toml | 3 +- extensions/completion/Cargo.toml | 19 +++ extensions/completion/src/keywords.rs | 138 ++++++++++++++++++++++ extensions/completion/src/lib.rs | 164 ++++++++++++++++++++++++++ 5 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 extensions/completion/Cargo.toml create mode 100644 extensions/completion/src/keywords.rs create mode 100644 extensions/completion/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 28046068d..5ac0b6cb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1620,6 +1620,14 @@ dependencies = [ "rustyline", ] +[[package]] +name = "limbo_completion" +version = "0.0.15" +dependencies = [ + "limbo_ext", + "mimalloc", +] + [[package]] name = "limbo_core" version = "0.0.15" diff --git a/Cargo.toml b/Cargo.toml index f555f726f..6bfa52177 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,8 @@ members = [ "bindings/rust", "bindings/wasm", "cli", - "core", + "core", + "extensions/completion", "extensions/core", "extensions/crypto", "extensions/kvstore", diff --git a/extensions/completion/Cargo.toml b/extensions/completion/Cargo.toml new file mode 100644 index 000000000..32b350871 --- /dev/null +++ b/extensions/completion/Cargo.toml @@ -0,0 +1,19 @@ +[package] +authors.workspace = true +edition.workspace = true +license.workspace = true +name = "limbo_completion" +repository.workspace = true +version.workspace = true + +[dependencies] +limbo_ext = { path = "../core", features = ["static"] } + +[target.'cfg(not(target_family = "wasm"))'.dependencies] +mimalloc = { version = "0.1", default-features = false } + +[lib] +crate-type = ["cdylib", "lib"] + +[features] +static = ["limbo_ext/static"] diff --git a/extensions/completion/src/keywords.rs b/extensions/completion/src/keywords.rs new file mode 100644 index 000000000..c92207464 --- /dev/null +++ b/extensions/completion/src/keywords.rs @@ -0,0 +1,138 @@ +pub(crate) static KEYWORDS: [&str; 136] = [ + "ABORT", + "ACTION", + "ADD", + "AFTER", + "ALL", + "ALTER", + "ANALYZE", + "AND", + "AS", + "ASC", + "ATTACH", + "AUTOINCREMENT", + "BEFORE", + "BEGIN", + "BETWEEN", + "BY", + "CASCADE", + "CASE", + "CAST", + "CHECK", + "COLLATE", + "COLUMN", + "COMMIT", + "CONFLICT", + "CONSTRAINT", + "CREATE", + "CROSS", + "CURRENT", + "CURRENT_DATE", + "CURRENT_TIME", + "CURRENT_TIMESTAMP", + "DATABASE", + "DEFAULT", + "DEFERRABLE", + "DEFERRED", + "DELETE", + "DESC", + "DETACH", + "DISTINCT", + "DO", + "DROP", + "EACH", + "ELSE", + "END", + "ESCAPE", + "EXCEPT", + "EXCLUSIVE", + "EXISTS", + "EXPLAIN", + "FAIL", + "FILTER", + "FOLLOWING", + "FOR", + "FOREIGN", + "FROM", + "FULL", + "GLO", + "GROUP", + "HAVING", + "IF", + "IGNORE", + "IMMEDIATE", + "IN", + "INDEX", + "INDEXED", + "INITIALLY", + "INNER", + "INSERT", + "INSTEAD", + "INTERSECT", + "INTO", + "IS", + "ISNULL", + "JOIN", + "KEY", + "LEFT", + "LIKE", + "LIMIT", + "MATCH", + "NATURAL", + "NO", + "NOT", + "NOTHING", + "NOTNULL", + "NULL", + "OF", + "OFFSET", + "ON", + "OR", + "ORDER", + "OUTER", + "OVER", + "PARTITION", + "PLAN", + "PRAGMA", + "PRECEDING", + "PRIMARY", + "QUERY", + "RAISE", + "RANGE", + "RECURSIVE", + "REFERENCES", + "REGEXP", + "REINDEX", + "RELEASE", + "RENAME", + "REPLACE", + "RESTRICT", + "RIGHT", + "ROLLBACK", + "ROW", + "ROWS", + "SAVEPOINT", + "SELECT", + "SET", + "TABLE", + "TEMP", + "TEMPORARY", + "THEN", + "TO", + "TRANSACTION", + "TRIGGER", + "UNBOUNDED", + "UNION", + "UNIQUE", + "UPDATE", + "USING", + "VACUUM", + "VALUES", + "VIEW", + "VIRTUAL", + "WHEN", + "WHERE", + "WINDOW", + "WITH", + "WITHOUT", +]; diff --git a/extensions/completion/src/lib.rs b/extensions/completion/src/lib.rs new file mode 100644 index 000000000..df23c9bc1 --- /dev/null +++ b/extensions/completion/src/lib.rs @@ -0,0 +1,164 @@ +mod keywords; + +use keywords::KEYWORDS; +use limbo_ext::{ + register_extension, ExtensionApi, ResultCode, VTabCursor, VTabModule, VTabModuleDerive, Value, +}; + +register_extension! { + vtabs: { CompletionVTab } +} + +macro_rules! try_option { + ($expr:expr, $err:expr) => { + match $expr { + Some(val) => val, + None => return $err, + } + }; +} + +#[derive(Debug, Default, PartialEq, Clone)] +enum CompletionPhase { + #[default] + FirstPhase = 0, + Keywords = 1, + Pragmas = 2, + Functions = 3, + Collations = 4, + Indexes = 5, + Triggers = 6, + Databases = 7, + Tables = 8, // Also VIEWs and TRIGGERs + Columns = 9, + Modules = 10, + Eof = 11, +} + +impl Into for CompletionPhase { + fn into(self) -> i64 { + use self::CompletionPhase::*; + match self { + FirstPhase => 0, + Keywords => 1, + Pragmas => 2, + Functions => 3, + Collations => 4, + Indexes => 5, + Triggers => 6, + Databases => 7, + Tables => 8, + Columns => 9, + Modules => 10, + Eof => 11, + } + } +} + +/// A virtual table that generates a sequence of integers +#[derive(Debug, VTabModuleDerive)] +struct CompletionVTab {} + +impl VTabModule for CompletionVTab { + type VCursor = CompletionCursor; + const NAME: &'static str = "completion"; + + fn connect(api: &ExtensionApi) -> ResultCode { + // Create table schema + let sql = "CREATE TABLE completion( + candidate TEXT, + prefix TEXT HIDDEN, + wholeline TEXT HIDDEN, + phase INT HIDDEN + )"; + api.declare_virtual_table(Self::NAME, sql) + } + + fn open() -> Self::VCursor { + CompletionCursor::default() + } + + fn column(cursor: &Self::VCursor, idx: u32) -> Value { + cursor.column(idx) + } + + fn next(cursor: &mut Self::VCursor) -> ResultCode { + cursor.next() + } + + fn eof(cursor: &Self::VCursor) -> bool { + cursor.eof() + } + + fn filter(cursor: &mut Self::VCursor, arg_count: i32, args: &[Value]) -> ResultCode { + todo!() + } +} + +/// The cursor for iterating over the generated sequence +#[derive(Debug, Default)] +struct CompletionCursor { + line: String, + prefix: String, + curr_row: String, + rowid: i64, + phase: CompletionPhase, + inter_phase_counter: usize, + // stmt: Statement + // conn: Connection +} + +impl CompletionCursor {} + +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 { + match self.phase { + CompletionPhase::Keywords => { + if self.inter_phase_counter >= KEYWORDS.len() { + self.curr_row.clear(); + self.phase = CompletionPhase::Databases; + } else { + self.inter_phase_counter += 1; + self.curr_row.push_str(KEYWORDS[self.inter_phase_counter]); + } + } + CompletionPhase::Databases => { + // TODO implement this when + // self.stmt = self.conn.prepare("PRAGMA database_list") + curr_col = 1; + next_phase = CompletionPhase::Tables; + } + _ => (), + } + } + ResultCode::OK + } + + fn eof(&self) -> bool { + self.phase == CompletionPhase::Eof + } + + fn column(&self, idx: u32) -> Value { + match idx { + 0 => Value::from_text(self.curr_row.clone()), // COMPLETION_COLUMN_CANDIDATE + 1 => Value::from_text(self.prefix.clone()), // COMPLETION_COLUMN_PREFIX + 2 => Value::from_text(self.line.clone()), // COMPLETION_COLUMN_WHOLELINE + 3 => Value::from_integer(self.phase.clone().into()), // COMPLETION_COLUMN_PHASE + _ => Value::null(), + } + } + + fn rowid(&self) -> i64 { + self.rowid + } +} + +#[cfg(test)] +mod tests {}