Skip to content

Commit 0032ccf

Browse files
ogorodnikAnthonyLeonardoGracio
authored andcommitted
V628-024 (2) Symbol diff has been added
1 parent 50cdb69 commit 0032ccf

File tree

11 files changed

+840
-47
lines changed

11 files changed

+840
-47
lines changed

source/ada/lsp-ada_documents.adb

Lines changed: 346 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ with GNATCOLL.VFS;
2525

2626
with VSS.Characters;
2727
with VSS.Strings.Conversions;
28+
with VSS.Strings.Cursors.Iterators.Characters;
2829

2930
with Langkit_Support.Symbols;
3031
with Langkit_Support.Text;
@@ -607,6 +608,323 @@ package body LSP.Ada_Documents is
607608
end;
608609
end Diff;
609610

611+
------------------
612+
-- Diff_Symbols --
613+
------------------
614+
615+
procedure Diff_Symbols
616+
(Self : Document;
617+
Span : LSP.Messages.Span;
618+
New_Text : VSS.Strings.Virtual_String;
619+
Edit : out LSP.Messages.TextEdit_Vector)
620+
is
621+
use LSP.Types;
622+
use LSP.Messages;
623+
use VSS.Strings;
624+
use VSS.Characters;
625+
626+
Old_Text : VSS.Strings.Virtual_String;
627+
Old_Lines : VSS.String_Vectors.Virtual_String_Vector;
628+
Old_Line : VSS.Strings.Virtual_String;
629+
Old_Length, New_Length : Natural;
630+
631+
First_Marker : VSS.Strings.Markers.Character_Marker;
632+
Last_Marker : VSS.Strings.Markers.Character_Marker;
633+
634+
begin
635+
Self.Span_To_Markers (Span, First_Marker, Last_Marker);
636+
637+
Old_Text := Self.Text.Slice (First_Marker, Last_Marker);
638+
Old_Lines := Old_Text.Split_Lines
639+
(Terminators => LSP_New_Line_Function_Set,
640+
Keep_Terminator => True);
641+
Old_Line := Old_Lines.Element (Old_Lines.Length);
642+
643+
Old_Length := Integer (Character_Length (Old_Text));
644+
New_Length := Integer (Character_Length (New_Text));
645+
646+
declare
647+
type LCS_Array is array
648+
(Natural range 0 .. Old_Length,
649+
Natural range 0 .. New_Length) of Integer;
650+
type LCS_Array_Access is access all LCS_Array;
651+
652+
procedure Free is
653+
new Ada.Unchecked_Deallocation (LCS_Array, LCS_Array_Access);
654+
655+
LCS : LCS_Array_Access := new LCS_Array;
656+
Match : Integer;
657+
Delete : Integer;
658+
Insert : Integer;
659+
660+
Old_Char : VSS.Strings.Cursors.Iterators.Characters.
661+
Character_Iterator := Old_Text.At_First_Character;
662+
New_Char : VSS.Strings.Cursors.Iterators.Characters.
663+
Character_Iterator := New_Text.At_First_Character;
664+
665+
Dummy : Boolean;
666+
667+
Old_Index, New_Index : Integer;
668+
669+
Changed_Block_Text : VSS.Strings.Virtual_String;
670+
Changed_Block_Span : LSP.Messages.Span := ((0, 0), (0, 0));
671+
Span_Set : Boolean := False;
672+
673+
-- to calculate span
674+
Current_Line_Number : Line_Number :=
675+
(if Natural (Span.last.character) = 0
676+
then Span.last.line - 1
677+
else Span.last.line);
678+
-- we do not have a line at all when the range end is on the
679+
-- begin of a line, so set Current_Line_Number to the previous one
680+
Old_Lines_Number : Natural := Old_Lines.Length;
681+
Cursor : VSS.Strings.Cursors.Iterators.Characters.
682+
Character_Iterator := Old_Line.After_Last_Character;
683+
684+
procedure Backward;
685+
-- Move old line Cursor backward, update Old_Line and
686+
-- Old_Lines_Number if needed
687+
688+
function Get_Position (Insert : Boolean) return Position;
689+
-- get Position for a Span based on Cursor to prepare first/last
690+
-- position for changes
691+
692+
procedure Prepare_Last_Span (Insert : Boolean);
693+
-- Store position based on Cursor to Changed_Block_Span.last if
694+
-- it is not stored yet
695+
696+
procedure Prepare_Change
697+
(Insert : Boolean;
698+
Char : VSS.Characters.Virtual_Character);
699+
-- Collect change information for Text_Edit in Changed_Block_Text
700+
-- and Changed_Block_Span
701+
702+
procedure Add_Prepared_Change;
703+
-- Add prepared New_String and corresponding Span into Text_Edit
704+
705+
--------------
706+
-- Backward --
707+
--------------
708+
709+
procedure Backward is
710+
begin
711+
if not Cursor.Backward
712+
and then Old_Lines_Number > 1
713+
then
714+
Current_Line_Number := Current_Line_Number - 1;
715+
Old_Lines_Number := Old_Lines_Number - 1;
716+
Old_Line := Old_Lines.Element (Old_Lines_Number);
717+
Cursor.Set_At_Last (Old_Line);
718+
end if;
719+
720+
Old_Index := Old_Index - 1;
721+
Dummy := Old_Char.Backward;
722+
end Backward;
723+
724+
------------------
725+
-- Get_Position --
726+
------------------
727+
728+
function Get_Position (Insert : Boolean) return Position
729+
is
730+
--------------
731+
-- Backward --
732+
--------------
733+
734+
function Backward return Position;
735+
function Backward return Position is
736+
C : VSS.Strings.Cursors.Iterators.Characters.
737+
Character_Iterator := Old_Line.At_Character (Cursor);
738+
begin
739+
-- "Cursor" is after the current character but we should
740+
-- insert before it
741+
if C.Backward then
742+
return
743+
(line => Current_Line_Number,
744+
character => C.First_UTF16_Offset);
745+
else
746+
return
747+
(line => Current_Line_Number,
748+
character => 0);
749+
end if;
750+
end Backward;
751+
752+
begin
753+
if not Cursor.Has_Element then
754+
return
755+
(line => Current_Line_Number,
756+
character => 0);
757+
758+
elsif Insert then
759+
-- "Cursor" is after the current character but we should
760+
-- insert before it
761+
return Backward;
762+
763+
else
764+
return
765+
(line => Current_Line_Number,
766+
character => Cursor.First_UTF16_Offset);
767+
end if;
768+
end Get_Position;
769+
770+
-----------------------
771+
-- Prepare_Last_Span --
772+
-----------------------
773+
774+
procedure Prepare_Last_Span (Insert : Boolean) is
775+
begin
776+
if not Span_Set then
777+
-- it is the first portion of a changed block so store
778+
-- last position of the changes
779+
Span_Set := True;
780+
Changed_Block_Span.last := Get_Position (Insert);
781+
end if;
782+
end Prepare_Last_Span;
783+
784+
--------------------
785+
-- Prepare_Change --
786+
--------------------
787+
788+
procedure Prepare_Change
789+
(Insert : Boolean;
790+
Char : VSS.Characters.Virtual_Character) is
791+
begin
792+
Prepare_Last_Span (Insert);
793+
-- accumulating new text for the changed block
794+
Changed_Block_Text.Prepend (Char);
795+
end Prepare_Change;
796+
797+
-------------------------
798+
-- Add_Prepared_Change --
799+
-------------------------
800+
801+
procedure Add_Prepared_Change is
802+
begin
803+
if not Span_Set then
804+
-- No information for Text_Edit
805+
return;
806+
end if;
807+
808+
Changed_Block_Span.first := Get_Position (False);
809+
810+
LSP.Messages.Prepend
811+
(Edit, LSP.Messages.TextEdit'
812+
(span => Changed_Block_Span,
813+
newText => Changed_Block_Text));
814+
815+
-- clearing
816+
Changed_Block_Text.Clear;
817+
818+
Changed_Block_Span := ((0, 0), (0, 0));
819+
Span_Set := False;
820+
end Add_Prepared_Change;
821+
822+
begin
823+
-- prepare LCS
824+
825+
-- default values for line 0
826+
for Index in 0 .. Old_Length loop
827+
LCS (Index, 0) := -5 * Index;
828+
end loop;
829+
830+
-- default values for the first column
831+
for Index in 0 .. New_Length loop
832+
LCS (0, Index) := -5 * Index;
833+
end loop;
834+
835+
-- calculate LCS
836+
for Row in 1 .. Old_Length loop
837+
New_Char.Set_At_First (New_Text);
838+
for Column in 1 .. New_Length loop
839+
Match := LCS (Row - 1, Column - 1) +
840+
(if Old_Char.Element = New_Char.Element
841+
then 10 -- +10 is the 'weight' for equal lines
842+
else -1); -- and -1 for the different
843+
844+
Delete := LCS (Row - 1, Column) - 5;
845+
Insert := LCS (Row, Column - 1) - 5;
846+
847+
LCS (Row, Column) := Integer'Max (Match, Insert);
848+
LCS (Row, Column) := Integer'Max (LCS (Row, Column), Delete);
849+
850+
Dummy := New_Char.Forward;
851+
end loop;
852+
Dummy := Old_Char.Forward;
853+
end loop;
854+
855+
-- iterate over LCS and create Text_Edit
856+
857+
Old_Char.Set_At_Last (Old_Text);
858+
New_Char.Set_At_Last (New_Text);
859+
Old_Index := Old_Length;
860+
New_Index := New_Length;
861+
862+
while Old_Index > 0
863+
and then New_Index > 0
864+
loop
865+
if LCS (Old_Index, New_Index) =
866+
LCS (Old_Index - 1, New_Index - 1) +
867+
(if Old_Char.Element = New_Char.Element
868+
then 10
869+
else -1)
870+
then
871+
-- both has elements
872+
if Old_Char.Element = New_Char.Element then
873+
-- elements are equal, add prepared Text_Edit
874+
Add_Prepared_Change;
875+
else
876+
-- elements are different, change old one by new
877+
Prepare_Change (False, New_Char.Element);
878+
end if;
879+
880+
-- move old element cursors backward
881+
Backward;
882+
883+
New_Index := New_Index - 1;
884+
Dummy := New_Char.Backward;
885+
886+
elsif LCS (Old_Index, New_Index) =
887+
LCS (Old_Index - 1, New_Index) - 5
888+
then
889+
-- element has been deleted, move old cursor backward
890+
Prepare_Last_Span (False);
891+
Backward;
892+
893+
elsif LCS (Old_Index, New_Index) =
894+
LCS (Old_Index, New_Index - 1) - 5
895+
then
896+
-- element has been inserted
897+
Prepare_Change (True, New_Char.Element);
898+
899+
New_Index := New_Index - 1;
900+
Dummy := New_Char.Backward;
901+
end if;
902+
end loop;
903+
904+
while Old_Index > 0 loop
905+
-- deleted
906+
Prepare_Last_Span (False);
907+
Backward;
908+
end loop;
909+
910+
while New_Index > 0 loop
911+
-- inserted
912+
Prepare_Change (True, New_Char.Element);
913+
914+
New_Index := New_Index - 1;
915+
Dummy := New_Char.Backward;
916+
end loop;
917+
918+
Add_Prepared_Change;
919+
Free (LCS);
920+
921+
exception
922+
when others =>
923+
Free (LCS);
924+
raise;
925+
end;
926+
end Diff_Symbols;
927+
610928
----------------
611929
-- Formatting --
612930
----------------
@@ -678,7 +996,6 @@ package body LSP.Ada_Documents is
678996
-- the GNAT standard way for messages (i.e: <filename>:<sloc>: <msg>)
679997

