Skip to content

Commit 630655b

Browse files
committed
refactor
1 parent 8e3c5eb commit 630655b

File tree

2 files changed

+78
-162
lines changed
  • lua/codecompanion/interactions/chat/tools/builtin/insert_edit_into_file
  • tests/interactions/chat/tools/builtin/insert_edit_into_file

2 files changed

+78
-162
lines changed

lua/codecompanion/interactions/chat/tools/builtin/insert_edit_into_file/init.lua

Lines changed: 78 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ local utils = require("codecompanion.utils")
5959
local api = vim.api
6060
local fmt = string.format
6161

62+
local diff_enabled = config.display.diff.enabled == true
63+
6264
---Load prompt from markdown file
6365
---@return string The prompt content
6466
local function load_prompt()
@@ -74,7 +76,7 @@ local PROMPT = load_prompt()
7476
---@param status "success"|"error"
7577
---@param msg string
7678
---@return table
77-
local function mk_response(status, msg)
79+
local function make_response(status, msg)
7880
return { status = status, data = msg }
7981
end
8082

@@ -139,14 +141,7 @@ local function handle_approval(opts)
139141

140142
return wait.for_decision(opts.diff_id, { "CodeCompanionDiffAccepted", "CodeCompanionDiffRejected" }, function(result)
141143
if result.accepted then
142-
if opts.bufnr then
143-
pcall(function()
144-
api.nvim_buf_call(opts.bufnr, function()
145-
vim.cmd("silent! w")
146-
end)
147-
end)
148-
end
149-
return opts.output_handler(mk_response("success", opts.success_msg))
144+
return opts.output_handler()
150145
end
151146

152147
get_rejection_reason(function(reason)
@@ -165,7 +160,7 @@ local function handle_approval(opts)
165160
end
166161
end
167162

168-
return opts.output_handler(mk_response("error", msg))
163+
return opts.output_handler(make_response("error", msg))
169164
end)
170165
end, wait_opts)
171166
end
@@ -488,20 +483,17 @@ end
488483
---@param opts table
489484
---@return table|nil
490485
local function check_for_conflicts(content, edits, opts)
491-
if not opts.dry_run then
492-
local conflicts = match_selector.detect_edit_conflicts(content, edits)
493-
if #conflicts > 0 then
494-
return {
495-
success = false,
496-
error = "conflicting_edits",
497-
conflicts = conflicts,
498-
conflict_descriptions = vim.tbl_map(function(c)
499-
return c.description
500-
end, conflicts),
501-
}
502-
end
486+
local conflicts = match_selector.detect_edit_conflicts(content, edits)
487+
if #conflicts > 0 then
488+
return {
489+
success = false,
490+
error = "conflicting_edits",
491+
conflicts = conflicts,
492+
conflict_descriptions = vim.tbl_map(function(c)
493+
return c.description
494+
end, conflicts),
495+
}
503496
end
504-
return nil
505497
end
506498

507499
---Validate required fields for a single edit
@@ -667,12 +659,12 @@ local function edit_file(action, chat_bufnr, output_handler, opts)
667659
local path = file_utils.validate_and_normalize_path(action.filepath)
668660

669661
if not path then
670-
return output_handler(mk_response("error", fmt("Error: Invalid or non-existent filepath `%s`", action.filepath)))
662+
return output_handler(make_response("error", fmt("Error: Invalid or non-existent filepath `%s`", action.filepath)))
671663
end
672664

673-
local current_content, read_err, file_info = read_file(path)
674-
if not current_content then
675-
return output_handler(mk_response("error", read_err or "Unknown error reading file"))
665+
local original_content, read_err, file_info = read_file(path)
666+
if not original_content then
667+
return output_handler(make_response("error", read_err or "Unknown error reading file"))
676668
end
677669

678670
if type(action.edits) == "string" then
@@ -682,104 +674,76 @@ local function edit_file(action, chat_bufnr, output_handler, opts)
682674
end
683675
end
684676

