Feature: LRU cache for search result

neovim_0_5
ray-x 3 years ago
parent d39cf20903
commit 940349759b

@ -84,6 +84,8 @@ in the same line). Using treesitter for file preview highlighter etc
- LSP Code Action, Code Lens, Code lens action
- LRU cache for treesitter nodes
# Why a new plugin
I'd like to go beyond what the system is providing.

@ -127,7 +127,7 @@ function M.new_list_view(opts)
if opts.rawdata then
data = items
else
log(items)
trace(items)
data = require"navigator.render".prepare_for_render(items, opts)
end

@ -0,0 +1,150 @@
-- lua-lru, LRU cache in Lua
-- Copyright (c) 2015 Boris Nagaev
-- See the LICENSE file for terms of use.
local lru = {}
function lru.new(max_size, max_bytes)
assert(max_size >= 1, "max_size must be >= 1")
assert(not max_bytes or max_bytes >= 1, "max_bytes must be >= 1")
-- current size
local size = 0
local bytes_used = 0
-- map is a hash map from keys to tuples
-- tuple: value, prev, next, key
-- prev and next are pointers to tuples
local map = {}
-- indices of tuple
local VALUE = 1
local PREV = 2
local NEXT = 3
local KEY = 4
local BYTES = 5
-- newest and oldest are ends of double-linked list
local newest = nil -- first
local oldest = nil -- last
local removed_tuple -- created in del(), removed in set()
-- remove a tuple from linked list
local function cut(tuple)
local tuple_prev = tuple[PREV]
local tuple_next = tuple[NEXT]
tuple[PREV] = nil
tuple[NEXT] = nil
if tuple_prev and tuple_next then
tuple_prev[NEXT] = tuple_next
tuple_next[PREV] = tuple_prev
elseif tuple_prev then
-- tuple is the oldest element
tuple_prev[NEXT] = nil
oldest = tuple_prev
elseif tuple_next then
-- tuple is the newest element
tuple_next[PREV] = nil
newest = tuple_next
else
-- tuple is the only element
newest = nil
oldest = nil
end
end
-- insert a tuple to the newest end
local function setNewest(tuple)
if not newest then
newest = tuple
oldest = tuple
else
tuple[NEXT] = newest
newest[PREV] = tuple
newest = tuple
end
end
local function del(key, tuple)
map[key] = nil
cut(tuple)
size = size - 1
bytes_used = bytes_used - (tuple[BYTES] or 0)
removed_tuple = tuple
end
-- removes elemenets to provide enough memory
-- returns last removed element or nil
local function makeFreeSpace(bytes)
while size + 1 > max_size or (max_bytes and bytes_used + bytes > max_bytes) do
assert(oldest, "not enough storage for cache")
del(oldest[KEY], oldest)
end
end
local function get(_, key)
local tuple = map[key]
if not tuple then
return nil
end
cut(tuple)
setNewest(tuple)
return tuple[VALUE]
end
local function set(_, key, value, bytes)
local tuple = map[key]
if tuple then
del(key, tuple)
end
if value ~= nil then
-- the value is not removed
bytes = max_bytes and (bytes or #value) or 0
makeFreeSpace(bytes)
local tuple1 = removed_tuple or {}
map[key] = tuple1
tuple1[VALUE] = value
tuple1[KEY] = key
tuple1[BYTES] = max_bytes and bytes
size = size + 1
bytes_used = bytes_used + bytes
setNewest(tuple1)
else
assert(key ~= nil, "Key may not be nil")
end
removed_tuple = nil
end
local function delete(_, key)
return set(_, key, nil)
end
local function mynext(_, prev_key)
local tuple
if prev_key then
tuple = map[prev_key][NEXT]
else
tuple = newest
end
if tuple then
return tuple[KEY], tuple[VALUE]
else
return nil
end
end
-- returns iterator for keys and values
local function lru_pairs()
return mynext, nil, nil
end
local mt = {
__index = {get = get, set = set, delete = delete, pairs = lru_pairs},
__pairs = lru_pairs
}
return setmetatable({}, mt)
end
return lru

@ -1,4 +1,5 @@
local M = {}
local util = require "navigator.util"
local gutil = require "guihua.util"
local lsp = require "vim.lsp"
@ -14,9 +15,8 @@ local is_win = vim.loop.os_uname().sysname:find("Windows")
local path_sep = require"navigator.util".path_sep()
local path_cur = require"navigator.util".path_cur()
cwd = gutil.add_pec(cwd)
ts_nodes = {}
ts_nodes_time = {}
local ts_nodes = require('navigator.lru').new(1000, 1024 * 1024)
local ts_nodes_time = require('navigator.lru').new(1000)
local TS_analysis_enabled = require"navigator".config_values().treesitter_analysis
-- extract symbol from range
@ -167,13 +167,18 @@ local function ts_functions(uri)
local bufnr = vim.uri_to_bufnr(uri)
local x = os.clock()
trace(ts_nodes)
if ts_nodes[uri] ~= nil then
local t = ts_nodes_time[uri]
local tsnodes = ts_nodes:get(uri)
if tsnodes ~= nil then
log("get data from cache")
local t = ts_nodes_time:get(uri) or 0
local fname = vim.uri_to_fname(uri)
local modified = vim.fn.getftime(fname)
if modified <= t then
trace(t, modified)
return ts_nodes[uri]
return tsnodes
else
ts_nodes:delete(uri)
ts_nodes_time:delete(uri)
end
end
local unload = false
@ -189,9 +194,9 @@ local function ts_functions(uri)
trace(cmd)
-- vim.cmd(cmd) -- todo: not sure if it is needed
end
ts_nodes[uri] = funcs
ts_nodes_time[uri] = os.time()
trace(funcs, ts_nodes)
ts_nodes:set(uri, funcs)
ts_nodes_time:set(uri, os.time())
trace(funcs, ts_nodes:get(uri))
trace(string.format("elapsed time: %.4f\n", os.clock() - x)) -- how long it tooks
return funcs
end
@ -261,7 +266,7 @@ function M.locations_to_items(locations)
end)
local uri_def = {}
log(locations)
trace(locations)
for i, loc in ipairs(locations) do
local funcs = nil
local item = lsp.util.locations_to_items({loc})[1]

