Delphi code completion fail with anonymous methods
Asked Answered
S

2

8

Please create a new FMX application, add a button and a memo to run this example. I have this code:

procedure TForm1.Button1Click(Sender: TObject);
begin
  TTask.Run(procedure
            var
              client: TIdHTTP;
              result: string;
            begin
              client := TIdHTTP.Create(nil);
              try
                try
                  client.ReadTimeout := 4000;
                  client.ConnectTimeout := 4000;
                  result := client.Get('a valid url here just as test');
                  TThread.Synchronize(nil, procedure
                                           begin
                                             Memo1.Lines.Add(result);
                                           end);
                except
                  on E: Exception do
                    begin
                      TThread.Synchronize(nil, procedure
                                           begin
                                             Memo1.Lines.Add(E.Message);
                                           end);
                    end
                end;
              finally
                client.Free;
              end;
            end);
end;

It works as I expect but the problem is in the IDE. If I place the cursor somewhere in the body of the anonymous function, I get the closing of the finally statement automatically.

How can I fix this?


First I am here

enter image description here

Then I press enter and I have this!

enter image description here

If you put the cursor at the beginning and not at the end of the line, you can add new spaces without the completion. How to solve this problem? Well, I have discovered that the issue happens because there is this code:

TThread.Synchronize(nil, procedure
                         begin
                           Memo1.Lines.Add(result);
                         end);

If you remove this code, the issue doens't happen anymore. Is this a bug in the IDE?

Sagamore answered 24/9, 2018 at 16:1 Comment(6)
I have a Delphi unit in which every Return causes an end to be inserted. (So, if I press Return five times, I get five end.) It is driving me crazy. I haven't seen this behaviour in previous Delphi versions.Pothead
I think that this is weird too, I see that Object Pascal Handbook by Marco Cantu suggests this pattern (the except inside), I think this is weirdMalformation
Known issue: quality.embarcadero.com/browse/RSP-18079Cynarra
I swear I recall a similar (but not identical) question I asked about a year ago, but can't find it. Basically, in the same scenario, when a) spawning an anonymous thread, and b) calling synchronize, and c) pressing enter within that synchronization, it would continuously add end; for every enter pressed.Squarerigger
@JerryDodge I blame the internet connection I have where I am at present (Bali) :-)Cynarra
@AndreasRejbrand Indeed, I put my prior comment before I saw yours. That does drive me crazy too. I copy a line break to the clipboard and paste it as needed.Squarerigger
B
3

Is this a bug in the IDE?

Yes, this is a bug in the IDE. Your code is syntactically valid.

How can I fix this?

The best way to avoid this is to create your code and surround it with try...except... to handle any exception:

  try
    MyClass := TComponent.Create(Self);
    try

    finally
      MyClass.Free;
    end;
  except on E: Exception do
  end;

So your code will be:

  TTask.Run(procedure
            var
              client: TIdHTTP;
              result: string;
            begin
              try
                Client := TIdHTTP.Create(nil);
                try
                  client.ReadTimeout := 4000;
                  client.ConnectTimeout := 4000;
                  result := client.Get('a valid url here just as test');
                  TThread.Synchronize(nil, procedure
                                           begin
                                             Memo1.Lines.Add(result);
                                           end);
                finally
                  Client.Free;
                end;
              except on E: Exception do
                begin
                  TThread.Synchronize(nil, procedure
                                           begin
                                             Memo1.Lines.Add(E.Message);
                                           end);
                  end;
              end;
            end;
Belted answered 24/9, 2018 at 17:6 Comment(7)
I thought that finally had to always enclose everything but it seems that the except goes out. Tried it and it worked!Sagamore
@RosannaTrevisan Sure ;)Belted
It still feels like an IDE bug to me. The OP's original code is perfectly syntactically valid.Pothead
Althogh, it will be a warning [dcc32 Warning] UnitName.pas(37): W1036 Variable 'Client' might not have been initializedBelted
Probably the warning is about the fact that if the exception happens before Client := TIdHTTP.Create(nil);, the variable Client will never be used. But still, I think that this could be a bug!Malformation
Switching the finally and except blocks around is a work around and not a solution and it may not be something that everyone can get away with. A use case might need the objects freed in the finally block to still be valid in the exception. Something can still be a bug if occurs frequently.Sneaker
@Andreas: To me too. It is an IDE bug. A pretty annoying bug too.Bluestone
I
8

Is this a bug in the IDE?

Yes. This is a defect. Please submit a report to Quality Portal.

Isomorph answered 25/9, 2018 at 12:28 Comment(0)
B
3

Is this a bug in the IDE?

Yes, this is a bug in the IDE. Your code is syntactically valid.

How can I fix this?

The best way to avoid this is to create your code and surround it with try...except... to handle any exception:

  try
    MyClass := TComponent.Create(Self);
    try

    finally
      MyClass.Free;
    end;
  except on E: Exception do
  end;

So your code will be:

  TTask.Run(procedure
            var
              client: TIdHTTP;
              result: string;
            begin
              try
                Client := TIdHTTP.Create(nil);
                try
                  client.ReadTimeout := 4000;
                  client.ConnectTimeout := 4000;
                  result := client.Get('a valid url here just as test');
                  TThread.Synchronize(nil, procedure
                                           begin
                                             Memo1.Lines.Add(result);
                                           end);
                finally
                  Client.Free;
                end;
              except on E: Exception do
                begin
                  TThread.Synchronize(nil, procedure
                                           begin
                                             Memo1.Lines.Add(E.Message);
                                           end);
                  end;
              end;
            end;
Belted answered 24/9, 2018 at 17:6 Comment(7)
I thought that finally had to always enclose everything but it seems that the except goes out. Tried it and it worked!Sagamore
@RosannaTrevisan Sure ;)Belted
It still feels like an IDE bug to me. The OP's original code is perfectly syntactically valid.Pothead
Althogh, it will be a warning [dcc32 Warning] UnitName.pas(37): W1036 Variable 'Client' might not have been initializedBelted
Probably the warning is about the fact that if the exception happens before Client := TIdHTTP.Create(nil);, the variable Client will never be used. But still, I think that this could be a bug!Malformation
Switching the finally and except blocks around is a work around and not a solution and it may not be something that everyone can get away with. A use case might need the objects freed in the finally block to still be valid in the exception. Something can still be a bug if occurs frequently.Sneaker
@Andreas: To me too. It is an IDE bug. A pretty annoying bug too.Bluestone

© 2022 - 2024 — McMap. All rights reserved.