diff --git a/lua/fittencode/actions/identify_programming_language.lua b/lua/fittencode/actions/identify_programming_language.lua new file mode 100644 index 0000000..c0d75b0 --- /dev/null +++ b/lua/fittencode/actions/identify_programming_language.lua @@ -0,0 +1,94 @@ +local api = vim.api + +local API = require('fittencode.api').api +local Base = require('fittencode.base') +local Config = require('fittencode.config') +local Log = require('fittencode.log') + +local M = {} + +local DEFER = 1000 + +-- milliseconds +local IPL_DEBOUNCE_TIME = 500 + +---@type uv_timer_t +local ipl_timer = nil + +local function _identify_current_buffer() + local buffer = api.nvim_get_current_buf() + local name = api.nvim_buf_get_name(buffer) + local ext = vim.fn.fnamemodify(name, ':e') + if #name > 0 and #ext > 0 then + return + end + local ipl = '' + local success, result = pcall(api.nvim_buf_get_var, buffer, 'fittencode_identify_programming_language') + if success and result and #result > 0 then + ipl = result + end + local filetype = api.nvim_get_option_value('filetype', { + buf = buffer, + }) + if #filetype > 0 and #ipl == 0 then + return + end + + local content = table.concat(api.nvim_buf_get_lines(buffer, 0, -1, false), '\n') + API.identify_programming_language({ + headless = true, + commit = false, + content = content, + on_success = function(suggestions) + if not suggestions or #suggestions == 0 then + return + end + local lang = suggestions[1] + if #lang == 0 then + return + end + lang = lang:lower() + lang = lang:gsub('^%s*(.-)%s*$', '%1') + if #lang == 0 then + return + end + lang = lang:gsub('c%+%+', 'cpp') + lang = lang:match('^(%w+)') + api.nvim_set_option_value('filetype', lang, { + buf = buffer, + }) + api.nvim_buf_set_var(buffer, 'fittencode_identify_programming_language', lang) + end, + }) +end + +local function _ipl_wrap() + Base.debounce(ipl_timer, function() + _identify_current_buffer() + end, IPL_DEBOUNCE_TIME) +end + +local function register_identify_current_buffer() + api.nvim_create_autocmd({ 'TextChangedI', 'BufReadPost' }, { + group = Base.augroup('IdentifyProgrammingLanguage'), + pattern = '*', + callback = function(params) + if not API.ready_for_generate() then + vim.defer_fn(function() + _ipl_wrap() + end, DEFER) + return + end + _ipl_wrap() + end, + desc = 'Identify programming language for current buffer', + }) +end + +function M.setup() + if Config.options.action.identify_programming_language.identify_buffer then + register_identify_current_buffer() + end +end + +return M diff --git a/lua/fittencode/actions/init.lua b/lua/fittencode/actions/init.lua new file mode 100644 index 0000000..af43492 --- /dev/null +++ b/lua/fittencode/actions/init.lua @@ -0,0 +1,7 @@ +local M = {} + +function M.setup() + require('fittencode.actions.identify_programming_language').setup() +end + +return M diff --git a/lua/fittencode/api.lua b/lua/fittencode/api.lua index a56766f..c886872 100644 --- a/lua/fittencode/api.lua +++ b/lua/fittencode/api.lua @@ -26,6 +26,9 @@ M.api = { get_current_status = function() return Engines.get_status() end, + ready_for_generate = function() + return Sessions.ready_for_generate() + end, triggering_completion = function() InlineEngine.triggering_completion() end, diff --git a/lua/fittencode/config.lua b/lua/fittencode/config.lua index 1fe8ea1..ad44254 100644 --- a/lua/fittencode/config.lua +++ b/lua/fittencode/config.lua @@ -28,6 +28,12 @@ local defaults = { -- Show "Fitten Code - Start Chat" in the editor context menu, when you right-click on the code. show_in_editor_context_menu = true, }, + identify_programming_language = { + -- Identify programming language of the current buffer + -- - Unamed buffer + -- - Buffer without file extension + identify_buffer = true, + } }, disable_specific_inline_completion = { -- Disable auto-completion for some specific file suffixes by entering them below diff --git a/lua/fittencode/engines/actions/content.lua b/lua/fittencode/engines/actions/content.lua index f3d74e7..3409781 100644 --- a/lua/fittencode/engines/actions/content.lua +++ b/lua/fittencode/engines/actions/content.lua @@ -128,6 +128,11 @@ function M:on_start(opts) self.conversations[self.current_eval] = Conversation:new(self.current_eval, opts.action) self.conversations[self.current_eval].location = opts.location self.conversations[self.current_eval].prompt = opts.prompt + self.conversations[self.current_eval].commit = opts.commit + + if self.conversations[self.current_eval].commit == false then + return + end local source_info = ' (' .. opts.location[1] .. ' ' .. opts.location[2] .. ':' .. opts.location[3] .. ')' local c_in = '# In`[' .. self.current_eval .. ']`:= ' .. opts.action .. source_info @@ -182,6 +187,10 @@ function M:on_end(opts) self.conversations[self.current_eval].elapsed_time = opts.elapsed_time self.conversations[self.current_eval].depth = opts.depth + if self.conversations[self.current_eval].commit == false then + return + end + self:commit({ lines = { '', @@ -214,6 +223,10 @@ function M:on_suggestions(suggestions) end self.conversations[self.current_eval].suggestions[#self.conversations[self.current_eval].suggestions + 1] = suggestions + if self.conversations[self.current_eval].commit == false then + return + end + if not self.has_suggestions[self.current_eval] then self.has_suggestions[self.current_eval] = true local cursor = self:commit({ @@ -236,6 +249,9 @@ function M:on_status(msg) if not msg then return end + if self.conversations[self.current_eval].commit == false then + return + end self:commit({ lines = { '', diff --git a/lua/fittencode/engines/actions/conversation.lua b/lua/fittencode/engines/actions/conversation.lua index f6883ee..b4c9a5f 100644 --- a/lua/fittencode/engines/actions/conversation.lua +++ b/lua/fittencode/engines/actions/conversation.lua @@ -7,6 +7,7 @@ ---@field elapsed_time integer ---@field depth integer ---@field location table -- [filename, row_start, row_end] +---@field commit boolean local M = {} function M:new(id, actions, references) diff --git a/lua/fittencode/engines/actions/init.lua b/lua/fittencode/engines/actions/init.lua index 207c912..9bca4c9 100644 --- a/lua/fittencode/engines/actions/init.lua +++ b/lua/fittencode/engines/actions/init.lua @@ -87,6 +87,7 @@ local status = nil ---@field content? string ---@field language? string ---@field headless? boolean +---@field commit? boolean ---@field on_success? function @function Callback when suggestions are ready ---@field on_error? function @function Callback when an error occurs @@ -138,6 +139,7 @@ local function on_stage_end(is_error, on_success, on_error) Log.debug('Action elapsed time: {}', elapsed_time) Log.debug('Action depth: {}', depth) + local ready = false if is_error then status:update(SC.ERROR) local err_msg = 'Error: fetch failed.' @@ -151,7 +153,7 @@ local function on_stage_end(is_error, on_success, on_error) schedule(on_success) else status:update(SC.SUGGESTIONS_READY) - schedule(on_success, content:get_current_suggestions()) + ready = true end end @@ -160,6 +162,10 @@ local function on_stage_end(is_error, on_success, on_error) depth = depth, }) + if ready then + schedule(on_success, content:get_current_suggestions()) + end + current_eval = current_eval + 1 lock = false end @@ -395,6 +401,7 @@ local function start_content(action_name, prompt_opts, range) current_eval = current_eval, action = action_name, prompt = vim.split(prompt_preview.content, '\n'), + commit = prompt_opts.action_opts.commit, location = { prompt_preview.filename, range.start[1], @@ -408,16 +415,18 @@ end ---@return nil function ActionsEngine.start_action(action, opts) opts = opts or {} + opts.commit = opts.commit == nil and true or opts.commit + if not opts.commit then + opts.headless = true + end local action_name = get_action_name(action) if not action_name then Log.error('Invalid Action: {}', action) return end - Log.debug('Start Action({})...', action_name) if lock then - Log.debug('Action is locked, skipping') return end @@ -438,11 +447,16 @@ function ActionsEngine.start_action(action, opts) fn.win_gotoid(window) end - local range = make_range(buffer) - Log.debug('Action range: {}', range) + local range = { + start = { 0, 0 }, + ['end'] = { 0, 0 }, + } + local filetype = '' - local filetype = make_filetype(buffer, range) - Log.debug('Action real filetype: {}', filetype) + if not opts.content then + range = make_range(buffer) + filetype = make_filetype(buffer, range) + end local prompt_opts = { window = window, @@ -455,7 +469,6 @@ function ActionsEngine.start_action(action, opts) prompt = opts and opts.prompt, action_opts = opts, } - Log.debug('Action prompt_opts: {}', prompt_opts) start_content(action_name, prompt_opts, range) _start_action(chat.window, chat.buffer, action, prompt_opts) diff --git a/lua/fittencode/init.lua b/lua/fittencode/init.lua index 48839fa..271fdc6 100644 --- a/lua/fittencode/init.lua +++ b/lua/fittencode/init.lua @@ -16,6 +16,7 @@ function M.setup(opts) local sessions = require('fittencode.sessions') sessions.setup() require('fittencode.engines').setup() + require('fittencode.actions').setup() require('fittencode.prompt_providers').setup() require('fittencode.color').setup_highlight() require('fittencode.commands').setup() diff --git a/lua/fittencode/prompt_providers/actions.lua b/lua/fittencode/prompt_providers/actions.lua index a5e62b7..a868ce3 100644 --- a/lua/fittencode/prompt_providers/actions.lua +++ b/lua/fittencode/prompt_providers/actions.lua @@ -82,7 +82,7 @@ local MAP_ACTION_PROMPTS = { end, OptimizeCode = 'Optimize the code above to make it faster and more efficient', RefactorCode = 'Refactor the code above', - IdentifyProgrammingLanguage = 'Identify the language used in the code above', + IdentifyProgrammingLanguage = 'Identify the language used in the code above and Give the name in short', AnalyzeData = 'Analyze the data above and Give the pattern of the data', TranslateText = function(ctx) assert(ctx.action_opts) diff --git a/lua/fittencode/views/chat.lua b/lua/fittencode/views/chat.lua index 1c17a31..30be1eb 100644 --- a/lua/fittencode/views/chat.lua +++ b/lua/fittencode/views/chat.lua @@ -123,8 +123,8 @@ function M:show() end vim.cmd('topleft vsplit') - vim.cmd('vertical resize ' .. 42) self.window = api.nvim_get_current_win() + vim.api.nvim_win_set_width(self.window, 42) api.nvim_win_set_buf(self.window, self.buffer) set_option_value_win(self.window)