Skip to content

Commit

Permalink
Revamp how diagnostics are handled
Browse files Browse the repository at this point in the history
- Automatically include diagnostics in selections. This means that the
chat will always know about them as well
- With diagnostics automatically included, CopilotChatFixDiagnostics is
no longer necessary, CopilotChatFix is enough
- With diagnostics automatically included, actions.help_actions also no
longer serves any purpose, CopilotChatExplain + CopilotChatFix is enough
- To better incorporate new workflow, also default to visual || buffer for
selection (which was popular config adjustment in first place anyway)

Signed-off-by: Tomas Slusny <[email protected]>
  • Loading branch information
deathbeam committed Nov 15, 2024
1 parent 78ea730 commit 8bc3106
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 170 deletions.
34 changes: 4 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,12 @@ Verify "[Copilot chat in the IDE](https://github.com/settings/copilot)" is enabl

#### Commands coming from default prompts

- `:CopilotChatExplain` - Write an explanation for the active selection as paragraphs of text
- `:CopilotChatExplain` - Write an explanation for the active selection and diagnostics as paragraphs of text
- `:CopilotChatReview` - Review the selected code
- `:CopilotChatFix` - There is a problem in this code. Rewrite the code to show it with the bug fixed
- `:CopilotChatOptimize` - Optimize the selected code to improve performance and readability
- `:CopilotChatDocs` - Please add documentation comment for the selection
- `:CopilotChatTests` - Please generate tests for my code
- `:CopilotChatFixDiagnostic` - Please assist with the following diagnostic issue in file
- `:CopilotChatCommit` - Write commit message for the change with commitizen convention
- `:CopilotChatCommitStaged` - Write commit message for the change with commitizen convention

Expand Down Expand Up @@ -181,9 +180,6 @@ local response = chat.response()
-- Pick a prompt using vim.ui.select
local actions = require("CopilotChat.actions")

-- Pick help actions
actions.pick(actions.help_actions())

