Add chat session

This commit is contained in:
2025-03-16 14:44:36 +01:00
parent 514a1e08ac
commit 2aeaed13b8
3 changed files with 132 additions and 56 deletions

View File

@ -2,6 +2,9 @@
local M = {}
-- Store conversation history
local conversation_history = {}
local function get_api_key()
-- Check for environment variable
local api_key = os.getenv("GEMINI_API_KEY")
@ -30,20 +33,29 @@ local function make_request(prompt, context)
end
local model = "gemini-2.0-flash"
local contents = {
{
parts = {
{
text = prompt,
},
},
},
}
local contents = {}
-- Add conversation history to the request
for _, message in ipairs(conversation_history) do
table.insert(contents, {
parts = {{
text = message.role .. ": " .. message.content
}}
})
end
-- If context is provided, add it to the contents
-- Add the current prompt
if context then
table.insert(contents[1].parts, 1, {
text = "Context:\n" .. context .. "\n\nQuery:\n",
table.insert(contents, {
parts = {{
text = "Context:\n" .. context .. "\n\nUser: " .. prompt
}}
})
else
table.insert(contents, {
parts = {{
text = "User: " .. prompt
}}
})
end
@ -82,6 +94,12 @@ local function make_request(prompt, context)
end
function M.get_response(prompt, context)
-- Add user message to history
table.insert(conversation_history, {
role = "User",
content = prompt
})
local result = make_request(prompt, context)
if result then
@ -98,7 +116,13 @@ function M.get_response(prompt, context)
and result.candidates[1].content.parts[1]
and result.candidates[1].content.parts[1].text
then
return result.candidates[1].content.parts[1].text
local response_text = result.candidates[1].content.parts[1].text
-- Add assistant response to history
table.insert(conversation_history, {
role = "Assistant",
content = response_text
})
return response_text
end
vim.notify("Unexpected response structure: " .. vim.inspect(result), vim.log.levels.ERROR)
@ -108,4 +132,9 @@ function M.get_response(prompt, context)
return nil
end
-- Add function to clear conversation history
function M.clear_conversation()
conversation_history = {}
end
return M

View File

@ -3,6 +3,10 @@
local api = require("gemini.api")
local M = {}
-- Store the buffer number of the chat window
local chat_bufnr = nil
local chat_winnr = nil
local function get_current_buffer_content()
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
return table.concat(lines, "\n")
@ -18,65 +22,77 @@ local function setup_treesitter_highlight(bufnr)
end
end
local function gemini_query(prompt, context)
local response = api.get_response(prompt, context)
local function update_chat_window(new_content)
if not chat_bufnr or not vim.api.nvim_buf_is_valid(chat_bufnr) then
-- Create a new buffer for chat
chat_bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(chat_bufnr, 'buftype', 'nofile')
vim.api.nvim_buf_set_option(chat_bufnr, 'filetype', 'markdown')
end
if response then
-- Create a scratch buffer
local new_buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(new_buf, 0, 0, false, vim.split(response, "\n"))
-- Set buffer options
vim.api.nvim_buf_set_option(new_buf, 'modifiable', false)
vim.api.nvim_buf_set_option(new_buf, 'buftype', 'nofile')
vim.api.nvim_buf_set_option(new_buf, 'filetype', 'markdown')
-- Calculate dimensions
local width = math.floor(vim.o.columns / 3)
local height = vim.o.lines - 2 -- Account for status line and command line
-- Calculate dimensions
local width = math.floor(vim.o.columns / 3)
local height = vim.o.lines - 2
if not chat_winnr or not vim.api.nvim_win_is_valid(chat_winnr) then
-- Create the window
local new_win = vim.api.nvim_open_win(new_buf, true, {
chat_winnr = vim.api.nvim_open_win(chat_bufnr, true, {
relative = "editor",
width = width,
height = height,
row = 0,
col = vim.o.columns - width,
border = "rounded",
title = "Google AI Response",
title = "Gemini Chat Session",
title_pos = "center",
style = "minimal"
})
-- Set window-local options
vim.api.nvim_win_set_option(new_win, 'wrap', true)
vim.api.nvim_win_set_option(new_win, 'linebreak', true)
vim.api.nvim_win_set_option(new_win, 'breakindent', true)
vim.api.nvim_win_set_option(chat_winnr, 'wrap', true)
vim.api.nvim_win_set_option(chat_winnr, 'linebreak', true)
vim.api.nvim_win_set_option(chat_winnr, 'breakindent', true)
-- Setup treesitter highlighting
setup_treesitter_highlight(new_buf)
setup_treesitter_highlight(chat_bufnr)
-- Set window-local keymaps
local close_keys = {'q', '<Esc>', '<CR>'}
for _, key in ipairs(close_keys) do
vim.keymap.set('n', key, function()
vim.api.nvim_win_close(new_win, true)
end, { buffer = new_buf, nowait = true })
end
vim.keymap.set('n', 'q', function()
vim.api.nvim_win_close(chat_winnr, true)
chat_winnr = nil
end, { buffer = chat_bufnr, nowait = true })
-- Add autocmd to enable closing with :q
vim.api.nvim_create_autocmd("BufWinLeave", {
buffer = new_buf,
callback = function()
if vim.api.nvim_win_is_valid(new_win) then
vim.api.nvim_win_close(new_win, true)
-- Add input mapping
vim.keymap.set('n', 'i', function()
vim.ui.input({ prompt = "Gemini: " }, function(input)
if input then
M.gemini_query(input)
end
end,
once = true,
})
end)
end, { buffer = chat_bufnr, nowait = true })
end
-- Return focus to the previous window
vim.cmd('wincmd p')
-- Make buffer modifiable
vim.api.nvim_buf_set_option(chat_bufnr, 'modifiable', true)
-- Update content
vim.api.nvim_buf_set_lines(chat_bufnr, -1, -1, false, vim.split(new_content, "\n"))
-- Make buffer unmodifiable again
vim.api.nvim_buf_set_option(chat_bufnr, 'modifiable', false)
-- Scroll to bottom
vim.api.nvim_win_set_cursor(chat_winnr, {vim.api.nvim_buf_line_count(chat_bufnr), 0})
-- Return focus to the previous window
vim.cmd('wincmd p')
end
local function gemini_query(prompt, context)
local response = api.get_response(prompt, context)
if response then
local formatted_content = "\n\nUser: " .. prompt .. "\n\nAssistant: " .. response
update_chat_window(formatted_content)
else
vim.notify("Failed to get a response from Gemini API", vim.log.levels.ERROR)
end
@ -112,24 +128,35 @@ function M.setup()
complete = "shellcmd",
})
-- Add command to clear chat history
vim.api.nvim_create_user_command("GeminiClearChat", function()
api.clear_conversation()
if chat_bufnr and vim.api.nvim_buf_is_valid(chat_bufnr) then
vim.api.nvim_buf_set_lines(chat_bufnr, 0, -1, false, {})
end
vim.notify("Chat history cleared", vim.log.levels.INFO)
end, {
desc = "Clear Gemini chat history"
})
-- Set up keymapping with 'gc' for 'gemini chat'
vim.keymap.set("n", "<leader>gc", function()
vim.ui.input({ prompt = "Gemini Query: " }, function(input)
vim.ui.input({ prompt = "Gemini: " }, function(input)
if input then
M.gemini_query(input)
end
end)
end, { desc = "Query Google AI (via Input)" })
end, { desc = "Chat with Gemini AI" })
-- Set up keymapping with 'gs' for 'gemini sync'
vim.keymap.set("n", "<leader>gs", function()
vim.ui.input({ prompt = "Gemini Query (with buffer context): " }, function(input)
vim.ui.input({ prompt = "Gemini (with buffer context): " }, function(input)
if input then
local buffer_content = get_current_buffer_content()
M.gemini_query(input, buffer_content)
end
end)
end, { desc = "Query Google AI (with buffer context)" })
end, { desc = "Chat with Gemini AI (with buffer context)" })
end
return M