mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 06:01:37 +03:00
Add Reexport Field to Suggestions (#1793)
Add the reexport field to suggestions
This commit is contained in:
parent
f55d66cb2c
commit
980ba8cb65
@ -428,7 +428,7 @@ val scalatestVersion = "3.3.0-SNAP2"
|
||||
val shapelessVersion = "2.4.0-M1"
|
||||
val slf4jVersion = "1.7.30"
|
||||
val slickVersion = "3.3.2"
|
||||
val sqliteVersion = "3.31.1"
|
||||
val sqliteVersion = "3.36.0.1"
|
||||
val tikaVersion = "1.24.1"
|
||||
val typesafeConfigVersion = "1.4.1"
|
||||
|
||||
|
@ -36,11 +36,6 @@ The license file can be found at `licenses/APACHE2.0`.
|
||||
Copyright notices related to this dependency can be found in the directory `com.fasterxml.jackson.core.jackson-annotations-2.11.1`.
|
||||
|
||||
|
||||
'sqlite-jdbc', licensed under the The Apache Software License, Version 2.0, is distributed with the engine.
|
||||
The license information can be found along with the copyright notices.
|
||||
Copyright notices related to this dependency can be found in the directory `org.xerial.sqlite-jdbc-3.31.1`.
|
||||
|
||||
|
||||
'shapeless_2.13', licensed under the Apache 2, is distributed with the engine.
|
||||
The license file can be found at `licenses/APACHE2.0`.
|
||||
Copyright notices related to this dependency can be found in the directory `com.chuusai.shapeless_2.13-2.3.3`.
|
||||
@ -116,6 +111,11 @@ The license file can be found at `licenses/APACHE2.0`.
|
||||
Copyright notices related to this dependency can be found in the directory `org.yaml.snakeyaml-1.26`.
|
||||
|
||||
|
||||
'sqlite-jdbc', licensed under the The Apache Software License, Version 2.0, is distributed with the engine.
|
||||
The license information can be found along with the copyright notices.
|
||||
Copyright notices related to this dependency can be found in the directory `org.xerial.sqlite-jdbc-3.36.0.1`.
|
||||
|
||||
|
||||
'slick_2.13', licensed under the Two-clause BSD-style license, is distributed with the engine.
|
||||
The license information can be found along with the copyright notices.
|
||||
Copyright notices related to this dependency can be found in the directory `com.typesafe.slick.slick_2.13-3.3.2`.
|
||||
|
@ -1,19 +1,3 @@
|
||||
/*
|
||||
* Copyright 2014 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2016 The Error Prone Authors.
|
||||
*
|
||||
@ -30,22 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2017 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2015 The Error Prone Authors.
|
||||
*
|
||||
@ -61,3 +29,35 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2014 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2017 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Copyright (c) 2008 Google Inc.
|
||||
|
||||
// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
|
||||
// www.source-code.biz, www.inventec.ch/chdh
|
||||
|
||||
/* Copyright (c) 2008 Google Inc.
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Copyright (c) 2008 Google Inc.
|
||||
|
||||
// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
|
||||
// www.source-code.biz, www.inventec.ch/chdh
|
||||
|
||||
/* Copyright (c) 2008 Google Inc.
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Copyright (c) 2008 Google Inc.
|
||||
|
||||
// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
|
||||
// www.source-code.biz, www.inventec.ch/chdh
|
||||
|
||||
/* Copyright (c) 2008 Google Inc.
|
||||
|
@ -422,6 +422,8 @@ interface SuggestionEntryScope {
|
||||
|
||||
// A type of suggestion entries.
|
||||
type SuggestionEntry =
|
||||
// A module
|
||||
| SuggestionEntryModule
|
||||
// A value constructor
|
||||
| SuggestionEntryAtom
|
||||
// A method defined on a type
|
||||
@ -431,41 +433,104 @@ type SuggestionEntry =
|
||||
// A local value
|
||||
| SuggestionEntryLocal;
|
||||
|
||||
interface SuggestionEntryAtom {
|
||||
externalId?: UUID;
|
||||
name: string;
|
||||
interface SuggestionEntryModule {
|
||||
/** The fully qualified module name. */
|
||||
module: string;
|
||||
arguments: SuggestionEntryArgument[];
|
||||
returnType: string;
|
||||
|
||||
/** The documentation string. */
|
||||
documentation?: string;
|
||||
|
||||
/** The fully qualified module name re-exporting this module. */
|
||||
reexport?: string;
|
||||
}
|
||||
|
||||
interface SuggestionEntryAtom {
|
||||
/** The external id. */
|
||||
externalId?: UUID;
|
||||
|
||||
/** The atom name. */
|
||||
name: string;
|
||||
|
||||
/** The module name where the atom is defined. */
|
||||
module: string;
|
||||
|
||||
/** The list of arguments. */
|
||||
arguments: SuggestionEntryArgument[];
|
||||
|
||||
/** The type of an atom. */
|
||||
returnType: string;
|
||||
|
||||
/** The documentation string. */
|
||||
documentation?: string;
|
||||
|
||||
/** The fully qualified module name re-exporting this module. */
|
||||
reexport?: string;
|
||||
|
||||
documentationHtml?: string;
|
||||
}
|
||||
|
||||
interface SuggestionEntryMethod {
|
||||
/** The external id. */
|
||||
externalId?: UUID;
|
||||
|
||||
/** The method name. */
|
||||
name: string;
|
||||
|
||||
/** The module name where this method is defined. */
|
||||
module: string;
|
||||
|
||||
/** The list of arguments. */
|
||||
arguments: SuggestionEntryArgument[];
|
||||
|
||||
/** The method self type. */
|
||||
selfType: string;
|
||||
|
||||
/** The return type of this method. */
|
||||
returnType: string;
|
||||
|
||||
/** The documentation string. */
|
||||
documentation?: string;
|
||||
|
||||
/** The fully qualified module name re-exporting this module. */
|
||||
reexport?: string;
|
||||
|
||||
documentationHtml?: string;
|
||||
}
|
||||
|
||||
interface SuggestionEntryFunction {
|
||||
/** The external id. */
|
||||
externalId?: UUID;
|
||||
|
||||
/** The function name. */
|
||||
name: string;
|
||||
|
||||
/** The module name where this function is defined. */
|
||||
module: string;
|
||||
|
||||
/** The list of arguments. */
|
||||
arguments: SuggestionEntryArgument[];
|
||||
|
||||
/** The function return type. */
|
||||
returnType: string;
|
||||
|
||||
/** The scope where the function is defined. */
|
||||
scope: SuggestionEntryScope;
|
||||
}
|
||||
|
||||
interface SuggestionEntryLocal {
|
||||
/** The external id. */
|
||||
externalId?: UUID;
|
||||
|
||||
/** The name of a value. */
|
||||
name: string;
|
||||
|
||||
/** The module where this value is defined. */
|
||||
module: string;
|
||||
|
||||
/** The type of a value. */
|
||||
returnType: string;
|
||||
|
||||
/** The scope where the value is defined. */
|
||||
scope: SuggestionEntryScope;
|
||||
}
|
||||
```
|
||||
@ -478,7 +543,7 @@ The suggestion entry type that is used as a filter in search requests.
|
||||
|
||||
```typescript
|
||||
// The kind of a suggestion.
|
||||
type SuggestionEntryType = Atom | Method | Function | Local;
|
||||
type SuggestionEntryType = Module | Atom | Method | Function | Local;
|
||||
```
|
||||
|
||||
### `SuggestionId`
|
||||
@ -699,6 +764,11 @@ interface Modify {
|
||||
* The scope to update.
|
||||
*/
|
||||
scope?: FieldUpdate<SuggestionEntryScope>;
|
||||
|
||||
/**
|
||||
* The reexport field to update.
|
||||
*/
|
||||
reexport?: FieldUpdate<String>;
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -29,6 +29,8 @@ object SearchProtocol {
|
||||
|
||||
private object SuggestionType {
|
||||
|
||||
val Module = "module"
|
||||
|
||||
val Atom = "atom"
|
||||
|
||||
val Method = "method"
|
||||
@ -40,6 +42,10 @@ object SearchProtocol {
|
||||
|
||||
implicit val suggestionEncoder: Encoder[Suggestion] =
|
||||
Encoder.instance[Suggestion] {
|
||||
case module: Suggestion.Module =>
|
||||
Encoder[Suggestion.Module]
|
||||
.apply(module)
|
||||
.deepMerge(Json.obj(CodecField.Type -> SuggestionType.Module.asJson))
|
||||
case atom: Suggestion.Atom =>
|
||||
Encoder[Suggestion.Atom]
|
||||
.apply(atom)
|
||||
@ -72,6 +78,9 @@ object SearchProtocol {
|
||||
implicit val suggestionDecoder: Decoder[Suggestion] =
|
||||
Decoder.instance { cursor =>
|
||||
cursor.downField(CodecField.Type).as[String].flatMap {
|
||||
case SuggestionType.Module =>
|
||||
Decoder[Suggestion.Module].tryDecode(cursor)
|
||||
|
||||
case SuggestionType.Atom =>
|
||||
Decoder[Suggestion.Atom].tryDecode(cursor)
|
||||
|
||||
@ -206,6 +215,7 @@ object SearchProtocol {
|
||||
* @param documentation the documentation string to update
|
||||
* @param documentationHtml the HTML documentation to update
|
||||
* @param scope the scope to update
|
||||
* @param reexport the module reexporting the suggestion
|
||||
*/
|
||||
case class Modify(
|
||||
id: SuggestionId,
|
||||
@ -216,7 +226,8 @@ object SearchProtocol {
|
||||
returnType: Option[FieldUpdate[String]] = None,
|
||||
documentation: Option[FieldUpdate[String]] = None,
|
||||
documentationHtml: Option[FieldUpdate[String]] = None,
|
||||
scope: Option[FieldUpdate[Suggestion.Scope]] = None
|
||||
scope: Option[FieldUpdate[Suggestion.Scope]] = None,
|
||||
reexport: Option[FieldUpdate[String]] = None
|
||||
) extends SuggestionsDatabaseUpdate
|
||||
|
||||
implicit val decoder: Decoder[SuggestionsDatabaseUpdate] =
|
||||
@ -260,6 +271,9 @@ object SearchProtocol {
|
||||
extends Enum[SuggestionKind]
|
||||
with CirceEnum[SuggestionKind] {
|
||||
|
||||
/** A module suggestion. */
|
||||
case object Module extends SuggestionKind
|
||||
|
||||
/** An atom suggestion. */
|
||||
case object Atom extends SuggestionKind
|
||||
|
||||
@ -281,6 +295,7 @@ 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
|
||||
@ -294,6 +309,7 @@ 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
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.enso.languageserver.search
|
||||
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props, Stash, Status}
|
||||
import akka.pattern.{ask, pipe}
|
||||
@ -42,7 +43,8 @@ import org.enso.searcher.{SuggestionsRepo, VersionsRepo}
|
||||
import org.enso.text.ContentVersion
|
||||
import org.enso.text.editing.model.Position
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
/** The handler of search requests.
|
||||
@ -96,8 +98,7 @@ final class SuggestionsHandler(
|
||||
with LazyLogging
|
||||
with UnhandledLogging {
|
||||
|
||||
import SuggestionsHandler.ProjectNameUpdated
|
||||
import context.dispatcher
|
||||
import SuggestionsHandler._
|
||||
|
||||
private val timeout = config.executionContext.requestTimeout
|
||||
|
||||
@ -195,7 +196,7 @@ final class SuggestionsHandler(
|
||||
|
||||
case SuggestionsHandler.Verified =>
|
||||
logger.info("Verified.")
|
||||
context.become(initialized(projectName, graph, Set()))
|
||||
context.become(initialized(projectName, graph, Set(), State()))
|
||||
unstashAll()
|
||||
|
||||
case Status.Failure(ex) =>
|
||||
@ -212,23 +213,33 @@ final class SuggestionsHandler(
|
||||
def initialized(
|
||||
projectName: String,
|
||||
graph: TypeGraph,
|
||||
clients: Set[ClientId]
|
||||
clients: Set[ClientId],
|
||||
state: State
|
||||
): Receive = {
|
||||
case AcquireCapability(
|
||||
client,
|
||||
CapabilityRegistration(ReceivesSuggestionsDatabaseUpdates())
|
||||
) =>
|
||||
sender() ! CapabilityAcquired
|
||||
context.become(initialized(projectName, graph, clients + client.clientId))
|
||||
context.become(
|
||||
initialized(projectName, graph, clients + client.clientId, state)
|
||||
)
|
||||
|
||||
case ReleaseCapability(
|
||||
client,
|
||||
CapabilityRegistration(ReceivesSuggestionsDatabaseUpdates())
|
||||
) =>
|
||||
sender() ! CapabilityReleased
|
||||
context.become(initialized(projectName, graph, clients - client.clientId))
|
||||
context.become(
|
||||
initialized(projectName, graph, clients - client.clientId, state)
|
||||
)
|
||||
|
||||
case msg: Api.SuggestionsDatabaseModuleUpdateNotification
|
||||
if state.isSuggestionUpdatesRunning =>
|
||||
state.suggestionUpdatesQueue.enqueue(msg)
|
||||
|
||||
case msg: Api.SuggestionsDatabaseModuleUpdateNotification =>
|
||||
logger.debug("Got module update [{}].", msg.module)
|
||||
val isVersionChanged =
|
||||
versionsRepo.getVersion(msg.module).map { digestOpt =>
|
||||
!digestOpt.map(ContentVersion(_)).contains(msg.version)
|
||||
@ -241,12 +252,19 @@ final class SuggestionsHandler(
|
||||
applyUpdatesIfVersionChanged
|
||||
.onComplete {
|
||||
case Success(Some(notification)) =>
|
||||
logger.debug("Complete module update [{}].", msg.module)
|
||||
if (notification.updates.nonEmpty) {
|
||||
clients.foreach { clientId =>
|
||||
sessionRouter ! DeliverToJsonController(clientId, notification)
|
||||
}
|
||||
}
|
||||
self ! SuggestionsHandler.SuggestionUpdatesCompleted
|
||||
case Success(None) =>
|
||||
logger.debug(
|
||||
"Skip module update, version not changed [{}].",
|
||||
msg.module
|
||||
)
|
||||
self ! SuggestionsHandler.SuggestionUpdatesCompleted
|
||||
case Failure(ex) =>
|
||||
logger.error(
|
||||
"Error applying suggestion database updates [{}, {}]. {}",
|
||||
@ -254,7 +272,16 @@ final class SuggestionsHandler(
|
||||
msg.version,
|
||||
ex.getMessage
|
||||
)
|
||||
self ! SuggestionsHandler.SuggestionUpdatesCompleted
|
||||
}
|
||||
context.become(
|
||||
initialized(
|
||||
projectName,
|
||||
graph,
|
||||
clients,
|
||||
state.copy(isSuggestionUpdatesRunning = true)
|
||||
)
|
||||
)
|
||||
|
||||
case Api.ExpressionUpdates(_, updates) =>
|
||||
logger.debug(
|
||||
@ -446,7 +473,21 @@ final class SuggestionsHandler(
|
||||
|
||||
case ProjectNameUpdated(name, updates) =>
|
||||
updates.foreach(sessionRouter ! _)
|
||||
context.become(initialized(name, graph, clients))
|
||||
context.become(initialized(name, graph, clients, state))
|
||||
|
||||
case SuggestionUpdatesCompleted =>
|
||||
if (state.suggestionUpdatesQueue.nonEmpty) {
|
||||
self ! state.suggestionUpdatesQueue.dequeue()
|
||||
}
|
||||
context.become(
|
||||
initialized(
|
||||
projectName,
|
||||
graph,
|
||||
clients,
|
||||
state.copy(isSuggestionUpdatesRunning = false)
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/** Transition the initialization process.
|
||||
@ -482,15 +523,17 @@ final class SuggestionsHandler(
|
||||
msg: Api.SuggestionsDatabaseModuleUpdateNotification
|
||||
): Future[SuggestionsDatabaseUpdateNotification] =
|
||||
for {
|
||||
actionResults <- suggestionsRepo.applyActions(msg.actions)
|
||||
(version, results) <- suggestionsRepo.applyTree(msg.updates)
|
||||
_ <- versionsRepo.setVersion(msg.module, msg.version.toDigest)
|
||||
actionResults <- suggestionsRepo.applyActions(msg.actions)
|
||||
treeResults <- suggestionsRepo.applyTree(msg.updates)
|
||||
exportResults <- suggestionsRepo.applyExports(msg.exports)
|
||||
version <- suggestionsRepo.currentVersion
|
||||
_ <- versionsRepo.setVersion(msg.module, msg.version.toDigest)
|
||||
} yield {
|
||||
val actionUpdates = actionResults.flatMap {
|
||||
case QueryResult(ids, Api.SuggestionsDatabaseAction.Clean(_)) =>
|
||||
ids.map(SuggestionsDatabaseUpdate.Remove)
|
||||
}
|
||||
val treeUpdates = results.flatMap {
|
||||
val treeUpdates = treeResults.flatMap {
|
||||
case QueryResult(ids, Api.SuggestionUpdate(suggestion, action)) =>
|
||||
val verb = action.getClass.getSimpleName
|
||||
action match {
|
||||
@ -517,9 +560,28 @@ final class SuggestionsHandler(
|
||||
}
|
||||
}
|
||||
}
|
||||
val exportUpdates = exportResults.flatMap { queryResult =>
|
||||
val update = queryResult.value
|
||||
update.action match {
|
||||
case Api.ExportsAction.Add() =>
|
||||
queryResult.ids.map { id =>
|
||||
SuggestionsDatabaseUpdate.Modify(
|
||||
id = id,
|
||||
reexport = Some(fieldUpdate(update.exports.module))
|
||||
)
|
||||
}
|
||||
case Api.ExportsAction.Remove() =>
|
||||
queryResult.ids.map { id =>
|
||||
SuggestionsDatabaseUpdate.Modify(
|
||||
id = id,
|
||||
reexport = Some(fieldRemove)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
SuggestionsDatabaseUpdateNotification(
|
||||
version,
|
||||
actionUpdates ++ treeUpdates
|
||||
actionUpdates ++ treeUpdates ++ exportUpdates
|
||||
)
|
||||
}
|
||||
|
||||
@ -542,6 +604,9 @@ final class SuggestionsHandler(
|
||||
private def fieldUpdate[A](value: A): FieldUpdate[A] =
|
||||
FieldUpdate(FieldAction.Set, Some(value))
|
||||
|
||||
private def fieldRemove[A]: FieldUpdate[A] =
|
||||
FieldUpdate[A](FieldAction.Remove, None)
|
||||
|
||||
/** Construct [[SuggestionArgumentUpdate]] from the runtime message.
|
||||
*
|
||||
* @param action the runtime message
|
||||
@ -599,6 +664,9 @@ final class SuggestionsHandler(
|
||||
|
||||
object SuggestionsHandler {
|
||||
|
||||
implicit private val dispatcher: ExecutionContext =
|
||||
ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor())
|
||||
|
||||
/** The notification about the project name update.
|
||||
*
|
||||
* @param projectName the new project name
|
||||
@ -622,6 +690,9 @@ object SuggestionsHandler {
|
||||
/** The notification that the suggestions database has been verified. */
|
||||
case object Verified
|
||||
|
||||
/** The notification that the suggestion updates are processed. */
|
||||
case object SuggestionUpdatesCompleted
|
||||
|
||||
/** The initialization state of the handler.
|
||||
*
|
||||
* @param project the project name
|
||||
@ -647,6 +718,18 @@ object SuggestionsHandler {
|
||||
} yield (name, graph)
|
||||
}
|
||||
|
||||
/** The suggestion updates state.
|
||||
*
|
||||
* @param suggestionUpdatesQueue the queue containing update messages
|
||||
* @param isSuggestionUpdatesRunning a flag for a running update action
|
||||
*/
|
||||
case class State(
|
||||
suggestionUpdatesQueue: mutable.Queue[
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification
|
||||
] = mutable.Queue.empty,
|
||||
isSuggestionUpdatesRunning: Boolean = false
|
||||
)
|
||||
|
||||
/** Creates a configuration object used to create a [[SuggestionsHandler]].
|
||||
*
|
||||
* @param config the server configuration
|
||||
|
@ -7,6 +7,12 @@ import org.enso.polyglot.Suggestion
|
||||
/** Suggestion instances used in tests. */
|
||||
object Suggestions {
|
||||
|
||||
val module: Suggestion.Module = Suggestion.Module(
|
||||
module = "Test.Main",
|
||||
documentation = None,
|
||||
documentationHtml = None
|
||||
)
|
||||
|
||||
val atom: Suggestion.Atom = Suggestion.Atom(
|
||||
externalId = None,
|
||||
module = "Test.Main",
|
||||
@ -95,6 +101,7 @@ object Suggestions {
|
||||
)
|
||||
|
||||
val all = Seq(
|
||||
module,
|
||||
atom,
|
||||
method,
|
||||
function,
|
||||
|
@ -1,5 +1,8 @@
|
||||
package org.enso.languageserver.search
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.{ActorRef, ActorSystem}
|
||||
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
|
||||
import org.apache.commons.io.FileUtils
|
||||
@ -14,7 +17,7 @@ import org.enso.languageserver.refactoring.ProjectNameChangedEvent
|
||||
import org.enso.languageserver.search.SearchProtocol.SuggestionDatabaseEntry
|
||||
import org.enso.languageserver.session.JsonSession
|
||||
import org.enso.languageserver.session.SessionRouter.DeliverToJsonController
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.polyglot.{ExportedSymbol, ModuleExports, Suggestion}
|
||||
import org.enso.polyglot.data.{Tree, TypeGraph}
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo, SqlVersionsRepo}
|
||||
@ -26,8 +29,7 @@ import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpecLike
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.util.UUID
|
||||
import scala.collection.immutable.ListSet
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{Await, Future}
|
||||
import scala.util.{Failure, Success}
|
||||
@ -134,6 +136,7 @@ class SuggestionsHandlerSpec
|
||||
"Foo.Main",
|
||||
contentsVersion(""),
|
||||
Vector(),
|
||||
Vector(),
|
||||
Tree.Root(Suggestions.all.toVector.map { suggestion =>
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(suggestion, Api.SuggestionAction.Add()),
|
||||
@ -179,6 +182,7 @@ class SuggestionsHandlerSpec
|
||||
"Foo.Main",
|
||||
contentsVersion(""),
|
||||
Vector(),
|
||||
Vector(),
|
||||
Tree.Root(
|
||||
Suggestions.all.toVector
|
||||
.map { suggestion =>
|
||||
@ -235,6 +239,13 @@ class SuggestionsHandlerSpec
|
||||
|
||||
val tree1 = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestions.module,
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestions.atom,
|
||||
@ -273,6 +284,7 @@ class SuggestionsHandlerSpec
|
||||
"Foo.Main",
|
||||
contentsVersion(""),
|
||||
Vector(),
|
||||
Vector(),
|
||||
tree1
|
||||
)
|
||||
|
||||
@ -347,12 +359,13 @@ class SuggestionsHandlerSpec
|
||||
"Foo.Main",
|
||||
contentsVersion("1"),
|
||||
Vector(),
|
||||
Vector(),
|
||||
tree2
|
||||
)
|
||||
|
||||
val updates2 = Seq(
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Modify(
|
||||
1L,
|
||||
2L,
|
||||
arguments = Some(
|
||||
Seq(
|
||||
SearchProtocol.SuggestionArgumentUpdate
|
||||
@ -367,7 +380,7 @@ class SuggestionsHandlerSpec
|
||||
)
|
||||
),
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Modify(
|
||||
3L,
|
||||
4L,
|
||||
scope = Some(
|
||||
SearchProtocol.FieldUpdate(
|
||||
SearchProtocol.FieldAction.Set,
|
||||
@ -375,8 +388,8 @@ class SuggestionsHandlerSpec
|
||||
)
|
||||
)
|
||||
),
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Remove(4L),
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Add(5L, Suggestions.local)
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Remove(5L),
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Add(6L, Suggestions.local)
|
||||
)
|
||||
router.expectMsg(
|
||||
DeliverToJsonController(
|
||||
@ -401,7 +414,7 @@ class SuggestionsHandlerSpec
|
||||
expectMsg(CapabilityAcquired)
|
||||
|
||||
val moduleName = "Test.Foo"
|
||||
val moduleAtom = Suggestion.Atom(
|
||||
val fooAtom = Suggestion.Atom(
|
||||
externalId = None,
|
||||
module = moduleName,
|
||||
name = "Foo",
|
||||
@ -410,13 +423,17 @@ class SuggestionsHandlerSpec
|
||||
documentation = None,
|
||||
documentationHtml = None
|
||||
)
|
||||
val fooAtom = moduleAtom.copy(returnType = "Test.Foo.Foo")
|
||||
val module = Suggestion.Module(
|
||||
module = moduleName,
|
||||
documentation = None,
|
||||
documentationHtml = None
|
||||
)
|
||||
|
||||
val tree = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
moduleAtom,
|
||||
module,
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
@ -436,6 +453,7 @@ class SuggestionsHandlerSpec
|
||||
"Foo.Main",
|
||||
contentsVersion(""),
|
||||
Vector(),
|
||||
Vector(),
|
||||
tree
|
||||
)
|
||||
|
||||
@ -514,6 +532,7 @@ class SuggestionsHandlerSpec
|
||||
"Foo.Main",
|
||||
contentsVersion(""),
|
||||
Vector(),
|
||||
Vector(),
|
||||
tree1
|
||||
)
|
||||
|
||||
@ -538,6 +557,7 @@ class SuggestionsHandlerSpec
|
||||
"Foo.Main",
|
||||
contentsVersion("1"),
|
||||
Vector(Api.SuggestionsDatabaseAction.Clean(Suggestions.atom.module)),
|
||||
Vector(),
|
||||
Tree.Root(Vector())
|
||||
)
|
||||
|
||||
@ -548,7 +568,7 @@ class SuggestionsHandlerSpec
|
||||
DeliverToJsonController(
|
||||
clientId,
|
||||
SearchProtocol.SuggestionsDatabaseUpdateNotification(
|
||||
updates1.size + 1L,
|
||||
updates1.size.toLong,
|
||||
updates2
|
||||
)
|
||||
)
|
||||
@ -559,6 +579,175 @@ class SuggestionsHandlerSpec
|
||||
all shouldEqual Seq()
|
||||
}
|
||||
|
||||
"apply export updates" taggedAs Retry in withDb {
|
||||
(_, _, router, _, handler) =>
|
||||
val clientId = UUID.randomUUID()
|
||||
|
||||
// acquire capability
|
||||
handler ! AcquireCapability(
|
||||
newJsonSession(clientId),
|
||||
CapabilityRegistration(ReceivesSuggestionsDatabaseUpdates())
|
||||
)
|
||||
expectMsg(CapabilityAcquired)
|
||||
|
||||
val tree1 = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestions.module,
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestions.atom,
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestions.method,
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector(
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestions.function,
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector(
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestions.local,
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// add tree
|
||||
handler ! Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
"Foo.Main",
|
||||
contentsVersion(""),
|
||||
Vector(),
|
||||
Vector(),
|
||||
tree1
|
||||
)
|
||||
|
||||
val updates1 = tree1.toVector.zipWithIndex.map { case (update, ix) =>
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Add(
|
||||
ix + 1L,
|
||||
update.suggestion
|
||||
)
|
||||
}
|
||||
router.expectMsg(
|
||||
DeliverToJsonController(
|
||||
clientId,
|
||||
SearchProtocol.SuggestionsDatabaseUpdateNotification(
|
||||
updates1.size.toLong,
|
||||
updates1
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val exportUpdateAdd =
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
"Foo.Bar",
|
||||
ListSet(
|
||||
ExportedSymbol.Module(
|
||||
Suggestions.module.module
|
||||
),
|
||||
ExportedSymbol.Atom(
|
||||
Suggestions.atom.module,
|
||||
Suggestions.atom.name
|
||||
),
|
||||
ExportedSymbol.Method(
|
||||
Suggestions.method.module,
|
||||
Suggestions.method.name
|
||||
)
|
||||
)
|
||||
),
|
||||
Api.ExportsAction.Add()
|
||||
)
|
||||
// apply updates1
|
||||
handler ! Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
"Foo.Main",
|
||||
contentsVersion("1"),
|
||||
Vector(),
|
||||
Vector(exportUpdateAdd),
|
||||
Tree.Root(Vector())
|
||||
)
|
||||
|
||||
val updates2 = Seq(1L, 2L, 3L).map { id =>
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Modify(
|
||||
id,
|
||||
reexport = Some(fieldUpdate(exportUpdateAdd.exports.module))
|
||||
)
|
||||
}
|
||||
router.expectMsg(
|
||||
DeliverToJsonController(
|
||||
clientId,
|
||||
SearchProtocol.SuggestionsDatabaseUpdateNotification(
|
||||
updates1.size.toLong + 1,
|
||||
updates2
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val exportUpdateRemove =
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
"Foo.Bar",
|
||||
ListSet(
|
||||
ExportedSymbol.Module(
|
||||
Suggestions.module.module
|
||||
),
|
||||
ExportedSymbol.Atom(
|
||||
Suggestions.atom.module,
|
||||
Suggestions.atom.name
|
||||
),
|
||||
ExportedSymbol.Method(
|
||||
Suggestions.method.module,
|
||||
Suggestions.method.name
|
||||
)
|
||||
)
|
||||
),
|
||||
Api.ExportsAction.Remove()
|
||||
)
|
||||
// apply updates2
|
||||
handler ! Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
"Foo.Main",
|
||||
contentsVersion("2"),
|
||||
Vector(),
|
||||
Vector(exportUpdateRemove),
|
||||
Tree.Root(Vector())
|
||||
)
|
||||
|
||||
val updates3 = Seq(1L, 2L, 3L).map { id =>
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Modify(
|
||||
id,
|
||||
reexport = Some(fieldRemove)
|
||||
)
|
||||
}
|
||||
router.expectMsg(
|
||||
DeliverToJsonController(
|
||||
clientId,
|
||||
SearchProtocol.SuggestionsDatabaseUpdateNotification(
|
||||
updates1.size.toLong + 2,
|
||||
updates3
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"get initial suggestions database version" taggedAs Retry in withDb {
|
||||
(_, _, _, _, handler) =>
|
||||
handler ! SearchProtocol.GetSuggestionsDatabaseVersion
|
||||
@ -706,13 +895,14 @@ class SuggestionsHandlerSpec
|
||||
|
||||
expectMsg(
|
||||
SearchProtocol.CompletionResult(
|
||||
7L,
|
||||
Suggestions.all.length.toLong,
|
||||
Seq(
|
||||
inserted(0).get,
|
||||
inserted(6).get,
|
||||
inserted(4).get,
|
||||
inserted(1).get,
|
||||
inserted(7).get,
|
||||
inserted(5).get,
|
||||
inserted(1).get
|
||||
inserted(6).get,
|
||||
inserted(2).get
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -720,7 +910,7 @@ class SuggestionsHandlerSpec
|
||||
|
||||
"search entries by self type" taggedAs Retry in withDb {
|
||||
(config, repo, _, _, handler) =>
|
||||
val (_, Seq(_, methodId, _, _, methodOnAnyId, _, _)) =
|
||||
val (_, Seq(_, _, methodId, _, _, methodOnAnyId, _, _)) =
|
||||
Await.result(repo.insertAll(Suggestions.all), Timeout)
|
||||
handler ! SearchProtocol.Completion(
|
||||
file = mkModulePath(config, "Main.enso"),
|
||||
@ -732,7 +922,7 @@ class SuggestionsHandlerSpec
|
||||
|
||||
expectMsg(
|
||||
SearchProtocol.CompletionResult(
|
||||
7L,
|
||||
Suggestions.all.length.toLong,
|
||||
Seq(methodId, methodOnAnyId).flatten
|
||||
)
|
||||
)
|
||||
@ -740,7 +930,10 @@ class SuggestionsHandlerSpec
|
||||
|
||||
"search entries based on supertypes of self" taggedAs Retry in withDb {
|
||||
(config, repo, _, _, handler) =>
|
||||
val (_, Seq(_, _, _, _, anyMethodId, numberMethodId, integerMethodId)) =
|
||||
val (
|
||||
_,
|
||||
Seq(_, _, _, _, _, anyMethodId, numberMethodId, integerMethodId)
|
||||
) =
|
||||
Await.result(repo.insertAll(Suggestions.all), Timeout)
|
||||
|
||||
handler ! SearchProtocol.Completion(
|
||||
@ -753,7 +946,7 @@ class SuggestionsHandlerSpec
|
||||
|
||||
expectMsg(
|
||||
SearchProtocol.CompletionResult(
|
||||
7L,
|
||||
Suggestions.all.length.toLong,
|
||||
Seq(integerMethodId, numberMethodId, anyMethodId).flatten
|
||||
)
|
||||
)
|
||||
@ -761,7 +954,7 @@ class SuggestionsHandlerSpec
|
||||
|
||||
"search entries for any" taggedAs Retry in withDb {
|
||||
(config, repo, _, _, handler) =>
|
||||
val (_, Seq(_, _, _, _, anyMethodId, _, _)) =
|
||||
val (_, Seq(_, _, _, _, _, anyMethodId, _, _)) =
|
||||
Await.result(repo.insertAll(Suggestions.all), Timeout)
|
||||
|
||||
handler ! SearchProtocol.Completion(
|
||||
@ -772,12 +965,17 @@ class SuggestionsHandlerSpec
|
||||
tags = None
|
||||
)
|
||||
|
||||
expectMsg(SearchProtocol.CompletionResult(7L, Seq(anyMethodId).flatten))
|
||||
expectMsg(
|
||||
SearchProtocol.CompletionResult(
|
||||
Suggestions.all.length.toLong,
|
||||
Seq(anyMethodId).flatten
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"search entries by return type" taggedAs Retry in withDb {
|
||||
(config, repo, _, _, handler) =>
|
||||
val (_, Seq(_, _, functionId, _, _, _, _)) =
|
||||
val (_, Seq(_, _, _, functionId, _, _, _, _)) =
|
||||
Await.result(repo.insertAll(Suggestions.all), Timeout)
|
||||
handler ! SearchProtocol.Completion(
|
||||
file = mkModulePath(config, "Main.enso"),
|
||||
@ -787,12 +985,17 @@ class SuggestionsHandlerSpec
|
||||
tags = None
|
||||
)
|
||||
|
||||
expectMsg(SearchProtocol.CompletionResult(7L, Seq(functionId).flatten))
|
||||
expectMsg(
|
||||
SearchProtocol.CompletionResult(
|
||||
Suggestions.all.length.toLong,
|
||||
Seq(functionId).flatten
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"search entries by tags" taggedAs Retry in withDb {
|
||||
(config, repo, _, _, handler) =>
|
||||
val (_, Seq(_, _, _, localId, _, _, _)) =
|
||||
val (_, Seq(_, _, _, _, localId, _, _, _)) =
|
||||
Await.result(repo.insertAll(Suggestions.all), Timeout)
|
||||
handler ! SearchProtocol.Completion(
|
||||
file = mkModulePath(config, "Main.enso"),
|
||||
@ -802,13 +1005,21 @@ class SuggestionsHandlerSpec
|
||||
tags = Some(Seq(SearchProtocol.SuggestionKind.Local))
|
||||
)
|
||||
|
||||
expectMsg(SearchProtocol.CompletionResult(7L, Seq(localId).flatten))
|
||||
expectMsg(
|
||||
SearchProtocol.CompletionResult(
|
||||
Suggestions.all.length.toLong,
|
||||
Seq(localId).flatten
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def fieldUpdate(value: String): SearchProtocol.FieldUpdate[String] =
|
||||
SearchProtocol.FieldUpdate(SearchProtocol.FieldAction.Set, Some(value))
|
||||
|
||||
private def fieldRemove[A]: SearchProtocol.FieldUpdate[A] =
|
||||
SearchProtocol.FieldUpdate(SearchProtocol.FieldAction.Remove, None)
|
||||
|
||||
def newSuggestionsHandler(
|
||||
config: Config,
|
||||
sessionRouter: TestProbe,
|
||||
|
@ -3,10 +3,13 @@ package org.enso.languageserver.websocket.json
|
||||
import io.circe.literal._
|
||||
import org.enso.languageserver.search.Suggestions
|
||||
import org.enso.languageserver.websocket.json.{SearchJsonMessages => json}
|
||||
import org.enso.polyglot.{ExportedSymbol, ModuleExports}
|
||||
import org.enso.polyglot.data.Tree
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.testkit.FlakySpec
|
||||
|
||||
import scala.collection.immutable.ListSet
|
||||
|
||||
class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
|
||||
"SuggestionsHandlerEvents" must {
|
||||
@ -23,6 +26,7 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
"Foo.Main",
|
||||
versionCalculator.evalVersion("1"),
|
||||
Vector(),
|
||||
Vector(),
|
||||
Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
@ -72,6 +76,7 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
"Foo.Main",
|
||||
versionCalculator.evalVersion("2"),
|
||||
Vector(),
|
||||
Vector(),
|
||||
Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
@ -140,6 +145,7 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
"Foo.Main",
|
||||
versionCalculator.evalVersion("3"),
|
||||
Vector(),
|
||||
Vector(),
|
||||
Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
@ -230,6 +236,7 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
"Foo.Main",
|
||||
versionCalculator.evalVersion("4"),
|
||||
Vector(),
|
||||
Vector(),
|
||||
Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
@ -300,30 +307,28 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
""")
|
||||
|
||||
// get suggestions database
|
||||
client.send(json.getSuggestionsDatabase(0))
|
||||
client.send(json.getSuggestionsDatabase(3))
|
||||
client.expectJson(json"""
|
||||
{ "jsonrpc" : "2.0",
|
||||
"id" : 0,
|
||||
"id" : 3,
|
||||
"result" : {
|
||||
"entries" : [
|
||||
{
|
||||
"id" : 4,
|
||||
{
|
||||
"id" : 1,
|
||||
"suggestion" : {
|
||||
"type" : "local",
|
||||
"externalId" : "dc077227-d9b6-4620-9b51-792c2a69419d",
|
||||
"type" : "atom",
|
||||
"module" : "Test.Main",
|
||||
"name" : "x",
|
||||
"returnType" : "Number",
|
||||
"scope" : {
|
||||
"start" : {
|
||||
"line" : 21,
|
||||
"character" : 0
|
||||
},
|
||||
"end" : {
|
||||
"line" : 89,
|
||||
"character" : 0
|
||||
"name" : "MyType",
|
||||
"arguments" : [
|
||||
{
|
||||
"name" : "a",
|
||||
"reprType" : "Any",
|
||||
"isSuspended" : false,
|
||||
"hasDefault" : false,
|
||||
"defaultValue" : null
|
||||
}
|
||||
}
|
||||
],
|
||||
"returnType" : "MyAtom"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -399,21 +404,23 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
}
|
||||
},
|
||||
{
|
||||
"id" : 1,
|
||||
"id" : 4,
|
||||
"suggestion" : {
|
||||
"type" : "atom",
|
||||
"type" : "local",
|
||||
"externalId" : ${Suggestions.local.externalId.get},
|
||||
"module" : "Test.Main",
|
||||
"name" : "MyType",
|
||||
"arguments" : [
|
||||
{
|
||||
"name" : "a",
|
||||
"reprType" : "Any",
|
||||
"isSuspended" : false,
|
||||
"hasDefault" : false,
|
||||
"defaultValue" : null
|
||||
"name" : "x",
|
||||
"returnType" : "Number",
|
||||
"scope" : {
|
||||
"start" : {
|
||||
"line" : 21,
|
||||
"character" : 0
|
||||
},
|
||||
"end" : {
|
||||
"line" : 89,
|
||||
"character" : 0
|
||||
}
|
||||
],
|
||||
"returnType" : "MyAtom"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -428,6 +435,7 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
"Foo.Main",
|
||||
versionCalculator.evalVersion("5"),
|
||||
Vector(),
|
||||
Vector(),
|
||||
Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
@ -540,12 +548,54 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
}
|
||||
""")
|
||||
|
||||
// remove items
|
||||
// update exports
|
||||
system.eventStream.publish(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
"Foo.Main",
|
||||
versionCalculator.evalVersion("6"),
|
||||
Vector(),
|
||||
Vector(
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
"Foo.Bar",
|
||||
ListSet(
|
||||
ExportedSymbol
|
||||
.Atom(Suggestions.atom.module, Suggestions.atom.name)
|
||||
)
|
||||
),
|
||||
Api.ExportsAction.Add()
|
||||
)
|
||||
),
|
||||
Tree.Root(Vector())
|
||||
)
|
||||
)
|
||||
client.expectJson(json"""
|
||||
{
|
||||
"jsonrpc" : "2.0",
|
||||
"method" : "search/suggestionsDatabaseUpdates",
|
||||
"params" : {
|
||||
"updates" : [
|
||||
{
|
||||
"type" : "Modify",
|
||||
"id" : 1,
|
||||
"reexport" : {
|
||||
"tag" : "Set",
|
||||
"value" : "Foo.Bar"
|
||||
}
|
||||
}
|
||||
],
|
||||
"currentVersion" : 8
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
// remove items
|
||||
system.eventStream.publish(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
"Foo.Main",
|
||||
versionCalculator.evalVersion("7"),
|
||||
Vector(Api.SuggestionsDatabaseAction.Clean(Suggestions.atom.module)),
|
||||
Vector(),
|
||||
Tree.Root(Vector())
|
||||
)
|
||||
)
|
||||
|
@ -0,0 +1,65 @@
|
||||
package org.enso.polyglot
|
||||
|
||||
import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo}
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
@JsonSubTypes(
|
||||
Array(
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExportedSymbol.Module],
|
||||
name = "exportedModule"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExportedSymbol.Atom],
|
||||
name = "exportedAtom"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExportedSymbol.Method],
|
||||
name = "exportedMethod"
|
||||
)
|
||||
)
|
||||
)
|
||||
sealed trait ExportedSymbol {
|
||||
def module: String
|
||||
|
||||
def name: String
|
||||
|
||||
def kind: Suggestion.Kind
|
||||
}
|
||||
object ExportedSymbol {
|
||||
|
||||
/** The module symbol.
|
||||
*
|
||||
* @param module the module name
|
||||
*/
|
||||
case class Module(module: String) extends ExportedSymbol {
|
||||
|
||||
override def name: String =
|
||||
module
|
||||
|
||||
override def kind: Suggestion.Kind =
|
||||
Suggestion.Kind.Module
|
||||
}
|
||||
|
||||
/** The atom symbol.
|
||||
*
|
||||
* @param module the module defining this atom
|
||||
* @param name the atom name
|
||||
*/
|
||||
case class Atom(module: String, name: String) extends ExportedSymbol {
|
||||
|
||||
override def kind: Suggestion.Kind =
|
||||
Suggestion.Kind.Atom
|
||||
}
|
||||
|
||||
/** The method symbol.
|
||||
*
|
||||
* @param module the module defining this method
|
||||
* @param name the method name
|
||||
*/
|
||||
case class Method(module: String, name: String) extends ExportedSymbol {
|
||||
|
||||
override def kind: Suggestion.Kind =
|
||||
Suggestion.Kind.Method
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package org.enso.polyglot
|
||||
|
||||
/** The module exporting a set of symbols.
|
||||
*
|
||||
* @param module the module name
|
||||
* @param symbols the set of exported symbols
|
||||
*/
|
||||
case class ModuleExports(module: String, symbols: Set[ExportedSymbol])
|
@ -9,6 +9,10 @@ import org.enso.logger.masking.ToLogString
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
@JsonSubTypes(
|
||||
Array(
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Suggestion.Module],
|
||||
name = "suggestionModule"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Suggestion.Atom],
|
||||
name = "suggestionAtom"
|
||||
@ -45,12 +49,16 @@ object Suggestion {
|
||||
|
||||
def apply(suggestion: Suggestion): Kind =
|
||||
suggestion match {
|
||||
case _: Module => Module
|
||||
case _: Atom => Atom
|
||||
case _: Method => Method
|
||||
case _: Function => Function
|
||||
case _: Local => Local
|
||||
}
|
||||
|
||||
/** The module suggestion. */
|
||||
case object Module extends Kind
|
||||
|
||||
/** The atom suggestion. */
|
||||
case object Atom extends Kind
|
||||
|
||||
@ -69,6 +77,7 @@ object Suggestion {
|
||||
|
||||
def apply(suggestion: Suggestion): Option[String] =
|
||||
suggestion match {
|
||||
case _: Module => None
|
||||
case _: Atom => None
|
||||
case method: Method => Some(method.selfType)
|
||||
case _: Function => None
|
||||
@ -117,6 +126,37 @@ object Suggestion {
|
||||
*/
|
||||
case class Scope(start: Position, end: Position)
|
||||
|
||||
/** A module.
|
||||
*
|
||||
* @param module the fully qualified module name
|
||||
* @param documentation the documentation string
|
||||
* @param documentationHtml the documentation rendered as HTML
|
||||
* @param reexport the module re-exporting this module
|
||||
*/
|
||||
case class Module(
|
||||
module: String,
|
||||
documentation: Option[String],
|
||||
documentationHtml: Option[String],
|
||||
reexport: Option[String] = None
|
||||
) extends Suggestion
|
||||
with ToLogString {
|
||||
|
||||
override def name: String =
|
||||
module
|
||||
|
||||
override def externalId: Option[ExternalId] =
|
||||
None
|
||||
|
||||
override def returnType: String =
|
||||
module
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
s"Module(module=$module,name=$name,documentation=" +
|
||||
(if (shouldMask) documentation.map(_ => STUB) else documentation) +
|
||||
s",reexport=$reexport)"
|
||||
}
|
||||
|
||||
/** A value constructor.
|
||||
*
|
||||
* @param externalId the external id
|
||||
@ -125,6 +165,8 @@ object Suggestion {
|
||||
* @param arguments the list of arguments
|
||||
* @param returnType the type of an atom
|
||||
* @param documentation the documentation string
|
||||
* @param documentationHtml the documentation rendered as HTML
|
||||
* @param reexport the module re-exporting this atom
|
||||
*/
|
||||
case class Atom(
|
||||
externalId: Option[ExternalId],
|
||||
@ -133,7 +175,8 @@ object Suggestion {
|
||||
arguments: Seq[Argument],
|
||||
returnType: String,
|
||||
documentation: Option[String],
|
||||
documentationHtml: Option[String]
|
||||
documentationHtml: Option[String],
|
||||
reexport: Option[String] = None
|
||||
) extends Suggestion
|
||||
with ToLogString {
|
||||
|
||||
@ -148,7 +191,8 @@ object Suggestion {
|
||||
s",documentation=" + (if (shouldMask) documentation.map(_ => STUB)
|
||||
else documentation) +
|
||||
s",documentationHtml=" + (if (shouldMask) documentationHtml.map(_ => STUB)
|
||||
else documentationHtml) + ")"
|
||||
else documentationHtml) +
|
||||
s",reexport=$reexport)"
|
||||
}
|
||||
|
||||
/** A function defined on a type or a module.
|
||||
@ -156,10 +200,12 @@ object Suggestion {
|
||||
* @param externalId the external id
|
||||
* @param module the module name
|
||||
* @param name the method name
|
||||
* @param arguments the function arguments
|
||||
* @param arguments the list of arguments
|
||||
* @param selfType the self type of a method
|
||||
* @param returnType the return type of a method
|
||||
* @param documentation the documentation string
|
||||
* @param documentationHtml the documentation rendered as HTML
|
||||
* @param reexport the module re-exporting this method
|
||||
*/
|
||||
case class Method(
|
||||
externalId: Option[ExternalId],
|
||||
@ -169,7 +215,8 @@ object Suggestion {
|
||||
selfType: String,
|
||||
returnType: String,
|
||||
documentation: Option[String],
|
||||
documentationHtml: Option[String]
|
||||
documentationHtml: Option[String],
|
||||
reexport: Option[String] = None
|
||||
) extends Suggestion
|
||||
with ToLogString {
|
||||
|
||||
@ -185,7 +232,7 @@ object Suggestion {
|
||||
else documentation) +
|
||||
s",documentationHtml=" + (if (shouldMask) documentationHtml.map(_ => STUB)
|
||||
else documentationHtml) +
|
||||
")"
|
||||
s",reexport=$reexport)"
|
||||
}
|
||||
|
||||
/** A local function definition.
|
||||
|
@ -8,7 +8,7 @@ import com.fasterxml.jackson.module.scala.{
|
||||
ScalaObjectMapper
|
||||
}
|
||||
import org.enso.logger.masking.{MaskedPath, MaskedString, ToLogString}
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.polyglot.{ModuleExports, Suggestion}
|
||||
import org.enso.polyglot.data.{Tree, TypeGraph}
|
||||
import org.enso.text.ContentVersion
|
||||
import org.enso.text.editing.model
|
||||
@ -576,6 +576,7 @@ object Runtime {
|
||||
* @param documentation the documentation string to update
|
||||
* @param documentationHtml the HTML documentation to update
|
||||
* @param scope the scope to update
|
||||
* @param reexport the reexport field to update
|
||||
*/
|
||||
case class Modify(
|
||||
externalId: Option[Option[Suggestion.ExternalId]] = None,
|
||||
@ -583,7 +584,8 @@ object Runtime {
|
||||
returnType: Option[String] = None,
|
||||
documentation: Option[Option[String]] = None,
|
||||
documentationHtml: Option[Option[String]] = None,
|
||||
scope: Option[Suggestion.Scope] = None
|
||||
scope: Option[Suggestion.Scope] = None,
|
||||
reexport: Option[Option[String]] = None
|
||||
) extends SuggestionAction
|
||||
with ToLogString {
|
||||
|
||||
@ -592,6 +594,7 @@ object Runtime {
|
||||
"Modify(" +
|
||||
s"externalId=$externalId," +
|
||||
s"artuments=${arguments.map(_.map(_.toLogString(shouldMask)))}," +
|
||||
s"returnType=$returnType" +
|
||||
s"documentation=" +
|
||||
(if (shouldMask) documentation.map(_.map(_ => STUB))
|
||||
else documentation) +
|
||||
@ -599,6 +602,7 @@ object Runtime {
|
||||
(if (shouldMask) documentationHtml.map(_.map(_ => STUB))
|
||||
else documentationHtml) +
|
||||
s",scope=$scope" +
|
||||
s"reexport=$reexport" +
|
||||
")"
|
||||
}
|
||||
}
|
||||
@ -623,6 +627,30 @@ object Runtime {
|
||||
case class Clean(module: String) extends SuggestionsDatabaseAction
|
||||
}
|
||||
|
||||
case class ExportsUpdate(
|
||||
exports: ModuleExports,
|
||||
action: ExportsAction
|
||||
)
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
@JsonSubTypes(
|
||||
Array(
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExportsAction.Add],
|
||||
name = "exportsActionAdd"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExportsAction.Remove],
|
||||
name = "exportsActionRemove"
|
||||
)
|
||||
)
|
||||
)
|
||||
sealed trait ExportsAction
|
||||
object ExportsAction {
|
||||
case class Add() extends ExportsAction
|
||||
case class Remove() extends ExportsAction
|
||||
}
|
||||
|
||||
/** A suggestion update.
|
||||
*
|
||||
* @param suggestion the original suggestion
|
||||
@ -635,7 +663,7 @@ object Runtime {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"SuggestionUpdate(" +
|
||||
"SuggestionUpdate(suggestion=" +
|
||||
suggestion.toLogString(shouldMask) +
|
||||
s",action=${action.toLogString(shouldMask)})"
|
||||
}
|
||||
@ -1264,12 +1292,14 @@ object Runtime {
|
||||
* @param module the module name
|
||||
* @param version the version of the module
|
||||
* @param actions the list of actions to apply to the suggestions database
|
||||
* @param exports the list of re-exported symbols
|
||||
* @param updates the list of suggestions extracted from module
|
||||
*/
|
||||
case class SuggestionsDatabaseModuleUpdateNotification(
|
||||
module: String,
|
||||
version: ContentVersion,
|
||||
actions: Vector[SuggestionsDatabaseAction],
|
||||
exports: Seq[ExportsUpdate],
|
||||
updates: Tree[SuggestionUpdate]
|
||||
) extends ApiNotification
|
||||
with ToLogString {
|
||||
@ -1280,6 +1310,7 @@ object Runtime {
|
||||
s"module=$module," +
|
||||
s"version=$version," +
|
||||
s"actions=$actions," +
|
||||
s"exports=$exports" +
|
||||
s"updates=${updates.map(_.toLogString(shouldMask))}" +
|
||||
")"
|
||||
}
|
||||
|
@ -53,6 +53,11 @@ public class ModuleScope implements TruffleObject {
|
||||
return module;
|
||||
}
|
||||
|
||||
/** @return the set of modules imported by this module. */
|
||||
public Set<ModuleScope> getImports() {
|
||||
return imports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a constructor in the module scope locally.
|
||||
*
|
||||
|
@ -0,0 +1,36 @@
|
||||
package org.enso.compiler.context
|
||||
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.pass.analyse.BindingAnalysis
|
||||
import org.enso.pkg.QualifiedName
|
||||
import org.enso.polyglot.{ExportedSymbol, ModuleExports}
|
||||
|
||||
final class ExportsBuilder {
|
||||
|
||||
/** Build module exports from the given IR.
|
||||
*
|
||||
* @param moduleName the module name
|
||||
* @param ir the module IR
|
||||
*/
|
||||
def build(moduleName: QualifiedName, ir: IR): ModuleExports = {
|
||||
val symbols = getBindings(ir).exportedSymbols.values.flatten
|
||||
.filter(_.module.getName != moduleName)
|
||||
.collect {
|
||||
case BindingsMap.ResolvedMethod(module, method) =>
|
||||
ExportedSymbol.Method(module.getName.toString, method.name)
|
||||
case BindingsMap.ResolvedConstructor(module, cons) =>
|
||||
ExportedSymbol.Atom(module.getName.toString, cons.name)
|
||||
case BindingsMap.ResolvedModule(module) =>
|
||||
ExportedSymbol.Module(module.getName.toString)
|
||||
}
|
||||
.toSet[ExportedSymbol]
|
||||
ModuleExports(moduleName.toString, symbols)
|
||||
}
|
||||
|
||||
private def getBindings(ir: IR): BindingsMap =
|
||||
ir.unsafeGetMetadata(
|
||||
BindingAnalysis,
|
||||
"Module without binding analysis in Exports Builder."
|
||||
)
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package org.enso.compiler.context
|
||||
|
||||
import org.enso.polyglot.ModuleExports
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
|
||||
object ModuleExportsDiff {
|
||||
|
||||
/** Compute difference between the module exports.
|
||||
*
|
||||
* @param prev exports before
|
||||
* @param current exports after
|
||||
* @return the list of updates
|
||||
*/
|
||||
def compute(
|
||||
prev: ModuleExports,
|
||||
current: ModuleExports
|
||||
): Seq[Api.ExportsUpdate] = {
|
||||
val added = current.symbols.diff(prev.symbols)
|
||||
val removed = prev.symbols.diff(current.symbols)
|
||||
val addedUpdate = Option.when(added.nonEmpty) {
|
||||
Api.ExportsUpdate(current.copy(symbols = added), Api.ExportsAction.Add())
|
||||
}
|
||||
val removedUpdate = Option.when(removed.nonEmpty) {
|
||||
Api.ExportsUpdate(
|
||||
current.copy(symbols = removed),
|
||||
Api.ExportsAction.Remove()
|
||||
)
|
||||
}
|
||||
(addedUpdate ++ removedUpdate).toSeq
|
||||
}
|
||||
}
|
@ -127,7 +127,7 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
|
||||
}
|
||||
|
||||
val builder: TreeBuilder = Vector.newBuilder
|
||||
builder += Tree.Node(buildModuleAtom(module), Vector())
|
||||
builder += Tree.Node(buildModule(module), Vector())
|
||||
|
||||
Tree.Root(
|
||||
go(builder, Scope(ir.children, ir.location))
|
||||
@ -156,7 +156,8 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
|
||||
selfType = selfType.toString,
|
||||
returnType = buildReturnType(returnTypeDef),
|
||||
documentation = doc,
|
||||
documentationHtml = doc.map(DocParserWrapper.runOnPureDoc)
|
||||
documentationHtml = doc.map(DocParserWrapper.runOnPureDoc),
|
||||
reexport = None
|
||||
)
|
||||
}
|
||||
|
||||
@ -204,15 +205,12 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
|
||||
}
|
||||
|
||||
/** Build an atom suggestion representing a module. */
|
||||
private def buildModuleAtom(module: QualifiedName): Suggestion =
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
private def buildModule(module: QualifiedName): Suggestion =
|
||||
Suggestion.Module(
|
||||
module = module.toString,
|
||||
name = module.item,
|
||||
arguments = Seq(),
|
||||
returnType = module.toString,
|
||||
documentation = None,
|
||||
documentationHtml = None
|
||||
documentationHtml = None,
|
||||
reexport = None
|
||||
)
|
||||
|
||||
/** Build suggestions for an atom definition. */
|
||||
@ -240,7 +238,8 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
|
||||
arguments = arguments.map(buildArgument),
|
||||
returnType = module.createChild(name).toString,
|
||||
documentation = doc,
|
||||
documentationHtml = doc.map(DocParserWrapper.runOnPureDoc)
|
||||
documentationHtml = doc.map(DocParserWrapper.runOnPureDoc),
|
||||
reexport = None
|
||||
)
|
||||
|
||||
/** Build getter methods from atom arguments. */
|
||||
|
@ -21,7 +21,15 @@ object SuggestionDiff {
|
||||
.filter {
|
||||
case Api.SuggestionUpdate(
|
||||
_,
|
||||
Api.SuggestionAction.Modify(None, None, None, None, None, None)
|
||||
Api.SuggestionAction.Modify(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
) =>
|
||||
false
|
||||
case _ =>
|
||||
@ -52,6 +60,8 @@ object SuggestionDiff {
|
||||
Api.SuggestionUpdate(e, Api.SuggestionAction.Remove())
|
||||
case These.There(e) =>
|
||||
Api.SuggestionUpdate(e, Api.SuggestionAction.Add())
|
||||
case These.Both(e1: Suggestion.Module, e2: Suggestion.Module) =>
|
||||
diffModules(e1, e2)
|
||||
case These.Both(e1: Suggestion.Atom, e2: Suggestion.Atom) =>
|
||||
diffAtoms(e1, e2)
|
||||
case These.Both(e1: Suggestion.Method, e2: Suggestion.Method) =>
|
||||
@ -130,6 +140,17 @@ object SuggestionDiff {
|
||||
op
|
||||
}
|
||||
|
||||
private def diffModules(
|
||||
e1: Suggestion.Module,
|
||||
e2: Suggestion.Module
|
||||
): Api.SuggestionUpdate = {
|
||||
var op = Api.SuggestionAction.Modify()
|
||||
if (e1.documentation != e2.documentation) {
|
||||
op = op.copy(documentation = Some(e2.documentation))
|
||||
}
|
||||
Api.SuggestionUpdate(e1, op)
|
||||
}
|
||||
|
||||
private def diffAtoms(
|
||||
e1: Suggestion.Atom,
|
||||
e2: Suggestion.Atom
|
||||
|
@ -1,12 +1,14 @@
|
||||
package org.enso.interpreter.instrument.job
|
||||
|
||||
import java.io.{File, IOException}
|
||||
import java.io.File
|
||||
import java.util.logging.Level
|
||||
|
||||
import cats.implicits._
|
||||
import org.enso.compiler.context.{
|
||||
Changeset,
|
||||
ExportsBuilder,
|
||||
ModuleContext,
|
||||
ModuleExportsDiff,
|
||||
SuggestionBuilder,
|
||||
SuggestionDiff
|
||||
}
|
||||
@ -21,11 +23,14 @@ import org.enso.interpreter.instrument.execution.{
|
||||
RuntimeContext
|
||||
}
|
||||
import org.enso.interpreter.runtime.Module
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope
|
||||
import org.enso.pkg.QualifiedName
|
||||
import org.enso.polyglot.{ModuleExports, Suggestion}
|
||||
import org.enso.polyglot.data.Tree
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.text.buffer.Rope
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.jdk.OptionConverters._
|
||||
|
||||
@ -38,6 +43,8 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
|
||||
import EnsureCompiledJob.CompilationStatus
|
||||
|
||||
private val exportsBuilder = new ExportsBuilder
|
||||
|
||||
/** @inheritdoc */
|
||||
override def run(implicit ctx: RuntimeContext): CompilationStatus = {
|
||||
ctx.locking.acquireWriteCompilationLock()
|
||||
@ -93,12 +100,32 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
compile(module)
|
||||
val changeset = applyEdits(new File(module.getPath))
|
||||
compile(module)
|
||||
.map { module =>
|
||||
.map { moduleScope =>
|
||||
val cacheInvalidationCommands =
|
||||
buildCacheInvalidationCommands(changeset, module.getLiteralSource)
|
||||
buildCacheInvalidationCommands(
|
||||
changeset,
|
||||
moduleScope.getModule.getLiteralSource
|
||||
)
|
||||
runInvalidationCommands(cacheInvalidationCommands)
|
||||
// There are two runtime flags that can disable suggestions for project
|
||||
// and global modules (libraries). They are used primarily in tests to
|
||||
// disable the suggestion updates and reduce the number of messages that
|
||||
// runtime sends.
|
||||
if (ctx.executionService.getContext.isProjectSuggestionsEnabled) {
|
||||
analyzeModule(module, changeset)
|
||||
// When the project module is compiled it can involve compilation of
|
||||
// global (library) modules, so we need to check if the global
|
||||
// suggestions are enabled as well.
|
||||
if (ctx.executionService.getContext.isGlobalSuggestionsEnabled) {
|
||||
getCompiledModules(moduleScope).foreach(analyzeModuleInScope)
|
||||
} else {
|
||||
// When the global suggestions are disabled, we will skip indexing
|
||||
// of external libraries, but still want to index the modules that
|
||||
// belongs to the project.
|
||||
val projectModules = getCompiledModules(moduleScope)
|
||||
.filter(m => rootName(m.getName) == rootName(module.getName))
|
||||
projectModules.foreach(analyzeModuleInScope)
|
||||
}
|
||||
analyzeModule(moduleScope.getModule, changeset)
|
||||
}
|
||||
runCompilationDiagnostics(module)
|
||||
}
|
||||
@ -115,6 +142,7 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
ctx.executionService.getLogger
|
||||
.log(Level.FINEST, s"Modules in scope: ${modulesInScope.map(_.getName)}")
|
||||
modulesInScope
|
||||
.filter(!_.isIndexed)
|
||||
.map { module =>
|
||||
compile(module) match {
|
||||
case Left(err) =>
|
||||
@ -127,11 +155,12 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
)
|
||||
)
|
||||
CompilationStatus.Failure
|
||||
case Right(module) =>
|
||||
case Right(moduleScope) =>
|
||||
if (ctx.executionService.getContext.isGlobalSuggestionsEnabled) {
|
||||
analyzeModuleInScope(module)
|
||||
getCompiledModules(moduleScope).foreach(analyzeModuleInScope)
|
||||
analyzeModuleInScope(moduleScope.getModule)
|
||||
}
|
||||
runCompilationDiagnostics(module)
|
||||
runCompilationDiagnostics(moduleScope.getModule)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,15 +168,6 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
private def analyzeModuleInScope(module: Module)(implicit
|
||||
ctx: RuntimeContext
|
||||
): Unit = {
|
||||
try module.getSource
|
||||
catch {
|
||||
case e: IOException =>
|
||||
ctx.executionService.getLogger.log(
|
||||
Level.SEVERE,
|
||||
s"Failed to get module source to analyze ${module.getName}",
|
||||
e
|
||||
)
|
||||
}
|
||||
if (!module.isIndexed && module.getLiteralSource != null) {
|
||||
ctx.executionService.getLogger
|
||||
.log(Level.FINEST, s"Analyzing module in scope ${module.getName}")
|
||||
@ -155,12 +175,15 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
val newSuggestions = SuggestionBuilder(module.getLiteralSource)
|
||||
.build(moduleName, module.getIr)
|
||||
.filter(isSuggestionGlobal)
|
||||
val version = ctx.versioning.evalVersion(module.getLiteralSource.toString)
|
||||
val version = ctx.versioning.evalVersion(module.getLiteralSource.toString)
|
||||
val prevExports = ModuleExports(moduleName.toString, Set())
|
||||
val newExports = exportsBuilder.build(module.getName, module.getIr)
|
||||
val notification = Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
module = moduleName.toString,
|
||||
version = version,
|
||||
actions =
|
||||
Vector(Api.SuggestionsDatabaseAction.Clean(moduleName.toString)),
|
||||
exports = ModuleExportsDiff.compute(prevExports, newExports),
|
||||
updates = SuggestionDiff.compute(Tree.empty, newSuggestions)
|
||||
)
|
||||
sendModuleUpdate(notification)
|
||||
@ -184,10 +207,14 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
.build(moduleName, module.getIr)
|
||||
val diff = SuggestionDiff
|
||||
.compute(prevSuggestions, newSuggestions)
|
||||
val prevExports = exportsBuilder.build(moduleName, changeset.ir)
|
||||
val newExports = exportsBuilder.build(moduleName, module.getIr)
|
||||
val exportsDiff = ModuleExportsDiff.compute(prevExports, newExports)
|
||||
val notification = Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
module = moduleName.toString,
|
||||
version = version,
|
||||
actions = Vector(),
|
||||
exports = exportsDiff,
|
||||
updates = diff
|
||||
)
|
||||
sendModuleUpdate(notification)
|
||||
@ -197,11 +224,14 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
val newSuggestions =
|
||||
SuggestionBuilder(module.getLiteralSource)
|
||||
.build(moduleName, module.getIr)
|
||||
val prevExports = ModuleExports(moduleName.toString, Set())
|
||||
val newExports = exportsBuilder.build(moduleName, module.getIr)
|
||||
val notification = Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
module = moduleName.toString,
|
||||
version = version,
|
||||
actions =
|
||||
Vector(Api.SuggestionsDatabaseAction.Clean(moduleName.toString)),
|
||||
exports = ModuleExportsDiff.compute(prevExports, newExports),
|
||||
updates = SuggestionDiff.compute(Tree.empty, newSuggestions)
|
||||
)
|
||||
sendModuleUpdate(notification)
|
||||
@ -278,16 +308,18 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
*/
|
||||
private def compile(
|
||||
module: Module
|
||||
)(implicit ctx: RuntimeContext): Either[Throwable, Module] = {
|
||||
)(implicit ctx: RuntimeContext): Either[Throwable, ModuleScope] = {
|
||||
val prevStage = module.getCompilationStage
|
||||
val compilationResult = Either.catchNonFatal {
|
||||
module.compileScope(ctx.executionService.getContext).getModule
|
||||
module.compileScope(ctx.executionService.getContext)
|
||||
}
|
||||
if (prevStage != module.getCompilationStage) {
|
||||
ctx.executionService.getLogger
|
||||
.log(
|
||||
Level.FINEST,
|
||||
s"Compiled ${module.getName} $prevStage->${module.getCompilationStage}"
|
||||
)
|
||||
}
|
||||
ctx.executionService.getLogger
|
||||
.log(
|
||||
Level.FINEST,
|
||||
s"Compiled ${module.getName} $prevStage->${module.getCompilationStage}"
|
||||
)
|
||||
compilationResult
|
||||
}
|
||||
|
||||
@ -369,7 +401,11 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
private def sendModuleUpdate(
|
||||
payload: Api.SuggestionsDatabaseModuleUpdateNotification
|
||||
)(implicit ctx: RuntimeContext): Unit =
|
||||
if (payload.actions.nonEmpty || !payload.updates.isEmpty) {
|
||||
if (
|
||||
payload.actions.nonEmpty ||
|
||||
payload.exports.nonEmpty ||
|
||||
!payload.updates.isEmpty
|
||||
) {
|
||||
ctx.endpoint.sendToClient(Api.Response(payload))
|
||||
}
|
||||
|
||||
@ -406,6 +442,7 @@ 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
|
||||
@ -441,6 +478,37 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
ctx: RuntimeContext
|
||||
): Iterable[Module] =
|
||||
ctx.executionService.getContext.getTopScope.getModules.asScala
|
||||
|
||||
private def getCompiledModules(moduleScope: ModuleScope): Seq[Module] = {
|
||||
@scala.annotation.tailrec
|
||||
def go(
|
||||
queue: mutable.Queue[ModuleScope],
|
||||
result: mutable.Set[Module]
|
||||
): Seq[Module] =
|
||||
if (queue.isEmpty) result.toSeq.reverse
|
||||
else {
|
||||
val scope = queue.dequeue()
|
||||
val scopeImports = scope.getImports
|
||||
result.add(scope.getModule)
|
||||
scopeImports.forEach { scopeImport =>
|
||||
if (!result.contains(scopeImport.getModule)) {
|
||||
queue.enqueue(scopeImport)
|
||||
result.add(scopeImport.getModule)
|
||||
}
|
||||
}
|
||||
|
||||
go(queue, result)
|
||||
}
|
||||
|
||||
val queue = mutable.Queue.empty[ModuleScope]
|
||||
moduleScope.getImports.forEach { moduleImport =>
|
||||
queue.enqueue(moduleImport)
|
||||
}
|
||||
go(queue, mutable.LinkedHashSet.empty)
|
||||
}
|
||||
|
||||
private def rootName(name: QualifiedName): String =
|
||||
name.path.headOption.getOrElse(name.item)
|
||||
}
|
||||
|
||||
object EnsureCompiledJob {
|
||||
|
@ -20,15 +20,12 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
implicit val passManager: PassManager = new Passes(defaultConfig).passManager
|
||||
|
||||
private val Module = QualifiedName(List("Unnamed"), "Test")
|
||||
private val ModuleAtomNode = Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
private val ModuleNode = Tree.Node(
|
||||
Suggestion.Module(
|
||||
module = Module.toString,
|
||||
name = Module.item,
|
||||
arguments = Seq(),
|
||||
returnType = Module.toString,
|
||||
documentation = None,
|
||||
documentationHtml = None
|
||||
documentationHtml = None,
|
||||
reexport = None
|
||||
),
|
||||
Vector()
|
||||
)
|
||||
@ -47,7 +44,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -77,7 +74,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -108,7 +105,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -139,7 +136,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -170,7 +167,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -202,7 +199,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -241,7 +238,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -274,7 +271,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -329,7 +326,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -363,7 +360,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -407,7 +404,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
"""MyAtom.bar a b = a + b"""
|
||||
val module = code.preprocessModule
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(Vector(ModuleAtomNode))
|
||||
build(code, module) shouldEqual Tree.Root(Vector(ModuleNode))
|
||||
}
|
||||
|
||||
"build method with associated type signature" in {
|
||||
@ -424,7 +421,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -472,7 +469,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -515,7 +512,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -549,7 +546,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -593,7 +590,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -644,7 +641,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -708,7 +705,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -759,7 +756,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -820,7 +817,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -867,7 +864,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -927,7 +924,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -975,7 +972,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1029,7 +1026,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1093,7 +1090,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1158,7 +1155,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1220,7 +1217,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1285,7 +1282,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1360,7 +1357,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1454,7 +1451,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1533,7 +1530,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1602,7 +1599,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion
|
||||
.Atom(
|
||||
@ -1682,7 +1679,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId =
|
||||
@ -1719,7 +1716,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -1775,7 +1772,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleAtomNode,
|
||||
ModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
|
@ -198,7 +198,7 @@ class RuntimeStdlibTest
|
||||
val suggestions = responses.collect {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(_, _, as, xs)
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(_, _, as, _, xs)
|
||||
) =>
|
||||
(xs.nonEmpty || as.nonEmpty) shouldBe true
|
||||
}
|
||||
@ -208,7 +208,13 @@ class RuntimeStdlibTest
|
||||
val stdlibSuggestions = responses.collect {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(module, _, as, xs)
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
module,
|
||||
_,
|
||||
as,
|
||||
_,
|
||||
xs
|
||||
)
|
||||
) if module.contains("Vector") =>
|
||||
(xs.nonEmpty || as.nonEmpty) shouldBe true
|
||||
xs.toVector.head.suggestion.module shouldEqual "Standard.Base.Data.Vector"
|
||||
@ -219,7 +225,13 @@ class RuntimeStdlibTest
|
||||
val builtinsSuggestions = responses.collect {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(module, _, as, xs)
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
module,
|
||||
_,
|
||||
as,
|
||||
_,
|
||||
xs
|
||||
)
|
||||
) if module.contains("Builtins") =>
|
||||
(xs.nonEmpty || as.nonEmpty) shouldBe true
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import java.nio.ByteBuffer
|
||||
import java.nio.file.{Files, Paths}
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.{LinkedBlockingQueue, TimeUnit}
|
||||
|
||||
import org.enso.interpreter.runtime.`type`.Constants
|
||||
import org.enso.pkg.{Package, PackageManager}
|
||||
import org.enso.polyglot._
|
||||
@ -163,15 +164,12 @@ class RuntimeSuggestionUpdatesTest
|
||||
module = moduleName,
|
||||
version = version,
|
||||
actions = Vector(Api.SuggestionsDatabaseAction.Clean(moduleName)),
|
||||
exports = Vector(),
|
||||
updates = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestion.Atom(
|
||||
None,
|
||||
moduleName,
|
||||
"Main",
|
||||
Seq(),
|
||||
Suggestion.Module(
|
||||
moduleName,
|
||||
None,
|
||||
None
|
||||
@ -240,6 +238,7 @@ class RuntimeSuggestionUpdatesTest
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
),
|
||||
actions = Vector(),
|
||||
exports = Vector(),
|
||||
updates = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
@ -324,6 +323,7 @@ class RuntimeSuggestionUpdatesTest
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
),
|
||||
actions = Vector(),
|
||||
exports = Vector(),
|
||||
updates = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
@ -428,6 +428,7 @@ class RuntimeSuggestionUpdatesTest
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
),
|
||||
actions = Vector(),
|
||||
exports = Vector(),
|
||||
updates = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
@ -542,6 +543,7 @@ class RuntimeSuggestionUpdatesTest
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
),
|
||||
actions = Vector(),
|
||||
exports = Vector(),
|
||||
updates = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
@ -682,6 +684,7 @@ class RuntimeSuggestionUpdatesTest
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
),
|
||||
actions = Vector(),
|
||||
exports = Vector(),
|
||||
updates = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
@ -789,15 +792,12 @@ class RuntimeSuggestionUpdatesTest
|
||||
module = moduleName,
|
||||
version = version,
|
||||
actions = Vector(Api.SuggestionsDatabaseAction.Clean(moduleName)),
|
||||
exports = Vector(),
|
||||
updates = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestion.Atom(
|
||||
None,
|
||||
moduleName,
|
||||
"Main",
|
||||
Seq(),
|
||||
Suggestion.Module(
|
||||
moduleName,
|
||||
None,
|
||||
None
|
||||
@ -908,4 +908,325 @@ class RuntimeSuggestionUpdatesTest
|
||||
)
|
||||
}
|
||||
|
||||
it should "index exports" in {
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val moduleName = "Enso_Test.Test.Main"
|
||||
|
||||
val mainCode =
|
||||
"""from Standard.Builtins import all
|
||||
|
|
||||
|import Enso_Test.Test.A
|
||||
|from Enso_Test.Test.A export all
|
||||
|
|
||||
|main = IO.println "Hello World!"
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val aCode =
|
||||
"""from Standard.Builtins import all
|
||||
|
|
||||
|type MyType
|
||||
| type MkA a
|
||||
|
|
||||
|Integer.fortytwo = 42
|
||||
|
|
||||
|hello = "Hello World!"
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
|
||||
val mainVersion = contentsVersion(mainCode)
|
||||
val mainFile = context.writeMain(mainCode)
|
||||
val aVersion = contentsVersion(aCode)
|
||||
val aFile = context.writeInSrcDir("A", aCode)
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(requestId, Api.CreateContextResponse(contextId))
|
||||
)
|
||||
|
||||
// open files
|
||||
context.send(
|
||||
Api.Request(Api.OpenFileNotification(mainFile, mainCode))
|
||||
)
|
||||
context.receiveNone shouldEqual None
|
||||
context.send(
|
||||
Api.Request(Api.OpenFileNotification(aFile, aCode))
|
||||
)
|
||||
context.receiveNone shouldEqual None
|
||||
|
||||
// push main
|
||||
context.send(
|
||||
Api.Request(
|
||||
requestId,
|
||||
Api.PushContextRequest(
|
||||
contextId,
|
||||
Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
context.receive(4) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
module = "Enso_Test.Test.A",
|
||||
version = aVersion,
|
||||
actions =
|
||||
Vector(Api.SuggestionsDatabaseAction.Clean("Enso_Test.Test.A")),
|
||||
exports = Vector(),
|
||||
updates = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestion.Module("Enso_Test.Test.A", None, None),
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestion.Atom(
|
||||
None,
|
||||
"Enso_Test.Test.A",
|
||||
"MkA",
|
||||
List(
|
||||
Suggestion
|
||||
.Argument("a", Constants.ANY, false, false, None)
|
||||
),
|
||||
"Enso_Test.Test.A.MkA",
|
||||
None,
|
||||
None
|
||||
),
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Enso_Test.Test.A",
|
||||
"a",
|
||||
List(
|
||||
Suggestion
|
||||
.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.A.MkA",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
)
|
||||
),
|
||||
"Enso_Test.Test.A.MkA",
|
||||
Constants.ANY,
|
||||
None,
|
||||
None
|
||||
),
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Enso_Test.Test.A",
|
||||
"fortytwo",
|
||||
List(
|
||||
Suggestion.Argument(
|
||||
"this",
|
||||
Constants.INTEGER,
|
||||
false,
|
||||
false,
|
||||
None
|
||||
)
|
||||
),
|
||||
Constants.INTEGER,
|
||||
Constants.ANY,
|
||||
None,
|
||||
None
|
||||
),
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Enso_Test.Test.A",
|
||||
"hello",
|
||||
List(
|
||||
Suggestion.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.A",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
)
|
||||
),
|
||||
"Enso_Test.Test.A",
|
||||
Constants.ANY,
|
||||
None,
|
||||
None
|
||||
),
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
module = moduleName,
|
||||
version = mainVersion,
|
||||
actions = Vector(Api.SuggestionsDatabaseAction.Clean(moduleName)),
|
||||
exports = Vector(
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
"Enso_Test.Test.Main",
|
||||
Set(
|
||||
ExportedSymbol.Atom("Enso_Test.Test.A", "MkA"),
|
||||
ExportedSymbol.Method("Enso_Test.Test.A", "hello")
|
||||
)
|
||||
),
|
||||
Api.ExportsAction.Add()
|
||||
)
|
||||
),
|
||||
updates = Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestion.Module(
|
||||
moduleName,
|
||||
None,
|
||||
None
|
||||
),
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
moduleName,
|
||||
"main",
|
||||
List(
|
||||
Suggestion
|
||||
.Argument(
|
||||
"this",
|
||||
"Enso_Test.Test.Main",
|
||||
false,
|
||||
false,
|
||||
None
|
||||
)
|
||||
),
|
||||
"Enso_Test.Test.Main",
|
||||
Constants.ANY,
|
||||
None,
|
||||
None
|
||||
),
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
context.consumeOut shouldEqual List("Hello World!")
|
||||
|
||||
// Modify the file
|
||||
context.send(
|
||||
Api.Request(
|
||||
Api.EditFileNotification(
|
||||
mainFile,
|
||||
Seq(
|
||||
TextEdit(
|
||||
model.Range(model.Position(3, 32), model.Position(3, 32)),
|
||||
" hiding hello"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
context.receive(2) should contain theSameElementsAs Seq(
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
module = moduleName,
|
||||
version = contentsVersion(
|
||||
"""from Standard.Builtins import all
|
||||
|
|
||||
|import Enso_Test.Test.A
|
||||
|from Enso_Test.Test.A export all hiding hello
|
||||
|
|
||||
|main = IO.println "Hello World!"
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
),
|
||||
actions = Vector(),
|
||||
exports = Vector(
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
"Enso_Test.Test.Main",
|
||||
Set(ExportedSymbol.Method("Enso_Test.Test.A", "hello"))
|
||||
),
|
||||
Api.ExportsAction.Remove()
|
||||
)
|
||||
),
|
||||
updates = Tree.Root(Vector())
|
||||
)
|
||||
),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
context.consumeOut shouldEqual List("Hello World!")
|
||||
|
||||
// Modify the file
|
||||
context.send(
|
||||
Api.Request(
|
||||
Api.EditFileNotification(
|
||||
mainFile,
|
||||
Seq(
|
||||
TextEdit(
|
||||
model.Range(model.Position(2, 0), model.Position(5, 0)),
|
||||
""
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
context.receive(2) should contain theSameElementsAs Seq(
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
module = moduleName,
|
||||
version = contentsVersion(
|
||||
"""from Standard.Builtins import all
|
||||
|
|
||||
|main = IO.println "Hello World!"
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
),
|
||||
actions = Vector(),
|
||||
exports = Vector(
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
"Enso_Test.Test.Main",
|
||||
Set(ExportedSymbol.Atom("Enso_Test.Test.A", "MkA"))
|
||||
),
|
||||
Api.ExportsAction.Remove()
|
||||
)
|
||||
),
|
||||
updates = Tree.Root(Vector())
|
||||
)
|
||||
),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
context.consumeOut shouldEqual List("Hello World!")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ 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()
|
||||
@ -29,6 +30,13 @@ object SuggestionRandom {
|
||||
}
|
||||
}
|
||||
|
||||
def nextSuggestionModule(): Suggestion.Module =
|
||||
Suggestion.Module(
|
||||
module = nextString(),
|
||||
documentation = optional(nextString()),
|
||||
documentationHtml = optional(nextString())
|
||||
)
|
||||
|
||||
def nextSuggestionAtom(): Suggestion.Atom =
|
||||
Suggestion.Atom(
|
||||
externalId = optional(UUID.randomUUID()),
|
||||
@ -84,11 +92,12 @@ object SuggestionRandom {
|
||||
)
|
||||
|
||||
def nextKind(): Suggestion.Kind =
|
||||
Random.nextInt(4) match {
|
||||
case 0 => Suggestion.Kind.Atom
|
||||
case 1 => Suggestion.Kind.Method
|
||||
case 2 => Suggestion.Kind.Function
|
||||
case 3 => Suggestion.Kind.Local
|
||||
Random.nextInt(5) match {
|
||||
case 0 => Suggestion.Kind.Module
|
||||
case 1 => Suggestion.Kind.Atom
|
||||
case 2 => Suggestion.Kind.Method
|
||||
case 3 => Suggestion.Kind.Function
|
||||
case 4 => Suggestion.Kind.Local
|
||||
case x => throw new NoSuchElementException(s"nextKind: $x")
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package org.enso.searcher
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.polyglot.data.Tree
|
||||
import org.enso.polyglot.runtime.Runtime.Api.{
|
||||
ExportsUpdate,
|
||||
SuggestionArgumentAction,
|
||||
SuggestionUpdate,
|
||||
SuggestionsDatabaseAction
|
||||
@ -84,7 +85,7 @@ trait SuggestionsRepo[F[_]] {
|
||||
*/
|
||||
def applyTree(
|
||||
tree: Tree[SuggestionUpdate]
|
||||
): F[(Long, Seq[QueryResult[SuggestionUpdate]])]
|
||||
): F[Seq[QueryResult[SuggestionUpdate]]]
|
||||
|
||||
/** Apply the sequence of actions on the database.
|
||||
*
|
||||
@ -95,6 +96,15 @@ trait SuggestionsRepo[F[_]] {
|
||||
actions: Seq[SuggestionsDatabaseAction]
|
||||
): F[Seq[QueryResult[SuggestionsDatabaseAction]]]
|
||||
|
||||
/** Apply the sequence of export updates on the database.
|
||||
*
|
||||
* @param updates the list of export updates
|
||||
* @return the result of applying the updates
|
||||
*/
|
||||
def applyExports(
|
||||
updates: Seq[ExportsUpdate]
|
||||
): F[Seq[QueryResult[ExportsUpdate]]]
|
||||
|
||||
/** Remove the suggestion.
|
||||
*
|
||||
* @param suggestion the suggestion to remove
|
||||
@ -132,7 +142,8 @@ trait SuggestionsRepo[F[_]] {
|
||||
returnType: Option[String],
|
||||
documentation: Option[Option[String]],
|
||||
documentationHtml: Option[Option[String]],
|
||||
scope: Option[Suggestion.Scope]
|
||||
scope: Option[Suggestion.Scope],
|
||||
reexport: Option[Option[String]]
|
||||
): F[(Long, Option[Long])]
|
||||
|
||||
/** Update a list of suggestions by external id.
|
||||
|
@ -5,4 +5,4 @@ package org.enso.searcher.data
|
||||
* @param ids the list of ids affected by the query
|
||||
* @param value the result value
|
||||
*/
|
||||
case class QueryResult[A](ids: Seq[Long], value: A)
|
||||
case class QueryResult[A](ids: Iterable[Long], value: A)
|
||||
|
@ -2,9 +2,11 @@ package org.enso.searcher.sql
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.polyglot.{ExportedSymbol, Suggestion}
|
||||
import org.enso.polyglot.data.Tree
|
||||
import org.enso.polyglot.runtime.Runtime.Api.{
|
||||
ExportsAction,
|
||||
ExportsUpdate,
|
||||
SuggestionAction,
|
||||
SuggestionArgumentAction,
|
||||
SuggestionUpdate,
|
||||
@ -39,7 +41,7 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
|
||||
/** Initialize the repo. */
|
||||
override def init: Future[Unit] =
|
||||
db.run(initQuery)
|
||||
db.run(initQuery.transactionally)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def clean: Future[Unit] =
|
||||
@ -66,7 +68,9 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
kinds: Option[Seq[Suggestion.Kind]],
|
||||
position: Option[Suggestion.Position]
|
||||
): Future[(Long, Seq[Long])] =
|
||||
db.run(searchQuery(module, selfType, returnType, kinds, position))
|
||||
db.run(
|
||||
searchQuery(module, selfType, returnType, kinds, position)
|
||||
)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def select(id: Long): Future[Option[Suggestion]] =
|
||||
@ -80,19 +84,25 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
override def insertAll(
|
||||
suggestions: Seq[Suggestion]
|
||||
): Future[(Long, Seq[Option[Long]])] =
|
||||
db.run(insertAllQuery(suggestions))
|
||||
db.run(insertAllQuery(suggestions).transactionally)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def applyTree(
|
||||
tree: Tree[SuggestionUpdate]
|
||||
): Future[(Long, Seq[QueryResult[SuggestionUpdate]])] =
|
||||
db.run(applyTreeQuery(tree))
|
||||
): Future[Seq[QueryResult[SuggestionUpdate]]] =
|
||||
db.run(applyTreeQuery(tree).transactionally)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def applyActions(
|
||||
actions: Seq[SuggestionsDatabaseAction]
|
||||
): Future[Seq[QueryResult[SuggestionsDatabaseAction]]] =
|
||||
db.run(applyActionsQuery(actions))
|
||||
db.run(applyActionsQuery(actions).transactionally)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def applyExports(
|
||||
updates: Seq[ExportsUpdate]
|
||||
): Future[Seq[QueryResult[ExportsUpdate]]] =
|
||||
db.run(applyExportsQuery(updates).transactionally)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def remove(suggestion: Suggestion): Future[Option[Long]] =
|
||||
@ -106,7 +116,7 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
override def removeAll(
|
||||
suggestions: Seq[Suggestion]
|
||||
): Future[(Long, Seq[Option[Long]])] =
|
||||
db.run(removeAllQuery(suggestions))
|
||||
db.run(removeAllQuery(suggestions).transactionally)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def update(
|
||||
@ -116,7 +126,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
returnType: Option[String],
|
||||
documentation: Option[Option[String]],
|
||||
documentationHtml: Option[Option[String]],
|
||||
scope: Option[Suggestion.Scope]
|
||||
scope: Option[Suggestion.Scope],
|
||||
reexport: Option[Option[String]]
|
||||
): Future[(Long, Option[Long])] =
|
||||
db.run(
|
||||
updateQuery(
|
||||
@ -126,7 +137,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
returnType,
|
||||
documentation,
|
||||
documentationHtml,
|
||||
scope
|
||||
scope,
|
||||
reexport
|
||||
)
|
||||
)
|
||||
|
||||
@ -134,7 +146,7 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
override def updateAll(
|
||||
expressions: Seq[(Suggestion.ExternalId, String)]
|
||||
): Future[(Long, Seq[Option[Long]])] =
|
||||
db.run(updateAllQuery(expressions))
|
||||
db.run(updateAllQuery(expressions).transactionally)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def renameProject(
|
||||
@ -149,7 +161,7 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
Seq[(Long, Int, String)]
|
||||
)
|
||||
] =
|
||||
db.run(renameProjectQuery(oldName, newName))
|
||||
db.run(renameProjectQuery(oldName, newName).transactionally)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def currentVersion: Future[Long] =
|
||||
@ -184,7 +196,7 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
* @return the current database size
|
||||
*/
|
||||
private[sql] def insertBatch(suggestions: Array[Suggestion]): Future[Int] =
|
||||
db.run(insertBatchQuery(suggestions))
|
||||
db.run(insertBatchQuery(suggestions).transactionally)
|
||||
|
||||
/** The query to initialize the repo. */
|
||||
private def initQuery: DBIO[Unit] = {
|
||||
@ -376,8 +388,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
*/
|
||||
private def applyTreeQuery(
|
||||
tree: Tree[SuggestionUpdate]
|
||||
): DBIO[(Long, Seq[QueryResult[SuggestionUpdate]])] = {
|
||||
val query = tree.toVector.map {
|
||||
): DBIO[Seq[QueryResult[SuggestionUpdate]]] = {
|
||||
val queries = tree.toVector.map {
|
||||
case update @ SuggestionUpdate(suggestion, action) =>
|
||||
val query = action match {
|
||||
case SuggestionAction.Add() =>
|
||||
@ -390,7 +402,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
returnType,
|
||||
doc,
|
||||
docHtml,
|
||||
scope
|
||||
scope,
|
||||
reexport
|
||||
) =>
|
||||
updateSuggestionQuery(
|
||||
suggestion,
|
||||
@ -399,15 +412,13 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
returnType,
|
||||
doc,
|
||||
docHtml,
|
||||
scope
|
||||
scope,
|
||||
reexport
|
||||
)
|
||||
}
|
||||
query.map(rs => QueryResult(rs.toSeq, update))
|
||||
}
|
||||
for {
|
||||
results <- DBIO.sequence(query)
|
||||
version <- currentVersionQuery
|
||||
} yield (version, results)
|
||||
DBIO.sequence(queries)
|
||||
}
|
||||
|
||||
/** The query to apply the sequence of actions on the database.
|
||||
@ -418,13 +429,77 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
private def applyActionsQuery(
|
||||
actions: Seq[SuggestionsDatabaseAction]
|
||||
): DBIO[Seq[QueryResult[SuggestionsDatabaseAction]]] = {
|
||||
val queries = actions.map {
|
||||
val removeActions = actions.map {
|
||||
case act @ SuggestionsDatabaseAction.Clean(module) =>
|
||||
removeByModuleQuery(Seq(module)).map { case (_, ids) =>
|
||||
QueryResult[SuggestionsDatabaseAction](ids, act)
|
||||
}
|
||||
for {
|
||||
ids <- removeModulesQuery(Seq(module))
|
||||
} yield QueryResult[SuggestionsDatabaseAction](ids, act)
|
||||
}
|
||||
DBIO.sequence(queries)
|
||||
DBIO.sequence(removeActions)
|
||||
}
|
||||
|
||||
/** The query that applies the sequence of export updates.
|
||||
*
|
||||
* @param updates the list of export updates
|
||||
* @return the result of applying actions
|
||||
*/
|
||||
private def applyExportsQuery(
|
||||
updates: Seq[ExportsUpdate]
|
||||
): DBIO[Seq[QueryResult[ExportsUpdate]]] = {
|
||||
def depth(module: String): Int =
|
||||
module.count(_ == '.')
|
||||
|
||||
def updateSuggestionReexport(module: String, symbol: ExportedSymbol) = {
|
||||
val moduleDepth = depth(module)
|
||||
sql"""
|
||||
update suggestions
|
||||
set reexport = $module
|
||||
where module = ${symbol.module}
|
||||
and name = ${symbol.name}
|
||||
and kind = ${SuggestionKind(symbol.kind)}
|
||||
and (
|
||||
reexport is null or
|
||||
length(reexport) - length(replace(reexport, '.', '')) > $moduleDepth
|
||||
)
|
||||
returning id
|
||||
""".as[Long]
|
||||
}
|
||||
|
||||
def unsetSuggestionReexport(module: String, symbol: ExportedSymbol) =
|
||||
sql"""
|
||||
update suggestions
|
||||
set reexport = null
|
||||
where module = ${symbol.module}
|
||||
and name = ${symbol.name}
|
||||
and kind = ${SuggestionKind(symbol.kind)}
|
||||
and reexport = $module
|
||||
returning id
|
||||
""".as[Long]
|
||||
|
||||
val actions = updates.flatMap { update =>
|
||||
val symbols = update.exports.symbols.toSeq
|
||||
update.action match {
|
||||
case ExportsAction.Add() =>
|
||||
symbols.map { symbol =>
|
||||
for {
|
||||
ids <- updateSuggestionReexport(update.exports.module, symbol)
|
||||
} yield QueryResult(ids, update)
|
||||
}
|
||||
case ExportsAction.Remove() =>
|
||||
symbols.map { symbol =>
|
||||
for {
|
||||
ids <- unsetSuggestionReexport(update.exports.module, symbol)
|
||||
} yield QueryResult(ids, update)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
rs <- DBIO.sequence(actions)
|
||||
_ <-
|
||||
if (rs.flatMap(_.ids).nonEmpty) incrementVersionQuery
|
||||
else DBIO.successful(())
|
||||
} yield rs
|
||||
}
|
||||
|
||||
/** The query to select the suggestion.
|
||||
@ -470,15 +545,29 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
private def removeByModuleQuery(
|
||||
modules: Seq[String]
|
||||
): DBIO[(Long, Seq[Long])] = {
|
||||
val selectQuery = Suggestions.filter(_.module.inSet(modules))
|
||||
val deleteQuery = for {
|
||||
rows <- selectQuery.result
|
||||
n <- selectQuery.delete
|
||||
version <- if (n > 0) incrementVersionQuery else currentVersionQuery
|
||||
} yield version -> rows.flatMap(_.id)
|
||||
ids <- removeModulesQuery(modules)
|
||||
version <-
|
||||
if (ids.nonEmpty) incrementVersionQuery else currentVersionQuery
|
||||
} yield version -> ids
|
||||
deleteQuery
|
||||
}
|
||||
|
||||
/** The query to remove the suggestions by module name
|
||||
*
|
||||
* @param modules the module names to remove
|
||||
* @return the list of removed suggestion ids
|
||||
*/
|
||||
private def removeModulesQuery(
|
||||
modules: Seq[String]
|
||||
): DBIO[Seq[Long]] = {
|
||||
val selectQuery = Suggestions.filter(_.module.inSet(modules))
|
||||
for {
|
||||
rows <- selectQuery.map(_.id).result
|
||||
_ <- selectQuery.delete
|
||||
} yield rows
|
||||
}
|
||||
|
||||
/** The query to remove a list of suggestions.
|
||||
*
|
||||
* @param suggestions the suggestions to remove
|
||||
@ -532,7 +621,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
returnType: Option[String],
|
||||
documentation: Option[Option[String]],
|
||||
documentationHtml: Option[Option[String]],
|
||||
scope: Option[Suggestion.Scope]
|
||||
scope: Option[Suggestion.Scope],
|
||||
reexport: Option[Option[String]]
|
||||
): DBIO[(Long, Option[Long])] =
|
||||
for {
|
||||
idOpt <- updateSuggestionQuery(
|
||||
@ -542,7 +632,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
returnType,
|
||||
documentation,
|
||||
documentationHtml,
|
||||
scope
|
||||
scope,
|
||||
reexport
|
||||
)
|
||||
version <- currentVersionQuery
|
||||
} yield (version, idOpt)
|
||||
@ -564,7 +655,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
returnType: Option[String],
|
||||
documentation: Option[Option[String]],
|
||||
documentationHtml: Option[Option[String]],
|
||||
scope: Option[Suggestion.Scope]
|
||||
scope: Option[Suggestion.Scope],
|
||||
reexport: Option[Option[String]]
|
||||
): DBIO[Option[Long]] = {
|
||||
val (raw, _) = toSuggestionRow(suggestion)
|
||||
val query = selectSuggestionQuery(raw)
|
||||
@ -619,7 +711,12 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
} yield r.map(_.sum)
|
||||
}
|
||||
}
|
||||
} yield (r1 ++ r2 ++ r3 ++ r4 ++ r5 ++ r6.flatten).sum
|
||||
r7 <- DBIO.sequenceOption {
|
||||
reexport.map { reexportOpt =>
|
||||
query.map(_.reexport).update(reexportOpt)
|
||||
}
|
||||
}
|
||||
} yield (r1 ++ r2 ++ r3 ++ r4 ++ r5 ++ r6.flatten ++ r7).sum
|
||||
for {
|
||||
id <- query.map(_.id).result.headOption
|
||||
n <- updateQ
|
||||
@ -893,6 +990,25 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
suggestion: Suggestion
|
||||
): (SuggestionRow, Seq[Suggestion.Argument]) =
|
||||
suggestion match {
|
||||
case Suggestion.Module(module, doc, docHtml, reexport) =>
|
||||
val row = SuggestionRow(
|
||||
id = None,
|
||||
externalIdLeast = None,
|
||||
externalIdMost = None,
|
||||
kind = SuggestionKind.MODULE,
|
||||
module = module,
|
||||
name = module,
|
||||
selfType = SelfTypeColumn.EMPTY,
|
||||
returnType = "",
|
||||
scopeStartLine = ScopeColumn.EMPTY,
|
||||
scopeStartOffset = ScopeColumn.EMPTY,
|
||||
scopeEndLine = ScopeColumn.EMPTY,
|
||||
scopeEndOffset = ScopeColumn.EMPTY,
|
||||
documentation = doc,
|
||||
documentationHtml = docHtml,
|
||||
reexport = reexport
|
||||
)
|
||||
row -> Seq()
|
||||
case Suggestion.Atom(
|
||||
expr,
|
||||
module,
|
||||
@ -900,7 +1016,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
args,
|
||||
returnType,
|
||||
doc,
|
||||
docHtml
|
||||
docHtml,
|
||||
reexport
|
||||
) =>
|
||||
val row = SuggestionRow(
|
||||
id = None,
|
||||
@ -916,7 +1033,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
scopeStartLine = ScopeColumn.EMPTY,
|
||||
scopeStartOffset = ScopeColumn.EMPTY,
|
||||
scopeEndLine = ScopeColumn.EMPTY,
|
||||
scopeEndOffset = ScopeColumn.EMPTY
|
||||
scopeEndOffset = ScopeColumn.EMPTY,
|
||||
reexport = reexport
|
||||
)
|
||||
row -> args
|
||||
case Suggestion.Method(
|
||||
@ -927,7 +1045,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
selfType,
|
||||
returnType,
|
||||
doc,
|
||||
docHtml
|
||||
docHtml,
|
||||
reexport
|
||||
) =>
|
||||
val row = SuggestionRow(
|
||||
id = None,
|
||||
@ -943,7 +1062,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
scopeStartLine = ScopeColumn.EMPTY,
|
||||
scopeStartOffset = ScopeColumn.EMPTY,
|
||||
scopeEndLine = ScopeColumn.EMPTY,
|
||||
scopeEndOffset = ScopeColumn.EMPTY
|
||||
scopeEndOffset = ScopeColumn.EMPTY,
|
||||
reexport = reexport
|
||||
)
|
||||
row -> args
|
||||
case Suggestion.Function(expr, module, name, args, returnType, scope) =>
|
||||
@ -961,7 +1081,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
scopeStartLine = scope.start.line,
|
||||
scopeStartOffset = scope.start.character,
|
||||
scopeEndLine = scope.end.line,
|
||||
scopeEndOffset = scope.end.character
|
||||
scopeEndOffset = scope.end.character,
|
||||
reexport = None
|
||||
)
|
||||
row -> args
|
||||
case Suggestion.Local(expr, module, name, returnType, scope) =>
|
||||
@ -979,7 +1100,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
scopeStartLine = scope.start.line,
|
||||
scopeStartOffset = scope.start.character,
|
||||
scopeEndLine = scope.end.line,
|
||||
scopeEndOffset = scope.end.character
|
||||
scopeEndOffset = scope.end.character,
|
||||
reexport = None
|
||||
)
|
||||
row -> Seq()
|
||||
}
|
||||
@ -1014,6 +1136,13 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
arguments: Seq[ArgumentRow]
|
||||
): Suggestion =
|
||||
suggestion.kind match {
|
||||
case SuggestionKind.MODULE =>
|
||||
Suggestion.Module(
|
||||
module = suggestion.module,
|
||||
documentation = suggestion.documentation,
|
||||
documentationHtml = suggestion.documentationHtml,
|
||||
reexport = suggestion.reexport
|
||||
)
|
||||
case SuggestionKind.ATOM =>
|
||||
Suggestion.Atom(
|
||||
externalId =
|
||||
@ -1023,7 +1152,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
arguments = arguments.sortBy(_.index).map(toArgument),
|
||||
returnType = suggestion.returnType,
|
||||
documentation = suggestion.documentation,
|
||||
documentationHtml = suggestion.documentationHtml
|
||||
documentationHtml = suggestion.documentationHtml,
|
||||
reexport = suggestion.reexport
|
||||
)
|
||||
case SuggestionKind.METHOD =>
|
||||
Suggestion.Method(
|
||||
@ -1035,7 +1165,8 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
selfType = suggestion.selfType,
|
||||
returnType = suggestion.returnType,
|
||||
documentation = suggestion.documentation,
|
||||
documentationHtml = suggestion.documentationHtml
|
||||
documentationHtml = suggestion.documentationHtml,
|
||||
reexport = suggestion.reexport
|
||||
)
|
||||
case SuggestionKind.FUNCTION =>
|
||||
Suggestion.Function(
|
||||
@ -1099,4 +1230,5 @@ final class SqlSuggestionsRepo(val db: SqlDatabase)(implicit
|
||||
l <- least
|
||||
m <- most
|
||||
} yield new UUID(m, l)
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ final class SqlVersionsRepo(db: SqlDatabase)(implicit ec: ExecutionContext)
|
||||
|
||||
/** Initialize the repo. */
|
||||
override def init: Future[Unit] =
|
||||
db.run(initQuery)
|
||||
db.run(initQuery.transactionally)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getVersion(module: String): Future[Option[Array[Byte]]] =
|
||||
@ -37,7 +37,7 @@ final class SqlVersionsRepo(db: SqlDatabase)(implicit ec: ExecutionContext)
|
||||
override def updateVersions(
|
||||
versions: Seq[(String, Array[Byte])]
|
||||
): Future[Unit] =
|
||||
db.run(updateVersionsQuery(versions))
|
||||
db.run(updateVersionsQuery(versions).transactionally)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def remove(module: String): Future[Unit] =
|
||||
|
@ -37,11 +37,11 @@ case class ArgumentRow(
|
||||
* @param name the suggestion name
|
||||
* @param selfType the self type of a suggestion
|
||||
* @param returnType the return type of a suggestion
|
||||
* @param documentation the documentation string
|
||||
* @param scopeStartLine the line of the start position of the scope
|
||||
* @param scopeStartOffset the offset of the start position of the scope
|
||||
* @param scopeEndLine the line of the end position of the scope
|
||||
* @param scopeEndOffset the offset of the end position of the scope
|
||||
* @param documentation the documentation string
|
||||
*/
|
||||
case class SuggestionRow(
|
||||
id: Option[Long],
|
||||
@ -52,12 +52,13 @@ case class SuggestionRow(
|
||||
name: String,
|
||||
selfType: String,
|
||||
returnType: String,
|
||||
documentation: Option[String],
|
||||
documentationHtml: Option[String],
|
||||
scopeStartLine: Int,
|
||||
scopeStartOffset: Int,
|
||||
scopeEndLine: Int,
|
||||
scopeEndOffset: Int
|
||||
scopeEndOffset: Int,
|
||||
documentation: Option[String],
|
||||
documentationHtml: Option[String],
|
||||
reexport: Option[String]
|
||||
)
|
||||
|
||||
/** A row in the suggestions_version table.
|
||||
@ -82,10 +83,11 @@ case class ModuleVersionRow(module: String, digest: Array[Byte])
|
||||
/** The type of a suggestion. */
|
||||
object SuggestionKind {
|
||||
|
||||
val ATOM: Byte = 0
|
||||
val METHOD: Byte = 1
|
||||
val FUNCTION: Byte = 2
|
||||
val LOCAL: Byte = 3
|
||||
val MODULE: Byte = 0
|
||||
val ATOM: Byte = 1
|
||||
val METHOD: Byte = 2
|
||||
val FUNCTION: Byte = 3
|
||||
val LOCAL: Byte = 4
|
||||
|
||||
/** Create a database suggestion kind.
|
||||
*
|
||||
@ -94,6 +96,7 @@ 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
|
||||
@ -152,16 +155,14 @@ final class ArgumentsTable(tag: Tag)
|
||||
final class SuggestionsTable(tag: Tag)
|
||||
extends Table[SuggestionRow](tag, "suggestions") {
|
||||
|
||||
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
|
||||
def externalIdLeast = column[Option[Long]]("external_id_least")
|
||||
def externalIdMost = column[Option[Long]]("external_id_most")
|
||||
def kind = column[Byte]("kind")
|
||||
def module = column[String]("module")
|
||||
def name = column[String]("name")
|
||||
def selfType = column[String]("self_type")
|
||||
def returnType = column[String]("return_type")
|
||||
def documentation = column[Option[String]]("documentation")
|
||||
def documentationHtml = column[Option[String]]("documentation_html")
|
||||
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
|
||||
def externalIdLeast = column[Option[Long]]("external_id_least")
|
||||
def externalIdMost = column[Option[Long]]("external_id_most")
|
||||
def kind = column[Byte]("kind")
|
||||
def module = column[String]("module")
|
||||
def name = column[String]("name")
|
||||
def selfType = column[String]("self_type")
|
||||
def returnType = column[String]("return_type")
|
||||
def scopeStartLine =
|
||||
column[Int]("scope_start_line", O.Default(ScopeColumn.EMPTY))
|
||||
def scopeStartOffset =
|
||||
@ -170,6 +171,10 @@ final class SuggestionsTable(tag: Tag)
|
||||
column[Int]("scope_end_line", O.Default(ScopeColumn.EMPTY))
|
||||
def scopeEndOffset =
|
||||
column[Int]("scope_end_offset", O.Default(ScopeColumn.EMPTY))
|
||||
def documentation = column[Option[String]]("documentation")
|
||||
def documentationHtml = column[Option[String]]("documentation_html")
|
||||
def reexport = column[Option[String]]("reexport")
|
||||
|
||||
def * =
|
||||
(
|
||||
id.?,
|
||||
@ -180,12 +185,13 @@ final class SuggestionsTable(tag: Tag)
|
||||
name,
|
||||
selfType,
|
||||
returnType,
|
||||
documentation,
|
||||
documentationHtml,
|
||||
scopeStartLine,
|
||||
scopeStartOffset,
|
||||
scopeEndLine,
|
||||
scopeEndOffset
|
||||
scopeEndOffset,
|
||||
documentation,
|
||||
documentationHtml,
|
||||
reexport
|
||||
) <>
|
||||
(SuggestionRow.tupled, SuggestionRow.unapply)
|
||||
|
||||
@ -195,6 +201,7 @@ final class SuggestionsTable(tag: Tag)
|
||||
def returnTypeIdx = index("suggestions_return_type_idx", returnType)
|
||||
def externalIdIdx =
|
||||
index("suggestions_external_id_idx", (externalIdLeast, externalIdMost))
|
||||
def reexportIdx = index("suggestions_reexport_idx", reexport)
|
||||
// NOTE: unique index should not contain nullable columns because SQLite
|
||||
// teats NULLs as distinct values.
|
||||
def uniqueIdx =
|
||||
@ -205,7 +212,6 @@ final class SuggestionsTable(tag: Tag)
|
||||
module,
|
||||
name,
|
||||
selfType,
|
||||
returnType,
|
||||
scopeStartLine,
|
||||
scopeStartOffset,
|
||||
scopeEndLine,
|
||||
@ -258,5 +264,5 @@ object SuggestionsVersion extends TableQuery(new SuggestionsVersionTable(_))
|
||||
object SchemaVersion extends TableQuery(new SchemaVersionTable(_)) {
|
||||
|
||||
/** The current schema version. */
|
||||
val CurrentVersion: Long = 3
|
||||
val CurrentVersion: Long = 4
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ package org.enso.searcher.sql
|
||||
import java.nio.file.{Files, Path}
|
||||
import java.util.UUID
|
||||
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.polyglot.{ExportedSymbol, ModuleExports, Suggestion}
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.searcher.data.QueryResult
|
||||
import org.enso.testkit.RetrySpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
@ -63,6 +64,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
for {
|
||||
_ <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
@ -74,6 +76,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
|
||||
val suggestions = Await.result(action, Timeout).map(_.suggestion)
|
||||
suggestions should contain theSameElementsAs Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
@ -85,6 +88,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
val action = for {
|
||||
(_, ids) <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
@ -100,7 +104,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
} yield (ids, results)
|
||||
|
||||
val (ids, results) = Await.result(action, Timeout)
|
||||
results should contain theSameElementsInOrderAs Seq(ids(1), None)
|
||||
results should contain theSameElementsInOrderAs Seq(ids(2), None)
|
||||
}
|
||||
|
||||
"get suggestions by empty method call info" taggedAs Retry in withRepo {
|
||||
@ -108,6 +112,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
val action = for {
|
||||
_ <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
@ -125,6 +130,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
val action = for {
|
||||
_ <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
@ -143,6 +149,8 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
for {
|
||||
(_, ids) <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
@ -159,7 +167,10 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
val (ids, all) = Await.result(action, Timeout)
|
||||
ids(0) shouldBe a[Some[_]]
|
||||
ids(1) shouldBe a[None.type]
|
||||
ids(2) shouldBe a[Some[_]]
|
||||
ids(3) shouldBe a[None.type]
|
||||
all.map(_.suggestion) should contain theSameElementsAs Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
@ -208,6 +219,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
val action = for {
|
||||
(_, idsIns) <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
@ -226,6 +238,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
val action = for {
|
||||
(v1, _) <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
@ -242,8 +255,9 @@ 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.function,
|
||||
@ -385,6 +399,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
"update suggestion by external id" taggedAs Retry in withRepo { repo =>
|
||||
val newReturnType = "Quux"
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -402,8 +417,9 @@ 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.function,
|
||||
@ -417,6 +433,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
s <- repo.select(id1.get)
|
||||
@ -430,8 +447,9 @@ 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.function,
|
||||
@ -445,6 +463,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
s <- repo.select(id1.get)
|
||||
@ -458,8 +477,9 @@ 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.function,
|
||||
@ -473,6 +493,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
Some(newReturnType),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
s <- repo.select(id1.get)
|
||||
@ -483,11 +504,12 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
s shouldEqual Some(suggestion.function.copy(returnType = newReturnType))
|
||||
}
|
||||
|
||||
"update suggestion documentation" taggedAs Retry in withRepo { repo =>
|
||||
"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.function,
|
||||
@ -501,6 +523,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
None,
|
||||
Some(Some(newDoc)),
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
s <- repo.select(id1.get)
|
||||
@ -511,11 +534,109 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
s shouldEqual Some(suggestion.atom.copy(documentation = Some(newDoc)))
|
||||
}
|
||||
|
||||
"update suggestion atom 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.function,
|
||||
suggestion.local
|
||||
)
|
||||
)
|
||||
(v2, id2) <- repo.update(
|
||||
suggestion.atom,
|
||||
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.atom.copy(documentationHtml = Some(newDoc))
|
||||
)
|
||||
}
|
||||
|
||||
"update suggestion module 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.function,
|
||||
suggestion.local
|
||||
)
|
||||
)
|
||||
(v2, id2) <- repo.update(
|
||||
suggestion.module,
|
||||
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.module.copy(documentation = Some(newDoc)))
|
||||
}
|
||||
|
||||
"update suggestion module 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.function,
|
||||
suggestion.local
|
||||
)
|
||||
)
|
||||
(v2, id2) <- repo.update(
|
||||
suggestion.module,
|
||||
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.module.copy(documentationHtml = Some(newDoc))
|
||||
)
|
||||
}
|
||||
|
||||
"update suggestion removing 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.function,
|
||||
@ -529,6 +650,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
None,
|
||||
Some(None),
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
s <- repo.select(id1.get)
|
||||
@ -539,14 +661,45 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
s shouldEqual Some(suggestion.atom.copy(documentation = None))
|
||||
}
|
||||
|
||||
"update suggestion removing HTML documentation" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
(v1, Seq(_, id1, _, _, _)) <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
suggestion.local
|
||||
)
|
||||
)
|
||||
(v2, id2) <- repo.update(
|
||||
suggestion.atom,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(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.atom.copy(documentationHtml = None))
|
||||
}
|
||||
|
||||
"update suggestion scope" taggedAs Retry in withRepo { repo =>
|
||||
val newScope = Suggestion.Scope(
|
||||
Suggestion.Position(14, 15),
|
||||
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.function,
|
||||
@ -560,7 +713,8 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(newScope)
|
||||
Some(newScope),
|
||||
None
|
||||
)
|
||||
s <- repo.select(id1.get)
|
||||
} yield (v1, id1, v2, id2, s)
|
||||
@ -575,8 +729,9 @@ 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.function,
|
||||
@ -590,6 +745,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
s <- repo.select(id1.get)
|
||||
@ -608,8 +764,9 @@ 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.function,
|
||||
@ -623,6 +780,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
s <- repo.select(id1.get)
|
||||
@ -649,8 +807,9 @@ 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.function,
|
||||
@ -664,6 +823,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
s <- repo.select(id1.get)
|
||||
@ -683,6 +843,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
val action = for {
|
||||
(v1, _) <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
@ -696,6 +857,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
} yield (v1, v2, id2)
|
||||
@ -707,6 +869,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
"change version after updateAll" taggedAs Retry in withRepo { repo =>
|
||||
val newReturnType = "Quux"
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -725,6 +888,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
repo =>
|
||||
val newReturnType = "Quux"
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -745,6 +909,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
val action = for {
|
||||
(_, ids) <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
@ -758,13 +923,14 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
val (ids, xs1, xs2, xs3, xs4, res) = Await.result(action, Timeout)
|
||||
|
||||
xs1 should contain theSameElementsAs ids.flatten.map((_, newModuleName))
|
||||
xs2 should contain theSameElementsAs Seq(ids(1)).flatten
|
||||
xs2 should contain theSameElementsAs Seq(ids(2)).flatten
|
||||
.map((_, newSelfType))
|
||||
xs3 should contain theSameElementsAs Seq(ids(2), ids(3)).flatten
|
||||
xs3 should contain theSameElementsAs Seq(ids(3), ids(4)).flatten
|
||||
.map((_, newReturnType))
|
||||
xs4 should contain theSameElementsAs Seq()
|
||||
res.map(_.suggestion) should contain theSameElementsAs Seq(
|
||||
suggestion.atom.copy(module = newModuleName),
|
||||
suggestion.module.copy(module = newModuleName),
|
||||
suggestion.atom.copy(module = newModuleName),
|
||||
suggestion.method
|
||||
.copy(module = newModuleName, selfType = newSelfType),
|
||||
suggestion.function
|
||||
@ -782,7 +948,13 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
|
||||
val atom = suggestion.atom.copy(module = "Test.Main.Test.Main")
|
||||
val all =
|
||||
Seq(atom, suggestion.method, suggestion.function, suggestion.local)
|
||||
Seq(
|
||||
suggestion.module,
|
||||
atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
suggestion.local
|
||||
)
|
||||
val action = for {
|
||||
(_, ids) <- repo.insertAll(all)
|
||||
(_, xs1, xs2, xs3, xs4) <- repo.renameProject("Test", "Best")
|
||||
@ -797,12 +969,14 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
case (idOpt, _) =>
|
||||
idOpt.map((_, newModuleName))
|
||||
}
|
||||
xs2 should contain theSameElementsAs Seq(ids(1)).flatten
|
||||
xs2 should contain theSameElementsAs Seq(ids(2)).flatten
|
||||
.map((_, newSelfType))
|
||||
xs3 should contain theSameElementsAs Seq(ids(2), ids(3)).flatten
|
||||
xs3 should contain theSameElementsAs Seq(ids(3), ids(4)).flatten
|
||||
.map((_, newReturnType))
|
||||
xs4 should contain theSameElementsAs Seq()
|
||||
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),
|
||||
@ -819,11 +993,12 @@ 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(atom, method, function, local)
|
||||
val all = Seq(module, atom, method, function, local)
|
||||
val action = for {
|
||||
(_, ids) <- repo.insertAll(all)
|
||||
(_, xs1, xs2, xs3, xs4) <- repo.renameProject("Test", "Best")
|
||||
@ -833,19 +1008,22 @@ 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(atom, method))
|
||||
.zip(Seq(module, atom, method))
|
||||
.flatMap {
|
||||
case (idOpt, _: Suggestion.Module) =>
|
||||
idOpt.map((_, newMainModuleName))
|
||||
case (idOpt, _: Suggestion.Atom) =>
|
||||
idOpt.map((_, newMainModuleName))
|
||||
case (idOpt, _) =>
|
||||
idOpt.map((_, newFooModuleName))
|
||||
}
|
||||
xs2 should contain theSameElementsAs Seq(ids(1)).flatten
|
||||
xs2 should contain theSameElementsAs Seq(ids(2)).flatten
|
||||
.map((_, newMainModuleName))
|
||||
xs3 should contain theSameElementsAs Seq(ids(2), ids(3)).flatten
|
||||
xs3 should contain theSameElementsAs Seq(ids(3), ids(4)).flatten
|
||||
.map((_, newReturnTypeName))
|
||||
xs4 should contain theSameElementsAs Seq()
|
||||
res.map(_.suggestion) should contain theSameElementsAs Seq(
|
||||
module.copy(module = newMainModuleName),
|
||||
atom.copy(module = newMainModuleName),
|
||||
method.copy(module = newFooModuleName, selfType = newMainModuleName),
|
||||
function.copy(returnType = newReturnTypeName),
|
||||
@ -873,7 +1051,13 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
)
|
||||
)
|
||||
val all =
|
||||
Seq(suggestion.atom, method, suggestion.function, suggestion.local)
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
method,
|
||||
suggestion.function,
|
||||
suggestion.local
|
||||
)
|
||||
val action = for {
|
||||
(_, ids) <- repo.insertAll(all)
|
||||
(_, xs1, xs2, xs3, xs4) <- repo.renameProject("Test", "Best")
|
||||
@ -883,15 +1067,16 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
val (ids, xs1, xs2, xs3, xs4, res) = Await.result(action, Timeout)
|
||||
|
||||
xs1 should contain theSameElementsAs ids.flatten.map((_, newModuleName))
|
||||
xs2 should contain theSameElementsAs Seq(ids(1)).flatten
|
||||
xs2 should contain theSameElementsAs Seq(ids(2)).flatten
|
||||
.map((_, newSelfType))
|
||||
xs3 should contain theSameElementsAs Seq(ids(2), ids(3)).flatten
|
||||
xs3 should contain theSameElementsAs Seq(ids(3), ids(4)).flatten
|
||||
.map((_, newReturnType))
|
||||
xs4 should contain theSameElementsAs Seq(ids(1)).flatMap {
|
||||
xs4 should contain theSameElementsAs Seq(ids(2)).flatMap {
|
||||
_.map((_, 1, newArgumentType))
|
||||
}
|
||||
res.map(_.suggestion) should contain theSameElementsAs Seq(
|
||||
suggestion.atom.copy(module = newModuleName),
|
||||
suggestion.module.copy(module = newModuleName),
|
||||
suggestion.atom.copy(module = newModuleName),
|
||||
method
|
||||
.copy(
|
||||
module = newModuleName,
|
||||
@ -932,8 +1117,162 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
v1 shouldEqual Some(v2)
|
||||
}
|
||||
|
||||
"apply export updates" taggedAs Retry in withRepo { repo =>
|
||||
val reexport = "Foo.Bar"
|
||||
val method = suggestion.method.copy(reexport = Some(reexport))
|
||||
val updates = Seq(
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
reexport,
|
||||
Set(ExportedSymbol.Module(suggestion.module.module))
|
||||
),
|
||||
Api.ExportsAction.Add()
|
||||
),
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
reexport,
|
||||
Set(ExportedSymbol.Method(method.module, method.name))
|
||||
),
|
||||
Api.ExportsAction.Remove()
|
||||
)
|
||||
)
|
||||
val action = for {
|
||||
(_, ids) <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
method,
|
||||
suggestion.function,
|
||||
suggestion.local
|
||||
)
|
||||
)
|
||||
results <- repo.applyExports(updates)
|
||||
} yield (ids, results)
|
||||
|
||||
val (ids, results) = Await.result(action, Timeout)
|
||||
results should contain theSameElementsAs Seq(
|
||||
QueryResult(ids(0).toSeq, updates(0)),
|
||||
QueryResult(ids(2).toSeq, updates(1))
|
||||
)
|
||||
}
|
||||
|
||||
"not apply exports with bigger module name" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val reexport = "Foo.Bar.Baz"
|
||||
val method = suggestion.method.copy(reexport = Some("Foo.Bar"))
|
||||
val updates = Seq(
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
reexport,
|
||||
Set(ExportedSymbol.Module(suggestion.module.module))
|
||||
),
|
||||
Api.ExportsAction.Add()
|
||||
),
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
reexport,
|
||||
Set(ExportedSymbol.Method(method.module, method.name))
|
||||
),
|
||||
Api.ExportsAction.Remove()
|
||||
)
|
||||
)
|
||||
val action = for {
|
||||
(_, ids) <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
method,
|
||||
suggestion.function,
|
||||
suggestion.local
|
||||
)
|
||||
)
|
||||
results <- repo.applyExports(updates)
|
||||
} yield (ids, results)
|
||||
|
||||
val (ids, results) = Await.result(action, Timeout)
|
||||
results should contain theSameElementsAs Seq(
|
||||
QueryResult(ids(0).toSeq, updates(0)),
|
||||
QueryResult(Seq(), updates(1))
|
||||
)
|
||||
}
|
||||
|
||||
"change version after applying exports" taggedAs Retry in withRepo { repo =>
|
||||
val reexport = "Foo.Bar"
|
||||
val method = suggestion.method.copy(reexport = Some(reexport))
|
||||
val updates = Seq(
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
reexport,
|
||||
Set(ExportedSymbol.Module(suggestion.module.module))
|
||||
),
|
||||
Api.ExportsAction.Add()
|
||||
),
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
reexport,
|
||||
Set(ExportedSymbol.Method(method.module, method.name))
|
||||
),
|
||||
Api.ExportsAction.Remove()
|
||||
)
|
||||
)
|
||||
val action = for {
|
||||
_ <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
method,
|
||||
suggestion.function,
|
||||
suggestion.local
|
||||
)
|
||||
)
|
||||
v1 <- repo.currentVersion
|
||||
results <- repo.applyExports(updates)
|
||||
v2 <- repo.currentVersion
|
||||
} yield (results, v1, v2)
|
||||
|
||||
val (results, v1, v2) = Await.result(action, Timeout)
|
||||
results.flatMap(_.ids).isEmpty shouldBe false
|
||||
v1 should not equal v2
|
||||
}
|
||||
|
||||
"not change version when exports not applied" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val reexport = "Foo.Bar"
|
||||
val updates = Seq(
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
reexport,
|
||||
Set(
|
||||
ExportedSymbol
|
||||
.Method(suggestion.method.module, suggestion.method.name)
|
||||
)
|
||||
),
|
||||
Api.ExportsAction.Remove()
|
||||
)
|
||||
)
|
||||
val action = for {
|
||||
_ <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.module,
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
suggestion.local
|
||||
)
|
||||
)
|
||||
v1 <- repo.currentVersion
|
||||
results <- repo.applyExports(updates)
|
||||
v2 <- repo.currentVersion
|
||||
} yield (results, v1, v2)
|
||||
|
||||
val (results, v1, v2) = Await.result(action, Timeout)
|
||||
results.flatMap(_.ids).isEmpty shouldBe true
|
||||
v1 shouldEqual v2
|
||||
}
|
||||
|
||||
"search suggestion by empty query" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -947,25 +1286,27 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
|
||||
"search suggestion by module" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
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)
|
||||
res <- repo.search(Some("Test.Main"), Seq(), None, None, None)
|
||||
} yield (id1, id2, id3, id4, res._2)
|
||||
} yield (id0, id1, id2, id3, id4, res._2)
|
||||
|
||||
val (id1, id2, id3, id4, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id1, id2, id3, id4).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 empty module" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.atom)
|
||||
id2 <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(Some(""), Seq(), None, None, None)
|
||||
} yield (res._2, Seq(id1, id2))
|
||||
} yield (res._2, Seq(id0, id1, id2))
|
||||
|
||||
val (res, globals) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs globals.flatten
|
||||
@ -973,6 +1314,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
|
||||
"search suggestion by self type" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
id2 <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -986,6 +1328,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
|
||||
"search suggestion by return type" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
id3 <- repo.insert(suggestion.function)
|
||||
@ -1000,6 +1343,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
"search suggestion by kind" taggedAs Retry in withRepo { repo =>
|
||||
val kinds = Seq(Suggestion.Kind.Atom, Suggestion.Kind.Local)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1013,6 +1357,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
|
||||
"search suggestion by empty kinds" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1026,6 +1371,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
|
||||
"search suggestion global by scope" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.atom)
|
||||
id2 <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1038,29 +1384,31 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
None,
|
||||
Some(Suggestion.Position(99, 42))
|
||||
)
|
||||
} yield (id1, id2, res._2)
|
||||
} yield (id0, id1, id2, res._2)
|
||||
|
||||
val (id1, id2, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id1, id2).flatten
|
||||
val (id0, id1, id2, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id0, id1, id2).flatten
|
||||
}
|
||||
|
||||
"search suggestion local by scope" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.atom)
|
||||
id2 <- repo.insert(suggestion.method)
|
||||
id3 <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <-
|
||||
repo.search(None, Seq(), None, None, Some(Suggestion.Position(1, 5)))
|
||||
} yield (id1, id2, id3, res._2)
|
||||
} yield (id0, id1, id2, id3, res._2)
|
||||
|
||||
val (id1, id2, id3, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id1, id2, id3).flatten
|
||||
val (id0, id1, id2, id3, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id0, id1, id2, id3).flatten
|
||||
}
|
||||
|
||||
"search suggestion by module and self type" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
id2 <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1082,6 +1430,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
repo =>
|
||||
val kinds = Seq(Suggestion.Kind.Atom, Suggestion.Kind.Local)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1102,6 +1451,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
"search suggestion by return type and scope" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1122,6 +1472,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
"search suggestion by kind and scope" taggedAs Retry in withRepo { repo =>
|
||||
val kinds = Seq(Suggestion.Kind.Atom, Suggestion.Kind.Local)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1142,6 +1493,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
"search suggestion by self and return types" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1163,6 +1515,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
repo =>
|
||||
val kinds = Seq(Suggestion.Kind.Atom, Suggestion.Kind.Local)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1184,6 +1537,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
repo =>
|
||||
val kinds = Seq(Suggestion.Kind.Atom, Suggestion.Kind.Local)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1208,6 +1562,7 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
Suggestion.Kind.Function
|
||||
)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
@ -1228,6 +1583,14 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
|
||||
object suggestion {
|
||||
|
||||
val module: Suggestion.Module =
|
||||
Suggestion.Module(
|
||||
module = "Test.Main",
|
||||
documentation = Some("This is a main module."),
|
||||
documentationHtml = Some("<p>This is a main module.</p>"),
|
||||
reexport = None
|
||||
)
|
||||
|
||||
val atom: Suggestion.Atom =
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1239,7 +1602,8 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
),
|
||||
returnType = "Pair",
|
||||
documentation = Some("Awesome"),
|
||||
documentationHtml = Some("")
|
||||
documentationHtml = Some("<p>Awesome</p>"),
|
||||
reexport = None
|
||||
)
|
||||
|
||||
val method: Suggestion.Method =
|
||||
@ -1251,7 +1615,8 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
selfType = "Test.Main",
|
||||
returnType = "IO",
|
||||
documentation = None,
|
||||
documentationHtml = None
|
||||
documentationHtml = None,
|
||||
reexport = None
|
||||
)
|
||||
|
||||
val function: Suggestion.Function =
|
||||
|
@ -1,3 +1,3 @@
|
||||
C03EC922F039EB5CC96F93A489453ADDD4FA6EBBF75713B05FE67B48CFF6ACBF
|
||||
5C2BC7B32FC933355013753E17DC0D4E66F826053D34BD6CDB22401513A17E34
|
||||
61713B297871FFB55B9BDF492049F90A7756BBEDF91DA31B72F0EFB98221754D
|
||||
C309ED3582802FAA0EA238BB40A24F34CACC4FC4A36334C260232FF49DCD9C72
|
||||
0
|
||||
|
@ -1,3 +1,3 @@
|
||||
2D9E29668392299BAE4C1D0D0D7562556712E78CF8FA1022853E42C9E4C05FA1
|
||||
28E1F445EA0515C2F3E73ABD9DDB73395B5E0F7A84D07038A1117B782E32A1C6
|
||||
0F4AF9C887470D371F850DF94EF5155B3872A8537D0B753FE5ADD94171DFCC35
|
||||
0
|
||||
|
@ -1,3 +1,3 @@
|
||||
0AED341E22D16C5722BF722FD5F92039464E9FE3CCD3288D3643F05E09AF62FB
|
||||
378F1F6DE5E96E55648F0B31C20AF99F1C9190926BE6429B0FC8BE7CAF9E6E47
|
||||
E4B0E3E0602F9227B8D9334913B2D903C652DADA95732D0F55E26B469E82B89C
|
||||
0
|
||||
|
Loading…
Reference in New Issue
Block a user