680998
if not PP_Messages.Is_Empty then
681-
682999
declare
6831000
Filename : constant String := URI_To_File
6841001
(Self => Context, URI => Self.URI);
@@ -718,19 +1035,39 @@ package body LSP.Ada_Documents is
7181035
-- diff for a part of the document
7191036

7201037
Out_Span := Self.To_LSP_Range (Out_Sloc);
721-
Diff
722-
(Self,
723-
VSS.Strings.Conversions.To_Virtual_String (S.all),
724-
Span,
725-
Out_Span,
726-
Edit);
1038+
1039+
-- Use line diff if the range is too wide
1040+
if Span.last.line - Span.first.line > 5 then
1041+
Diff
1042+
(Self,
1043+
VSS.Strings.Conversions.To_Virtual_String (S.all),
1044+
Span,
1045+
Out_Span,
1046+
Edit);
1047+
else
1048+
declare
1049+
Formatted : constant VSS.Strings.Virtual_String :=
1050+
VSS.Strings.Conversions.To_Virtual_String (S.all);
1051+
Slice : VSS.Strings.Virtual_String;
1052+
1053+
begin
1054+
LSP.Lal_Utils.Span_To_Slice (Formatted, Out_Span, Slice);
1055+
1056+
Diff_Symbols
1057+
(Self,
1058+
Span,
1059+
Slice,
1060+
Edit);
1061+
end;
1062+
end if;
7271063
end if;
7281064

7291065
GNAT.Strings.Free (S);
7301066
return True;
7311067

7321068
exception
733-
when others =>
1069+
when E : others =>
1070+
Lal_PP_Output.Trace (E);
7341071
GNAT.Strings.Free (S);
7351072
return False;
7361073
end Formatting;
@@ -870,10 +1207,9 @@ package body LSP.Ada_Documents is
8701207
(1 .. Char_Vectors.Last_Index (Output) - 1);
8711208
Edit_Text : constant VSS.Strings.Virtual_String :=
8721209
VSS.Strings.Conversions.To_Virtual_String (Output_Str);
873-
Text_Edit : constant LSP.Messages.TextEdit := (Edit_Span, Edit_Text);
8741210

8751211
begin
876-
Edit.Append (Text_Edit);
1212+
Self.Diff_Symbols (Edit_Span, Edit_Text, Edit);
8771213
end;
8781214

8791215
return True;

0 commit comments

Comments
 (0)