Skip to content

Commit 92caea3

Browse files
committed
U310-037 Add defensive code to the import package refactoring
Only suggest packages to import on non null nodes of Identifier or Dotted_Name kind. Fix package import suggestion message when only a with clause is needed. Order TextEdits in applyEdits response by line number.
1 parent 83a9006 commit 92caea3

File tree

18 files changed

+332
-228
lines changed

18 files changed

+332
-228
lines changed

source/ada/lsp-ada_handlers-refactor_imports_commands.adb

Lines changed: 142 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
------------------------------------------------------------------------------
1717

1818
with Ada.Strings.UTF_Encoding;
19+
with Ada.Strings.Unbounded;
1920
with Ada.Strings.Wide_Wide_Unbounded;
2021

2122
with Langkit_Support.Text;
@@ -122,13 +123,25 @@ package body LSP.Ada_Handlers.Refactor_Imports_Commands is
122123
Null_Unbounded_Wide_Wide_String;
123124
use type Ada.Strings.Wide_Wide_Unbounded.
124125
Unbounded_Wide_Wide_String;
126+
125127
begin
126128
if Suggestion.With_Clause_Text /= "" then
127-
-- Add with clause and prefix
129+
if Suggestion.Prefix_Text /= "" then
130+
-- Add with clause and prefix
131+
Title :=
132+
Title
133+
& "Add 'with' clause for "
134+
& Suggestion.With_Clause_Text
135+
& " and prefix the object with "
136+
& Suggestion.Prefix_Text;
128137

129-
Title := Title & "Add 'with' clause for "
130-
& Suggestion.With_Clause_Text & " and prefix the object with "
131-
& Suggestion.Prefix_Text;
138+
else
139+
-- Add with clause and leave the prefix as it is
140+
Title :=
141+
Title
142+
& "Add 'with' clause for "
143+
& Suggestion.With_Clause_Text;
144+
end if;
132145
else
133146
-- Only add prefix
134147

@@ -167,96 +180,84 @@ package body LSP.Ada_Handlers.Refactor_Imports_Commands is
167180
Commands_Vector.Append (Item);
168181
end Append_Suggestion;
169182

170-
-------------
171-
-- Execute --
172-
-------------
183+
----------------------------------
184+
-- Command_To_Refactoring_Edits --
185+
----------------------------------
173186

174-
overriding procedure Execute
175-
(Self : Command;
176-
Handler : not null access LSP.Server_Notification_Receivers.
177-
Server_Notification_Receiver'Class;
178-
Client : not null access LSP.Client_Message_Receivers.
179-
Client_Message_Receiver'Class;
180-
Error : in out LSP.Errors.Optional_ResponseError)
187+
function Command_To_Refactoring_Edits
188+
(Self : Command;
189+
Context : LSP.Ada_Contexts.Context;
190+
Document : LSP.Ada_Documents.Document_Access)
191+
return Laltools.Refactor.Refactoring_Edits
181192
is
182-
use type Libadalang.Common.Ada_Node_Kind_Type;
183-
use type Libadalang.Slocs.Source_Location;
184-
use type VSS.Strings.Virtual_String;
185-
186-
Message_Handler : LSP.Ada_Handlers.Message_Handler renames
187-
LSP.Ada_Handlers.Message_Handler (Handler.all);
188-
Context : LSP.Ada_Contexts.Context renames
189-
Message_Handler.Contexts.Get (Self.Context).all;
190-
191-
Document : constant LSP.Ada_Documents.Document_Access :=
192-
Message_Handler.Get_Open_Document (Self.Where.textDocument.uri);
193-
Apply : LSP.Messages.Client_Requests.Workspace_Apply_Edit_Request;
194-
Node : constant Libadalang.Analysis.Ada_Node :=
193+
use Langkit_Support.Text;
194+
use Libadalang.Analysis;
195+
use Libadalang.Common;
196+
use Libadalang.Slocs;
197+
use Laltools.Refactor;
198+
use VSS.Strings;
199+
use VSS.Strings.Conversions;
200+
201+
Node : Ada_Node :=
195202
Document.Get_Node_At (Context, Self.Where.position);
196-
Loc : LSP.Messages.Location;
197-
Edit : LSP.Messages.AnnotatedTextEdit;
198203