-- Pick prompt actions
actions.pick(actions.prompt_actions({
selection = require("CopilotChat.select").visual,
Expand Down Expand Up @@ -227,15 +223,15 @@ Also see [here](/lua/CopilotChat/config.lua):
history_path = vim.fn.stdpath('data') .. '/copilotchat_history', -- Default path to stored history
callback = nil, -- Callback to use when ask response is received

-- default selection (visual or line)
-- default selection
selection = function(source)
return select.visual(source) or select.line(source)
return select.visual(source) or select.buffer(source)
end,

-- default prompts
prompts = {
Explain = {
prompt = '/COPILOT_EXPLAIN Write an explanation for the active selection as paragraphs of text.',
prompt = '/COPILOT_EXPLAIN Write an explanation for the active selection and diagnostics as paragraphs of text.',
},
Review = {
prompt = '/COPILOT_REVIEW Review the selected code.',
Expand All @@ -255,10 +251,6 @@ Also see [here](/lua/CopilotChat/config.lua):
Tests = {
prompt = '/COPILOT_GENERATE Please generate tests for my code.',
},
FixDiagnostic = {
prompt = 'Please assist with the following diagnostic issue in file:',
selection = select.diagnostics,
},
Commit = {
prompt = 'Write commit message for the change with commitizen convention. Make sure the title has maximum 50 characters and message is wrapped at 72 characters. Wrap the whole message in code block with language gitcommit.',
selection = select.gitdiff,
Expand Down Expand Up @@ -462,15 +454,6 @@ Requires [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) plug
```lua
-- lazy.nvim keys

-- Show help actions with telescope
{
"<leader>cch",
function()
local actions = require("CopilotChat.actions")
require("CopilotChat.integrations.telescope").pick(actions.help_actions())
end,
desc = "CopilotChat - Help actions",
},
-- Show prompts actions with telescope
{
"<leader>ccp",
Expand All @@ -494,15 +477,6 @@ Requires [fzf-lua](https://github.com/ibhagwan/fzf-lua) plugin to be installed.
```lua
-- lazy.nvim keys

-- Show help actions with fzf-lua
{
"<leader>cch",
function()
local actions = require("CopilotChat.actions")
require("CopilotChat.integrations.fzflua").pick(actions.help_actions())
end,
desc = "CopilotChat - Help actions",
},
-- Show prompts actions with fzf-lua
{
"<leader>ccp",
Expand Down
31 changes: 4 additions & 27 deletions lua/CopilotChat/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,14 @@
---@field prompt string: The prompt to display
---@field actions table<string, CopilotChat.config.prompt>: A table with the actions to pick from

local select = require('CopilotChat.select')
local chat = require('CopilotChat')
local utils = require('CopilotChat.utils')

local M = {}

--- Diagnostic help actions
---@param config CopilotChat.config?: The chat configuration
---@return CopilotChat.integrations.actions?: The help actions
function M.help_actions(config)
local bufnr = vim.api.nvim_get_current_buf()
local winnr = vim.api.nvim_get_current_win()
local cursor = vim.api.nvim_win_get_cursor(winnr)
local line_diagnostics = vim.diagnostic.get(bufnr, { lnum = cursor[1] - 1 })

if #line_diagnostics == 0 then
return nil
end

return {
prompt = 'Copilot Chat Help Actions',
actions = {
['Fix diagnostic'] = vim.tbl_extend('keep', {
prompt = 'Please assist with fixing the following diagnostic issue in file:',
selection = select.diagnostics,
}, config or {}),
['Explain diagnostic'] = vim.tbl_extend('keep', {
prompt = 'Please explain the following diagnostic issue in file:',
selection = select.diagnostics,
}, config or {}),
},
}
function M.help_actions()
utils.deprecate('help_actions()', 'prompt_actions()')
return M.prompt_actions()
end

--- User prompt actions
Expand Down
20 changes: 12 additions & 8 deletions lua/CopilotChat/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ local select = require('CopilotChat.select')
--- @field bufnr number
--- @field winnr number

---@class CopilotChat.config.selection.diagnostic
---@field message string
---@field severity string
---@field start_row number
---@field start_col number
---@field end_row number
---@field end_col number

---@class CopilotChat.config.selection
---@field lines string
---@field diagnostics table<CopilotChat.config.selection.diagnostic>?
---@field filename string?
---@field filetype string?
---@field start_row number?
---@field start_col number?
---@field end_row number?
---@field end_col number?
---@field prompt_extra string?

---@class CopilotChat.config.prompt
---@field prompt string?
Expand Down Expand Up @@ -106,15 +114,15 @@ return {
history_path = vim.fn.stdpath('data') .. '/copilotchat_history', -- Default path to stored history
callback = nil, -- Callback to use when ask response is received

-- default selection (visual or line)
-- default selection
selection = function(source)
return select.visual(source) or select.line(source)
return select.visual(source) or select.buffer(source)
end,

-- default prompts
prompts = {
Explain = {
prompt = '/COPILOT_EXPLAIN Write an explanation for the active selection as paragraphs of text.',
prompt = '/COPILOT_EXPLAIN Write an explanation for the active selection and diagnostics as paragraphs of text.',
},
Review = {
prompt = '/COPILOT_REVIEW Review the selected code.',
Expand Down Expand Up @@ -170,10 +178,6 @@ return {
Tests = {
prompt = '/COPILOT_GENERATE Please generate tests for my code.',
},
FixDiagnostic = {
prompt = 'Please assist with the following diagnostic issue in file:',
selection = select.diagnostics,
},
Commit = {
prompt = 'Write commit message for the change with commitizen convention. Make sure the title has maximum 50 characters and message is wrapped at 72 characters. Wrap the whole message in code block with language gitcommit.',
selection = select.gitdiff,
Expand Down
57 changes: 43 additions & 14 deletions lua/CopilotChat/copilot.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
---@field content string?

---@class CopilotChat.copilot.ask.opts
---@field selection string?
---@field selection CopilotChat.config.selection?
---@field embeddings table<CopilotChat.copilot.embed>?
---@field filename string?
---@field filetype string?
Expand Down Expand Up @@ -152,24 +152,56 @@ local function get_cached_token()
return nil
end

local function generate_selection_message(filename, filetype, start_row, end_row, selection)
if not selection or selection == '' then
local function generate_selection_message(filename, filetype, selection)
local content = selection.lines

if not content or content == '' then
return ''
end

local content = selection
if start_row > 0 then
local lines = vim.split(selection, '\n')
if selection.start_row and selection.start_row > 0 then
local lines = vim.split(content, '\n')
local total_lines = #lines
local max_length = #tostring(total_lines)
for i, line in ipairs(lines) do
local formatted_line_number = string.format('%' .. max_length .. 'd', i - 1 + start_row)
local formatted_line_number =
string.format('%' .. max_length .. 'd', i - 1 + selection.start_row)
lines[i] = formatted_line_number .. ': ' .. line
end
content = table.concat(lines, '\n')
end

return string.format('Active selection: `%s`\n```%s\n%s\n```', filename, filetype, content)
local out = string.format('Active selection: `%s`\n```%s\n%s\n```', filename, filetype, content)

if selection.diagnostics then
local diagnostics = {}
for _, diagnostic in ipairs(selection.diagnostics) do
local start_row = diagnostic.start_row
local end_row = diagnostic.end_row
if start_row == end_row then
table.insert(
diagnostics,
string.format('%s line=%d: %s', diagnostic.severity, start_row, diagnostic.message)
)
else
table.insert(
diagnostics,
string.format(
'%s line=%d-%d: %s',
diagnostic.severity,
start_row,
end_row,
diagnostic.message
)
)
end
end

out =
string.format('%s\nDiagnostics: `%s`\n%s\n', out, filename, table.concat(diagnostics, '\n'))
end

return out
end

local function generate_embeddings_message(embeddings)
Expand Down Expand Up @@ -473,9 +505,7 @@ function Copilot:ask(prompt, opts)
local embeddings = opts.embeddings or {}
local filename = opts.filename or ''
local filetype = opts.filetype or ''
local selection = opts.selection or ''
local start_row = opts.start_row or 0
local end_row = opts.end_row or 0
local selection = opts.selection or {}
local system_prompt = opts.system_prompt or prompts.COPILOT_INSTRUCTIONS
local model = opts.model or 'gpt-4o-2024-05-13'
local temperature = opts.temperature or 0.1
Expand All @@ -484,7 +514,7 @@ function Copilot:ask(prompt, opts)
self.current_job = job_id

log.trace('System prompt: ' .. system_prompt)
log.trace('Selection: ' .. selection)
log.trace('Selection: ' .. (selection.lines or ''))
log.debug('Prompt: ' .. prompt)
log.debug('Embeddings: ' .. #embeddings)
log.debug('Filename: ' .. filename)
Expand All @@ -501,8 +531,7 @@ function Copilot:ask(prompt, opts)
log.debug('Tokenizer: ' .. tokenizer)
tiktoken_load(tokenizer)

local selection_message =
generate_selection_message(filename, filetype, start_row, end_row, selection)
local selection_message = generate_selection_message(filename, filetype, selection)
local embeddings_message = generate_embeddings_message(embeddings)

-- Count required tokens that we cannot reduce
Expand Down
Loading

0 comments on commit 8bc3106

Please sign in to comment.