Send conversion method suggestions (#7857)

close #7320

Changelog:
- update: enable conversion suggestions
- fix: conversion suggestion building
- fix: conversion suggestion types
- fix: conversion JSON-RPC representation

# Important Notes
For example, the [`Day_Of_Week_From`](5150c14afd/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Day_Of_Week_From.enso) conversion is sent as


```json
{
"type":"Add",
"id":32,
"suggestion":{
"type":"method",
"module":"Standard.Base.Data.Time.Day_Of_Week_From",
"name":"from",
"arguments":[
{
"name":"that",
"reprType":"Standard.Base.Data.Numbers.Integer",
"isSuspended":false,
"hasDefault":false,
"defaultValue":null,
"tagValues":null
},
{
"name":"first_day",
"reprType":"Standard.Base.Data.Time.Day_Of_Week.Day_Of_Week",
"isSuspended":false,
"hasDefault":true,
"defaultValue":"Day_Of_Week.Sunday",
"tagValues":[
"Standard.Base.Data.Time.Day_Of_Week.Day_Of_Week.Sunday",
"Standard.Base.Data.Time.Day_Of_Week.Day_Of_Week.Monday",
"Standard.Base.Data.Time.Day_Of_Week.Day_Of_Week.Tuesday",
"Standard.Base.Data.Time.Day_Of_Week.Day_Of_Week.Wednesday",
"Standard.Base.Data.Time.Day_Of_Week.Day_Of_Week.Thursday",
"Standard.Base.Data.Time.Day_Of_Week.Day_Of_Week.Friday",
"Standard.Base.Data.Time.Day_Of_Week.Day_Of_Week.Saturday"
]
},
{
"name":"start_at_zero",
"reprType":"Standard.Base.Data.Boolean.Boolean",
"isSuspended":false,
"hasDefault":true,
"defaultValue":"False",
"tagValues":[
"Standard.Base.Data.Boolean.Boolean.True",
"Standard.Base.Data.Boolean.Boolean.False"
]
}
],
"selfType":"Standard.Base.Data.Time.Day_Of_Week.Day_Of_Week",
"returnType":"Standard.Base.Data.Time.Day_Of_Week.Day_Of_Week",
"isStatic":false,
"documentation":" Convert from an integer to a Day_Of_Week\n\nArguments:\n- `that`: The first day of the week.\n- `first_day`: The first day of the week.\n- `start_at_zero`: If True, first day of the week is 0 otherwise is 1.",
"annotations":[

]
}
}
```
This commit is contained in:
Dmitry Bushev 2023-09-21 10:00:31 +01:00 committed by GitHub
parent b0a5ac2c19
commit c88765d259
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 216 additions and 120 deletions

View File

@ -126,19 +126,12 @@ object SearchProtocol {
private def conversionToMethod(
conversion: Suggestion.Conversion
): Suggestion.DefinedMethod = {
val arg = Suggestion.Argument(
Suggestion.Kind.Conversion.From,
conversion.selfType,
false,
false,
None
)
): Suggestion.DefinedMethod =
Suggestion.DefinedMethod(
conversion.externalId,
conversion.module,
conversion.name,
arg +: conversion.arguments,
conversion.arguments,
conversion.returnType,
conversion.returnType,
conversion.isStatic,
@ -146,7 +139,6 @@ object SearchProtocol {
conversion.annotations,
conversion.reexport
)
}
private def getterToMethod(
getter: Suggestion.Getter

View File

@ -159,21 +159,24 @@ final class SuggestionBuilder[A: IndexedSource](
case definition.Method
.Conversion(
Name.MethodReference(_, _, _, _, _),
Name.Literal(sourceTypeName, _, _, _, _),
Name.MethodReference(typePtr, _, _, _, _),
_,
Function.Lambda(args, body, _, _, _, _),
_,
_,
_
) if ConversionsEnabled =>
val typeSignature = ir.getMetadata(TypeSignatures)
) =>
val selfType = typePtr.flatMap { typePointer =>
typePointer
.getMetadata(MethodDefinitions)
.map(_.target.qualifiedName)
}
val conversion = buildConversion(
body.getExternalId,
module,
selfType,
args,
sourceTypeName,
doc,
typeSignature
doc
)
go(tree += Tree.Node(conversion, Vector()), scope)
@ -286,20 +289,25 @@ final class SuggestionBuilder[A: IndexedSource](
private def buildConversion(
externalId: Option[IR.ExternalId],
module: QualifiedName,
selfType: Option[QualifiedName],
args: Seq[DefinitionArgument],
sourceTypeName: String,
doc: Option[String],
typeSignature: Option[TypeSignatures.Metadata]
doc: Option[String]
): Suggestion.Conversion = {
val typeSig = buildTypeSignatureFromMetadata(typeSignature)
val (methodArgs, returnTypeDef) =
buildFunctionArguments(args, typeSig)
val methodArgs =
args.map { arg =>
buildTypeSignatureFromMetadata(
arg.getMetadata(TypeSignatures)
).headOption
.map(buildTypedArgument(arg, _))
.getOrElse(buildArgument(arg))
}.tail
Suggestion.Conversion(
externalId = externalId,
module = module.toString,
arguments = methodArgs,
selfType = sourceTypeName,
returnType = buildReturnType(returnTypeDef),
selfType = methodArgs.head.reprType,
returnType = selfType.fold(Any)(_.toString),
documentation = doc
)
}
@ -725,7 +733,9 @@ final class SuggestionBuilder[A: IndexedSource](
expr match {
case Literal.Number(_, value, _, _, _) => Some(value)
case Literal.Text(text, _, _, _) => Some(text)
case _ => None
case Application.Prefix(name, path, _, _, _, _) =>
Some(path.map(_.value.showCode()).mkString(".") + "." + name.showCode())
case other => Some(other.showCode())
}
/** Build scope from the location. */
@ -745,9 +755,6 @@ final class SuggestionBuilder[A: IndexedSource](
object SuggestionBuilder {
/** TODO[DB] enable conversions when they get the runtime support. */
private val ConversionsEnabled: Boolean = false
/** Creates the suggestion builder for a module.
*
* @param module the module to index

View File

@ -857,14 +857,14 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
}
"build conversion method for simple type" in {
pending
val code =
"""type MyAtom a
"""import Standard.Base.Data.Numbers
|
|type Foo
| Value foo
|
|## My conversion
|MyAtom.from : Number -> MyAtom
|MyAtom.from a = MyAtom a
|Foo.from (that:Numbers.Number) = Foo.Value a
|""".stripMargin
val module = code.preprocessModule
@ -873,31 +873,42 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
ModuleNode,
Tree.Node(
Suggestion.Type(
externalId = None,
module = "Unnamed.Test",
name = "MyType",
params = Seq(
Suggestion
.Argument("a", SuggestionBuilder.Any, false, false, None)
),
returnType = "Unnamed.Test.MyType",
externalId = None,
module = "Unnamed.Test",
name = "Foo",
params = Seq(),
returnType = "Unnamed.Test.Foo",
parentType = Some(SuggestionBuilder.Any),
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.DefinedMethod(
Suggestion.Constructor(
externalId = None,
module = "Unnamed.Test",
name = "a",
name = "Value",
arguments = Seq(
Suggestion
.Argument("foo", SuggestionBuilder.Any, false, false, None)
),
returnType = "Unnamed.Test.Foo",
documentation = None,
annotations = Seq()
),
Vector()
),
Tree.Node(
Suggestion.Getter(
externalId = None,
module = "Unnamed.Test",
name = "foo",
arguments = List(
Suggestion
.Argument("self", "Unnamed.Test.MyType", false, false, None)
.Argument("self", "Unnamed.Test.Foo", false, false, None)
),
selfType = "Unnamed.Test.MyType",
selfType = "Unnamed.Test.Foo",
returnType = SuggestionBuilder.Any,
isStatic = false,
documentation = None,
annotations = Seq()
),
@ -908,10 +919,16 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
externalId = None,
module = "Unnamed.Test",
arguments = Seq(
Suggestion.Argument("a", "Number", false, false, None)
Suggestion.Argument(
"that",
"Standard.Base.Data.Numbers.Number",
false,
false,
None
)
),
returnType = "Unnamed.Test.MyType",
selfType = "Number",
selfType = "Standard.Base.Data.Numbers.Number",
returnType = "Unnamed.Test.Foo",
documentation = Some(" My conversion")
),
Vector()
@ -920,21 +937,14 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
)
}
"build conversion method for complex type" in {
pending
"build conversion method with extra arguments" in {
val code =
"""type MyMaybe
| Some a
| None
"""import Standard.Base.Data.Numbers
|
|type New
| Newtype x
|type Foo
| Value foo bar
|
|## My conversion method
|New.from : MyMaybe -> New
|New.from opt = case opt of
| Some a -> Newtype a
| None -> Newtype 0
|Foo.from (that:Numbers.Number) other=1 = Foo.Value that other
|""".stripMargin
val module = code.preprocessModule
@ -945,9 +955,9 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
Suggestion.Type(
externalId = None,
module = "Unnamed.Test",
name = "MyMaybe",
name = "Foo",
params = Seq(),
returnType = "Unnamed.Test.MyMaybe",
returnType = "Unnamed.Test.Foo",
parentType = Some(SuggestionBuilder.Any),
documentation = None
),
@ -957,85 +967,46 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
Suggestion.Constructor(
externalId = None,
module = "Unnamed.Test",
name = "Some",
name = "Value",
arguments = Seq(
Suggestion
.Argument("a", SuggestionBuilder.Any, false, false, None)
.Argument("foo", SuggestionBuilder.Any, false, false, None),
Suggestion
.Argument("bar", SuggestionBuilder.Any, false, false, None)
),
returnType = "Unnamed.Test.MyMaybe",
returnType = "Unnamed.Test.Foo",
documentation = None,
annotations = Seq()
),
Vector()
),
Tree.Node(
Suggestion.DefinedMethod(
Suggestion.Getter(
externalId = None,
module = "Unnamed.Test",
name = "a",
name = "foo",
arguments = List(
Suggestion
.Argument("self", "Unnamed.Test.MyMaybe", false, false, None)
.Argument("self", "Unnamed.Test.Foo", false, false, None)
),
selfType = "Unnamed.Test.MyMaybe",
selfType = "Unnamed.Test.Foo",
returnType = SuggestionBuilder.Any,
isStatic = false,
documentation = None,
annotations = Seq()
),
Vector()
),
Tree.Node(
Suggestion.Constructor(
externalId = None,
module = "Unnamed.Test",
name = "None",
arguments = Seq(),
returnType = "Unnamed.Test.None",
documentation = None,
annotations = Seq()
),
Vector()
),
Tree.Node(
Suggestion.Type(
externalId = None,
module = "Unnamed.Test",
name = "New",
params = Seq(),
returnType = "Unnamed.Test.New",
parentType = Some(SuggestionBuilder.Any),
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Constructor(
Suggestion.Getter(
externalId = None,
module = "Unnamed.Test",
name = "Newtype",
arguments = Seq(
Suggestion
.Argument("x", SuggestionBuilder.Any, false, false, None)
),
returnType = "Unnamed.Test.New",
documentation = None,
annotations = Seq()
),
Vector()
),
Tree.Node(
Suggestion.DefinedMethod(
externalId = None,
module = "Unnamed.Test",
name = "x",
name = "bar",
arguments = List(
Suggestion
.Argument("self", "Unnamed.Test.New", false, false, None)
.Argument("self", "Unnamed.Test.Foo", false, false, None)
),
selfType = "Unnamed.Test.New",
selfType = "Unnamed.Test.Foo",
returnType = SuggestionBuilder.Any,
isStatic = false,
documentation = None,
annotations = Seq()
),
@ -1046,12 +1017,138 @@ class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
externalId = None,
module = "Unnamed.Test",
arguments = Seq(
Suggestion.Argument(
"that",
"Standard.Base.Data.Numbers.Number",
false,
false,
None
),
Suggestion
.Argument("opt", "Unnamed.Test.MyMaybe", false, false, None)
.Argument(
"other",
SuggestionBuilder.Any,
false,
true,
Some("1")
)
),
returnType = "Unnamed.Test.MyType",
selfType = "Unnamed.Test.MyMaybe",
documentation = Some(" My conversion method")
selfType = "Standard.Base.Data.Numbers.Number",
returnType = "Unnamed.Test.Foo",
documentation = None
),
Vector()
)
)
)
}
"build conversion method with extra typed arguments" in {
val code =
"""import Standard.Base.Data.Numbers
|from Standard.Base.Data.Boolean import Boolean
|
|type Foo
| Value foo bar
|
|Foo.from (that:Numbers.Number) (other:Boolean=Boolean.True) = Foo.Value that other
|""".stripMargin
val module = code.preprocessModule
build(code, module) shouldEqual Tree.Root(
Vector(
ModuleNode,
Tree.Node(
Suggestion.Type(
externalId = None,
module = "Unnamed.Test",
name = "Foo",
params = Seq(),
returnType = "Unnamed.Test.Foo",
parentType = Some(SuggestionBuilder.Any),
documentation = None
),
Vector()
),
Tree.Node(
Suggestion.Constructor(
externalId = None,
module = "Unnamed.Test",
name = "Value",
arguments = Seq(
Suggestion
.Argument("foo", SuggestionBuilder.Any, false, false, None),
Suggestion
.Argument("bar", SuggestionBuilder.Any, false, false, None)
),
returnType = "Unnamed.Test.Foo",
documentation = None,
annotations = Seq()
),
Vector()
),
Tree.Node(
Suggestion.Getter(
externalId = None,
module = "Unnamed.Test",
name = "foo",
arguments = List(
Suggestion
.Argument("self", "Unnamed.Test.Foo", false, false, None)
),
selfType = "Unnamed.Test.Foo",
returnType = SuggestionBuilder.Any,
documentation = None,
annotations = Seq()
),
Vector()
),
Tree.Node(
Suggestion.Getter(
externalId = None,
module = "Unnamed.Test",
name = "bar",
arguments = List(
Suggestion
.Argument("self", "Unnamed.Test.Foo", false, false, None)
),
selfType = "Unnamed.Test.Foo",
returnType = SuggestionBuilder.Any,
documentation = None,
annotations = Seq()
),
Vector()
),
Tree.Node(
Suggestion.Conversion(
externalId = None,
module = "Unnamed.Test",
arguments = Seq(
Suggestion.Argument(
"that",
"Standard.Base.Data.Numbers.Number",
false,
false,
None
),
Suggestion
.Argument(
"other",
"Standard.Base.Data.Boolean.Boolean",
false,
true,
Some("Boolean.True"),
Some(
List(
"Standard.Base.Data.Boolean.Boolean.True",
"Standard.Base.Data.Boolean.Boolean.False"
)
)
)
),
selfType = "Standard.Base.Data.Numbers.Number",
returnType = "Unnamed.Test.Foo",
documentation = None
),
Vector()
)