From 2053a2621a56177da34032a055dffe3f7bb384d1 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Wed, 7 Oct 2020 11:35:38 -0400 Subject: [PATCH] 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) --- lua/telescope/make_entry.lua | 234 +++++++++++++++++----------- lua/tests/manual/newline_tables.lua | 3 +- 2 files changed, 147 insertions(+), 90 deletions(-) diff --git a/lua/telescope/make_entry.lua b/lua/telescope/make_entry.lua index bca2aa1..276f149 100644 --- a/lua/telescope/make_entry.lua +++ b/lua/telescope/make_entry.lua @@ -7,11 +7,6 @@ local get_default = utils.get_default local make_entry = {} -make_entry.types = { - GENERIC = 0, - FILE = 1, -} - local transform_devicons if has_devicons then transform_devicons = function(filename, display, disable_devicons) @@ -29,88 +24,82 @@ else end end -function make_entry.gen_from_string() - return function(line) - return { - valid = line ~= "", - entry_type = make_entry.types.GENERIC, +do + local lookup_keys = { + display = 1, + ordinal = 1, + 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 -function make_entry.gen_from_file(opts) - -- local opts = vim.deepcopy(init_opts or {}) - opts = opts or {} +do + local lookup_keys = { + ordinal = 1, + value = 1, + filename = 1, + cwd = 2, + } - local cwd = vim.fn.expand(opts.cwd or vim.fn.getcwd()) - local disable_devicons = opts.disable_devicons - local shorten_path = opts.shorten_path + function make_entry.gen_from_file(opts) + opts = opts or {} - local make_display = function(line) - local display = line - if shorten_path then - display = utils.path_shorten(line) + local cwd = vim.fn.expand(opts.cwd or vim.fn.getcwd()) + + local disable_devicons = opts.disable_devicons + local shorten_path = opts.shorten_path + + local mt_file_entry = {} + + mt_file_entry.cwd = cwd + mt_file_entry.display = function(entry) + local display = entry.value + if shorten_path then + display = utils.path_shorten(display) + end + + return transform_devicons(entry.value, display, disable_devicons) end - display = transform_devicons(line, display, disable_devicons) + mt_file_entry.__index = function(t, k) + local raw = rawget(mt_file_entry, k) + if raw then return raw end - return display - end + if k == "path" then + return t.cwd .. path.separator .. t.value + end - return function(line) - local entry = { - ordinal = line, - value = line, + return rawget(t, rawget(lookup_keys, k)) + end - entry_type = make_entry.types.FILE, - filename = line, - path = cwd .. utils.get_separator() .. line, - } - - entry.display = make_display(line) - - return entry + return function(line) + return setmetatable({line}, mt_file_entry) + end end end -function make_entry.gen_from_vimgrep(opts) - opts = opts or {} +do + local lookup_keys = { + value = 1, + ordinal = 1, + } - local display_string = "%s:%s%s" - - local make_display = function(entry) - 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+):(.*)]]) + -- Gets called only once to parse everything out for the vimgrep, after that looks up directly. + local parse = function(t) + local _, _, filename, lnum, col, text = string.find(t.value, [[([^:]+):(%d+):(%d+):(.*)]]) local ok ok, lnum = pcall(tonumber, lnum) @@ -119,19 +108,89 @@ function make_entry.gen_from_vimgrep(opts) ok, col = pcall(tonumber, col) if not ok then col = nil end - return { - valid = line ~= "", + t.filename = filename + t.lnum = lnum + t.col = col + t.text = text - value = line, - ordinal = line, - display = make_display, + return {filename, lnum, col, text} + end - entry_type = make_entry.types.FILE, - filename = filename, - lnum = lnum, - col = col, - text = text, - } + local execute_keys = { + path = function(t) + return t.cwd .. path.separator .. t.filename, false + end, + + 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 @@ -320,10 +379,7 @@ function make_entry.gen_from_tagfile(opts) end return function(line) - local entry = { - entry_type = make_entry.types.GENERIC, - - } + local entry = {} local d = make_display(line) entry.valid = next(d) ~= nil entry.display = d.display @@ -338,8 +394,8 @@ function make_entry.gen_from_packages(opts) opts = opts or {} local make_display = function(module_name) - local path = package.searchpath(module_name, package.path) or "" - local display = string.format("%-" .. opts.column_len .. "s : %s", module_name, vim.fn.fnamemodify(path, ":~:.")) + local p_path = package.searchpath(module_name, package.path) or "" + local display = string.format("%-" .. opts.column_len .. "s : %s", module_name, vim.fn.fnamemodify(p_path, ":~:.")) return display end diff --git a/lua/tests/manual/newline_tables.lua b/lua/tests/manual/newline_tables.lua index 2212da9..ec4988e 100644 --- a/lua/tests/manual/newline_tables.lua +++ b/lua/tests/manual/newline_tables.lua @@ -1,9 +1,10 @@ --- require('plenary.reload').reload_module('telescope') +require('plenary.reload').reload_module('telescope') local finders = require('telescope.finders') local pickers = require('telescope.pickers') local sorters = require('telescope.sorters') local previewers = require('telescope.previewers') +local make_entry = require('telescope.make_entry') local my_list = { 'lua/telescope/WIP.lua',