@@ -25,6 +25,7 @@ with GNATCOLL.VFS;
25
25
26
26
with VSS.Characters ;
27
27
with VSS.Strings.Conversions ;
28
+ with VSS.Strings.Cursors.Iterators.Characters ;
28
29
29
30
with Langkit_Support.Symbols ;
30
31
with Langkit_Support.Text ;
@@ -607,6 +608,323 @@ package body LSP.Ada_Documents is
607
608
end ;
608
609
end Diff ;
609
610
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
+
610
928
-- --------------
611
929
-- Formatting --
612
930
-- --------------
@@ -678,7 +996,6 @@ package body LSP.Ada_Documents is
678
996
-- the GNAT standard way for messages (i.e: <filename>:<sloc>: <msg>)
679
997
680
998
if not PP_Messages.Is_Empty then
681
-
682
999
declare
683
1000
Filename : constant String := URI_To_File
684
1001
(Self => Context, URI => Self.URI);
@@ -718,19 +1035,39 @@ package body LSP.Ada_Documents is
718
1035
-- diff for a part of the document
719
1036
720
1037
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 ;
727
1063
end if ;
728
1064
729
1065
GNAT.Strings.Free (S);
730
1066
return True;
731
1067
732
1068
exception
733
- when others =>
1069
+ when E : others =>
1070
+ Lal_PP_Output.Trace (E);
734
1071
GNAT.Strings.Free (S);
735
1072
return False;
736
1073
end Formatting ;
@@ -870,10 +1207,9 @@ package body LSP.Ada_Documents is
870
1207
(1 .. Char_Vectors.Last_Index (Output) - 1 );
871
1208
Edit_Text : constant VSS.Strings.Virtual_String :=
872
1209
VSS.Strings.Conversions.To_Virtual_String (Output_Str);
873
- Text_Edit : constant LSP.Messages.TextEdit := (Edit_Span, Edit_Text);
874
1210
875
1211
begin
876
- Edit.Append (Text_Edit );
1212
+ Self.Diff_Symbols (Edit_Span, Edit_Text, Edit );
877
1213
end ;
878
1214
879
1215
return True;
0 commit comments