@@ -25,6 +25,7 @@ with GNATCOLL.VFS;
2525
2626with VSS.Characters ;
2727with VSS.Strings.Conversions ;
28+ with VSS.Strings.Cursors.Iterators.Characters ;
2829
2930with Langkit_Support.Symbols ;
3031with 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