@ -2,6 +2,7 @@
-- to fit in navigator.lua
local gui = require "navigator.gui"
local fn = vim.fn
local lru = require('navigator.lru').new(500, 1024 * 1024)
local ok, ts_locals = pcall(require, "nvim-treesitter.locals")
@ -235,19 +236,41 @@ function M.goto_previous_usage(bufnr)
return M.goto_adjacent_usage(bufnr, -1)
end
local function key(fname, filter)
return fname .. vim.inspect(filter)
end
local function get_all_nodes(bufnr, filter, summary)
trace(bufnr, filter, summary)
bufnr = bufnr or 0
summary = summary or false
if not parsers.has_parser() then
print("ts not loaded")
end
local fname = vim.fn.expand("%:p:f")
local uri = vim.uri_from_fname(fname)
if bufnr ~= 0 then
uri = vim.uri_from_bufnr(bufnr)
fname = vim.uri_to_fname(uri)
end
local ftime = vim.fn.getftime(fname)
local hash = key(fname, filter)
local result = lru:get(hash)
if result ~= nil and result.ftime == ftime then
log("get data from cache")
return result.nodes, result.length
end
if result ~= nil and result.ftime ~= ftime then
lru:delete(hash)
end
trace(bufnr, filter, summary)
if not bufnr then
print("get_all_node invalide bufnr")
end
summary = summary or false
if not parsers.has_parser() then
print("ts not loaded")
end
path_sep = require"navigator.util".path_sep()
path_cur = require"navigator.util".path_cur()
local display_filename = fname:gsub(cwd .. path_sep, path_cur, 1)
@ -366,6 +389,8 @@ local function get_all_nodes(bufnr, filter, summary)
end
end
trace(all_nodes)
local nd = {nodes = all_nodes, ftime = vim.fn.getftime(fname), length = length}
lru:set(hash, nd)
return all_nodes, length
end

Loading…
Cancel
Save