diff options
-rw-r--r-- | src/files_map-editor.adb | 385 | ||||
-rw-r--r-- | src/files_map-editor.ads | 16 | ||||
-rw-r--r-- | src/types.ads | 5 |
3 files changed, 245 insertions, 161 deletions
diff --git a/src/files_map-editor.adb b/src/files_map-editor.adb index 885bc1bc1..54e0c044f 100644 --- a/src/files_map-editor.adb +++ b/src/files_map-editor.adb @@ -16,9 +16,104 @@ -- Software Foundation, 59 Temple Place - Suite 330, Boston, MA -- 02111-1307, USA. -with Ada.Text_IO; +with Logging; use Logging; package body Files_Map.Editor is + -- Count the length of newlines at position P in TEXT. + -- Result can be: + -- 1 for (LF, CR) + -- 2 for (LF+CR or CR+LF), + -- 0 for non-newlines. + function Is_Newline (Text : File_Buffer; P : Source_Ptr) return Natural is + begin + if Text (P) = ASCII.CR then + if P < Text'Last and then Text (P + 1) = ASCII.LF then + return 2; + else + return 1; + end if; + elsif Text (P) = ASCII.LF then + if P < Text'Last and then Text (P + 1) = ASCII.CR then + return 2; + else + return 1; + end if; + else + return 0; + end if; + end Is_Newline; + + procedure Compute_Lines (File : Source_File_Entry) + is + pragma Assert (File <= Source_Files.Last); + F : Source_File_Record renames Source_Files.Table (File); + L : Positive; + P : Source_Ptr; + Nl : Natural; + begin + Lines_Tables.Init (F.Lines); + + L := 1; + P := Source_Ptr_Org; + loop + File_Add_Line_Number (File, L, P); + L := L + 1; + + loop + Nl := Is_Newline (F.Source.all, P); + if Nl = 0 then + P := P + 1; + else + P := P + Source_Ptr (Nl); + exit; + end if; + exit when P = F.Source'Last; + end loop; + + Skip_Gap (File, P); + exit when P = F.Source'Last; + end loop; + end Compute_Lines; + + procedure Check_Buffer_Lines (File : Source_File_Entry) + is + pragma Assert (File <= Source_Files.Last); + F : Source_File_Record renames Source_Files.Table (File); + L : Positive; + P : Source_Ptr; + Nl : Natural; + begin + L := 1; + P := Source_Ptr_Org; + Main_Loop: loop + if F.Lines.Table (L) /= P then + Log_Line ("offset mismatch for line" & Natural'Image (L)); + end if; + + exit Main_Loop when P = F.File_Length; + + -- Skip until eol. + loop + Nl := Is_Newline (F.Source.all, P); + if Nl = 0 then + P := P + 1; + else + P := P + Source_Ptr (Nl); + exit; + end if; + exit Main_Loop when P = F.File_Length; + end loop; + + Skip_Gap (File, P); + + L := L + 1; + end loop Main_Loop; + + if Lines_Tables.Last (F.Lines) /= L then + Log_Line ("incorrect number of lines"); + end if; + end Check_Buffer_Lines; + -- Compute the number of character in FILE between [START_POS; END_POS) function Get_Range_Length (File : Source_File_Entry; Start_Pos : Source_Ptr; @@ -57,11 +152,13 @@ package body Files_Map.Editor is Diff : Source_Ptr; begin if Line = Lines_Tables.Last (F.Lines) then - New_Start := F.File_Length; - if New_Start = F.Gap_Start then - -- No move. + -- Moved to the end of the buffer. + if F.Gap_Start >= F.File_Length then + pragma Assert (F.Gap_Start = F.File_Length + 2); + -- Already there. return; end if; + New_Start := F.Source'Last - F.Gap_Last + F.Gap_Start; else New_Start := Line_To_Position (File, Line + 1); if New_Start = F.Gap_Last + 1 then @@ -80,6 +177,11 @@ package body Files_Map.Editor is -- Move [A] to [B]. F.Source (F.Gap_Last - Diff + 1 .. F.Gap_Last) := F.Source (New_Start .. New_Start + Diff - 1); + + if F.Gap_Start >= F.File_Length then + F.File_Length := F.File_Length + Gap_Len; + end if; + -- Renumber -- Lines starting from line + 1 until location < Gap_Start should -- have their location added by gap_len. @@ -95,6 +197,11 @@ package body Files_Map.Editor is -- Move [A] to [B]. F.Source (F.Gap_Start .. F.Gap_Start + Diff - 1) := F.Source (F.Gap_Last + 1 .. F.Gap_Last + 1 + Diff - 1); + + if New_Start + Gap_Len >= F.File_Length then + F.File_Length := F.File_Length - Gap_Len; + end if; + -- Renumber -- Lines starting from LINE downto location > Gap_Start should have -- their location substracted by gap_len. @@ -109,34 +216,10 @@ package body Files_Map.Editor is F.Gap_Last := New_Start + Gap_Len - 1; end Move_Gap; - -- Count the length of newlines at position P in TEXT. - -- Result can be: - -- 1 for (LF, CR) - -- 2 for (LF+CR or CR+LF), - -- 0 for non-newlines. - function Is_Newline (Text : String; P : Positive) return Natural is - begin - if Text (P) = ASCII.CR then - if P < Text'Last and then Text (P + 1) = ASCII.LF then - return 2; - else - return 1; - end if; - elsif Text (P) = ASCII.LF then - if P < Text'Last and then Text (P + 1) = ASCII.CR then - return 2; - else - return 1; - end if; - else - return 0; - end if; - end Is_Newline; - -- Count the number of newlines (LF, CR, LF+CR or CR+LF) in TEXT. - function Count_Newlines (Text : String) return Natural + function Count_Newlines (Text : File_Buffer) return Natural is - P : Positive; + P : Source_Ptr; Res : Natural; R : Natural; begin @@ -145,7 +228,7 @@ package body Files_Map.Editor is while P <= Text'Last loop R := Is_Newline (Text, P); if R > 0 then - P := P + R; + P := P + Source_Ptr (R); Res := Res + 1; else P := P + 1; @@ -160,94 +243,106 @@ package body Files_Map.Editor is Start_Off : Natural; End_Line : Positive; End_Off : Natural; - Text : String) + Text : File_Buffer) is pragma Assert (File <= Source_Files.Last); F : Source_File_Record renames Source_Files.Table (File); - Start_Pos : constant Source_Ptr := - Line_To_Position (File, Start_Line) + Source_Ptr (Start_Off); - End_Pos : constant Source_Ptr := - Line_To_Position (File, End_Line) + Source_Ptr (End_Off); - Text_Size : constant Source_Ptr := Text'Length; - Gap_Size : Source_Ptr; - Range_Size : Source_Ptr; begin - Gap_Size := F.Gap_Last - F.Gap_Start + 1; - Range_Size := Get_Range_Length (File, Start_Pos, End_Pos); - - -- Check there is enough space. - if Text_Size > Gap_Size + Range_Size then - raise Constraint_Error; - end if; - - -- Move gap + -- Move gap to the end of end_line. At least a part of End_Line + -- remains as the end position is exclusive but valid. Move_Gap (File, End_Line); - -- Replace text, handle new lines. - -- Deletion. - -- End_Pos --| |---- Gap_Start - -- | [ABCDEFGHn][XXX] | - -- => | [ABRGHn][XXXXXX] | - -- |-- Start_Pos - -- - -- Insertion - -- End_Pos --| |---- Gap_Start - -- | [ABCDn][XXXXX] | - -- => | [ABRRRDn][XXX] | - -- |-- Start_Pos + -- The gap has moved, so every offset may have changed... declare - Move_Len : constant Source_Ptr := F.Gap_Start - End_Pos; - New_Pos : constant Source_Ptr := Start_Pos + Text_Size; + Start_Pos : constant Source_Ptr := + Line_To_Position (File, Start_Line) + Source_Ptr (Start_Off); + End_Pos : constant Source_Ptr := + Line_To_Position (File, End_Line) + Source_Ptr (End_Off); + Text_Len : constant Source_Ptr := Text'Length; + Gap_Size : constant Source_Ptr := F.Gap_Last - F.Gap_Start + 1; + Range_Size : Source_Ptr; begin - F.Source (New_Pos .. New_Pos + Move_Len - 1) := - F.Source (End_Pos .. End_Pos + Move_Len - 1); - -- FIXME: clear gap when extended. - F.Gap_Start := New_Pos + Move_Len; - -- Copy. - F.Source (Start_Pos .. Start_Pos + Text_Size - 1) := - File_Buffer (Text); - end; + Range_Size := Get_Range_Length (File, Start_Pos, End_Pos); - -- Renumber. - declare - use Lines_Tables; - Text_Lines : constant Natural := Count_Newlines (Text); - Orig_Lines : constant Natural := End_Line - Start_Line; - Diff : constant Integer := Text_Lines - Orig_Lines; - Orig_Last : constant Natural := Last (F.Lines); - P : Positive; - Nl_Len : Natural; - L : Natural; - begin - -- No change in newlines. - if Text_Lines = 0 and then Orig_Lines = 0 then - return; + -- Check there is enough space. + if Text_Len > Gap_Size + Range_Size then + raise Constraint_Error; end if; - -- Make room for lines table. - if Diff /= 0 then - if Diff > 0 then - Set_Last (F.Lines, Orig_Last + Diff); - end if; - F.Lines.Table (End_Line + Diff .. Orig_Last + Diff) := - F.Lines.Table (End_Line .. Orig_Last); - if Diff < 0 then - Set_Last (F.Lines, Orig_Last + Diff); + -- Replace text, handle new lines. + -- Deletion. + -- End_Pos --| |---- Gap_Start + -- | [ABCDEFGHn][XXX] | + -- => | [ABRGHn][XXXXXX] | + -- |-- Start_Pos + -- + -- Insertion + -- End_Pos --| |---- Gap_Start + -- | [ABCDn][XXXXX] | + -- => | [ABRRRDn][XXX] | + -- |-- Start_Pos + declare + Move_Len : constant Source_Ptr := F.Gap_Start - End_Pos; + New_Pos : constant Source_Ptr := Start_Pos + Text_Len; + begin + F.Source (New_Pos .. New_Pos + Move_Len - 1) := + F.Source (End_Pos .. End_Pos + Move_Len - 1); + -- Copy. + F.Source (Start_Pos .. Start_Pos + Text_Len - 1) := Text; + + -- If the gap was after the end of the file, then adjust end of + -- file. + if F.Gap_Start > F.File_Length then + F.File_Length := F.File_Length + Text_Len - Range_Size; end if; - end if; + + -- FIXME: clear gap when extended. + F.Gap_Start := New_Pos + Move_Len; + end; -- Renumber. - P := Text'First; - L := Start_Line + 1; - while P <= Text'Last loop - Nl_Len := Is_Newline (Text, P); - if Nl_Len = 0 then - P := P + 1; - else - P := P + Nl_Len; - F.Lines.Table (L) := Start_Pos + Source_Ptr (P - Text'First); + declare + use Lines_Tables; + Text_Lines : constant Natural := Count_Newlines (Text); + Orig_Lines : constant Natural := End_Line - Start_Line; + Diff : constant Integer := Text_Lines - Orig_Lines; + Orig_Last : constant Natural := Last (F.Lines); + P : Source_Ptr; + Nl_Len : Natural; + L : Natural; + begin + -- No change in newlines. + if Text_Lines = 0 and then Orig_Lines = 0 then + return; end if; - end loop; + + -- Make room for lines table. + if Diff /= 0 then + if Diff > 0 then + Set_Last (F.Lines, Orig_Last + Diff); + end if; + F.Lines.Table (End_Line + 1 + Diff .. Orig_Last + Diff) := + F.Lines.Table (End_Line + 1 .. Orig_Last); + if Diff < 0 then + Set_Last (F.Lines, Orig_Last + Diff); + end if; + end if; + + -- Renumber. + P := Text'First; + L := Start_Line + 1; + while P <= Text'Last loop + Nl_Len := Is_Newline (Text, P); + if Nl_Len = 0 then + P := P + 1; + else + P := P + Source_Ptr (Nl_Len); + F.Lines.Table (L) := Start_Pos + (P - Text'First); + end if; + end loop; + + Check_Buffer_Lines (File); + end; end; end Replace_Text; @@ -256,11 +351,11 @@ package body Files_Map.Editor is Start_Off : Natural; End_Line : Positive; End_Off : Natural; - Str : Thin_String_Ptr; - Str_Len : Natural) is + Text_Ptr : File_Buffer_Ptr; + Text_Len : Source_Ptr) is begin Replace_Text (File, Start_Line, Start_Off, End_Line, End_Off, - Str (1 .. Str_Len)); + Text_Ptr (0 .. Text_Len - 1)); end Replace_Text_Ptr; procedure Set_Gap (File : Source_File_Entry; @@ -275,36 +370,51 @@ package body Files_Map.Editor is end Set_Gap; procedure Check_Buffer_Content (File : Source_File_Entry; - Str : Thin_String_Ptr; - Str_Len : Natural) + Str : File_Buffer_Ptr; + Str_Len : Source_Ptr) is - use Ada.Text_IO; pragma Assert (File <= Source_Files.Last); F : Source_File_Record renames Source_Files.Table (File); begin -- Check length. declare - Buf_Len : constant Source_Ptr := - F.File_Length - (F.Gap_Last - F.Gap_Start); + Buf_Len : Source_Ptr; begin - if Str_Len /= Natural (Buf_Len) then - Put_Line (Standard_Error, - "length mismatch (text:" & Natural'Image (Str_Len) + Buf_Len := F.File_Length; + if F.Gap_Start < F.File_Length then + Buf_Len := F.File_Length - (F.Gap_Last + 1 - F.Gap_Start); + if F.File_Length + 1 /= F.Source'Last then + Log_Line ("bad file length"); + end if; + else + if F.Gap_Start /= F.File_Length + 2 then + Log_Line ("bad position of gap at end of file"); + end if; + end if; + if Str_Len /= Buf_Len then + Log_Line ("length mismatch - text:" & Source_Ptr'Image (Str_Len) & ", buffer:" & Source_Ptr'Image (Buf_Len)); end if; end; + -- Check presence of EOT. + if F.Source (F.File_Length) /= EOT then + Log_Line ("missing first EOT"); + end if; + if F.Source (F.File_Length + 1) /= EOT then + Log_Line ("missing second EOT"); + end if; + -- Check content. declare - T_Pos : Natural; + T_Pos : Source_Ptr; S_Pos : Source_Ptr; begin - T_Pos := Str'First; + T_Pos := Source_Ptr_Org; S_Pos := Source_Ptr_Org; - while T_Pos <= Str_Len loop + while T_Pos < Str_Len loop if F.Source (S_Pos) /= Str (T_Pos) then - Put_Line (Standard_Error, - "difference at offset" & Natural'Image (T_Pos)); + Log_Line ("difference at offset" & Source_Ptr'Image (T_Pos)); exit; end if; T_Pos := T_Pos + 1; @@ -316,40 +426,7 @@ package body Files_Map.Editor is end; -- Check lines. - declare - L : Positive; - P : Source_Ptr; - Nl : Natural; - subtype Buf_Subtype is String (1 .. Natural (F.File_Length)); - begin - L := 1; - P := Source_Ptr_Org; - loop - if F.Lines.Table (L) /= P then - Put_Line (Standard_Error, - "offset mismatch for line" & Natural'Image (L)); - end if; - L := L + 1; - - loop - Nl := Is_Newline - (Buf_Subtype (F.Source (Source_Ptr_Org - .. F.File_Length - 1)), - Positive (1 + P)); - if Nl = 0 then - P := P + 1; - else - P := P + Source_Ptr (Nl); - exit; - end if; - end loop; - - if P = F.Gap_Start then - P := F.Gap_Last + 1; - end if; - exit when P = F.File_Length; - end loop; - end; + Check_Buffer_Lines (File); end Check_Buffer_Content; end Files_Map.Editor; diff --git a/src/files_map-editor.ads b/src/files_map-editor.ads index e2e533110..5e2cb21e7 100644 --- a/src/files_map-editor.ads +++ b/src/files_map-editor.ads @@ -22,23 +22,29 @@ package Files_Map.Editor is Start_Off : Natural; End_Line : Positive; End_Off : Natural; - Text : String); + Text : File_Buffer); procedure Replace_Text_Ptr (File : Source_File_Entry; Start_Line : Positive; Start_Off : Natural; End_Line : Positive; End_Off : Natural; - Str : Thin_String_Ptr; - Str_Len : Natural); + Text_Ptr : File_Buffer_Ptr; + Text_Len : Source_Ptr); -- Set the position of the GAP in FILE. procedure Set_Gap (File : Source_File_Entry; First : Source_Ptr; Last : Source_Ptr); + -- Recompute lines number. + procedure Compute_Lines (File : Source_File_Entry); + + -- Check lines of FILE are correct. + procedure Check_Buffer_Lines (File : Source_File_Entry); + -- Check that content of FILE is STR[1 .. STR_LEN]. procedure Check_Buffer_Content (File : Source_File_Entry; - Str : Thin_String_Ptr; - Str_Len : Natural); + Str : File_Buffer_Ptr; + Str_Len : Source_Ptr); end Files_Map.Editor; diff --git a/src/types.ads b/src/types.ads index 876b3ca01..e7a7439b7 100644 --- a/src/types.ads +++ b/src/types.ads @@ -95,8 +95,9 @@ package Types is type Source_File_Entry is new Uns32; No_Source_File_Entry: constant Source_File_Entry := 0; - -- Index into a file buffer. - type Source_Ptr is new Uns32; + -- Index into a file buffer. Use a signed integers, so that empty string + -- works correctly. + type Source_Ptr is new Int32 range 0 .. Int32'Last; -- Valid bounds of any file buffer. Source_Ptr_Org : constant Source_Ptr := 0; |