diff --git a/src/main.lua b/src/main.lua index a0bb4d31..f7913ed2 100644 --- a/src/main.lua +++ b/src/main.lua @@ -339,6 +339,8 @@ function love.load() local baseconf = { view = viewconf, editor = editorconf, + --- console history entries kept; 0 for unlimited + input_history = 1000, autotest = autotest, mode = mode, } diff --git a/src/model/input/history.lua b/src/model/input/history.lua index cd02fc95..d0fc02e8 100644 --- a/src/model/input/history.lua +++ b/src/model/input/history.lua @@ -3,12 +3,17 @@ require('util.dequeue') require('util.string.string') require('util.debug') +local DEFAULT_LIMIT = 1000 + --- @class History: Dequeue --- @field index integer +--- @field limit integer entry cap, 0 for unlimited History = class.create() -function History.new() +--- @param limit integer? entry cap; 0 means unlimited +function History.new(limit) local self = Dequeue.typed('string[]') + self.limit = limit or DEFAULT_LIMIT setmetatable(self, { __index = function(t, k) @@ -25,6 +30,7 @@ function History:remember(input) if string.is_non_empty_string_array(input) then if not self.index then self:append(input) + self:_trim() return true else local entry = self:get(self.index) @@ -33,12 +39,28 @@ function History:remember(input) local new_s = string.unlines(new) if hist ~= new_s then self:append(input) + self:_trim() end end end return false end +--- Evict oldest entries past the limit +--- @private +function History:_trim() + if self.limit == 0 then return end + while #self > self.limit do + self:pop_front() + if self.index then + self.index = self.index - 1 + if self.index < 1 then + self:reset_index() + end + end + end +end + function History:reset_index() self.index = nil end diff --git a/src/model/input/userInputModel.lua b/src/model/input/userInputModel.lua index 4613feec..5875570a 100644 --- a/src/model/input/userInputModel.lua +++ b/src/model/input/userInputModel.lua @@ -48,7 +48,7 @@ function UserInputModel.new(cfg, eval, oneshot, custom_label) local self = setmetatable({ oneshot = oneshot, entered = InputText(), - history = History(), + history = History(cfg.input_history), evaluator = eval, cursor = Cursor(), selection = InputSelection(), @@ -354,7 +354,7 @@ end --- @param history boolean? function UserInputModel:reset(history) if history and self:keep_history() then - self.history = History() + self.history = History(self.cfg.input_history) end self:clear_input() end diff --git a/tests/input/history_spec.lua b/tests/input/history_spec.lua index f4a50fd1..143ad5f7 100644 --- a/tests/input/history_spec.lua +++ b/tests/input/history_spec.lua @@ -22,6 +22,39 @@ describe('history #history', function() history = History() end) + it('caps the number of entries', function() + local h = History(10) + for i = 1, 15 do + h:remember({ 'entry ' .. i }) + end + assert.are_equal(10, h:length()) + assert.same({ 'entry 6' }, h:get(1)) + assert.same({ 'entry 15' }, h:get(10)) + end) + + it('treats 0 as unlimited', function() + local h = History(0) + for i = 1, 15 do + h:remember({ 'entry ' .. i }) + end + assert.are_equal(15, h:length()) + end) + + it('defaults the limit', function() + assert.are_equal(1000, History().limit) + end) + + it('keeps navigation position across eviction', function() + local h = History(100) + for i = 1, 100 do + h:remember({ 'e' .. i }) + end + h:history_back({ '' }) + local at = h:get(h.index) + h:remember({ 'typed' }) + assert.same(at, h:get(h.index)) + end) + it('remembers', function() history:remember(t1) local h1 = { t1 }