Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Import" code action sometimes fails and deletes a range of code #4092

Open
EthanOlpin opened this issue Dec 17, 2024 · 5 comments
Open

"Import" code action sometimes fails and deletes a range of code #4092

EthanOlpin opened this issue Dec 17, 2024 · 5 comments
Labels
bug Something isn't working help wanted Contributions encouraged priority:high

Comments

@EthanOlpin
Copy link

Description

Occasionally an "Import" action is suggested for the type annotation on function arguments that, when applied, sometimes causes:

  1. A range of code to be deleted near the cursor
  2. No import to be added

Reproduction Steps

I've experienced a bit of non-determinism with the issue that makes it difficult to give exact steps to reproduce. I am able to get the issue to occur somewhat frequently by repeatedly doing variations of the following:

  1. Begin writing a top-level method with a type annotation that uses a missing import e.g.
    fn foo(set: set.Set(String)) {
      todo
    }
  2. Return the cursor to the module name for the import and apply the suggested "Import action"
  3. Notice that some code is deleted

Environment:

  • Editor VSCode
  • Gleam VSCode Extension: 2.10.0
  • OS: Ubuntu on WSL 2
  • Gleam: 1.6.3
  • Gleam Runtime: Erlang OTP 25

I am not sure where to look for logs that would be useful here, I only see INFO compile=... in the Gleam Language Server output in VSCode.

Screenshots / Recording

Suggested action Applied action
image image

Recording: https://github.com/user-attachments/assets/885af89e-9dd5-402f-830d-c399e51590ba

@EthanOlpin EthanOlpin added the bug Something isn't working label Dec 17, 2024
@lpil
Copy link
Member

lpil commented Dec 19, 2024

Oh dear! Thank you for the report

@lpil lpil added help wanted Contributions encouraged priority:high labels Dec 19, 2024
@GearsDatapacks
Copy link
Member

I'll look into this

@GearsDatapacks
Copy link
Member

GearsDatapacks commented Dec 19, 2024

Ok digging into this a bit, this seems to be a bit of a wider issue than with this specific code action. I can't reproduce this very easily; sometimes I can get this to happen, sometimes it works as normal, and sometimes I get not code action at all.
In a unit test the code you sent fails to suggest a code action ever.

This seems to be a more general caching issue. Seemingly, errors in function signatures cause a bigger disruption in the LSP's ability to function more so than errors inside a function body. I would imagine this is due to some short-circuiting nature of the compiler when type-checking the top-level of a module.

While trying to write a test to capture this behaviour, I noticed that this line was triggering an early return and not even checking for code actions, which is probably because the module did not compile properly. In a real project though, the compiler has some information on the module from when it was valid, causing it to suggest a code action which produces an invalid program after application.

I'm not sure what we should do about this; we could try to make modules more fault tolerant and still compile them when function signatures have errors in them, or we could maybe somehow mark code actions as stale once there is an error and we can no longer provide useful actions (if LSP has an easy way to do that)

@lpil
Copy link
Member

lpil commented Dec 20, 2024

@EthanOlpin do you have syntax errors in your code? Can you share your code that reproduces the issue please 🙏

@EthanOlpin
Copy link
Author

EthanOlpin commented Dec 20, 2024

I have encountered this issue in code that is free of syntax errors at the time of applying the action.

I recognize this isn't ideal but I've found one set of steps that somewhat reliably produces the issue for me with a small amount of code. Still, timing/caching seems to be a factor here making this <100% reliable.

  1. Starting with an empty project with one empty main module
  2. Build the project
  3. In the main module write the following code:
    fn foo(s: set.Set(Int)) {
      todo
    }
  4. Navigate the cursor to set and accept the offered Import action (the action behaves how one would expect and adds import gleam/set). I found that I can save / pause here and allow things to settle without affecting the final result.
  5. Write a new function in the same module. The speed at which you type this out seems to affect the final outcome, in particular if I type slowly here I seem to be less likely to encounter the issue.
    fn bar(d: dict.Dict(Int, Int)) {
      todo
    }
  6. Once everything is typed out, after any amount of time, accept the suggested Import action for dict, some code is deleted. Although the import gleam/dict line gets added successfully, in this case.

If I pay close attention during Step 5 I sometimes see that a code action is suggested while typing out the second method. You can see that in this recording of the full process:

https://github.com/user-attachments/assets/1589f638-7eef-4781-be20-873c45d8fea1.

Full code:

import gleam/set

fn foo(s: set.Set(Int)) {
  todo
}

// Applying "Import `gleam/dict`" deletes everything between `dict` and `todo`"
fn bar(d: dict.Dict(Int, Int)) { 
  todo
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Contributions encouraged priority:high
Projects
None yet
Development

No branches or pull requests

3 participants