685-
if #current_content > constants.LIMITS.FILE_SIZE_MAX then
677+
if #original_content > constants.LIMITS.FILE_SIZE_MAX then
686678
return output_handler(
687-
mk_response(
679+
make_response(
688680
"error",
689681
fmt(
690682
"Error: File too large (%d bytes). Maximum supported size is %d bytes.",
691-
#current_content,
683+
#original_content,
692684
constants.LIMITS.FILE_SIZE_MAX
693685
)
694686
)
695687
)
696688
end
697689

698-
local dry_run = process_edits(current_content, action.edits, {
699-
dry_run = true,
690+
local edit = process_edits(original_content, action.edits, {
700691
path = path,
701692
file_info = file_info,
702693
mode = action.mode,
703694
})
704695

705-
if not dry_run.success then
706-
local error_message = match_selector.format_helpful_error(dry_run, action.edits)
707-
return output_handler(mk_response("error", error_message))
696+
if not edit.success then
697+
local error_message = match_selector.format_helpful_error(edit, action.edits)
698+
return output_handler(make_response("error", error_message))
708699
end
709700

710-
local strategies_summary = table.concat(
711-
vim.tbl_map(function(strategy)
712-
return strategy:gsub("_", " ")
713-
end, dry_run.strategies),
714-
", "
715-
)
716-
717-
if action.dryRun then
718-
local ok, edit_word = pcall(utils.pluralize, #action.edits, "edit")
719-
if not ok then
720-
edit_word = "edit(s)"
721-
end
722-
return output_handler(
723-
mk_response(
724-
"success",
725-
fmt(
726-
"DRY RUN - Successfully processed %d %s using strategies: %s\nFile: `%s`\n\nTo apply these changes, set 'dryRun': false",
727-
#action.edits,
728-
edit_word,
729-
strategies_summary,
730-
action.filepath
731-
)
732-
)
733-
)
734-
end
701+
local success_msg = fmt("Edited `%s` file%s", action.filepath, extract_explanation(action))
735702

736-
local write_ok, write_err = write_file(path, dry_run.content, file_info)
737-
if not write_ok then
738-
return output_handler(mk_response("error", fmt("Error writing to `%s`: %s", action.filepath, write_err)))
703+
local function apply_edits()
704+
local write_ok, write_err = write_file(path, edit.content, file_info)
705+
if not write_ok then
706+
output_handler(make_response("error", fmt("Error writing to `%s`: %s", action.filepath, write_err)))
707+
return
708+
end
709+
output_handler(make_response("success", success_msg))
739710
end
740711

741-
-- If the tool has been approved then skip showing the diff
742-
if approvals:is_approved(chat_bufnr, { tool_name = "insert_edit_into_file" }) then
743-
return output_handler(
744-
mk_response("success", fmt("Edited `%s` file%s", action.filepath, extract_explanation(action)))
745-
)
712+
local approved = approvals:is_approved(chat_bufnr, { tool_name = "insert_edit_into_file" })
713+
if approved or diff_enabled == false then
714+
return apply_edits()
746715
end
747716

717+
-- Show diff for user review
748718
local diff_id = math.random(10000000)
749-
local from_lines = vim.split(current_content, "\n", { plain = true })
750-
local to_lines = vim.split(dry_run.content, "\n", { plain = true })
751-
752-
-- Detect filetype from path
719+
local from_lines = vim.split(original_content, "\n", { plain = true })
720+
local to_lines = vim.split(edit.content, "\n", { plain = true })
753721
local ft = vim.filetype.match({ filename = path }) or "text"
754722

755723
local diff_helpers = require("codecompanion.helpers")
756-
local diff_ui = diff_helpers.show_diff({
724+
diff_helpers.show_diff({
725+
chat_bufnr = chat_bufnr,
726+
diff_id = diff_id,
727+
ft = ft,
757728
from_lines = from_lines,
758729
to_lines = to_lines,
759-
ft = ft,
760730
title = action.filepath,
761-
diff_id = diff_id,
762-
chat_bufnr = chat_bufnr,
763731
tool_name = "insert_edit_into_file",
764732
})
765733

766-
local success_msg = fmt("Edited `%s` file%s", action.filepath, extract_explanation(action))
767-
768734
if opts.require_confirmation_after then
769735
return handle_approval({
770-
diff_id = diff_id,
771736
chat_bufnr = chat_bufnr,
737+
diff_id = diff_id,
772738
name = action.filepath,
773-
diff_ui = diff_ui,
774739
success_msg = success_msg,
775-
on_reject = function()
776-
return write_file(path, current_content, file_info)
740+
output_handler = function()
741+
apply_edits()
777742
end,
778-
output_handler = output_handler,
779743
})
780-
else
781-
return output_handler(mk_response("success", success_msg))
782744
end
745+
746+
return apply_edits()
783747
end
784748

785749
---@param bufnr number
@@ -789,19 +753,17 @@ end
789753
---@param opts table|nil
790754
local function edit_buffer(bufnr, chat_bufnr, action, output_handler, opts)
791755
opts = opts or {}
792-
local diff_id = math.random(10000000)
793756

794757
if not api.nvim_buf_is_loaded(bufnr) then
795758
vim.fn.bufload(bufnr)
796759
end
797760

798761
local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false)
799-
local current_content = table.concat(lines, "\n")
800-
local original_content = vim.deepcopy(lines)
762+
local original_content = table.concat(lines, "\n")
801763

802764
local file_info = {
803-
has_trailing_newline = current_content:match("\n$") ~= nil,
804-
is_empty = current_content == "",
765+
has_trailing_newline = original_content:match("\n$") ~= nil,
766+
is_empty = original_content == "",
805767
}
806768

807769
if type(action.edits) == "string" then
@@ -811,8 +773,7 @@ local function edit_buffer(bufnr, chat_bufnr, action, output_handler, opts)
811773
end
812774
end
813775

814-
local dry_run = process_edits(current_content, action.edits, {
815-
dry_run = true,
776+
local edit = process_edits(original_content, action.edits, {
816777
buffer = bufnr,
817778
file_info = file_info,
818779
mode = action.mode,
@@ -821,59 +782,38 @@ local function edit_buffer(bufnr, chat_bufnr, action, output_handler, opts)
821782
local buffer_name = api.nvim_buf_get_name(bufnr)
822783
local display_name = buffer_name ~= "" and vim.fn.fnamemodify(buffer_name, ":.") or fmt("buffer %d", bufnr)
823784

824-
if not dry_run.success then
825-
local error_message = match_selector.format_helpful_error(dry_run, action.edits)
785+
if not edit.success then
786+
local error_message = match_selector.format_helpful_error(edit, action.edits)
826787
return output_handler(
827-
mk_response("error", fmt("Error processing edits for `%s`:\n%s", display_name, error_message))
788+
make_response("error", fmt("Error processing edits for `%s`:\n%s", display_name, error_message))
828789
)
829790
end
830791

831-
local strategies_summary = table.concat(
832-
vim.tbl_map(function(strategy)
833-
return strategy:gsub("_", " ")
834-
end, dry_run.strategies),
835-
", "
836-
)
837-
838-
if action.dryRun then
839-
local ok, edit_word = pcall(utils.pluralize, #action.edits, "edit")
840-
if not ok then
841-
edit_word = "edit(s)"
842-
end
843-
return output_handler(
844-
mk_response(
845-
"success",
846-
fmt(
847-
"DRY RUN - Successfully processed %d %s using strategies: %s\nBuffer: `%s`\n\nTo apply these changes, set 'dryRun': false",
848-
#action.edits,
849-
edit_word,
850-
strategies_summary,
851-
display_name
852-
)
853-
)
854-
)
855-
end
856-
857-
local final_lines = vim.split(dry_run.content, "\n", { plain = true })
858-
api.nvim_buf_set_lines(bufnr, 0, -1, false, final_lines)
859-
792+
local content = vim.split(edit.content, "\n", { plain = true })
860793
local success_msg = fmt("Edited `%s` buffer%s", display_name, extract_explanation(action))
861794

862-
-- If the tool has been approved then skip showing the diff
863-
if approvals:is_approved(chat_bufnr, { tool_name = "insert_edit_into_file" }) then
795+
local function apply_edits()
796+
api.nvim_buf_set_lines(bufnr, 0, -1, false, content)
864797
api.nvim_buf_call(bufnr, function()
865798
vim.cmd("silent write")
866799
end)
867-
return output_handler(mk_response("success", success_msg))
800+
output_handler(make_response("success", success_msg))
801+
end
802+
803+
local approved = approvals:is_approved(chat_bufnr, { tool_name = "insert_edit_into_file" })
804+
if approved or diff_enabled == false then
805+
return apply_edits()
868806
end
869807

870808
local ft = vim.bo[bufnr].filetype or "text"
871809

810+
-- A user may want to see the diff even though they don't need to approve it
811+
local diff_id = math.random(10000000)
872812
local diff_helpers = require("codecompanion.helpers")
873-
local diff_ui = diff_helpers.show_diff({
813+
diff_helpers.show_diff({
874814
chat_bufnr = chat_bufnr,
875-
from_lines = original_content,
876-
to_lines = final_lines,
815+
from_lines = vim.deepcopy(lines),
816+
to_lines = content,
877817
ft = ft,
878818
title = display_name,
879819
diff_id = diff_id,
@@ -882,17 +822,18 @@ local function edit_buffer(bufnr, chat_bufnr, action, output_handler, opts)
882822

883823
if opts.require_confirmation_after then
884824
return handle_approval({
885-
diff_id = diff_id,
825+
bufnr = bufnr,
886826
chat_bufnr = chat_bufnr,
827+
diff_id = diff_id,
887828
name = display_name,
888-
bufnr = bufnr,
889-
diff_ui = diff_ui,
890829
success_msg = success_msg,
891-
output_handler = output_handler,
830+
output_handler = function()
831+
apply_edits()
832+
end,
892833
})
893834
end
894835

895-
return output_handler(mk_response("success", success_msg))
836+
return apply_edits()
896837
end
897838

898839
---@class CodeCompanion.Tool.EditFile: CodeCompanion.Tools.Tool
@@ -909,7 +850,7 @@ return {
909850
if args.edits then
910851
local fixed_args, error_msg = fix_edits_if_needed(args)
911852
if not fixed_args then
912-
return output_handler(mk_response("error", fmt("Invalid edits format: %s", error_msg)))
853+
return output_handler(make_response("error", fmt("Invalid edits format: %s", error_msg)))
913854
end
914855
args = fixed_args
915856
end
@@ -958,11 +899,6 @@ return {
958899
additionalProperties = false,
959900
},
960901
},
961-
dryRun = {
962-
type = "boolean",
963-
default = false,
964-
description = "When true, validates edits and shows what would be changed without applying them. Only use when explicitly requested.",
965-
},
966902
mode = {
967903
type = "string",
968904
enum = { "append", "overwrite" },
@@ -974,7 +910,7 @@ return {
974910
description = "Brief explanation of what the edits accomplish",
975911
},
976912
},
977-
required = { "filepath", "edits", "explanation", "mode", "dryRun" },
913+
required = { "filepath", "edits", "explanation", "mode" },
978914
additionalProperties = false,
979915
},
980916
strict = true,

0 commit comments

Comments
 (0)