@@ -59,6 +59,8 @@ local utils = require("codecompanion.utils")
5959local api = vim .api
6060local 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
6466local 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 }
7981end
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 )
171166end
@@ -488,20 +483,17 @@ end
488483--- @param opts table
489484--- @return table | nil
490485local 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
505497end
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\n File: `%s`\n\n To 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 ()
783747end
784748
785749--- @param bufnr number
@@ -789,19 +753,17 @@ end
789753--- @param opts table | nil
790754local 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\n Buffer: `%s`\n\n To 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 ( )
896837end
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