diff --git a/lua/fittencode/engines/actions/init.lua b/lua/fittencode/engines/actions/init.lua index 0cc73f9d..de7525dc 100644 --- a/lua/fittencode/engines/actions/init.lua +++ b/lua/fittencode/engines/actions/init.lua @@ -1,5 +1,6 @@ local api = vim.api local fn = vim.fn +local uv = vim.uv or vim.loop local Base = require('fittencode.base') local Chat = require('fittencode.views.chat') @@ -12,7 +13,6 @@ local PromptProviders = require('fittencode.prompt_providers') local Sessions = require('fittencode.sessions') local Status = require('fittencode.status') local Preprocessing = require('fittencode.preprocessing') -local TaskScheduler = require('fittencode.tasks') local Unicode = require('fittencode.unicode') local schedule = Base.schedule @@ -62,11 +62,6 @@ local chat = nil ---@type ActionsContent local content = nil -local TASK_DEFAULT = 1 -local TASK_HEADLESS = 2 ----@type table -local tasks = {} - -- One by one evaluation local lock = false @@ -110,22 +105,13 @@ local function get_action_type(action) return ACTION_TYPES[action] end -local function _create_task(headless) - if headless then - return tasks[TASK_HEADLESS]:create() - else - return tasks[TASK_DEFAULT]:create() - end -end - ----@param task_id integer +---@param timestamp integer ---@param suggestions Suggestions ---@return Suggestions?, integer? -local function preprocessing(presug, task_id, headless, preprocess_format, suggestions) - local match = headless and tasks[TASK_HEADLESS]:match_clean(task_id, nil, nil, false) or - tasks[TASK_DEFAULT]:match_clean(task_id, nil, nil) - local ms = match[2] - if not match[1] or not suggestions or #suggestions == 0 then +local function preprocessing(presug, timestamp, preprocess_format, suggestions) + local ms = math.floor((uv.hrtime() - timestamp) / 1000000) + Log.debug('A<{}> Received and time elapsed: {} ms', string.format('%x', timestamp), ms) + if not suggestions or #suggestions == 0 then return nil, ms end local opts = { @@ -412,10 +398,11 @@ local function chain_actions(opts) } end Promise:new(function(resolve, reject) - local task_id = _create_task(headless) - Sessions.request_generate_one_stage(task_id, prompt_ctx, function(id, prompt, suggestions) - local lines, ms = preprocessing(presug, id, headless, preprocess_format, suggestions) - Log.debug('ActionsEngine<{}> Preprocessed: {}, Generated: {}', string.format('%x', id), lines, suggestions) + local last_timestamp = uv.hrtime() + Log.debug('A<{}> Send request', string.format('%x', last_timestamp)) + Sessions.request_generate_one_stage(last_timestamp, prompt_ctx, function(timestamp, prompt, suggestions) + local lines, ms = preprocessing(presug, timestamp, preprocess_format, suggestions) + Log.debug('A<{}> Preprocessed: {}, Generated: {}', string.format('%x', timestamp), lines, suggestions) elapsed_time = elapsed_time + ms if not lines or #lines == 0 then if extra_newline then @@ -815,12 +802,8 @@ local CHAT_MODEL = { function ActionsEngine.setup() chat = Chat:new(CHAT_MODEL) content = Content:new(chat) - tasks[TASK_DEFAULT] = TaskScheduler:new('ActionsEngine/Default') - tasks[TASK_DEFAULT]:setup() - tasks[TASK_HEADLESS] = TaskScheduler:new('ActionsEngine/Headless') - tasks[TASK_HEADLESS]:setup() status = Status:new({ - tag = 'ActionsEngine', + tag = 'A', ready_idle = true, }) setup_actions_menu() diff --git a/lua/fittencode/engines/inline/init.lua b/lua/fittencode/engines/inline/init.lua index fbf49352..889046fe 100644 --- a/lua/fittencode/engines/inline/init.lua +++ b/lua/fittencode/engines/inline/init.lua @@ -1,4 +1,5 @@ local api = vim.api +local uv = vim.uv or vim.loop local Base = require('fittencode.base') local Config = require('fittencode.config') @@ -9,7 +10,6 @@ local Model = require('fittencode.engines.inline.model') local Sessions = require('fittencode.sessions') local Status = require('fittencode.status') local Preprocessing = require('fittencode.preprocessing') -local TaskScheduler = require('fittencode.tasks') local PromptProviders = require('fittencode.prompt_providers') local schedule = Base.schedule @@ -21,9 +21,6 @@ local M = {} ---@class InlineModel local model = nil ----@class TaskScheduler -local tasks = nil - ---@type Status local status = nil @@ -44,6 +41,8 @@ local CURSORMOVED_INTERVAL = 120 ---@type uv_timer_t? local cursormoved_timer = nil +local last_timestamp = 0 + local function testandclear_cursor_ignored(row, col) local c = ignore_cursor ignore_cursor = nil @@ -66,18 +65,16 @@ function M.update_disabled() end ---@param ctx PromptContext ----@param task_id integer +---@param timestamp integer ---@param suggestions? Suggestions ---@return Suggestions? -local function preprocessing(ctx, task_id, suggestions) - local row, col = Base.get_cursor(ctx.window) - if not row or not col then - return - end - local match = tasks:match_clean(task_id, row, col) - if not match[1] then +local function preprocessing(ctx, timestamp, suggestions) + if timestamp ~= last_timestamp then + Log.debug('I<{}> Received but outdated', string.format('%x', timestamp)) return end + local ms = math.floor((uv.hrtime() - timestamp) / 1000000) + Log.debug('I<{}> Received and time elapsed: {} ms', string.format('%x', timestamp), ms) if not suggestions or #suggestions == 0 then return end @@ -183,14 +180,15 @@ end local function _generate_one_stage(row, col, on_success, on_error) status:update(SC.GENERATING) - local task_id = tasks:create(row, col) + last_timestamp = uv.hrtime() local ctx = PromptProviders.get_current_prompt_ctx(row, col) - Sessions.request_generate_one_stage(task_id, ctx, function(id, _, suggestions) - local lines = preprocessing(ctx, id, suggestions) - Log.debug('InlineEngine<{}> Preprocessed: {}, Generated: {}', string.format('%x', id), lines, suggestions) + Log.debug('I<{}> Send request at cursor: {}', string.format('%x', last_timestamp), { row, col }) + Sessions.request_generate_one_stage(last_timestamp, ctx, function(timestamp, _, suggestions) + local lines = preprocessing(ctx, timestamp, suggestions) if lines and #lines > 0 then + Log.debug('I<{}> Preprocessed: {}, Generated: {}', string.format('%x', timestamp), lines, suggestions) status:update(SC.SUGGESTIONS_READY) - apply_new_suggestions(task_id, row, col, lines) + apply_new_suggestions(timestamp, row, col, lines) schedule(on_success, vim.deepcopy(lines)) else status:update(SC.NO_MORE_SUGGESTIONS) @@ -210,16 +208,16 @@ end ---@param on_success? function ---@param on_error? function function M.generate_one_stage(row, col, force, delaytime, on_success, on_error) - Log.debug('Start generating one stage') + -- Log.debug('Start generating one stage') if not force and model:cache_hit(row, col) and M.has_suggestions() then status:update(SC.SUGGESTIONS_READY) render_virt_text_segments(model:get_suggestions_segments()) schedule(on_success, model:make_new_trim_commmited_suggestions()) - Log.debug('CACHE HIT') + -- Log.debug('CACHE HIT') return else - Log.debug('CACHE MISS') + -- Log.debug('CACHE MISS') end if inline_suggestions_ready() then @@ -422,7 +420,7 @@ function M.reset() clear_virt_text_all() end model:reset() - tasks:clear() + last_timestamp = 0 end ---@return boolean @@ -698,9 +696,7 @@ end function M.setup() model = Model:new() - tasks = TaskScheduler:new('InlineEngine') - tasks:setup() - status = Status:new({ tag = 'InlineEngine' }) + status = Status:new({ tag = 'I' }) setup_keymaps() setup_keyfilters() setup_autocmds() diff --git a/lua/fittencode/sessions.lua b/lua/fittencode/sessions.lua index 9b4ed285..63ca61a3 100644 --- a/lua/fittencode/sessions.lua +++ b/lua/fittencode/sessions.lua @@ -130,10 +130,10 @@ function M.ready_for_generate() return key_storage:get_key_by_name(current_username) ~= nil end ----@param task_id integer +---@param timestamp integer ---@param on_success function|nil ---@param on_error function|nil -function M.request_generate_one_stage(task_id, opts, on_success, on_error) +function M.request_generate_one_stage(timestamp, opts, on_success, on_error) local api_key = key_storage:get_key_by_name(current_username) if api_key == nil then -- Log.debug('Key is not found') @@ -147,7 +147,7 @@ function M.request_generate_one_stage(task_id, opts, on_success, on_error) end client:generate_one_stage(api_key, params, function(generated_text) - schedule(on_success, task_id, prompt, generate_suggestions(generated_text)) + schedule(on_success, timestamp, prompt, generate_suggestions(generated_text)) end, on_error) end diff --git a/lua/fittencode/status.lua b/lua/fittencode/status.lua index bd4c9801..5fca0a84 100644 --- a/lua/fittencode/status.lua +++ b/lua/fittencode/status.lua @@ -69,7 +69,7 @@ function M:update(status) -- Force `lualine` to update statusline -- vim.cmd('redrawstatus') _force_update_lualine() - Log.debug('{} -> {}', self.tag, name) + Log.debug(self.tag .. ' > ' .. name) end self.idle_timer = Base.debounce(self.idle_timer, function() if vim.tbl_contains(self.filters, self.current) then diff --git a/lua/fittencode/tasks.lua b/lua/fittencode/tasks.lua deleted file mode 100644 index 685d41db..00000000 --- a/lua/fittencode/tasks.lua +++ /dev/null @@ -1,117 +0,0 @@ -local uv = vim.uv or vim.loop - -local Log = require('fittencode.log') - ----@class Task ----@field row integer ----@field col integer ----@field timestamp integer @timestamp of the task when it was created, in nanoseconds, since the Unix epoch - ----@class TaskScheduler ----@field tag string ----@field list table @list of tasks ----@field threshold? integer @threshold for clean up ----@field timeout_recycling_timer? uv_timer_t @timer for recycling timeout tasks ----@field clear function - ----@class TaskScheduler -local TaskScheduler = {} - -local MS_TO_NS = 1000000 --- TODO: make this configurable -local TASK_TIMEOUT = 6000 * MS_TO_NS -local RECYCLING_CYCLE = 100 - -function TaskScheduler:new(tag) - local obj = { - tag = tag, - list = {}, - threshold = nil, - timeout_recycling_timer = nil, - } - self.__index = self - return setmetatable(obj, self) -end - -function TaskScheduler:setup() - self.list = {} - self.timeout_recycling_timer = uv.new_timer() - if self.timeout_recycling_timer then - self.timeout_recycling_timer:start(RECYCLING_CYCLE, RECYCLING_CYCLE, function() - self:timeout_recycling() - end) - else - Log.error('Failed to create timeout recycling timer') - end -end - ----@param row? integer ----@param col? integer ----@return integer -function TaskScheduler:create(row, col) - local timestamp = uv.hrtime() - table.insert(self.list, #self.list + 1, { row = row, col = col, timestamp = timestamp }) - if row and col then - Log.debug('Task<{}>: {} Created at ({}, {})', self.tag, string.format('%x', timestamp), row, col) - else - Log.debug('Task<{}>: {} Created', self.tag, string.format('%x', timestamp)) - end - return timestamp -end - ----@param task_id integer -function TaskScheduler:schedule_clean(task_id) - self.threshold = task_id - if not self.timeout_recycling_timer then - self:timeout_recycling() - end -end - ----@param task_id integer ----@param row? integer ----@param col? integer ----@return table -function TaskScheduler:match_clean(task_id, row, col, clean) - local match_found = false - local ms = 0 - clean = clean == nil and true or clean - for i = #self.list, 1, -1 do - local task = self.list[i] - if task.timestamp == task_id and task.row == row and task.col == col then - ms = math.floor((uv.hrtime() - task.timestamp) / MS_TO_NS) - Log.debug('Task<{}>: {} Matched, Time elapsed: {} ms', self.tag, string.format('%x', task_id), ms) - match_found = true - break - end - end - if clean then - self:schedule_clean(task_id) - end - return { match_found, ms } -end - ----@param timestamp integer ----@return boolean -local function is_timeout(timestamp) - return uv.hrtime() - timestamp > TASK_TIMEOUT -end - -function TaskScheduler:timeout_recycling() - if self.threshold == -1 then - self.list = {} - -- Log.debug('All tasks removed') - else - self.list = vim.tbl_filter(function(task) - return not is_timeout(task.timestamp) and (not self.threshold or task.timestamp > self.threshold) - end, self.list) - -- Log.debug('Tasks recycled') - end - self.threshold = nil -end - -function TaskScheduler:clear() - -- self:schedule_clean(-1) - self.list = {} -end - -return TaskScheduler