[ANN] Call for testing - refactors and code actions in VSCode extension

Me and @cristianoc have been working on adding the needed infrastructure for code actions/refactors in the VSCode extension, and we now feel we’re ready for testing of the initial batch of code actions we’ve been working on.

This is a first step in exploring code actions, and we hope we’ll be able to slowly but steadily work our way towards a really solid foundation that’ll help make working with ReScript as frictionless as possible.

For those who don’t know, code actions is the blue/yellow lightbulb that pops up as you move the keyboard cursor around in your code. Code actions are great for removing the need for mechanical work/mundane tasks, and are primarily used for 1) quick fixes for warnings/errors produced by the compiler, and 2) refactoring.

For the ReScript VSCode extension, code actions can be divided into the same two categories:

  1. Quick fixes are code actions derived from compiler warnings and errors that occur, and are designed to speed up fixing common compilation errors.
  2. Refactors are more “advanced”, and can make use of both the full ReScript AST, as well as type information (as long as the sources compile).

Information about all code actions we’ve added (including examples/GIFs), and some more technical information is available in this PR: https://github.com/rescript-lang/rescript-vscode/pull/373. Check that out for a full rundown of what actions you can test in this initial iteration.

EDIT: I figured out the GIFs! It was what we in Sweden call “skit bakom spakarna”.

Here’s a few example GIFs (check the PR out for all of them):


Insert missing variant cases


Add type annotation


Unwrap optional


If/ternary to switch


Insert missing record fields

How to test

Grab a recent testing vsix from CI for the code-actions branch (huge thanks to @fham for implementing this in CI!). Here’s one from when this post was created, but it’s a good idea to grab an as recent one as possible.

Test the various code actions, preferably in “real” code bases if you have access to that, and report back here. We’ll start by reporting here in this thread, but we might move issue reporting to GitHub if there’s lots of back and forth.

We expect there to be issues, so we’re mainly interested in:

  • Outright bugs
  • Things that are hard to understand
  • Things not working like you expected them to work

What’s next

We’re also very much interested in your general feedback, and of course your ideas! We have additional ideas for code actions that we will be exploring, but this is a good time to draw on the experience and ideas of the entire community - what are you struggling with in your work with ReScript that you think a code action could help make easier?

We’ll post our ideas as separate responses to this thread, so they are easy to upvote if you find them interesting.

Finally, thanks for giving it a go! Me and @cristianoc have had lots of fun implementing this first iteration, and we hope this is going to bring some quality-of-life improvements for you as ReScript developers.

26 Likes

Idea: Extract code block into function

Select a block of code and extract it to a function, automatically adding parameters etc as necessary. Essentially the same as a lot of other languages have.

5 Likes

Idea: Actions for React dependency arrays

This is very React specific, but would help bridging one of the major frictions when coming from JS/TS to React development in ReScript - dealing with arities of React hooks.

This is what a React hook with a dependency array typically looks like:

let someMemoizedValue = React.useMemo2(
  // React will memoize the return of the function below and use a `===` comparison of `someParam` and `otherParam` to decide when to rerun the memoization.
  () => calculateSomething(someParam, otherParam), 
  (someParam, otherParam) // This is referred to as the "dependency array", and decides when the memoization reruns
)

Now, the big friction in ReScript compared to JS/TS is that you have to keep track of how many things you have in your dependency array (hence useMemo2 above, and I’d need useMemo3 if I were to add another dependency). It’s also “worsened” a bit by the fact that useMemo1 actually takes an array of a single element, not a tuple.

So, in short, in ReScript we need to do a bunch of tiring work as we add and remove things from dependency arrays.

This proposal would deal with this by helping the user convert between the right functions (useMemo2, useMemo3 etc) as things are added, as well as the actual type passed (nothing for useMemo0 and useMemo, array for useMemo1, tuple for everything else).

let memoized = React.useMemo1(() => {
  let someIntermediateValue = calculate(someValue, someOtherValue)
                                                       ^-- cursor
  someIntermediateValue
}, [someValue])

Gives action Add 'someOtherValue' as useMemo dependency, and then turns the above into:

let memoized = React.useMemo2(() => {
  let someIntermediateValue = calculate(someValue, someOtherValue)
  someIntermediateValue
}, (someValue, someOtherValue))

Notice how it changes the fn call to useMemo2, and replaces the array with a tuple with that value added.

We should also consider the reverse of this - putting your cursor on a value that’s already in the dependency array should suggest a code action that does the reverse, removing the value from the dependency array etc.

3 Likes

This is fantastic, Gabriel!

3 Likes

omg the next release of the vscode extension gonna be awesome !!!

Thx a lot for all this work. Rescript is going on the right way :smiley:

2 Likes

Incredible work! Thank you @zth @cristianoc and @fham :clap: :clap: :clap:

6 Likes

Awesome! It will improve the huge DX. Thank you!

1 Like