-- -- Copyright (C) 2022-2023, AdaCore -- -- SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -- with Ada.Containers.Vectors; with Ada.Strings; with Ada.Strings.Fixed; with VSS.Characters; with VSS.Characters.Latin; with VSS.Strings.Conversions; with VSS.String_Vectors; with VSS.Text_Streams; with VSS.Text_Streams.File_Input; with VSS.Text_Streams.File_Output; with VSS.Strings.Cursors.Iterators; with VSS.Strings.Cursors.Iterators.Characters; package body LAL_Refactor.File_Edits is package Positive_Vectors is new Ada.Containers.Vectors (Positive, Positive); subtype Positive_Vector is Positive_Vectors.Vector; procedure Compute_Line_Length (Line : VSS.Strings.Virtual_String; Length : out Natural; Tabs_Length : out Positive_Vector); -- Computes Line's Length by iterating through each character and -- considering that each VSS.Characters.Latin.Character_Tabulation has a -- maximum length of 8. The length of each -- VSS.Characters.Latin.Character_Tabulation also appended to Tabs_Length. ------------------------- -- Compute_Line_Length -- ------------------------- procedure Compute_Line_Length (Line : VSS.Strings.Virtual_String; Length : out Natural; Tabs_Length : out Positive_Vector) is Character_It : VSS.Strings.Cursors.Iterators.Characters.Character_Iterator := Line.Before_First_Character; use type VSS.Characters.Virtual_Character; begin Length := 0; Tabs_Length := Positive_Vectors.Empty_Vector; while Character_It.Forward loop if Character_It.Element = VSS.Characters.Latin.Character_Tabulation then Tabs_Length.Append (8 - (Length mod 8)); Length := @ + (8 - (@ mod 8)); else Length := @ + 1; end if; end loop; end Compute_Line_Length; ----------------- -- Apply_Edits -- ----------------- procedure Apply_Edits (Edits : LAL_Refactor.Text_Edit_Map) is use File_Name_To_Virtual_String_Maps; File_Edits : constant File_Name_To_Virtual_String_Map := Apply_Edits (Edits); Edits_Cursor : Cursor := File_Edits.First; Edits_Length_Image : constant String := Ada.Strings.Fixed.Trim (File_Edits.Length'Image, Ada.Strings.Both); File_Counter : Positive := 1; begin if File_Edits.Is_Empty then return; end if; Refactor_Trace.Trace ("Editing files on disk"); while Has_Element (Edits_Cursor) loop declare Output : VSS.Text_Streams.File_Output.File_Output_Text_Stream; Buffer : constant VSS.Strings.Virtual_String := Element (Edits_Cursor); Ignore : Boolean := True; begin Log_Progress (Current => File_Counter, Total => Edits_Length_Image, Message => "Editing " & Key (Edits_Cursor)); Output.Create (VSS.Strings.Conversions.To_Virtual_String (Key (Edits_Cursor))); Output.Put (Buffer, Ignore); Output.Close; exception when E : others => Refactor_Trace.Trace ("Failed to edit " & Key (Edits_Cursor)); Refactor_Trace.Trace (E); end; Next (Edits_Cursor); File_Counter := @ + 1; end loop; end Apply_Edits; ----------------- -- Apply_Edits -- ----------------- function Apply_Edits (Edits : LAL_Refactor.Text_Edit_Map) return File_Name_To_Virtual_String_Map is use LAL_Refactor.Text_Edit_Ordered_Maps; Edits_Cursor : LAL_Refactor.Text_Edit_Ordered_Maps.Cursor := Edits.First; Result : File_Name_To_Virtual_String_Map; Edits_Length_Image : constant String := Ada.Strings.Fixed.Trim (Edits.Length'Image, Ada.Strings.Both); File_Counter : Positive := 1; begin if Edits.Is_Empty then return Result; end if; Refactor_Trace.Trace ("Found edits for " & Edits_Length_Image & " files"); while Has_Element (Edits_Cursor) loop declare Original_Filename : constant VSS.Strings.Virtual_String := VSS.Strings.Conversions.To_Virtual_String (Key (Edits_Cursor)); Original_File : VSS.Text_Streams.File_Input.File_Input_Text_Stream; Input_Buffer : VSS.Strings.Virtual_String; Output_Buffer : VSS.Strings.Virtual_String; Text_Edits : constant Constant_Reference_Type := Constant_Reference (Edits, Edits_Cursor); Text_Edits_Cursor : Text_Edit_Ordered_Sets.Cursor := Text_Edits.Last; Current_Text_Edit : Text_Edit := Text_Edit_Ordered_Sets.Element (Text_Edits_Cursor); Inside_Text_Edit : Boolean := False; Current_Line_Number : Natural; Current_Column_Number : Natural; Current_Character : VSS.Characters.Virtual_Character; Success : Boolean := True; begin Log_Progress (Current => File_Counter, Total => Edits_Length_Image, Message => "Creating buffer with " & Key (Edits_Cursor) & " edits"); Original_File.Open (Original_Filename, "utf-8"); while not Original_File.Is_End_Of_Stream loop Original_File.Get (Current_Character, Success); Input_Buffer.Append (Current_Character); end loop; Original_File.Close; declare Lines : constant VSS.String_Vectors.Virtual_String_Vector := Input_Buffer.Split_Lines (Keep_Terminator => True); Lines_Iterator : constant VSS.String_Vectors.Reversible_Iterator := VSS.String_Vectors.Iterate (Lines); Lines_Cursor : VSS.String_Vectors.Cursor := VSS.String_Vectors.Last (Lines_Iterator); Current_Line : VSS.Strings.Virtual_String; Current_Line_Length : Natural; Current_Line_Tabs_Size : Positive_Vector; Ignore : Boolean; use type VSS.Characters.Virtual_Character; begin Current_Line_Tabs_Size.Reserve_Capacity (10); Current_Line_Number := Lines.Length; while VSS.String_Vectors.Has_Element (Lines_Cursor) loop Current_Line := VSS.String_Vectors.Element (Lines, Lines_Cursor); Compute_Line_Length (Current_Line, Current_Line_Length, Current_Line_Tabs_Size); Current_Column_Number := Current_Line_Length; declare Current_Line_Character_It : VSS.Strings.Cursors.Iterators.Characters. Character_Iterator := Current_Line.After_Last_Character; begin while Current_Line_Character_It.Backward loop if not Inside_Text_Edit then Output_Buffer.Prepend (Current_Line_Character_It.Element); end if; if Current_Text_Edit /= No_Text_Edit then if Current_Line_Number = Natural (Current_Text_Edit.Location.Start_Line) and then Current_Column_Number = Natural (Current_Text_Edit.Location. Start_Column) then Text_Edit_Ordered_Sets.Previous (Text_Edits_Cursor); if Text_Edit_Ordered_Sets. Has_Element (Text_Edits_Cursor) then Current_Text_Edit := Text_Edit_Ordered_Sets. Element (Text_Edits_Cursor); else Current_Text_Edit := No_Text_Edit; end if; Inside_Text_Edit := False; end if; if Current_Line_Number = Natural (Current_Text_Edit.Location.End_Line) and then Current_Column_Number = Natural (Current_Text_Edit.Location. End_Column) then Output_Buffer.Prepend (VSS.Strings.Conversions.To_Virtual_String (To_String (Current_Text_Edit.Text))); Inside_Text_Edit := True; end if; end if; if Current_Line_Character_It.Element = VSS.Characters.Latin.Character_Tabulation then Current_Column_Number := @ - Current_Line_Tabs_Size.Last_Element; Current_Line_Tabs_Size.Delete_Last; else Current_Column_Number := @ - 1; end if; end loop; end; Lines_Cursor := VSS.String_Vectors.Previous (Lines_Iterator, Lines_Cursor); Current_Line_Number := @ - 1; end loop; end; Result.Insert (Text_Edit_Ordered_Maps.Key (Edits_Cursor), Output_Buffer); exception when E : others => Refactor_Trace.Trace ("Failed to process " & Key (Edits_Cursor) & " edits at" & Current_Line_Number'Image & ":" & Ada.Strings.Fixed.Trim (Current_Column_Number'Image, Ada.Strings.Both)); Refactor_Trace.Trace (E); end; Text_Edit_Ordered_Maps.Next (Edits_Cursor); File_Counter := @ + 1; end loop; return Result; end Apply_Edits; end LAL_Refactor.File_Edits;