Add support for conversions in Language Server (#3175)

This commit is contained in:
Dmitry Bushev 2021-12-03 11:31:44 +03:00 committed by GitHub
parent c4d22102cf
commit 607330712a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 650 additions and 515 deletions

View File

@ -140,7 +140,6 @@ transport formats, please look [here](./protocol-architecture).
- [`search/suggestionsDatabaseUpdate`](#searchsuggestionsdatabaseupdate)
- [`search/suggestionsOrderDatabaseUpdate`](#searchsuggestionsorderdatabaseupdate)
- [`search/completion`](#searchcompletion)
- [`search/import`](#searchimport)
- [Input/Output Operations](#inputoutput-operations)
- [`io/redirectStandardOutput`](#ioredirectstandardoutput)
- [`io/suppressStandardOutput`](#iosuppressstandardoutput)
@ -3882,49 +3881,6 @@ the type match.
- [`ModuleNameNotResolvedError`](#modulenamenotresolvederror) the module name
cannot be extracted from the provided file path parameter
### `search/import`
Sent from client to the server to receive the information required for module
import.
- **Type:** Request
- **Direction:** Client -> Server
- **Connection:** Protocol
- **Visibility:** Public
#### Parameters
```typescript
{
/**
* The id of suggestion to import.
*/
id: SuggestionId;
}
```
#### Result
```typescript
{
/**
* The definition module of the suggestion.
*/
module: String;
/**
* The name of the resolved suggestion.
*/
symbol: String;
/**
* The list of modules that re-export the suggestion. Modules are ordered
* from the least to most nested.
*/
exports: Export[];
}
```
#### Errors
- [`SuggestionsDatabaseError`](#suggestionsdatabaseerror) an error accessing the

View File

@ -471,7 +471,6 @@ class JsonConnectionController(
.props(requestTimeout, suggestionsHandler),
Completion -> search.CompletionHandler
.props(requestTimeout, suggestionsHandler),
Import -> search.ImportHandler.props(requestTimeout, suggestionsHandler),
ExecuteExpression -> ExecuteExpressionHandler
.props(rpcSession.clientId, requestTimeout, contextRegistry),
AttachVisualisation -> AttachVisualisationHandler

View File

@ -68,7 +68,6 @@ object JsonRpc {
.registerRequest(GetSuggestionsDatabaseVersion)
.registerRequest(InvalidateSuggestionsDatabase)
.registerRequest(Completion)
.registerRequest(Import)
.registerRequest(RenameProject)
.registerRequest(ProjectInfo)
.registerRequest(EditionsListAvailable)

View File

@ -1,79 +0,0 @@
package org.enso.languageserver.requesthandler.search
import akka.actor.{Actor, ActorRef, Cancellable, Props, Status}
import com.typesafe.scalalogging.LazyLogging
import org.enso.jsonrpc._
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.search.SearchApi.{
Import,
SuggestionsDatabaseError
}
import org.enso.languageserver.search.{SearchFailureMapper, SearchProtocol}
import org.enso.languageserver.util.UnhandledLogging
import scala.concurrent.duration.FiniteDuration
/** A request handler for `search/import` command.
*
* @param timeout request timeout
* @param suggestionsHandler a reference to the suggestions handler
*/
class ImportHandler(
timeout: FiniteDuration,
suggestionsHandler: ActorRef
) extends Actor
with LazyLogging
with UnhandledLogging {
import context.dispatcher
override def receive: Receive = requestStage
private def requestStage: Receive = {
case Request(Import, id, Import.Params(suggestionId)) =>
suggestionsHandler ! SearchProtocol.Import(suggestionId)
val cancellable =
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
context.become(responseStage(id, sender(), cancellable))
}
private def responseStage(
id: Id,
replyTo: ActorRef,
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
logger.error("Search import error.", ex)
replyTo ! ResponseError(Some(id), SuggestionsDatabaseError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
logger.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)
case msg: SearchProtocol.SearchFailure =>
replyTo ! ResponseError(Some(id), SearchFailureMapper.mapFailure(msg))
case SearchProtocol.ImportResult(module, symbol, exports) =>
replyTo ! ResponseResult(
Import,
id,
Import.Result(module, symbol, exports)
)
cancellable.cancel()
context.stop(self)
}
}
object ImportHandler {
/** Creates configuration object used to create a [[ImportHandler]].
*
* @param timeout request timeout
* @param suggestionsHandler a reference to the suggestions handler
*/
def props(timeout: FiniteDuration, suggestionsHandler: ActorRef): Props =
Props(new ImportHandler(timeout, suggestionsHandler))
}

View File

@ -3,7 +3,6 @@ package org.enso.languageserver.search
import org.enso.jsonrpc.{Error, HasParams, HasResult, Method, Unused}
import org.enso.languageserver.filemanager.Path
import org.enso.languageserver.search.SearchProtocol.{
Export,
SuggestionDatabaseEntry,
SuggestionId,
SuggestionKind,
@ -90,20 +89,6 @@ object SearchApi {
}
}
case object Import extends Method("search/import") {
case class Params(id: Long)
case class Result(module: String, symbol: String, exports: Seq[Export])
implicit val hasParams = new HasParams[this.type] {
type Params = Import.Params
}
implicit val hasResult = new HasResult[this.type] {
type Result = Import.Result
}
}
case object SuggestionsDatabaseError
extends Error(7001, "Suggestions database error")

View File

@ -60,6 +60,14 @@ object SearchProtocol {
)
.dropNullValues
case conversion: Suggestion.Conversion =>
Encoder[Suggestion.Method]
.apply(conversionToMethod(conversion))
.deepMerge(
Json.obj(CodecField.Type -> SuggestionType.Method.asJson)
)
.dropNullValues
case function: Suggestion.Function =>
Encoder[Suggestion.Function]
.apply(function)
@ -75,6 +83,29 @@ object SearchProtocol {
.dropNullValues
}
private def conversionToMethod(
conversion: Suggestion.Conversion
): Suggestion.Method = {
val arg = Suggestion.Argument(
Suggestion.Kind.Conversion.From,
conversion.sourceType,
false,
false,
None
)
Suggestion.Method(
conversion.externalId,
conversion.module,
Suggestion.Kind.Conversion.From,
arg +: conversion.arguments,
conversion.returnType,
conversion.returnType,
conversion.documentation,
conversion.documentationHtml,
conversion.reexport
)
}
implicit val suggestionDecoder: Decoder[Suggestion] =
Decoder.instance { cursor =>
cursor.downField(CodecField.Type).as[String].flatMap {
@ -280,6 +311,9 @@ object SearchProtocol {
/** A method suggestion. */
case object Method extends SuggestionKind
/** A conversion suggestion. */
case object Conversion extends SuggestionKind
/** A function suggestion. */
case object Function extends SuggestionKind
@ -295,11 +329,12 @@ object SearchProtocol {
*/
def apply(kind: Suggestion.Kind): SuggestionKind =
kind match {
case Suggestion.Kind.Module => Module
case Suggestion.Kind.Atom => Atom
case Suggestion.Kind.Method => Method
case Suggestion.Kind.Function => Function
case Suggestion.Kind.Local => Local
case Suggestion.Kind.Module => Module
case Suggestion.Kind.Atom => Atom
case Suggestion.Kind.Method => Method
case Suggestion.Kind.Conversion => Conversion
case Suggestion.Kind.Function => Function
case Suggestion.Kind.Local => Local
}
/** Convert from API kind to [[Suggestion.Kind]]
@ -309,11 +344,12 @@ object SearchProtocol {
*/
def toSuggestion(kind: SuggestionKind): Suggestion.Kind =
kind match {
case Module => Suggestion.Kind.Module
case Atom => Suggestion.Kind.Atom
case Method => Suggestion.Kind.Method
case Function => Suggestion.Kind.Function
case Local => Suggestion.Kind.Local
case Module => Suggestion.Kind.Module
case Atom => Suggestion.Kind.Atom
case Method => Suggestion.Kind.Method
case Conversion => Suggestion.Kind.Conversion
case Function => Suggestion.Kind.Function
case Local => Suggestion.Kind.Local
}
}
@ -413,18 +449,6 @@ object SearchProtocol {
*/
case class CompletionResult(currentVersion: Long, results: Seq[SuggestionId])
/** The request returning the info about the suggestion import.
*
* @param id the requested suggestion id
*/
case class Import(id: SuggestionId)
/** The request returning the info about the suggestion import.
*
* @param suggestion the requested suggestion
*/
case class ImportSuggestion(suggestion: Suggestion)
/** Base trait for export statements. */
sealed trait Export {
def module: String

View File

@ -27,10 +27,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.refactoring.ProjectNameChangedEvent
import org.enso.languageserver.runtime.RuntimeFailureMapper
import org.enso.languageserver.search.SearchProtocol._
import org.enso.languageserver.search.handler.{
ImportModuleHandler,
InvalidateModulesIndexHandler
}
import org.enso.languageserver.search.handler.InvalidateModulesIndexHandler
import org.enso.languageserver.session.SessionRouter.DeliverToJsonController
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedPath
@ -352,23 +349,6 @@ final class SuggestionsHandler(
}
.pipeTo(sender())
case Import(suggestionId) =>
val action = for {
result <- suggestionsRepo.select(suggestionId)
} yield result
.map(SearchProtocol.ImportSuggestion)
.getOrElse(SearchProtocol.SuggestionNotFoundError)
val runtimeFailureMapper = RuntimeFailureMapper(contentRootManager)
val handler = context.system.actorOf(
ImportModuleHandler.props(
runtimeFailureMapper,
timeout,
runtimeConnector
)
)
action.pipeTo(handler)(sender())
case FileDeletedEvent(path) =>
getModuleName(projectName, path)
.flatMap { either =>

View File

@ -1,93 +0,0 @@
package org.enso.languageserver.search.handler
import akka.actor.{Actor, ActorRef, Cancellable, Props}
import com.typesafe.scalalogging.LazyLogging
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.runtime.RuntimeFailureMapper
import org.enso.languageserver.search.SearchProtocol
import org.enso.languageserver.util.UnhandledLogging
import org.enso.polyglot.runtime.Runtime.Api
import java.util.UUID
import scala.concurrent.duration.FiniteDuration
/** A request handler for import module command.
*
* @param runtimeFailureMapper mapper for runtime failures
* @param timeout request timeout
* @param runtime reference to the runtime connector
*/
final class ImportModuleHandler(
runtimeFailureMapper: RuntimeFailureMapper,
timeout: FiniteDuration,
runtime: ActorRef
) extends Actor
with LazyLogging
with UnhandledLogging {
import context.dispatcher
override def receive: Receive = requestStage
private def requestStage: Receive = {
case SearchProtocol.ImportSuggestion(suggestion) =>
runtime ! Api.Request(
UUID.randomUUID(),
Api.ImportSuggestionRequest(suggestion)
)
val cancellable =
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
context.become(responseStage(sender(), cancellable))
case msg: SearchProtocol.SearchFailure =>
sender() ! msg
context.stop(self)
}
private def responseStage(
replyTo: ActorRef,
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
replyTo ! RequestTimeout
context.stop(self)
case Api.Response(_, Api.ImportSuggestionResponse(module, sym, exports)) =>
replyTo ! SearchProtocol.ImportResult(
module,
sym,
exports.map(toSearchExport)
)
cancellable.cancel()
context.stop(self)
case Api.Response(_, error: Api.Error) =>
replyTo ! runtimeFailureMapper.mapApiError(error)
cancellable.cancel()
context.stop(self)
}
private def toSearchExport(export: Api.Export): SearchProtocol.Export =
export match {
case Api.Export.Unqualified(module) =>
SearchProtocol.Export.Unqualified(module)
case Api.Export.Qualified(module, alias) =>
SearchProtocol.Export.Qualified(module, alias)
}
}
object ImportModuleHandler {
/** Creates a configuration object used to create [[ImportModuleHandler]].
*
* @param runtimeFailureMapper mapper for runtime failures
* @param timeout request timeout
* @param runtime reference to the runtime conector
*/
def props(
runtimeFailureMapper: RuntimeFailureMapper,
timeout: FiniteDuration,
runtime: ActorRef
): Props =
Props(new ImportModuleHandler(runtimeFailureMapper, timeout, runtime))
}

View File

@ -21,6 +21,10 @@ import org.enso.logger.masking.ToLogString
value = classOf[Suggestion.Method],
name = "suggestionMethod"
),
new JsonSubTypes.Type(
value = classOf[Suggestion.Conversion],
name = "suggestionConversion"
),
new JsonSubTypes.Type(
value = classOf[Suggestion.Function],
name = "suggestionFunction"
@ -49,11 +53,12 @@ object Suggestion {
def apply(suggestion: Suggestion): Kind =
suggestion match {
case _: Module => Module
case _: Atom => Atom
case _: Method => Method
case _: Function => Function
case _: Local => Local
case _: Module => Module
case _: Atom => Atom
case _: Method => Method
case _: Conversion => Conversion
case _: Function => Function
case _: Local => Local
}
/** The module suggestion. */
@ -65,6 +70,12 @@ object Suggestion {
/** The method suggestion. */
case object Method extends Kind
/** The conversion suggestion. */
case object Conversion extends Kind {
val From = "from"
val To = "to"
}
/** The function suggestion. */
case object Function extends Kind
@ -80,6 +91,7 @@ object Suggestion {
case _: Module => None
case _: Atom => None
case method: Method => Some(method.selfType)
case _: Conversion => None
case _: Function => None
case _: Local => None
}
@ -235,6 +247,46 @@ object Suggestion {
s",reexport=$reexport)"
}
/** A conversion function.
*
* @param externalId the external id
* @param module the module name
* @param arguments the list of arguments
* @param sourceType the source type of a conversion
* @param returnType the return type of a conversion
* @param documentation the documentation string
* @param documentationHtml the documentation rendered as HTML
* @param reexport the module re-exporting this conversion
*/
case class Conversion(
externalId: Option[ExternalId],
module: String,
arguments: Seq[Argument],
sourceType: String,
returnType: String,
documentation: Option[String],
documentationHtml: Option[String],
reexport: Option[String] = None
) extends Suggestion {
/** @inheritdoc */
override def name: String =
Kind.Conversion.From
/** @inheritdoc */
override def toLogString(shouldMask: Boolean): String =
"Conversion(" +
s"module=$module," +
s"arguments=${arguments.map(_.toLogString(shouldMask))}," +
s"sourceType=$sourceType," +
s"returnType=$returnType," +
s"documentation=" + (if (shouldMask) documentation.map(_ => STUB)
else documentation) +
s",documentationHtml=" + (if (shouldMask) documentationHtml.map(_ => STUB)
else documentationHtml) +
s",reexport=$reexport)"
}
/** A local function definition.
*
* @param externalId the external id

View File

@ -198,14 +198,6 @@ object Runtime {
value = classOf[Api.VerifyModulesIndexResponse],
name = "verifyModulesIndexResponse"
),
new JsonSubTypes.Type(
value = classOf[Api.ImportSuggestionRequest],
name = "importSuggestionRequest"
),
new JsonSubTypes.Type(
value = classOf[Api.ImportSuggestionResponse],
name = "importSuggestionResponse"
),
new JsonSubTypes.Type(
value = classOf[Api.GetTypeGraphRequest],
name = "getTypeGraphRequest"
@ -1382,31 +1374,6 @@ object Runtime {
final case class VerifyModulesIndexResponse(remove: Seq[String])
extends ApiResponse
/** A request to return info needed to import the suggestion.
*
* @param suggestion the suggestion to import
*/
final case class ImportSuggestionRequest(suggestion: Suggestion)
extends ApiRequest
with ToLogString {
/** @inheritdoc */
override def toLogString(shouldMask: Boolean): String =
s"ImportSuggestionRequest(suggestion=${suggestion.toLogString(shouldMask)})"
}
/** The result of the import request.
*
* @param module the definition module of the symbol
* @param symbol the resolved symbol
* @param exports the list of exports of the symbol
*/
final case class ImportSuggestionResponse(
module: String,
symbol: String,
exports: Seq[Export]
) extends ApiResponse
/** A request for the type hierarchy graph. */
final case class GetTypeGraphRequest() extends ApiRequest

View File

@ -80,6 +80,27 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
)
go(tree ++= methodOpt.map(Tree.Node(_, subforest)), scope)
case IR.Module.Scope.Definition.Method
.Conversion(
IR.Name.MethodReference(_, _, _, _, _),
IR.Name.Literal(sourceTypeName, _, _, _, _, _),
IR.Function.Lambda(args, body, _, _, _, _),
_,
_,
_
) if ConversionsEnabled =>
val typeSignature = ir.getMetadata(TypeSignatures)
val conversion = buildConversion(
body.getExternalId,
module,
args,
sourceTypeName,
doc,
typeSignature,
bindings
)
go(tree += Tree.Node(conversion, Vector()), scope)
case IR.Expression.Binding(
name,
IR.Function.Lambda(args, body, _, _, _, _),
@ -167,7 +188,34 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
)
}
/** Build a function suggestion */
/** Build a conversion suggestion. */
private def buildConversion(
externalId: Option[IR.ExternalId],
module: QualifiedName,
args: Seq[IR.DefinitionArgument],
sourceTypeName: String,
doc: Option[String],
typeSignature: Option[TypeSignatures.Metadata],
bindings: Option[BindingAnalysis.Metadata]
): Suggestion.Conversion = {
val typeSig = buildTypeSignatureFromMetadata(typeSignature, bindings)
val (methodArgs, returnTypeDef) =
buildFunctionArguments(args, typeSig)
Suggestion.Conversion(
externalId = externalId,
module = module.toString,
arguments = methodArgs,
sourceType = sourceTypeName,
returnType = buildReturnType(returnTypeDef),
documentation = doc,
documentationHtml = doc.map(
DocParserWrapper.runOnPureDoc(_, Suggestion.Kind.Conversion.From)
),
reexport = None
)
}
/** Build a function suggestion. */
private def buildFunction(
externalId: Option[IR.ExternalId],
module: QualifiedName,
@ -569,6 +617,9 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
object SuggestionBuilder {
/** TODO[DB] enable conversions when they get the runtime support. */
private val ConversionsEnabled: Boolean = false
/** Create the suggestion builder.
*
* @param source the text source

View File

@ -50,9 +50,6 @@ object CommandFactory {
case payload: Api.VerifyModulesIndexRequest =>
new VerifyModulesIndexCmd(request.requestId, payload)
case payload: Api.ImportSuggestionRequest =>
new ImportSuggestionCmd(request.requestId, payload)
case _: Api.GetTypeGraphRequest =>
new GetTypeGraphCommand(request.requestId)

View File

@ -1,126 +0,0 @@
package org.enso.interpreter.instrument.command
import org.enso.compiler.data.BindingsMap
import org.enso.compiler.pass.analyse.BindingAnalysis
import org.enso.interpreter.instrument.execution.RuntimeContext
import org.enso.interpreter.runtime.Module
import org.enso.polyglot.Suggestion
import org.enso.polyglot.runtime.Runtime.Api
import scala.concurrent.{ExecutionContext, Future}
/** A command that gathers info required for suggestion import.
*
* @param maybeRequestId an option with request id
* @param request a request for suggestion import
*/
final class ImportSuggestionCmd(
maybeRequestId: Option[Api.RequestId],
val request: Api.ImportSuggestionRequest
) extends Command(maybeRequestId) {
import ImportSuggestionCmd._
/** Executes a request.
*
* @param ctx contains suppliers of services to perform a request
* @param ec execution context
*/
override def execute(implicit
ctx: RuntimeContext,
ec: ExecutionContext
): Future[Unit] = Future {
val suggestion = request.suggestion
reply(
Api.ImportSuggestionResponse(
suggestion.module,
suggestion.name,
findExports.sortBy(_.depth).map(_.export)
)
)
}
/** Find re-exports of the given symbol.
*
* @param ctx contains suppliers of services to perform a request
*/
private def findExports(implicit ctx: RuntimeContext): Seq[ExportResult] = {
val suggestion = request.suggestion
val topScope =
ctx.executionService.getContext.getCompiler.context.getTopScope
val builder = Vector.newBuilder[ExportResult]
topScope.getModules
.stream()
.filter(isCompiled)
.forEach { module =>
module.getIr.getMetadata(BindingAnalysis).foreach { bindings =>
builder ++= getQualifiedExport(
module,
suggestion,
bindings
)
builder ++= getUnqualifiedExport(module, suggestion, bindings)
}
}
builder.result()
}
/** Extract the qualified export from the bindings map. */
private def getQualifiedExport(
module: Module,
suggestion: Suggestion,
bindings: BindingsMap
): Option[ExportResult] = {
bindings.resolvedExports
.find(_.module.getName.toString == suggestion.module)
.filter(_.exportedAs.isDefined)
.map { exportedModule =>
val qualified = Api.Export.Qualified(
module.getName.toString,
exportedModule.exportedAs
)
ExportResult(qualified, getDepth(module))
}
}
/** Extract the unqualified export from the bindings map. */
private def getUnqualifiedExport(
module: Module,
suggestion: Suggestion,
bindings: BindingsMap
): Option[ExportResult] = {
bindings.exportedSymbols.get(suggestion.name).flatMap { resolvedExports =>
resolvedExports
.find(_.module.getName.toString == suggestion.module)
.map { _ =>
val unqualified = Api.Export.Unqualified(module.getName.toString)
ExportResult(unqualified, getDepth(module))
}
}
}
private def getDepth(module: Module): Int =
module.getName.path.size
private def isCompiled(module: Module): Boolean =
module.getIr != null
}
object ImportSuggestionCmd {
/** Module that exports target symbol.
*
* @param name the module name
*/
private case class ExportingModule(name: String)
/** An intermediate result of exports resolution.
*
* @param export the module export
* @param depth how nested is the exporting module
*/
private case class ExportResult(export: Api.Export, depth: Int)
}

View File

@ -442,11 +442,12 @@ class EnsureCompiledJob(protected val files: Iterable[File])
private def isSuggestionGlobal(suggestion: Suggestion): Boolean =
suggestion match {
case _: Suggestion.Module => true
case _: Suggestion.Atom => true
case _: Suggestion.Method => true
case _: Suggestion.Function => false
case _: Suggestion.Local => false
case _: Suggestion.Module => true
case _: Suggestion.Atom => true
case _: Suggestion.Method => true
case _: Suggestion.Conversion => true
case _: Suggestion.Function => false
case _: Suggestion.Local => false
}
private def getCompilationStatus(

View File

@ -647,6 +647,188 @@ class SuggestionBuilderTest extends CompilerTest {
)
}
"build conversion method for simple type" in {
pending
implicit val moduleContext: ModuleContext = freshModuleContext
val code =
"""type MyAtom a
|
|## My conversion
|MyAtom.from : Number -> MyAtom
|MyAtom.from a = MyAtom a
|""".stripMargin
val module = code.preprocessModule
build(code, module) shouldEqual Tree.Root(
Vector(
ModuleNode,
Tree.Node(
Suggestion.Atom(
externalId = None,
module = "Unnamed.Test",
name = "MyType",
arguments = Seq(
Suggestion
.Argument("a", SuggestionBuilder.Any, false, false, None)
),
returnType = "Unnamed.Test.MyType",
documentation = None,
documentationHtml = None
),
Vector()
),
Tree.Node(
Suggestion.Method(
externalId = None,
module = "Unnamed.Test",
name = "a",
arguments = List(
Suggestion
.Argument("this", "Unnamed.Test.MyType", false, false, None)
),
selfType = "Unnamed.Test.MyType",
returnType = SuggestionBuilder.Any,
documentation = None,
documentationHtml = None
),
Vector()
),
Tree.Node(
Suggestion.Conversion(
externalId = None,
module = "Unnamed.Test",
arguments = Seq(
Suggestion.Argument("a", "Number", false, false, None)
),
returnType = "Unnamed.Test.MyType",
sourceType = "Number",
documentation = Some(" My conversion"),
documentationHtml =
Some(DocParserWrapper.runOnPureDoc(" My conversion", "from"))
),
Vector()
)
)
)
}
"build conersion method for complex type" in {
pending
implicit val moduleContext: ModuleContext = freshModuleContext
val code =
"""type MyMaybe
| type Some a
| type None
|
|type Newtype x
|
|## My conversion method
|Newtype.from : MyMaybe -> Newtype
|Newtype.from opt = case opt of
| Some a -> Newtype a
| None -> Newtype 0
|""".stripMargin
val module = code.preprocessModule
build(code, module) shouldEqual Tree.Root(
Vector(
ModuleNode,
Tree.Node(
Suggestion.Atom(
externalId = None,
module = "Unnamed.Test",
name = "Some",
arguments = Seq(
Suggestion
.Argument("a", SuggestionBuilder.Any, false, false, None)
),
returnType = "Unnamed.Test.MyMaybe",
documentation = None,
documentationHtml = None
),
Vector()
),
Tree.Node(
Suggestion.Method(
externalId = None,
module = "Unnamed.Test",
name = "a",
arguments = List(
Suggestion
.Argument("this", "Unnamed.Test.MyMaybe", false, false, None)
),
selfType = "Unnamed.Test.MyMaybe",
returnType = SuggestionBuilder.Any,
documentation = None,
documentationHtml = None
),
Vector()
),
Tree.Node(
Suggestion.Atom(
externalId = None,
module = "Unnamed.Test",
name = "None",
arguments = Seq(),
returnType = "Unnamed.Test.None",
documentation = None,
documentationHtml = None
),
Vector()
),
Tree.Node(
Suggestion.Atom(
externalId = None,
module = "Unnamed.Test",
name = "Newtype",
arguments = Seq(
Suggestion
.Argument("x", SuggestionBuilder.Any, false, false, None)
),
returnType = "Unnamed.Test.Newtype",
documentation = None,
documentationHtml = None
),
Vector()
),
Tree.Node(
Suggestion.Method(
externalId = None,
module = "Unnamed.Test",
name = "x",
arguments = List(
Suggestion
.Argument("this", "Unnamed.Test.Newtype", false, false, None)
),
selfType = "Unnamed.Test.NewType",
returnType = SuggestionBuilder.Any,
documentation = None,
documentationHtml = None
),
Vector()
),
Tree.Node(
Suggestion.Conversion(
externalId = None,
module = "Unnamed.Test",
arguments = Seq(
Suggestion
.Argument("opt", "Unnamed.Test.MyMaybe", false, false, None)
),
returnType = "Unnamed.Test.MyType",
sourceType = "Unnamed.Test.MyMaybe",
documentation = Some(" My conversion method"),
documentationHtml = Some(
DocParserWrapper.runOnPureDoc(" My conversion method", "from")
)
),
Vector()
)
)
)
}
"build function simple" in {
implicit val moduleContext: ModuleContext = freshModuleContext

View File

@ -22,11 +22,12 @@ object SuggestionRandom {
def nextSuggestion(): Suggestion = {
nextKind() match {
case Suggestion.Kind.Module => nextSuggestionModule()
case Suggestion.Kind.Atom => nextSuggestionAtom()
case Suggestion.Kind.Method => nextSuggestionMethod()
case Suggestion.Kind.Function => nextSuggestionFunction()
case Suggestion.Kind.Local => nextSuggestionLocal()
case Suggestion.Kind.Module => nextSuggestionModule()
case Suggestion.Kind.Atom => nextSuggestionAtom()
case Suggestion.Kind.Method => nextSuggestionMethod()
case Suggestion.Kind.Conversion => nextSuggestionMethod()
case Suggestion.Kind.Function => nextSuggestionFunction()
case Suggestion.Kind.Local => nextSuggestionLocal()
}
}

View File

@ -1066,6 +1066,41 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
reexport = reexport
)
row -> args
case Suggestion.Conversion(
expr,
module,
args,
sourceType,
returnType,
doc,
docHtml,
reexport
) =>
val firstArg = Suggestion.Argument(
Suggestion.Kind.Conversion.From,
sourceType,
false,
false,
None
)
val row = SuggestionRow(
id = None,
externalIdLeast = expr.map(_.getLeastSignificantBits),
externalIdMost = expr.map(_.getMostSignificantBits),
kind = SuggestionKind.CONVERSION,
module = module,
name = toConversionMethodName(sourceType, returnType),
selfType = SelfTypeColumn.EMPTY,
returnType = returnType,
documentation = doc,
documentationHtml = docHtml,
scopeStartLine = ScopeColumn.EMPTY,
scopeStartOffset = ScopeColumn.EMPTY,
scopeEndLine = ScopeColumn.EMPTY,
scopeEndOffset = ScopeColumn.EMPTY,
reexport = reexport
)
row -> (firstArg +: args)
case Suggestion.Function(expr, module, name, args, returnType, scope) =>
val row = SuggestionRow(
id = None,
@ -1106,6 +1141,13 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
row -> Seq()
}
/** Create the method name for conversion */
private def toConversionMethodName(
sourceType: String,
returnType: String
): String =
s"${Suggestion.Kind.Conversion.From}_${sourceType}_${returnType}"
/** Convert the argument to a row in the arguments table. */
private def toArgumentRow(
suggestionId: Long,
@ -1168,6 +1210,18 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
documentationHtml = suggestion.documentationHtml,
reexport = suggestion.reexport
)
case SuggestionKind.CONVERSION =>
Suggestion.Conversion(
externalId =
toUUID(suggestion.externalIdLeast, suggestion.externalIdMost),
module = suggestion.module,
arguments = arguments.sortBy(_.index).tail.map(toArgument),
sourceType = arguments.minBy(_.index).tpe,
returnType = suggestion.returnType,
documentation = suggestion.documentation,
documentationHtml = suggestion.documentationHtml,
reexport = suggestion.reexport
)
case SuggestionKind.FUNCTION =>
Suggestion.Function(
externalId =

View File

@ -83,11 +83,12 @@ case class ModuleVersionRow(module: String, digest: Array[Byte])
/** The type of a suggestion. */
object SuggestionKind {
val MODULE: Byte = 0
val ATOM: Byte = 1
val METHOD: Byte = 2
val FUNCTION: Byte = 3
val LOCAL: Byte = 4
val MODULE: Byte = 0
val ATOM: Byte = 1
val METHOD: Byte = 2
val FUNCTION: Byte = 3
val LOCAL: Byte = 4
val CONVERSION: Byte = 5
/** Create a database suggestion kind.
*
@ -96,11 +97,12 @@ object SuggestionKind {
*/
def apply(kind: Suggestion.Kind): Byte =
kind match {
case Suggestion.Kind.Module => MODULE
case Suggestion.Kind.Atom => ATOM
case Suggestion.Kind.Method => METHOD
case Suggestion.Kind.Function => FUNCTION
case Suggestion.Kind.Local => LOCAL
case Suggestion.Kind.Module => MODULE
case Suggestion.Kind.Atom => ATOM
case Suggestion.Kind.Method => METHOD
case Suggestion.Kind.Conversion => CONVERSION
case Suggestion.Kind.Function => FUNCTION
case Suggestion.Kind.Local => LOCAL
}
}

View File

@ -67,6 +67,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -79,6 +80,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -91,6 +93,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -115,6 +118,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -133,6 +137,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -155,6 +160,8 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.atom,
suggestion.method,
suggestion.method,
suggestion.conversion,
suggestion.conversion,
suggestion.function,
suggestion.function,
suggestion.local,
@ -169,10 +176,15 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
ids(1) shouldBe a[None.type]
ids(2) shouldBe a[Some[_]]
ids(3) shouldBe a[None.type]
ids(4) shouldBe a[Some[_]]
ids(5) shouldBe a[None.type]
ids(6) shouldBe a[Some[_]]
ids(7) shouldBe a[None.type]
all.map(_.suggestion) should contain theSameElementsAs Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -222,6 +234,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -241,6 +254,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -255,11 +269,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
"remove all suggestions" taggedAs Retry in withRepo { repo =>
val action = for {
(_, Seq(_, id1, _, _, id4)) <- repo.insertAll(
(_, Seq(_, id1, _, _, _, id4)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -402,6 +417,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
Some(id4) <- repo.insert(suggestion.local)
res <-
@ -417,11 +433,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
"update suggestion external id" taggedAs Retry in withRepo { repo =>
val newUuid = UUID.randomUUID()
val action = for {
(v1, Seq(_, _, id1, _, _)) <- repo.insertAll(
(v1, Seq(_, _, id1, _, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -447,11 +464,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
"update suggestion removing external id" taggedAs Retry in withRepo {
repo =>
val action = for {
(v1, Seq(_, _, _, id1, _)) <- repo.insertAll(
(v1, Seq(_, _, _, _, id1, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -477,11 +495,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
"update suggestion return type" taggedAs Retry in withRepo { repo =>
val newReturnType = "NewType"
val action = for {
(v1, Seq(_, _, _, id1, _)) <- repo.insertAll(
(v1, Seq(_, _, _, _, id1, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -507,11 +526,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
"update suggestion atom documentation" taggedAs Retry in withRepo { repo =>
val newDoc = "My Doc"
val action = for {
(v1, Seq(_, id1, _, _, _)) <- repo.insertAll(
(v1, Seq(_, id1, _, _, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -538,11 +558,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
repo =>
val newDoc = "My Doc"
val action = for {
(v1, Seq(_, id1, _, _, _)) <- repo.insertAll(
(v1, Seq(_, id1, _, _, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -571,11 +592,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
repo =>
val newDoc = "My Doc"
val action = for {
(v1, Seq(id1, _, _, _, _)) <- repo.insertAll(
(v1, Seq(id1, _, _, _, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -602,11 +624,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
repo =>
val newDoc = "My Doc"
val action = for {
(v1, Seq(id1, _, _, _, _)) <- repo.insertAll(
(v1, Seq(id1, _, _, _, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -631,14 +654,83 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
)
}
"update suggestion removing documentation" taggedAs Retry in withRepo {
"update suggestion conversion documentation" taggedAs Retry in withRepo {
repo =>
val newDoc = "My Doc"
val action = for {
(v1, Seq(_, id1, _, _, _)) <- repo.insertAll(
(v1, Seq(_, _, _, id1, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
)
(v2, id2) <- repo.update(
suggestion.conversion,
None,
None,
None,
Some(Some(newDoc)),
None,
None,
None
)
s <- repo.select(id1.get)
} yield (v1, id1, v2, id2, s)
val (v1, id1, v2, id2, s) = Await.result(action, Timeout)
v1 should not equal v2
id1 shouldEqual id2
s shouldEqual Some(
suggestion.conversion.copy(documentation = Some(newDoc))
)
}
"update suggestion conversion HTML documentation" taggedAs Retry in withRepo {
repo =>
val newDoc = "My Doc"
val action = for {
(v1, Seq(_, _, _, id1, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
)
(v2, id2) <- repo.update(
suggestion.conversion,
None,
None,
None,
None,
Some(Some(newDoc)),
None,
None
)
s <- repo.select(id1.get)
} yield (v1, id1, v2, id2, s)
val (v1, id1, v2, id2, s) = Await.result(action, Timeout)
v1 should not equal v2
id1 shouldEqual id2
s shouldEqual Some(
suggestion.conversion.copy(documentationHtml = Some(newDoc))
)
}
"update suggestion removing documentation" taggedAs Retry in withRepo {
repo =>
val action = for {
(v1, Seq(_, id1, _, _, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -664,11 +756,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
"update suggestion removing HTML documentation" taggedAs Retry in withRepo {
repo =>
val action = for {
(v1, Seq(_, id1, _, _, _)) <- repo.insertAll(
(v1, Seq(_, id1, _, _, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -697,11 +790,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
Suggestion.Position(42, 43)
)
val action = for {
(v1, Seq(_, _, _, _, id1)) <- repo.insertAll(
(v1, Seq(_, _, _, _, _, id1)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -729,11 +823,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
Api.SuggestionArgumentAction.Remove(1)
)
val action = for {
(v1, Seq(_, id1, _, _, _)) <- repo.insertAll(
(v1, Seq(_, id1, _, _, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -764,11 +859,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
Api.SuggestionArgumentAction.Add(3, suggestion.atom.arguments(1))
)
val action = for {
(v1, Seq(_, id1, _, _, _)) <- repo.insertAll(
(v1, Seq(_, id1, _, _, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -807,11 +903,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
)
)
val action = for {
(v1, Seq(_, id1, _, _, _)) <- repo.insertAll(
(v1, Seq(_, id1, _, _, _, _)) <- repo.insertAll(
Seq(
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -846,6 +943,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -872,6 +970,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
id4 <- repo.insert(suggestion.local)
v1 <- repo.currentVersion
@ -891,6 +990,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
v1 <- repo.currentVersion
@ -912,6 +1012,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -925,14 +1026,22 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
xs1 should contain theSameElementsAs ids.flatten.map((_, newModuleName))
xs2 should contain theSameElementsAs Seq(ids(2)).flatten
.map((_, newSelfType))
xs3 should contain theSameElementsAs Seq(ids(3), ids(4)).flatten
.map((_, newReturnType))
xs4 should contain theSameElementsAs Seq()
xs3 should contain theSameElementsAs Seq(ids(3), ids(4), ids(5)).flatten
.map {
case id if ids(3).get == id => (id, "Best.Main.Bar")
case id => (id, newReturnType)
}
xs4 should contain theSameElementsAs Seq((ids(3).get, 0, "Best.Main.Foo"))
res.map(_.suggestion) should contain theSameElementsAs Seq(
suggestion.module.copy(module = newModuleName),
suggestion.atom.copy(module = newModuleName),
suggestion.method
.copy(module = newModuleName, selfType = newSelfType),
suggestion.conversion.copy(
module = newModuleName,
sourceType = "Best.Main.Foo",
returnType = "Best.Main.Bar"
),
suggestion.function
.copy(module = newModuleName, returnType = newReturnType),
suggestion.local
@ -952,6 +1061,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -971,15 +1081,25 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
}
xs2 should contain theSameElementsAs Seq(ids(2)).flatten
.map((_, newSelfType))
xs3 should contain theSameElementsAs Seq(ids(3), ids(4)).flatten
.map((_, newReturnType))
xs4 should contain theSameElementsAs Seq()
xs3 should contain theSameElementsAs Seq(ids(3), ids(4), ids(5)).flatten
.map {
case id if ids(3).get == id => (id, "Best.Main.Bar")
case id => (id, newReturnType)
}
xs4 should contain theSameElementsAs Seq(
(ids(3).get, 0, "Best.Main.Foo")
)
res.map(_.suggestion) should contain theSameElementsAs Seq(
suggestion.module
.copy(module = newModuleName),
atom.copy(module = "Best.Main.Test.Main"),
suggestion.method
.copy(module = newModuleName, selfType = newSelfType),
suggestion.conversion.copy(
module = newModuleName,
sourceType = "Best.Main.Foo",
returnType = "Best.Main.Bar"
),
suggestion.function
.copy(module = newModuleName, returnType = newReturnType),
suggestion.local
@ -993,12 +1113,13 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
val newFooModuleName = "Best.Foo"
val newReturnTypeName = "Best.Main.MyType"
val module = suggestion.module.copy(module = "Test.Main")
val atom = suggestion.atom.copy(module = "Test.Main")
val method = suggestion.method.copy(module = "Test.Foo")
val function = suggestion.function.copy(module = "Bar.Main")
val local = suggestion.local.copy(module = "Bar.Main")
val all = Seq(module, atom, method, function, local)
val module = suggestion.module.copy(module = "Test.Main")
val atom = suggestion.atom.copy(module = "Test.Main")
val method = suggestion.method.copy(module = "Test.Foo")
val conversion = suggestion.conversion.copy(module = "Test.Foo")
val function = suggestion.function.copy(module = "Bar.Main")
val local = suggestion.local.copy(module = "Bar.Main")
val all = Seq(module, atom, method, conversion, function, local)
val action = for {
(_, ids) <- repo.insertAll(all)
(_, xs1, xs2, xs3, xs4) <- repo.renameProject("Test", "Best")
@ -1008,7 +1129,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
val (ids, xs1, xs2, xs3, xs4, res) = Await.result(action, Timeout)
xs1 should contain theSameElementsAs ids
.zip(Seq(module, atom, method))
.zip(Seq(module, atom, method, conversion))
.flatMap {
case (idOpt, _: Suggestion.Module) =>
idOpt.map((_, newMainModuleName))
@ -1019,13 +1140,23 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
}
xs2 should contain theSameElementsAs Seq(ids(2)).flatten
.map((_, newMainModuleName))
xs3 should contain theSameElementsAs Seq(ids(3), ids(4)).flatten
.map((_, newReturnTypeName))
xs4 should contain theSameElementsAs Seq()
xs3 should contain theSameElementsAs Seq(ids(3), ids(4), ids(5)).flatten
.map {
case id if ids(3).get == id => (id, "Best.Main.Bar")
case id => (id, newReturnTypeName)
}
xs4 should contain theSameElementsAs Seq(
(ids(3).get, 0, "Best.Main.Foo")
)
res.map(_.suggestion) should contain theSameElementsAs Seq(
module.copy(module = newMainModuleName),
atom.copy(module = newMainModuleName),
method.copy(module = newFooModuleName, selfType = newMainModuleName),
module.copy(module = newMainModuleName),
atom.copy(module = newMainModuleName),
method.copy(module = newFooModuleName, selfType = newMainModuleName),
suggestion.conversion.copy(
module = newFooModuleName,
sourceType = "Best.Main.Foo",
returnType = "Best.Main.Bar"
),
function.copy(returnType = newReturnTypeName),
local.copy(returnType = newReturnTypeName)
)
@ -1055,6 +1186,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -1069,10 +1201,16 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
xs1 should contain theSameElementsAs ids.flatten.map((_, newModuleName))
xs2 should contain theSameElementsAs Seq(ids(2)).flatten
.map((_, newSelfType))
xs3 should contain theSameElementsAs Seq(ids(3), ids(4)).flatten
.map((_, newReturnType))
xs4 should contain theSameElementsAs Seq(ids(2)).flatMap {
_.map((_, 1, newArgumentType))
xs3 should contain theSameElementsAs Seq(ids(3), ids(4), ids(5)).flatten
.map {
case id if ids(3).get == id => (id, "Best.Main.Bar")
case id => (id, newReturnType)
}
xs4 should contain theSameElementsAs Seq(ids(2), ids(3)).flatMap {
_.map {
case id if ids(3).get == id => (id, 0, "Best.Main.Foo")
case id => (id, 1, newArgumentType)
}
}
res.map(_.suggestion) should contain theSameElementsAs Seq(
suggestion.module.copy(module = newModuleName),
@ -1088,6 +1226,11 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
)
}
),
suggestion.conversion.copy(
module = newModuleName,
sourceType = "Best.Main.Foo",
returnType = "Best.Main.Bar"
),
suggestion.function
.copy(module = newModuleName, returnType = newReturnType),
suggestion.local
@ -1142,6 +1285,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -1182,6 +1326,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -1221,6 +1366,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -1256,6 +1402,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
suggestion.module,
suggestion.atom,
suggestion.method,
suggestion.conversion,
suggestion.function,
suggestion.local
)
@ -1275,6 +1422,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
res <- repo.search(None, Seq(), None, None, None)
@ -1289,13 +1437,21 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
id0 <- repo.insert(suggestion.module)
id1 <- repo.insert(suggestion.atom)
id2 <- repo.insert(suggestion.method)
id3 <- repo.insert(suggestion.function)
id4 <- repo.insert(suggestion.local)
id3 <- repo.insert(suggestion.conversion)
id4 <- repo.insert(suggestion.function)
id5 <- repo.insert(suggestion.local)
res <- repo.search(Some("Test.Main"), Seq(), None, None, None)
} yield (id0, id1, id2, id3, id4, res._2)
} yield (id0, id1, id2, id3, id4, id5, res._2)
val (id0, id1, id2, id3, id4, res) = Await.result(action, Timeout)
res should contain theSameElementsAs Seq(id0, id1, id2, id3, id4).flatten
val (id0, id1, id2, id3, id4, id5, res) = Await.result(action, Timeout)
res should contain theSameElementsAs Seq(
id0,
id1,
id2,
id3,
id4,
id5
).flatten
}
"search suggestion by empty module" taggedAs Retry in withRepo { repo =>
@ -1303,10 +1459,11 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
id0 <- repo.insert(suggestion.module)
id1 <- repo.insert(suggestion.atom)
id2 <- repo.insert(suggestion.method)
id3 <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
res <- repo.search(Some(""), Seq(), None, None, None)
} yield (res._2, Seq(id0, id1, id2))
} yield (res._2, Seq(id0, id1, id2, id3))
val (res, globals) = Await.result(action, Timeout)
res should contain theSameElementsAs globals.flatten
@ -1317,6 +1474,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
id2 <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
res <- repo.search(None, Seq("Test.Main"), None, None, None)
@ -1331,6 +1489,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
id3 <- repo.insert(suggestion.function)
id4 <- repo.insert(suggestion.local)
res <- repo.search(None, Seq(), Some("Test.Main.MyType"), None, None)
@ -1346,6 +1505,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
id1 <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
id4 <- repo.insert(suggestion.local)
res <- repo.search(None, Seq(), None, Some(kinds), None)
@ -1360,6 +1520,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
res <- repo.search(None, Seq(), None, Some(Seq()), None)
@ -1374,6 +1535,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
id0 <- repo.insert(suggestion.module)
id1 <- repo.insert(suggestion.atom)
id2 <- repo.insert(suggestion.method)
id3 <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
res <-
@ -1384,10 +1546,10 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
None,
Some(Suggestion.Position(99, 42))
)
} yield (id0, id1, id2, res._2)
} yield (id0, id1, id2, id3, res._2)
val (id0, id1, id2, res) = Await.result(action, Timeout)
res should contain theSameElementsAs Seq(id0, id1, id2).flatten
val (id0, id1, id2, id3, res) = Await.result(action, Timeout)
res should contain theSameElementsAs Seq(id0, id1, id2, id3).flatten
}
"search suggestion local by scope" taggedAs Retry in withRepo { repo =>
@ -1395,14 +1557,15 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
id0 <- repo.insert(suggestion.module)
id1 <- repo.insert(suggestion.atom)
id2 <- repo.insert(suggestion.method)
id3 <- repo.insert(suggestion.function)
id3 <- repo.insert(suggestion.conversion)
id4 <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
res <-
repo.search(None, Seq(), None, None, Some(Suggestion.Position(1, 5)))
} yield (id0, id1, id2, id3, res._2)
} yield (id0, id1, id2, id3, id4, res._2)
val (id0, id1, id2, id3, res) = Await.result(action, Timeout)
res should contain theSameElementsAs Seq(id0, id1, id2, id3).flatten
val (id0, id1, id2, id3, id4, res) = Await.result(action, Timeout)
res should contain theSameElementsAs Seq(id0, id1, id2, id3, id4).flatten
}
"search suggestion by module and self type" taggedAs Retry in withRepo {
@ -1411,6 +1574,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
id2 <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
res <- repo.search(
@ -1433,6 +1597,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
id4 <- repo.insert(suggestion.local)
res <- repo.search(
@ -1454,6 +1619,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
id4 <- repo.insert(suggestion.local)
res <- repo.search(
@ -1475,6 +1641,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
id1 <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
res <- repo.search(
@ -1496,6 +1663,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
res <- repo.search(
@ -1518,6 +1686,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
id4 <- repo.insert(suggestion.local)
res <- repo.search(
@ -1540,6 +1709,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
id4 <- repo.insert(suggestion.local)
res <- repo.search(
@ -1565,6 +1735,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
_ <- repo.insert(suggestion.module)
_ <- repo.insert(suggestion.atom)
_ <- repo.insert(suggestion.method)
_ <- repo.insert(suggestion.conversion)
_ <- repo.insert(suggestion.function)
_ <- repo.insert(suggestion.local)
res <- repo.search(
@ -1619,6 +1790,18 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
reexport = None
)
val conversion: Suggestion.Conversion =
Suggestion.Conversion(
externalId = Some(UUID.randomUUID()),
module = "Test.Main",
arguments = Seq(),
sourceType = "Test.Main.Foo",
returnType = "Test.Main.Bar",
documentation = None,
documentationHtml = None,
reexport = None
)
val function: Suggestion.Function =
Suggestion.Function(
externalId = Some(UUID.randomUUID()),