-
Notifications
You must be signed in to change notification settings - Fork 56
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
Complete pipeable functions from dot completion on record #1054
base: master
Are you sure you want to change the base?
Conversation
…cord is type t of module
"label": "->SomeModule.getName", | ||
"kind": 12, | ||
"tags": [], | ||
"detail": "t => string", | ||
"documentation": null, | ||
"sortText": "getName", | ||
"insertText": "->SomeModule.getName" | ||
}, { | ||
"label": "name", | ||
"kind": 5, | ||
"tags": [], | ||
"detail": "string", | ||
"documentation": {"kind": "markdown", "value": "```rescript\nname: string\n```\n\n```rescript\ntype t = {name: string}\n```"} | ||
}] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the relevant test output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any chances this affects library design? Core?
Path n | ||
Path SomeModule. | ||
[{ | ||
"label": "->SomeModule.getName", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious about the function coming before the field
also, any thoughts about making .
a valid source language construct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you expand a bit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 If I write x.
I see x->getName()
before x.field
, which can be surprising to people working with records. Just wondering.
2 Could x.getName()
be actually part of the language, or will it always need to be expressed as x->getName
? Autocompletion is right now suggestive of something that does not exist in the language. Not necessarily a bad thing, just thinking aloud about possible pros and cons here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Ahh right! That's a good point. It's currently sorted on the field name and function name. We could sort it whatever way we think works best. I'd say either this (sort it together with the regular fields) or put it at the bottom. I'd favor the current way, I think.
- Oh, that's.... a very interesting idea! That would solve quite a few of our issues. Let's explore that!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that is about what called "uniform function call syntax"
And there was a similar discussion in ReasonML reasonml/reason#1638 early day
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the context. Will read through.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we could figure out a good way to do that, pointing out the module to use with the call (someRecord.SomeModule.someFn()
), but with a small enough twist that it's still more convenient than pipe, but not ambigious wrt the current behavior of SomeModule
there being a hint.
Anyway, just completing the actual pipe, but via dot, is a good start for DX imo.
I don't think so. It should mostly just simplify things, and I also think with this we could get rid of some the special casing for pipe completions we have, and just add the annotation to types shipped with the compiler. |
Ok, so this PR explicitly handles (resolvable) record types only, because this was originally intended to cater to a potential use case in https://github.com/rescript-lang/experimental-rescript-webapi. But, this could easily be useful in more general cases. Examples:
The latter part opens up some pretty good optimizations for people used to JS/TS: "some string".
// ^com JS/TS users would expect the above to complete for Expanding dot completion to also complete for relevant pipe functions here would make that much more discoverable. I would also think it would mean there's a good chance people will just learn the convention of "main type, pipe functions from module of that main type" automatically because they'll see what their expected dot completion is resolved to. This would be a massive win because that means that the tooling could help teach one of the larger differences between how things are done in ReScript vs JS. |
// ^com | ||
// ^dv- | ||
|
||
@mainTypeForModule(SomeOtherModule) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this also work when SomeOtherModule
is another file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's effectively just a path to a module, so it'll take that exact module path and try to complete it from whatever point you're completing. So it's simulating doing whateverToComplete-><pathFromMainTypeForModuleHere>.<com>
.
type typeOutsideModule = {nname: string} | ||
|
||
module SomeOtherModule = { | ||
type t = typeOutsideModule |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this alias as t
a hard requirement to make this work?
Being newish to ReScript type t
is a little strange and it feels a bit like an artificial limitation right now.
Complete for pipeable functions from the parent module for a record when that record is the
type t
of the parent module.Example:
This should complete for both
name
(record field) andgetName
(function from parent module that takes this record as the first argument).Experimental
@editor.completeFrom
annotationThis PR also introduces an experimental
@editor.completeFrom
annotation, that lets you provide more modules the tooling can draw additional completions from, without that type needing to be defined as thetype t
of that module. This is useful because there are situations where you can't/don't want to define a type astype t
inside of a module, but you still want to get completions from that module. Examples include recursive type chains, where the type definition might need to live outside of the module, and similar.Annotating with
@editor.completeFrom
will make the editor tooling complete for pipes from the provided module too. Example:This will complete for:
nname
(record field)getNName
(pipe from module)getNName2
(pipe from module)The annotation has no other effect than providing a hint for the editor tooling.
Follow ups
@editor.completeFrom
annotation can be applied to the regular pipe completion mechanism as well in a separate PR.@editor.completeFrom
as well.TODO ✅
This is an almost working PoC. A few things remain:n.->SomeModule.getName
where we obviously wantn->SomeModule.getName
t
(essentially - allow aliases to type t too)@mainModule
directive so that we can trigger completions for types that aren't necessary a realtype t
for various reasons, but that de facto is the main type of a module