telescope-frecency.nvim/lua/telescope/_extensions/frecency/sql_wrapper.lua
tami5 cf3a1deb9c
follow sqlite new release (#40)
* refactor(sql_wrapper): follow sqlite new release

* update readme
2021-09-01 08:36:12 +03:00

177 lines
5.1 KiB
Lua

local util = require("telescope._extensions.frecency.util")
local vim = vim
local has_sqlite, sqlite = pcall(require, "sqlite")
if not has_sqlite then
error("This plugin requires sqlite.lua (https://github.com/tami5/sqlite.lua) " .. tostring(sqlite))
end
-- TODO: pass in max_timestamps from db.lua
local MAX_TIMESTAMPS = 10
local db_table = {}
db_table.files = "files"
db_table.timestamps = "timestamps"
db_table.workspaces = "workspaces"
--
-- TODO: NEXT!
-- extend substr sorter to have modes:
-- when current string is prefixed by `:foo`, results are tag_names that come from tags/workspaces table. (if `:foo ` token is incomplete it is ignored)
-- when a complete workspace tag is matched ':foobar:', results are indexed_files filtered by if their parent_dir is a descendant of the workspace_dir
-- a recursive scan_dir() result is added to the :foobar: filter results; any non-indexed_files are given a score of zero, and are alphabetically sorted below the indexed_results
-- make tab completion for tab_names in insert mode`:foo|` state: cycles through available options
local M = {}
function M:new()
local o = {}
setmetatable(o, self)
self.__index = self
self.db = nil
return o
end
function M:bootstrap(db_root)
if self.db then return end
-- opts = opts or {}
-- self.max_entries = opts.max_entries or 2000
-- create the db if it doesn't exist
db_root = db_root or vim.fn.stdpath('data')
local db_filename = db_root .. "/file_frecency.sqlite3"
self.db = sqlite:open(db_filename)
if not self.db then
print("error")
return
end
local first_run = false
if not self.db:exists(db_table.files) then
first_run = true
-- create tables if they don't exist
self.db:create(db_table.files, {
id = {"INTEGER", "PRIMARY", "KEY"},
count = "INTEGER",
path = "TEXT"
})
self.db:create(db_table.timestamps, {
id = {"INTEGER", "PRIMARY", "KEY"},
file_id = "INTEGER",
timestamp = "REAL"
-- FOREIGN KEY(file_id) REFERENCES files(id)
})
end
self.db:close()
return first_run
end
--
function M:do_transaction(t, params)
-- print(vim.inspect(t))
-- print(vim.inspect(params))
return self.db:with_open(function(db)
local case = {
[1] = function() return db:select(t.cmd_data, params) end,
[2] = function() return db:insert(t.cmd_data, params) end,
[3] = function() return db:delete(t.cmd_data, params) end,
[4] = function() return db:eval(t.cmd_data, params) end,
}
return case[t.cmd]()
end)
end
local cmd = {
select = 1,
insert = 2,
delete = 3,
eval = 4,
}
local queries = {
file_get_descendant_of = {
cmd = cmd.eval,
cmd_data = "SELECT * FROM files WHERE path LIKE :path"
},
file_add_entry = {
cmd = cmd.insert,
cmd_data = db_table.files
},
file_delete_entry = {
cmd = cmd.delete,
cmd_data = db_table.files
},
file_get_entries = {
cmd = cmd.select,
cmd_data = db_table.files
},
file_update_counter = {
cmd = cmd.eval,
cmd_data = "UPDATE files SET count = count + 1 WHERE path == :path;"
},
timestamp_add_entry = {
cmd = cmd.eval,
cmd_data = "INSERT INTO timestamps (file_id, timestamp) values(:file_id, julianday('now'));"
},
timestamp_delete_entry = {
cmd = cmd.delete,
cmd_data = db_table.timestamps
},
timestamp_get_all_entries = {
cmd = cmd.select,
cmd_data = db_table.timestamps,
},
timestamp_get_all_entry_ages = {
cmd = cmd.eval,
cmd_data = "SELECT id, file_id, CAST((julianday('now') - julianday(timestamp)) * 24 * 60 AS INTEGER) AS age FROM timestamps;"
},
timestamp_delete_before_id = {
cmd = cmd.eval,
cmd_data = "DELETE FROM timestamps WHERE id < :id and file_id == :file_id;"
},
}
M.queries = queries
--
local function row_id(entry)
return (not vim.tbl_isempty(entry)) and entry[1].id or nil
end
function M:update(filepath)
local filestat = util.fs_stat(filepath)
if (vim.tbl_isempty(filestat) or
filestat.exists == false or
filestat.isdirectory == true) then
return end
-- create entry if it doesn't exist
local file_id
file_id = row_id(self:do_transaction(queries.file_get_entries, {where = {path = filepath}}))
if not file_id then
self:do_transaction(queries.file_add_entry, {path = filepath, count = 1})
file_id = row_id(self:do_transaction(queries.file_get_entries, {where = {path = filepath}}))
else
-- ..or update existing entry
self:do_transaction(queries.file_update_counter, {path = filepath})
end
-- register timestamp for this update
self:do_transaction(queries.timestamp_add_entry, {file_id = file_id})
-- trim timestamps to MAX_TIMESTAMPS per file (there should be up to MAX_TS + 1 at this point)
local timestamps = self:do_transaction(queries.timestamp_get_all_entries, {where = {file_id = file_id}})
local trim_at = timestamps[(#timestamps - MAX_TIMESTAMPS) + 1]
if trim_at then
self:do_transaction(queries.timestamp_delete_before_id, {id = trim_at.id, file_id = file_id})
end
end
return M