199-
Edits : LSP.Messages.WorkspaceEdit renames Apply.params.edit;
200-
Version : constant LSP.Messages.VersionedTextDocumentIdentifier :=
201-
Document.Versioned_Identifier;
202-
begin
203-
Apply.params.label :=
204-
(Is_Set => True,
205-
Value =>
206-
VSS.Strings.Conversions.To_Virtual_String (Command'External_Tag));
207-
if Message_Handler.Versioned_Documents then
208-
Edits.documentChanges.Append
209-
(LSP.Messages.Document_Change'
210-
(Kind => LSP.Messages.Text_Document_Edit,
211-
Text_Document_Edit =>
212-
(textDocument => (Version.uri, (True, Version.version)),
213-
edits => <>)));
214-
end if;
204+
Edits : Laltools.Refactor.Refactoring_Edits;
215205

216-
-- Add prefix.
206+
begin
207+
-- Add prefix
217208

218209
if not Self.Prefix.Is_Empty
219-
and then Node.Kind = Libadalang.Common.Ada_Identifier
210+
and then Node.Kind in Ada_Identifier
220211
then
221212
-- If this is a DottedName them remove the current prefix and replace
222213
-- it by the suggested one. Otherwise, just add the prepend the
223214
-- prefix
224215

225-
if Node.Parent.Kind = Libadalang.Common.Ada_Dotted_Name then
216+
while Node.Parent.Kind in Ada_Dotted_Name_Range loop
217+
Node := Node.Parent;
218+
end loop;
219+
220+
if Node.Kind in Ada_Dotted_Name_Range then
221+
Node := Node.As_Dotted_Name.F_Suffix.As_Ada_Node;
222+
end if;
223+
224+
if Node.Parent.Kind = Ada_Dotted_Name then
226225
-- Node.Parent is the full Dotted Name: this includes the
227226
-- current prefixes and the identifier. Using this SLOC instead
228227
-- of only the current prefixes SLOC is better since this covers
229228
-- cases when the Dotted Name is splitted in multiple lines.
230229

231-
Loc := LSP.Lal_Utils.Get_Node_Location (Node.Parent);
232-
Edit.span := (Loc.span.first, Loc.span.last);
233-
Edit.newText :=
234-
Self.Prefix & VSS.Strings.To_Virtual_String (Node.Text);
235-
else
236-
Loc := LSP.Lal_Utils.Get_Node_Location (Node);
237-
Edit.span := (Loc.span.first, Loc.span.first);
238-
Edit.newText := Self.Prefix;
239-
end if;
230+
Safe_Insert
231+
(Edits => Edits.Text_Edits,
232+
File_Name => Node.Unit.Get_Filename,
233+
Edit =>
234+
Text_Edit'
235+
(Location =>
236+
Make_Range
237+
(Start_Sloc
238+
(Node.Parent.As_Dotted_Name.F_Prefix.Sloc_Range),
239+
Start_Sloc (Node.Sloc_Range)),
240+
Text =>
241+
Ada.Strings.Unbounded.To_Unbounded_String
242+
(To_UTF8 (To_Wide_Wide_String (Self.Prefix)))));
240243

241-
if Message_Handler.Versioned_Documents then
242-
Edits.documentChanges (1).Text_Document_Edit.edits.Append (Edit);
243244
else
244-
if Edits.changes.Contains (Self.Where.textDocument.uri) then
245-
Edits.changes (Self.Where.textDocument.uri).Append
246-
(LSP.Messages.TextEdit (Edit));
247-
else
248-
declare
249-
Text_Edits : LSP.Messages.TextEdit_Vector;
250-
begin
251-
Text_Edits.Append (LSP.Messages.TextEdit (Edit));
252-
Edits.changes.Include
253-
(Self.Where.textDocument.uri, Text_Edits);
254-
end;
255-
end if;
245+
Safe_Insert
246+
(Edits => Edits.Text_Edits,
247+
File_Name => Node.Unit.Get_Filename,
248+
Edit =>
249+
Text_Edit'
250+
(Location =>
251+
Make_Range
252+
(Start_Sloc (Node.Sloc_Range),
253+
Start_Sloc (Node.Sloc_Range)),
254+
Text =>
255+
Ada.Strings.Unbounded.To_Unbounded_String
256+
(To_UTF8 (To_Wide_Wide_String (Self.Prefix)))));
256257
end if;
257258
end if;
258259

259-
-- Add with clause.
260+
-- Add with clause
260261

261262
if not Self.With_Clause.Is_Empty then
262263
declare
@@ -270,39 +271,84 @@ package body LSP.Ada_Handlers.Refactor_Imports_Commands is
270271
Last => Last);
271272
begin
272273
if S /= Libadalang.Slocs.No_Source_Location then
273-
Edit.span := LSP.Lal_Utils.To_Span (S);
274274
if Last then
275-
Edit.newText :=
276-
Document.Line_Terminator
277-
& "with " & Self.With_Clause & ";";
275+
Safe_Insert
276+
(Edits => Edits.Text_Edits,
277+
File_Name => Node.Unit.Get_Filename,
278+
Edit =>
279+
Text_Edit'
280+
(Location => Make_Range (S, S),
281+
Text =>
282+
Ada.Strings.Unbounded.To_Unbounded_String
283+
(To_UTF8 (To_Wide_Wide_String
284+
(Document.Line_Terminator
285+
& "with " & Self.With_Clause & ";")))));
278286

279287
else
280-
Edit.newText :=
281-
"with " & Self.With_Clause & ";"
282-
& Document.Line_Terminator;
288+
Safe_Insert
289+
(Edits => Edits.Text_Edits,
290+
File_Name => Node.Unit.Get_Filename,
291+
Edit =>
292+
Text_Edit'
293+
(Location => Make_Range (S, S),
294+
Text =>
295+
Ada.Strings.Unbounded.To_Unbounded_String
296+
(To_UTF8 (To_Wide_Wide_String
297+
("with " & Self.With_Clause & ";"
298+
& Document.Line_Terminator)))));
283299
end if;
284300

285-
if Message_Handler.Versioned_Documents then
286-
Edits.documentChanges (1).Text_Document_Edit.edits.Append
287-
(Edit);
288-
else
289-
if Edits.changes.Contains (Self.Where.textDocument.uri) then
290-
Edits.changes (Self.Where.textDocument.uri).Append
291-
(LSP.Messages.TextEdit (Edit));
292-
else
293-
declare
294-
Text_Edits : LSP.Messages.TextEdit_Vector;
295-
begin
296-
Text_Edits.Append (LSP.Messages.TextEdit (Edit));
297-
Edits.changes.Include
298-
(Self.Where.textDocument.uri, Text_Edits);
299-
end;
300-
end if;
301-
end if;
302301
end if;
303302
end;
304303
end if;
305304

305+
return Edits;
306+
end Command_To_Refactoring_Edits;
307+
308+
-------------
309+
-- Execute --
310+
-------------
311+
312+
overriding procedure Execute
313+
(Self : Command;
314+
Handler : not null access LSP.Server_Notification_Receivers.
315+
Server_Notification_Receiver'Class;
316+
Client : not null access LSP.Client_Message_Receivers.
317+
Client_Message_Receiver'Class;
318+
Error : in out LSP.Errors.Optional_ResponseError)
319+
is
320+
use Laltools.Refactor;
321+
use LSP.Messages;
322+
use LSP.Types;
323+
use VSS.Strings;
324+
use VSS.Strings.Conversions;
325+
326+
Message_Handler : LSP.Ada_Handlers.Message_Handler renames
327+
LSP.Ada_Handlers.Message_Handler (Handler.all);
328+
Context : LSP.Ada_Contexts.Context renames
329+
Message_Handler.Contexts.Get (Self.Context).all;
330+
331+
Document : constant LSP.Ada_Documents.Document_Access :=
332+
Message_Handler.Get_Open_Document (Self.Where.textDocument.uri);
333+
334+
Apply : Client_Requests.Workspace_Apply_Edit_Request;
335+
Workspace_Edits : WorkspaceEdit renames Apply.params.edit;
336+
Label : Optional_Virtual_String renames Apply.params.label;
337+
338+
Edits : constant Refactoring_Edits :=
339+
Self.Command_To_Refactoring_Edits (Context, Document);
340+
341+
begin
342+
Workspace_Edits :=
343+
LSP.Lal_Utils.To_Workspace_Edit
344+
(Edits => Edits,
345+
Resource_Operations => Message_Handler.Resource_Operations,
346+
Versioned_Documents => Message_Handler.Versioned_Documents,
347+
Document_Provider => Message_Handler'Access);
348+
Label :=
349+
(Is_Set => True,
350+
Value => To_Virtual_String (Command'External_Tag));
351+
306352
Client.On_Workspace_Apply_Edit_Request (Apply);
307353

308354
exception

source/ada/lsp-ada_handlers-refactor_imports_commands.ads

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ with Ada.Streams;
2121

2222
with VSS.Strings;
2323

24+
with Laltools.Refactor;
2425
with Laltools.Refactor_Imports;
2526

2627
with LSP.Client_Message_Receivers;
@@ -75,6 +76,14 @@ private
7576
V : Command);
7677
-- Write the command in a JSON Stream
7778

79+
function Command_To_Refactoring_Edits
80+
(Self : Command;
81+
Context : LSP.Ada_Contexts.Context;
82+
Document : LSP.Ada_Documents.Document_Access)
83+
return Laltools.Refactor.Refactoring_Edits;
84+
-- Converts Self into Laltools.Refactor.Refactoring_Edits that can be
85+
-- converted in a WorkspaceEdit.
86+
7887
for Command'Write use Write_Command;
7988
for Command'External_Tag use "als-refactor-imports";
8089

0 commit comments

Comments
 (0)