tweag / nickel

Better configuration for less

Home Page:https://nickel-lang.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[LSP] Support symbol rename

suimong opened this issue · comments

Using Nickel for a while, it occurs to me that the defining features of Nickel, i.e. recursion, laziness, merge system etc. makes writing program based on "single source of truth" a breeze, meaning that you define static data somewhere once, and everywhere else you can refer to that data with nickel symbol. Which brings the topic of this issue: supporting symbol rename in the LSP.

Right now with VSCode, the LSP does nothing when placing the cursor on a symbol and pressing "F2", but imagine if it does, the experience of refactoring nickel code with VSCode or any other LSP-supporting editor, would be comparable to, even better than that of Java with Intellij or Python with PyCharm.

Describe the solution you'd like

In VSCode at least, pressing 'F2' on a symbol shows the rename dialog box, user enter a new name, VSCode shows preview of all usages of that symbol within the workspace, and do the job.

Describe alternatives you've considered

ast-grep + Nickel's tree-sitter grammar is a potentially viable alternative, though I haven't tried it yet.

My current understanding is that, because we don't have a proper Concrete Syntax Tree (the LSP uses the same parser as the main Nickel executable), some information is lost during parsing, which makes non-trivial refactoring task hard to implement.

That being said, renaming is probably one that is doable, as we keep all the identifier positions around, and we are already computing usage sites. Maybe @jneem will know better.

Ah, that makes sense. This should be lower priority feature request, especially now that ADT is on the way 🤩

Symbol rename should certainly be doable.

One caveat is that some of the usage tracking is "best effort" right now, particularly when merges and contract application is involved. For example, the lsp currently recognizes that the two "foo"s in { foo = 1 } | { foo | Number } refer to one another. But if you put a function in between, like (std.function.id { foo = 1 }) | { foo | Number } it doesn't recognize that they refer to one another. It's possible that symbol rename shouldn't try to resolve merges at all, and just work with static scopes...

The {foo = 1} | {foo | Number} case is interesting. I don't know for sure if there's an obviously right solution (only rename one of them, or rename both) - at least none of the solutions sounds entirely unreasonable to me. However, it might be indeed more conservative to start by only renaming based on static scopes.