feat: Use metatables to use less memory (#152)

Instead of storing everything in key / value pair hashes, we store as much as possible in an array and then reference the items in the array using metatables.

This provides us with a much lower memory footprint and just one level of table lookup indirection (so I think the speed will not be noticeably different)
This commit is contained in:
TJ DeVries
2020-10-07 11:35:38 -04:00
committed by GitHub
parent d32d4a6e0f
commit 2053a2621a
2 changed files with 147 additions and 90 deletions

View File

@@ -7,11 +7,6 @@ local get_default = utils.get_default
local make_entry = {} local make_entry = {}
make_entry.types = {
GENERIC = 0,
FILE = 1,
}
local transform_devicons local transform_devicons
if has_devicons then if has_devicons then
transform_devicons = function(filename, display, disable_devicons) transform_devicons = function(filename, display, disable_devicons)
@@ -29,88 +24,82 @@ else
end end
end end
function make_entry.gen_from_string() do
return function(line) local lookup_keys = {
return { display = 1,
valid = line ~= "", ordinal = 1,
entry_type = make_entry.types.GENERIC, value = 1,
value = line,
ordinal = line,
display = line,
} }
local mt_string_entry = {
__index = function(t, k)
return rawget(t, rawget(lookup_keys, k))
end
}
function make_entry.gen_from_string()
return function(line)
return setmetatable({
line,
}, mt_string_entry)
end
end end
end end
function make_entry.gen_from_file(opts) do
-- local opts = vim.deepcopy(init_opts or {}) local lookup_keys = {
ordinal = 1,
value = 1,
filename = 1,
cwd = 2,
}
function make_entry.gen_from_file(opts)
opts = opts or {} opts = opts or {}
local cwd = vim.fn.expand(opts.cwd or vim.fn.getcwd()) local cwd = vim.fn.expand(opts.cwd or vim.fn.getcwd())
local disable_devicons = opts.disable_devicons local disable_devicons = opts.disable_devicons
local shorten_path = opts.shorten_path local shorten_path = opts.shorten_path
local make_display = function(line) local mt_file_entry = {}
local display = line
mt_file_entry.cwd = cwd
mt_file_entry.display = function(entry)
local display = entry.value
if shorten_path then if shorten_path then
display = utils.path_shorten(line) display = utils.path_shorten(display)
end end
display = transform_devicons(line, display, disable_devicons) return transform_devicons(entry.value, display, disable_devicons)
end
return display mt_file_entry.__index = function(t, k)
local raw = rawget(mt_file_entry, k)
if raw then return raw end
if k == "path" then
return t.cwd .. path.separator .. t.value
end
return rawget(t, rawget(lookup_keys, k))
end end
return function(line) return function(line)
local entry = { return setmetatable({line}, mt_file_entry)
ordinal = line, end
value = line,
entry_type = make_entry.types.FILE,
filename = line,
path = cwd .. utils.get_separator() .. line,
}
entry.display = make_display(line)
return entry
end end
end end
function make_entry.gen_from_vimgrep(opts) do
opts = opts or {} local lookup_keys = {
value = 1,
ordinal = 1,
}
local display_string = "%s:%s%s" -- Gets called only once to parse everything out for the vimgrep, after that looks up directly.
local parse = function(t)
local make_display = function(entry) local _, _, filename, lnum, col, text = string.find(t.value, [[([^:]+):(%d+):(%d+):(.*)]])
local display = entry.value
local display_filename
if opts.shorten_path then
display_filename = utils.path_shorten(entry.filename)
else
display_filename = entry.filename
end
local coordinates = ""
if not opts.disable_coordinates then
coordinates = string.format("%s:%s:", entry.lnum, entry.col)
end
display = transform_devicons(
entry.filename,
string.format(display_string, display_filename, coordinates, entry.text),
opts
)
return display
end
return function(line)
-- TODO: Consider waiting to do this string.find
-- TODO: Is this the fastest way to get each of these?
-- Or could we just walk the text and check for colons faster?
local _, _, filename, lnum, col, text = string.find(line, [[([^:]+):(%d+):(%d+):(.*)]])
local ok local ok
ok, lnum = pcall(tonumber, lnum) ok, lnum = pcall(tonumber, lnum)
@@ -119,19 +108,89 @@ function make_entry.gen_from_vimgrep(opts)
ok, col = pcall(tonumber, col) ok, col = pcall(tonumber, col)
if not ok then col = nil end if not ok then col = nil end
return { t.filename = filename
valid = line ~= "", t.lnum = lnum
t.col = col
t.text = text
value = line, return {filename, lnum, col, text}
ordinal = line, end
display = make_display,
entry_type = make_entry.types.FILE, local execute_keys = {
filename = filename, path = function(t)
lnum = lnum, return t.cwd .. path.separator .. t.filename, false
col = col, end,
text = text,
filename = function(t)
return parse(t)[1], true
end,
lnum = function(t)
return parse(t)[2], true
end,
col = function(t)
return parse(t)[3], true
end,
text = function(t)
return parse(t)[4], true
end,
} }
function make_entry.gen_from_vimgrep(opts)
opts = opts or {}
local shorten_path = opts.shorten_path
local disable_coordinates = opts.disable_coordinates
local disable_devicons = opts.disable_devicons
local display_string = "%s:%s%s"
local mt_vimgrep_entry = {}
mt_vimgrep_entry.cwd = vim.fn.expand(opts.cwd or vim.fn.getcwd())
mt_vimgrep_entry.display = function(entry)
local display = entry.value
local display_filename
if shorten_path then
display_filename = utils.path_shorten(entry.filename)
else
display_filename = entry.filename
end
local coordinates = ""
if not disable_coordinates then
coordinates = string.format("%s:%s:", entry.lnum, entry.col)
end
display = transform_devicons(
entry.filename,
string.format(display_string, display_filename, coordinates, entry.text),
disable_devicons
)
return display
end
mt_vimgrep_entry.__index = function(t, k)
local raw = rawget(mt_vimgrep_entry, k)
if raw then return raw end
local executor = rawget(execute_keys, k)
if executor then
local val, save = executor(t)
if save then rawset(t, k, val) end
return val
end
return rawget(t, rawget(lookup_keys, k))
end
return function(line)
return setmetatable({line}, mt_vimgrep_entry)
end
end end
end end
@@ -320,10 +379,7 @@ function make_entry.gen_from_tagfile(opts)
end end
return function(line) return function(line)
local entry = { local entry = {}
entry_type = make_entry.types.GENERIC,
}
local d = make_display(line) local d = make_display(line)
entry.valid = next(d) ~= nil entry.valid = next(d) ~= nil
entry.display = d.display entry.display = d.display
@@ -338,8 +394,8 @@ function make_entry.gen_from_packages(opts)
opts = opts or {} opts = opts or {}
local make_display = function(module_name) local make_display = function(module_name)
local path = package.searchpath(module_name, package.path) or "" local p_path = package.searchpath(module_name, package.path) or ""
local display = string.format("%-" .. opts.column_len .. "s : %s", module_name, vim.fn.fnamemodify(path, ":~:.")) local display = string.format("%-" .. opts.column_len .. "s : %s", module_name, vim.fn.fnamemodify(p_path, ":~:."))
return display return display
end end

View File

@@ -1,9 +1,10 @@
-- require('plenary.reload').reload_module('telescope') require('plenary.reload').reload_module('telescope')
local finders = require('telescope.finders') local finders = require('telescope.finders')
local pickers = require('telescope.pickers') local pickers = require('telescope.pickers')
local sorters = require('telescope.sorters') local sorters = require('telescope.sorters')
local previewers = require('telescope.previewers') local previewers = require('telescope.previewers')
local make_entry = require('telescope.make_entry')
local my_list = { local my_list = {
'lua/telescope/WIP.lua', 'lua/telescope/WIP.lua',