Add chat session
This commit is contained in:
parent
514a1e08ac
commit
2aeaed13b8
20
README.md
20
README.md
@ -63,6 +63,26 @@ The AI response appears in a floating window. You can close it using:
|
||||
- `<Enter>` key
|
||||
- `:q` command
|
||||
|
||||
## Chat Features
|
||||
|
||||
The plugin now maintains a continuous chat session with Gemini:
|
||||
|
||||
- All conversations appear in a persistent chat window
|
||||
- Chat history is maintained throughout the session
|
||||
- Press `i` in the chat window to enter a new query
|
||||
- Press `q` to close the chat window (history is preserved)
|
||||
- Use `:GeminiClearChat` to clear the conversation history
|
||||
|
||||
### Chat Window Controls
|
||||
|
||||
While in the chat window:
|
||||
- `i`: Enter a new query
|
||||
- `q`: Close the window
|
||||
- Normal mode scrolling commands work as expected
|
||||
- Window automatically scrolls to show new messages
|
||||
|
||||
The chat window appears on the right side of your editor and preserves the entire conversation history until you explicitly clear it or restart Neovim.
|
||||
|
||||
## Features
|
||||
|
||||
- Floating window interface
|
||||
|
@ -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 = {}
|
||||
|
||||
-- If context is provided, add it to the contents
|
||||
-- Add conversation history to the request
|
||||
for _, message in ipairs(conversation_history) do
|
||||
table.insert(contents, {
|
||||
parts = {{
|
||||
text = message.role .. ": " .. message.content
|
||||
}}
|
||||
})
|
||||
end
|
||||
|
||||
-- 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
|
||||
|
@ -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)
|
||||
|
||||
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')
|
||||
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
|
||||
|
||||
-- Calculate dimensions
|
||||
local width = math.floor(vim.o.columns / 3)
|
||||
local height = vim.o.lines - 2 -- Account for status line and command line
|
||||
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 })
|
||||
vim.keymap.set('n', 'q', function()
|
||||
vim.api.nvim_win_close(chat_winnr, true)
|
||||
chat_winnr = nil
|
||||
end, { buffer = chat_bufnr, nowait = 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)
|
||||
end, { buffer = chat_bufnr, nowait = true })
|
||||
end
|
||||
|
||||
-- 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)
|
||||
end
|
||||
end,
|
||||
once = true,
|
||||
})
|
||||
-- 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user