|
| 1 | +------------------------------------------------------------------------------ |
| 2 | +-- Language Server Protocol -- |
| 3 | +-- -- |
| 4 | +-- Copyright (C) 2022, AdaCore -- |
| 5 | +-- -- |
| 6 | +-- This is free software; you can redistribute it and/or modify it under -- |
| 7 | +-- terms of the GNU General Public License as published by the Free Soft- -- |
| 8 | +-- ware Foundation; either version 3, or (at your option) any later ver- -- |
| 9 | +-- sion. This software is distributed in the hope that it will be useful, -- |
| 10 | +-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- |
| 11 | +-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public -- |
| 12 | +-- License for more details. You should have received a copy of the GNU -- |
| 13 | +-- General Public License distributed with this software; see file -- |
| 14 | +-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy -- |
| 15 | +-- of the license. -- |
| 16 | +------------------------------------------------------------------------------ |
| 17 | + |
| 18 | +with GNATCOLL.Utils; |
| 19 | +with LSP.Ada_Documents; |
| 20 | +with VSS.Strings.Character_Iterators; |
| 21 | +with VSS.Strings.Conversions; |
| 22 | + |
| 23 | +package body LSP.Ada_Completions.Generic_Assoc is |
| 24 | + |
| 25 | + ------------------------ |
| 26 | + -- Propose_Completion -- |
| 27 | + ------------------------ |
| 28 | + |
| 29 | + procedure Propose_Completion |
| 30 | + (Self : |
| 31 | + LSP.Ada_Completions.Parameters.Parameter_Completion_Provider; |
| 32 | + Sloc : Langkit_Support.Slocs.Source_Location; |
| 33 | + Token : Libadalang.Common.Token_Reference; |
| 34 | + Node : Libadalang.Analysis.Ada_Node; |
| 35 | + Limit : Natural; |
| 36 | + Filter : in out LSP.Ada_Completions.Filters.Filter; |
| 37 | + Names : in out Ada_Completions.Completion_Maps.Map; |
| 38 | + Unsorted_Res : in out LSP.Messages.CompletionItem_Vector) |
| 39 | + is |
| 40 | + pragma Unreferenced (Filter, Sloc, Names); |
| 41 | + use Libadalang.Analysis; |
| 42 | + use Libadalang.Common; |
| 43 | + use LSP.Ada_Completions.Generic_Assoc_Utils; |
| 44 | + |
| 45 | + Elem_Node : constant Element := Search_Element (Node); |
| 46 | + Token_Kind : Libadalang.Common.Token_Kind := Kind (Data (Token)); |
| 47 | + Whitespace_Prefix : VSS.Strings.Virtual_String; |
| 48 | + -- Empty if we already have a whitespace before a "," |
| 49 | + |
| 50 | + Designators : Laltools.Common.Node_Vectors.Vector; |
| 51 | + |
| 52 | + function Match_Designators |
| 53 | + (Child : Laltools.Common.Node_Vectors.Vector; |
| 54 | + Parent : Laltools.Common.Node_Vectors.Vector) |
| 55 | + return Boolean; |
| 56 | + -- Return True if all the designators of Child are also in Parent |
| 57 | + |
| 58 | + function In_Parent |
| 59 | + (Desg : Libadalang.Analysis.Ada_Node; |
| 60 | + Parent : Laltools.Common.Node_Vectors.Vector) return Boolean; |
| 61 | + |
| 62 | + procedure Generate_Snippets |
| 63 | + (Spec_Designators : Laltools.Common.Node_Vectors.Vector; |
| 64 | + Param_Types : Param_To_Type_Maps.Map; |
| 65 | + Decl : Basic_Decl; |
| 66 | + Title : VSS.Strings.Virtual_String; |
| 67 | + Snippet_Prefix : VSS.Strings.Virtual_String; |
| 68 | + Completion_Prefix : VSS.Strings.Virtual_String); |
| 69 | + |
| 70 | + --------------- |
| 71 | + -- In_Parent -- |
| 72 | + --------------- |
| 73 | + |
| 74 | + function In_Parent |
| 75 | + (Desg : Libadalang.Analysis.Ada_Node; |
| 76 | + Parent : Laltools.Common.Node_Vectors.Vector) return Boolean |
| 77 | + is |
| 78 | + Desg_Text : constant Langkit_Support.Text.Text_Type := Desg.Text; |
| 79 | + begin |
| 80 | + for P of Parent loop |
| 81 | + if P.Text = Desg_Text then |
| 82 | + return True; |
| 83 | + end if; |
| 84 | + end loop; |
| 85 | + |
| 86 | + return False; |
| 87 | + end In_Parent; |
| 88 | + |
| 89 | + ----------------------- |
| 90 | + -- Match_Designators -- |
| 91 | + ----------------------- |
| 92 | + |
| 93 | + function Match_Designators |
| 94 | + (Child : Laltools.Common.Node_Vectors.Vector; |
| 95 | + Parent : Laltools.Common.Node_Vectors.Vector) |
| 96 | + return Boolean is |
| 97 | + begin |
| 98 | + for C of Child loop |
| 99 | + if not In_Parent (C, Parent) then |
| 100 | + return False; |
| 101 | + end if; |
| 102 | + end loop; |
| 103 | + |
| 104 | + return True; |
| 105 | + end Match_Designators; |
| 106 | + |
| 107 | + ----------------------- |
| 108 | + -- Generate_Snippets -- |
| 109 | + ----------------------- |
| 110 | + |
| 111 | + procedure Generate_Snippets |
| 112 | + (Spec_Designators : Laltools.Common.Node_Vectors.Vector; |
| 113 | + Param_Types : Param_To_Type_Maps.Map; |
| 114 | + Decl : Basic_Decl; |
| 115 | + Title : VSS.Strings.Virtual_String; |
| 116 | + Snippet_Prefix : VSS.Strings.Virtual_String; |
| 117 | + Completion_Prefix : VSS.Strings.Virtual_String) |
| 118 | + is |
| 119 | + Params_Snippet : VSS.Strings.Virtual_String; |
| 120 | + Snippet_Index : Integer := |
| 121 | + Integer (Spec_Designators.Length); |
| 122 | + Use_Named_Notation : constant Boolean := |
| 123 | + Limit > 0 |
| 124 | + and then (Snippet_Index = 1 or else Snippet_Index >= Limit); |
| 125 | + begin |
| 126 | + if Match_Designators (Designators, Spec_Designators) then |
| 127 | + |
| 128 | + for Desg of reverse Spec_Designators loop |
| 129 | + declare |
| 130 | + Name : constant VSS.Strings.Virtual_String := |
| 131 | + VSS.Strings.To_Virtual_String (Desg.Text); |
| 132 | + Item : LSP.Messages.CompletionItem; |
| 133 | + Snippet : VSS.Strings.Virtual_String; |
| 134 | + Type_Text : constant VSS.Strings.Virtual_String := |
| 135 | + (if Param_Types.Contains (Desg) |
| 136 | + then VSS.Strings.To_Virtual_String |
| 137 | + (Param_Types (Desg).Node.Text) |
| 138 | + else ""); |
| 139 | + begin |
| 140 | + -- Check if Desg is already present |
| 141 | + if not In_Parent (Desg, Designators) then |
| 142 | + -- Add snippet for Desg if it matches the |
| 143 | + -- current prefix |
| 144 | + if Token_Kind in Ada_Par_Open | Ada_Comma |
| 145 | + or else |
| 146 | + Name.Starts_With |
| 147 | + (Completion_Prefix, |
| 148 | + VSS.Strings.Identifier_Caseless) |
| 149 | + then |
| 150 | + -- Snippet Format: "Name => " |
| 151 | + Item.label := Name; |
| 152 | + Item.insertTextFormat := |
| 153 | + (True, LSP.Messages.PlainText); |
| 154 | + Item.insertText := (True, Value => <>); |
| 155 | + Item.insertText.Value.Append |
| 156 | + (Whitespace_Prefix); |
| 157 | + Item.insertText.Value.Append (Name); |
| 158 | + Item.insertText.Value.Append (" => "); |
| 159 | + Item.kind := (True, LSP.Messages.Field); |
| 160 | + if Param_Types (Desg).Is_Value then |
| 161 | + Item.insertText.Value.Append (Type_Text); |
| 162 | + Item.label.Append (" => "); |
| 163 | + Item.label.Append (Type_Text); |
| 164 | + end if; |
| 165 | + Unsorted_Res.Append (Item); |
| 166 | + end if; |
| 167 | + |
| 168 | + if Use_Named_Notation then |
| 169 | + -- If Type_Text : "Name => ${idx:Type}" |
| 170 | + -- Else: "Name => $idx" |
| 171 | + Snippet.Append (Name); |
| 172 | + Snippet.Append (" => "); |
| 173 | + if Param_Types (Desg).Is_Value then |
| 174 | + Snippet.Append (Type_Text); |
| 175 | + else |
| 176 | + Snippet.Append ("$"); |
| 177 | + if not Type_Text.Is_Empty then |
| 178 | + Snippet.Append ("{"); |
| 179 | + end if; |
| 180 | + Snippet.Append |
| 181 | + (VSS.Strings.Conversions.To_Virtual_String |
| 182 | + (GNATCOLL.Utils.Image |
| 183 | + (Snippet_Index, Min_Width => 1))); |
| 184 | + if not Type_Text.Is_Empty then |
| 185 | + Snippet.Append (":"); |
| 186 | + Snippet.Append (Type_Text); |
| 187 | + Snippet.Append ("}"); |
| 188 | + end if; |
| 189 | + end if; |
| 190 | + else |
| 191 | + -- If Type_Text : "${idx:Name : Type}" |
| 192 | + -- Else: "${idx:Name}" |
| 193 | + if Param_Types (Desg).Is_Value then |
| 194 | + Snippet.Append (Type_Text); |
| 195 | + else |
| 196 | + Snippet.Append ("${"); |
| 197 | + Snippet.Append |
| 198 | + (VSS.Strings.Conversions.To_Virtual_String |
| 199 | + (GNATCOLL.Utils.Image |
| 200 | + (Snippet_Index, Min_Width => 1))); |
| 201 | + Snippet.Append (":"); |
| 202 | + Snippet.Append (Name); |
| 203 | + if not Type_Text.Is_Empty then |
| 204 | + Snippet.Append (" : "); |
| 205 | + Snippet.Append (Type_Text); |
| 206 | + end if; |
| 207 | + Snippet.Append ("}"); |
| 208 | + end if; |
| 209 | + end if; |
| 210 | + Snippet.Append (", "); |
| 211 | + Params_Snippet.Prepend (Snippet); |
| 212 | + end if; |
| 213 | + Snippet_Index := Snippet_Index - 1; |
| 214 | + end; |
| 215 | + end loop; |
| 216 | + |
| 217 | + -- If the string is empty => nothing to do |
| 218 | + if not Params_Snippet.Is_Empty |
| 219 | + and then Token_Kind in Ada_Par_Open | Ada_Comma |
| 220 | + then |
| 221 | + declare |
| 222 | + Last : |
| 223 | + VSS.Strings.Character_Iterators.Character_Iterator |
| 224 | + := Params_Snippet.At_Last_Character; |
| 225 | + Success : Boolean with Unreferenced; |
| 226 | + |
| 227 | + begin |
| 228 | + -- Remove the last 2 characters which are ", " and |
| 229 | + -- replace it by ")" and the final tab stop |
| 230 | + Success := Last.Backward; |
| 231 | + Success := Last.Backward; |
| 232 | + |
| 233 | + Params_Snippet := |
| 234 | + Params_Snippet.Slice |
| 235 | + (Params_Snippet.At_First_Character, Last); |
| 236 | + Params_Snippet.Append (")$0"); |
| 237 | + end; |
| 238 | + |
| 239 | + Params_Snippet.Prepend (Snippet_Prefix); |
| 240 | + |
| 241 | + declare |
| 242 | + Item : LSP.Messages.CompletionItem; |
| 243 | + begin |
| 244 | + Item.label := Title; |
| 245 | + Item.insertTextFormat := |
| 246 | + (True, LSP.Messages.Snippet); |
| 247 | + Item.insertText := (True, Value => <>); |
| 248 | + Item.insertText.Value.Append (Whitespace_Prefix); |
| 249 | + Item.insertText.Value.Append (Params_Snippet); |
| 250 | + Item.kind := (True, LSP.Messages.Snippet); |
| 251 | + LSP.Ada_Documents.Set_Completion_Item_Documentation |
| 252 | + (Context => Self.Context.all, |
| 253 | + BD => Decl, |
| 254 | + Item => Item, |
| 255 | + Compute_Doc_And_Details => |
| 256 | + Self.Compute_Doc_And_Details); |
| 257 | + Unsorted_Res.Append (Item); |
| 258 | + end; |
| 259 | + end if; |
| 260 | + end if; |
| 261 | + end Generate_Snippets; |
| 262 | + |
| 263 | + begin |
| 264 | + if Elem_Node = Null_Element then |
| 265 | + return; |
| 266 | + end if; |
| 267 | + |
| 268 | + Designators := Get_Designators (Elem_Node); |
| 269 | + |
| 270 | + if Token_Kind = Ada_Whitespace then |
| 271 | + Token_Kind := Kind (Data (Previous (Token, Exclude_Trivia => True))); |
| 272 | + |
| 273 | + elsif Token_Kind = Ada_Comma then |
| 274 | + Whitespace_Prefix.Append (" "); |
| 275 | + end if; |
| 276 | + |
| 277 | + declare |
| 278 | + Completion_Prefix : constant VSS.Strings.Virtual_String := |
| 279 | + VSS.Strings.To_Virtual_String (Node.Text); |
| 280 | + begin |
| 281 | + for Spec of Get_Spec_Designators |
| 282 | + (E => Elem_Node, |
| 283 | + Context => Self.Context) |
| 284 | + loop |
| 285 | + Generate_Snippets |
| 286 | + (Spec_Designators => Spec.Param_Vector, |
| 287 | + Param_Types => Spec.Param_Types, |
| 288 | + Decl => Spec.Decl, |
| 289 | + Title => Spec.Title, |
| 290 | + Snippet_Prefix => Spec.Prefix, |
| 291 | + Completion_Prefix => Completion_Prefix); |
| 292 | + end loop; |
| 293 | + end; |
| 294 | + end Propose_Completion; |
| 295 | + |
| 296 | +end LSP.Ada_Completions.Generic_Assoc; |
0 commit comments