-
-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: adding mini.diff module as inline diff mechanism #210
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
e50fa04
feat: adding mini.diff module
bassamsdata b2f5e02
fix: fixing the diff against original buffer
bassamsdata 77dff08
fix: safely call mini.diff plugin
bassamsdata bf1c10e
fix: safely check mini.diff, check if tests pass
bassamsdata a4d138d
feat: add diff_method to the config
bassamsdata f8853e3
docs: adding mini.diff to the README
bassamsdata 6f2b3c4
fix: rename miniDiff to mini_diff for consistency
bassamsdata File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
-- implementing mini.diff for llm changes | ||
-- to see the main logic, please head to the setup function below | ||
local M = {} | ||
|
||
local original_buffer_content = {} -- Store the original buffer content | ||
local codecompanion_buffers = {} -- Store which buffers are using CodeCompanion source | ||
local revert_timers = {} -- Store timers for reverting to Git source | ||
local log = require("codecompanion.utils.log") | ||
local ok, MiniDiff = pcall(require, "mini.diff") | ||
if not ok then | ||
return log:error("Failed to load mini.diff: ", vim.log.levels.WARN) | ||
end | ||
local git_source = MiniDiff.gen_source.git() | ||
local REVERT_DELAY = 5 * 60 * 1000 -- 5 minutes | ||
|
||
---@param buf_id number | ||
---@return boolean Whether | ||
local function is_valid_buffer(buf_id) | ||
return buf_id and vim.api.nvim_buf_is_valid(buf_id) | ||
end | ||
|
||
-- store a buffer variable to know which buffer is using codecompanion | ||
---@param buf_id number | ||
---@param source string | ||
local function set_diff_source(buf_id, source) | ||
if is_valid_buffer(buf_id) then | ||
log:debug("Setting diff source for buffer %d to '%s'", buf_id, source) | ||
vim.b[buf_id].diffCompGit = source | ||
else | ||
log:debug("Attempted to set diff source for invalid buffer %d", buf_id) | ||
end | ||
end | ||
|
||
-- Define the codecompanion source for mini.diff to use | ||
---@see https://github.com/echasnovski/mini.nvim/blob/main/doc/mini_diff.txt | ||
---@class CodeCompanionSource | ||
---@field name string | ||
---@field attach fun(buf_id: number): boolean | ||
---@field detach fun(buf_id: number) | ||
local codecompanion_source = { | ||
name = "codecompanion", | ||
---@param buf_id number | ||
---@return boolean whether the attachment was successful | ||
attach = function(buf_id) | ||
if not is_valid_buffer(buf_id) then | ||
return false | ||
end | ||
original_buffer_content[buf_id] = vim.api.nvim_buf_get_lines(buf_id, 0, -1, false) | ||
log:trace("original_buffer_content assinged" .. "step 0") | ||
set_diff_source(buf_id, "llm") | ||
return true | ||
end, | ||
---@param buf_id number | ||
detach = function(buf_id) | ||
original_buffer_content[buf_id] = nil | ||
log:trace("original_buffer_content detached" .. "step 1") | ||
set_diff_source(buf_id, "git") | ||
end, | ||
} | ||
|
||
-- used to switch back to diff agaist llm changes | ||
---@param buf_id number | ||
function M.switch_to_codecompanion(buf_id) | ||
if not codecompanion_buffers[buf_id] then | ||
log:debug("Switching buffer %d to CodeCompanion source", buf_id) | ||
codecompanion_buffers[buf_id] = true | ||
MiniDiff.disable(buf_id) | ||
MiniDiff.enable(buf_id, { source = codecompanion_source }) | ||
M.update_diff(buf_id) | ||
set_diff_source(buf_id, "llm") | ||
else | ||
log:debug("Buffer %d is already using CodeCompanion source", buf_id) | ||
end | ||
end | ||
|
||
-- used to switch back to diff agaist git, used in 'gr' and 'ga' or by timer. | ||
---@param buf_id number | ||
function M.switch_to_git(buf_id) | ||
if codecompanion_buffers[buf_id] then | ||
log:debug("Switching buffer %d to Git source", buf_id) | ||
codecompanion_buffers[buf_id] = nil | ||
MiniDiff.disable(buf_id) | ||
MiniDiff.enable(buf_id, { source = git_source }) | ||
set_diff_source(buf_id, "git") | ||
else | ||
log:debug("Buffer %d is already using Git source", buf_id) | ||
end | ||
end | ||
|
||
function M.update_diff(buf_id) | ||
if not is_valid_buffer(buf_id) then | ||
return | ||
end | ||
|
||
local current_content = vim.api.nvim_buf_get_lines(buf_id, 0, -1, false) | ||
pcall(MiniDiff.set_ref_text, buf_id, original_buffer_content[buf_id] or {}) | ||
original_buffer_content[buf_id] = current_content | ||
log:trace("original_buffer_content assinged " .. "step 1") | ||
end | ||
|
||
-- this function is called every time the diff is updated to schedule return | ||
-- to default behaviour of mini.diff against git, if the user didn't already | ||
-- press 'gr' to reject or 'ga' to accept. | ||
---@param buf_id number | ||
---@param delay number | ||
function M.schedule_revert_to_git(buf_id, delay) | ||
if revert_timers[buf_id] then | ||
log:debug("Stopping existing revert timer for buffer %d", buf_id) | ||
revert_timers[buf_id]:stop() | ||
end | ||
log:debug("Scheduling revert to Git source for buffer %d in %d milliseconds", buf_id, delay) | ||
revert_timers[buf_id] = vim.defer_fn(function() | ||
M.switch_to_git(buf_id) | ||
revert_timers[buf_id] = nil | ||
end, delay) | ||
end | ||
|
||
-- setup is the main mechanism/logic for this module. | ||
---@param config? table | ||
function M.setup(config) | ||
config = config or {} | ||
-- MiniDiff.setup({ source = git_source }) | ||
|
||
vim.api.nvim_create_autocmd("User", { | ||
pattern = "CodeCompanionInline*", | ||
callback = function(args) | ||
local buf_id = args.buf | ||
if not is_valid_buffer(buf_id) then | ||
return | ||
end | ||
|
||
if args.match == "CodeCompanionInlineStarted" then | ||
M.switch_to_codecompanion(buf_id) | ||
elseif args.match == "CodeCompanionInlineFinished" then | ||
-- local current_content = vim.api.nvim_buf_get_lines(buf_id, 0, -1, false) | ||
pcall(MiniDiff.set_ref_text, buf_id, original_buffer_content[buf_id] or {}) | ||
-- original_buffer_content[buf_id] = current_content | ||
log:trace("original_buffer_content assinged " .. "step 2") | ||
M.schedule_revert_to_git(buf_id, REVERT_DELAY) | ||
MiniDiff.toggle_overlay() | ||
end | ||
end, | ||
}) | ||
|
||
vim.api.nvim_create_autocmd("BufReadPost", { | ||
callback = function(args) | ||
local buf_id = args.buf | ||
if is_valid_buffer(buf_id) and vim.b[buf_id].diffCompGit == nil then | ||
set_diff_source(buf_id, "git") | ||
end | ||
end, | ||
}) | ||
end | ||
|
||
---@param buf_id number | ||
function M.accept(buf_id) | ||
if not is_valid_buffer(buf_id) then | ||
return | ||
end | ||
|
||
original_buffer_content[buf_id] = vim.api.nvim_buf_get_lines(buf_id, 0, -1, false) | ||
M.update_diff(buf_id) | ||
M.switch_to_git(buf_id) | ||
end | ||
|
||
---@param buf_id number | ||
function M.reject(buf_id) | ||
if not is_valid_buffer(buf_id) then | ||
return | ||
end | ||
|
||
vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, original_buffer_content[buf_id] or {}) | ||
M.update_diff(buf_id) -- NOTE: why do we do this here again | ||
M.switch_to_git(buf_id) | ||
end | ||
|
||
-- APIS --------------------------------------------------------------- | ||
|
||
-- this API function could benefit for the user to know which diff he's using | ||
-- at the current monoment - could be used in the statusline | ||
---@param buf_id? number | ||
---@return string | ||
function M.get_current_source(buf_id) | ||
buf_id = buf_id or vim.api.nvim_get_current_buf() | ||
return vim.b[buf_id].diffCompGit or "git" | ||
end | ||
|
||
-- API to force switch back to git, could used by keymap | ||
---@param buf_id? number | ||
function M.force_git(buf_id) | ||
buf_id = buf_id or vim.api.nvim_get_current_buf() | ||
M.switch_to_git(buf_id) | ||
end | ||
|
||
-- API to force switch to codecompanion, could used by keymap | ||
---@param buf_id? number | ||
function M.force_codecompanion(buf_id) | ||
buf_id = buf_id or vim.api.nvim_get_current_buf() | ||
if not is_valid_buffer(buf_id) then | ||
print("Invalid buffer ID") | ||
return | ||
end | ||
|
||
-- Ensure we have original content to diff against | ||
if not original_buffer_content[buf_id] then | ||
original_buffer_content[buf_id] = vim.api.nvim_buf_get_lines(buf_id, 0, -1, false) | ||
end | ||
|
||
M.switch_to_codecompanion(buf_id) | ||
-- Force an update of the diff | ||
M.update_diff(buf_id) | ||
end | ||
|
||
return M |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should give users the choice of a provider i.e.
default
ormini_diff
. I think we should hardcode therevert_delay
in too.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a setting called
config.display.inline.diff.diff_method
that can be set to eitherdefault
ormini_diff
. I'm not sure if this is what you asking for.I also removed the
revert_delay
setting and replaced it with a hardcoded value.