diff options
Diffstat (limited to 'lua/telescope/actions/history.lua')
| -rw-r--r-- | lua/telescope/actions/history.lua | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/lua/telescope/actions/history.lua b/lua/telescope/actions/history.lua new file mode 100644 index 0000000..8ec58e2 --- /dev/null +++ b/lua/telescope/actions/history.lua @@ -0,0 +1,186 @@ +local conf = require('telescope.config').values +local Path = require('plenary.path') + +local uv = vim.loop + +---@brief [[ +--- A base implementation of a prompt history that provides a simple history +--- and can be replaced with a custom implementation. +--- +--- For example: We provide a extension for a smart history that uses sql.nvim +--- to map histories to metadata, like the calling picker or cwd. +--- +--- So you have a history for: +--- - find_files project_1 +--- - grep_string project_1 +--- - live_grep project_1 +--- - find_files project_2 +--- - grep_string project_2 +--- - live_grep project_2 +--- - etc +--- +--- See github.com/nvim-telescope/telescope-smart-history.nvim +---@brief ]] +---@tag telescope.actions.history + +-- TODO(conni2461): currently not present in plenary path only sync. +-- But sync is just unnecessary here +local write_async = function(path, txt, flag) + uv.fs_open(path, flag, 438, function(open_err, fd) + assert(not open_err, open_err) + uv.fs_write(fd, txt, -1, function(write_err) + assert(not write_err, write_err) + uv.fs_close(fd, function(close_err) + assert(not close_err, close_err) + end) + end) + end) +end + +local append_async = function(path, txt) write_async(path, txt, "a") end + +local histories = {} + +--- Manages prompt history +---@class History @Manages prompt history +---@field enabled boolean: Will indicate if History is enabled or disabled +---@field path string: Will point to the location of the history file +---@field limit string: Will have the limit of the history. Can be nil, if limit is disabled. +---@field content table: History table. Needs to be filled by your own History implementation +---@field index number: Used to keep track of the next or previous index. Default is #content + 1 +histories.History = {} +histories.History.__index = histories.History + +--- Create a new History +---@param opts table: Defines the behavior of History +---@field init function: Will be called after handling configuration (required) +---@field append function: How to append a new prompt item (required) +---@field reset function: What happens on reset. Will be called when telescope closes (required) +---@field pre_get function: Will be called before a next or previous item will be returned (optional) +function histories.History:new(opts) + local obj = {} + if not conf.history or type(conf.history) ~= "table" then + obj.enabled = false + return setmetatable(obj, self) + end + obj.enabled = true + if conf.history.limit then + obj.limit = conf.history.limit + end + obj.path = vim.fn.expand(conf.history.path) + obj.content = {} + obj.index = 1 + + opts.init(obj) + obj._reset = opts.reset + obj._append = opts.append + obj._pre_get = opts.pre_get + + return setmetatable(obj, self) +end + +--- Shorthand to create a new history +function histories.new(...) + return histories.History:new(...) +end + +--- Will reset the history index to the default initial state. Will happen after the picker closed +function histories.History:reset() + if not self.enabled then return end + self._reset(self) +end + +--- Append a new line to the history +---@param line string: current line that will be appended +---@param picker table: the current picker object +---@param no_reset boolean: On default it will reset the state at the end. If you don't want to do this set to true +function histories.History:append(line, picker, no_reset) + if not self.enabled then return end + self._append(self, line, picker, no_reset) +end + +--- Will return the next history item. Can be nil if there are no next items +---@param line string: the current line +---@param picker table: the current picker object +---@return string: the next history item +function histories.History:get_next(line, picker) + if not self.enabled then + print("You are cycling to next the history item but history is disabled.", + "Read ':help telescope.defaults.history'") + return false + end + if self._pre_get then self._pre_get(self, line, picker) end + + local next_idx = self.index + 1 + if next_idx <= #self.content then + self.index = next_idx + return self.content[next_idx] + end + self.index = #self.content + 1 + return nil +end + +--- Will return the previous history item. Can be nil if there are no previous items +---@param line string: the current line +---@param picker table: the current picker object +---@return string: the previous history item +function histories.History:get_prev(line, picker) + if not self.enabled then + print("You are cycling to previous the history item but history is disabled.", + "Read ':help telescope.defaults.history'") + return false + end + if self._pre_get then self._pre_get(self, line, picker) end + + local next_idx = self.index - 1 + if self.index == #self.content + 1 then + if line ~= '' then self:append(line, picker, true) end + end + if next_idx >= 1 then + self.index = next_idx + return self.content[next_idx] + end + return nil +end + +--- A simple implementation of history. +--- +--- It will keep one unified history across all pickers. +histories.get_simple_history = function() + return histories.new({ + init = function(obj) + local p = Path:new(obj.path) + if not p:exists() then p:touch({ parents = true }) end + + obj.content = Path:new(obj.path):readlines() + obj.index = #obj.content + table.remove(obj.content, obj.index) + end, + reset = function(self) + self.index = #self.content + 1 + end, + append = function(self, line, _, no_reset) + if line ~= '' then + if self.content[#self.content] ~= line then + table.insert(self.content, line) + + local len = #self.content + if self.limit and len > self.limit then + local diff = len - self.limit + for i = diff, 1, -1 do + table.remove(self.content, i) + end + write_async(self.path, table.concat(self.content, '\n') .. '\n', 'w') + else + append_async(self.path, line .. '\n') + end + end + end + if not no_reset then + self:reset() + end + end, + }) +end + +return histories |
