aboutsummaryrefslogtreecommitdiffstats
path: root/src/files_map-editor.adb
diff options
context:
space:
mode:
authorTristan Gingold <tgingold@free.fr>2018-11-23 19:33:55 +0100
committerTristan Gingold <tgingold@free.fr>2018-11-23 19:33:55 +0100
commit021f946659b84a9803ae9148cc1315059f5dfc15 (patch)
tree86424ff98028e5755e5b384dbb10f33097fe2489 /src/files_map-editor.adb
parent1830a66709ad88f74832c6e92fc68f8e75d00089 (diff)
downloadghdl-021f946659b84a9803ae9148cc1315059f5dfc15.tar.gz
ghdl-021f946659b84a9803ae9148cc1315059f5dfc15.tar.bz2
ghdl-021f946659b84a9803ae9148cc1315059f5dfc15.zip
Add files_map-editor for libghdl.
Diffstat (limited to 'src/files_map-editor.adb')
-rw-r--r--src/files_map-editor.adb175
1 files changed, 175 insertions, 0 deletions
diff --git a/src/files_map-editor.adb b/src/files_map-editor.adb
new file mode 100644
index 000000000..a1f673e83
--- /dev/null
+++ b/src/files_map-editor.adb
@@ -0,0 +1,175 @@
+package body Files_Map.Editor is
+ -- Compute the number of character in FILE between [START_POS; END_POS)
+ function Get_Range_Length (File : Source_File_Entry;
+ Start_Pos : Source_Ptr;
+ End_Pos : Source_Ptr) return Source_Ptr
+ is
+ pragma Assert (File <= Source_Files.Last);
+ F : Source_File_Record renames Source_Files.Table (File);
+ Res : Source_Ptr;
+ begin
+ pragma Assert (End_Pos >= Start_Pos);
+ pragma Assert (End_Pos <= F.File_Length);
+ Res := End_Pos - Start_Pos;
+
+ -- Returns now if the gap is outside the range.
+ if F.Gap_Last < Start_Pos or else F.Gap_Start >= End_Pos then
+ return Res;
+ end if;
+
+ -- Check the gap is completly within the range.
+ if F.Gap_Last > End_Pos or else F.Gap_Start <= Start_Pos then
+ raise Internal_Error;
+ end if;
+
+ return Res - (F.Gap_Last - F.Gap_Start + 1);
+ end Get_Range_Length;
+
+ -- Move the gap to the end of LINE.
+ procedure Move_Gap (File : Source_File_Entry;
+ Line : Positive)
+ is
+ use Lines_Tables;
+
+ F : Source_File_Record renames Source_Files.Table (File);
+ New_Start : Source_Ptr;
+ Gap_Len : Source_Ptr;
+ 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.
+ return;
+ end if;
+ else
+ New_Start := Line_To_Position (File, Line + 1);
+ if New_Start = F.Gap_Last + 1 then
+ -- No move (the gap is already at end of LINE).
+ return;
+ end if;
+ end if;
+
+ Gap_Len := F.Gap_Last - F.Gap_Start + 1;
+
+ if New_Start < F.Gap_Start then
+ -- The gap is moved toward the start of the file by DIFF bytes:
+ -- | [A][XXXX] |
+ -- => | [XXXX][B] |
+ Diff := F.Gap_Start - New_Start;
+ -- Move [A] to [B].
+ F.Source (F.Gap_Last - Diff + 1 .. F.Gap_Last) :=
+ F.Source (New_Start .. New_Start + Diff - 1);
+ -- Renumber
+ -- Lines starting from line + 1 until location < Gap_Start should
+ -- have their location added by gap_len.
+ for L in Line + 1 .. Last (F.Lines) loop
+ exit when F.Lines.Table (L) >= F.Gap_Start;
+ F.Lines.Table (L) := F.Lines.Table (L) + Gap_Len;
+ end loop;
+ else
+ -- The gap is moved toward the end of the file by DIFF bytes.
+ -- | [XXXX][A] |
+ -- => | [B][XXXX] |
+ Diff := New_Start - F.Gap_Start;
+ -- 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);
+ -- Renumber
+ -- Lines starting from LINE downto location > Gap_Start should have
+ -- their location substracted by gap_len.
+ for L in reverse 1 .. Line loop
+ exit when F.Lines.Table (L) <= F.Gap_Start;
+ F.Lines.Table (L) := F.Lines.Table (L) - Gap_Len;
+ end loop;
+ end if;
+
+ -- Adjust gap.
+ F.Gap_Start := New_Start;
+ F.Gap_Last := New_Start + Gap_Len - 1;
+ end Move_Gap;
+
+ procedure Replace_Text (File : Source_File_Entry;
+ Start_Line : Positive;
+ Start_Off : Natural;
+ End_Line : Positive;
+ End_Off : Natural;
+ Text : String)
+ 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 (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
+ declare
+ Move_Len : constant Source_Ptr := F.Gap_Start - End_Pos;
+ New_Pos : constant Source_Ptr := Start_Pos + Text_Size;
+ 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;
+
+ -- Renumber.
+ if Start_Line /= End_Line then
+ -- Not handled.
+ -- FIXME: also handle newlines in TEXT.
+ raise Internal_Error;
+ end if;
+ end Replace_Text;
+
+ 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) is
+ begin
+ Replace_Text (File, Start_Line, Start_Off, End_Line, End_Off,
+ Str (1 .. Str_Len));
+ end Replace_Text_Ptr;
+
+ procedure Set_Gap (File : Source_File_Entry;
+ First : Source_Ptr;
+ Last : Source_Ptr)
+ is
+ pragma Assert (File <= Source_Files.Last);
+ F : Source_File_Record renames Source_Files.Table (File);
+ begin
+ F.Gap_Start := First;
+ F.Gap_Last := Last;
+ end Set_Gap;
+end Files_Map.Editor;