mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 13:41:39 +03:00
Fix the Suggestions Database Updates Handling (#1161)
Misc updates to the Suggestions database updates handling algorithm
This commit is contained in:
parent
8e07e0347f
commit
3d65ffd3cd
@ -90,11 +90,7 @@ final class SuggestionsHandler(
|
||||
context.system.eventStream
|
||||
.subscribe(self, classOf[Api.ExpressionValuesComputed])
|
||||
context.system.eventStream
|
||||
.subscribe(self, classOf[Api.SuggestionsDatabaseUpdateNotification])
|
||||
context.system.eventStream
|
||||
.subscribe(self, classOf[Api.SuggestionsDatabaseReIndexNotification])
|
||||
context.system.eventStream
|
||||
.subscribe(self, classOf[Api.SuggestionsDatabaseIndexUpdateNotification])
|
||||
.subscribe(self, classOf[Api.SuggestionsDatabaseModuleUpdateNotification])
|
||||
context.system.eventStream.subscribe(self, classOf[ProjectNameChangedEvent])
|
||||
context.system.eventStream.subscribe(self, classOf[FileDeletedEvent])
|
||||
context.system.eventStream
|
||||
@ -143,24 +139,7 @@ final class SuggestionsHandler(
|
||||
sender() ! CapabilityReleased
|
||||
context.become(initialized(projectName, clients - client.clientId))
|
||||
|
||||
case msg: Api.SuggestionsDatabaseIndexUpdateNotification =>
|
||||
applyIndexedModuleUpdates(msg.updates.toSeq)
|
||||
.onComplete {
|
||||
case Success(notification) =>
|
||||
if (notification.updates.nonEmpty) {
|
||||
clients.foreach { clientId =>
|
||||
sessionRouter ! DeliverToJsonController(clientId, notification)
|
||||
}
|
||||
}
|
||||
case Failure(ex) =>
|
||||
log.error(
|
||||
ex,
|
||||
"Error applying suggestion database updates: {}",
|
||||
msg.updates.map(_.file)
|
||||
)
|
||||
}
|
||||
|
||||
case msg: Api.SuggestionsDatabaseUpdateNotification =>
|
||||
case msg: Api.SuggestionsDatabaseModuleUpdateNotification =>
|
||||
applyDatabaseUpdates(msg)
|
||||
.onComplete {
|
||||
case Success(notification) =>
|
||||
@ -173,25 +152,7 @@ final class SuggestionsHandler(
|
||||
log.error(
|
||||
ex,
|
||||
"Error applying suggestion database updates: {}",
|
||||
msg.updates
|
||||
)
|
||||
}
|
||||
|
||||
case msg: Api.SuggestionsDatabaseReIndexNotification =>
|
||||
log.debug(s"ReIndex ${msg.moduleName} ${msg.updates.map(_.suggestion)}")
|
||||
applyReIndexUpdates(msg.updates)
|
||||
.onComplete {
|
||||
case Success(notification) =>
|
||||
if (notification.updates.nonEmpty) {
|
||||
clients.foreach { clientId =>
|
||||
sessionRouter ! DeliverToJsonController(clientId, notification)
|
||||
}
|
||||
}
|
||||
case Failure(ex) =>
|
||||
log.error(
|
||||
ex,
|
||||
"Error applying suggestion re-index updates: {}",
|
||||
msg.updates
|
||||
msg.file
|
||||
)
|
||||
}
|
||||
|
||||
@ -211,6 +172,20 @@ final class SuggestionsHandler(
|
||||
}
|
||||
SuggestionsDatabaseUpdateNotification(version, updates)
|
||||
}
|
||||
.onComplete {
|
||||
case Success(notification) =>
|
||||
if (notification.updates.nonEmpty) {
|
||||
clients.foreach { clientId =>
|
||||
sessionRouter ! DeliverToJsonController(clientId, notification)
|
||||
}
|
||||
}
|
||||
case Failure(ex) =>
|
||||
log.error(
|
||||
ex,
|
||||
"Error applying changes from computed values: {}",
|
||||
updates
|
||||
)
|
||||
}
|
||||
|
||||
case GetSuggestionsDatabaseVersion =>
|
||||
suggestionsRepo.currentVersion
|
||||
@ -314,68 +289,6 @@ final class SuggestionsHandler(
|
||||
}
|
||||
}
|
||||
|
||||
private def applyIndexedModuleUpdates(
|
||||
updates: Seq[Api.IndexedModule]
|
||||
): Future[SuggestionsDatabaseUpdateNotification] = {
|
||||
def createIndexedModuleUpdatesBatch(
|
||||
contents: String,
|
||||
file: java.io.File,
|
||||
updates: Seq[Api.SuggestionsDatabaseUpdate.Add]
|
||||
): Future[Seq[Api.SuggestionsDatabaseUpdate.Add]] =
|
||||
fileVersionsRepo
|
||||
.updateVersion(file, versionCalculator.evalDigest(contents))
|
||||
.map(versionChanged => if (versionChanged) updates else Seq())
|
||||
def getBatches =
|
||||
Future
|
||||
.traverse(updates) { indexed =>
|
||||
createIndexedModuleUpdatesBatch(
|
||||
indexed.contents,
|
||||
indexed.file,
|
||||
indexed.updates
|
||||
)
|
||||
}
|
||||
.map(_.flatten)
|
||||
for {
|
||||
batch <- getBatches
|
||||
update <- applyReIndexUpdates(batch)
|
||||
} yield update
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the suggestions database re-index update.
|
||||
*
|
||||
* Function clears existing module suggestions from the database, inserts new
|
||||
* suggestions and builds the notification containing combined removed and
|
||||
* added suggestions.
|
||||
*
|
||||
* @param updates the list of updates after the full module re-index
|
||||
* @return the API suggestions database update notification
|
||||
*/
|
||||
private def applyReIndexUpdates(
|
||||
updates: Seq[Api.SuggestionsDatabaseUpdate.Add]
|
||||
): Future[SuggestionsDatabaseUpdateNotification] = {
|
||||
val added = updates.map(_.suggestion)
|
||||
val modules = updates.map(_.suggestion.module).distinct
|
||||
log.debug(s"Applying re-index updates; modules=$modules")
|
||||
for {
|
||||
(_, removedIds) <- suggestionsRepo.removeAllByModule(modules)
|
||||
(version, addedIds) <- suggestionsRepo.insertAll(added)
|
||||
} yield {
|
||||
val updatesRemoved = removedIds.map(SuggestionsDatabaseUpdate.Remove)
|
||||
val updatesAdded = (addedIds zip added).flatMap {
|
||||
case (Some(id), suggestion) =>
|
||||
Some(SuggestionsDatabaseUpdate.Add(id, suggestion))
|
||||
case (None, suggestion) =>
|
||||
log.error("Failed to insert re-index suggestion: {}", suggestion)
|
||||
None
|
||||
}
|
||||
SuggestionsDatabaseUpdateNotification(
|
||||
version,
|
||||
updatesRemoved :++ updatesAdded
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the suggestions database update.
|
||||
*
|
||||
@ -386,28 +299,42 @@ final class SuggestionsHandler(
|
||||
* @return the API suggestions database update notification
|
||||
*/
|
||||
private def applyDatabaseUpdates(
|
||||
msg: Api.SuggestionsDatabaseUpdateNotification
|
||||
msg: Api.SuggestionsDatabaseModuleUpdateNotification
|
||||
): Future[SuggestionsDatabaseUpdateNotification] = {
|
||||
val (added, removed) = msg.updates
|
||||
.foldLeft((Seq[Suggestion](), Seq[Suggestion]())) {
|
||||
case ((add, remove), msg: Api.SuggestionsDatabaseUpdate.Add) =>
|
||||
(add :+ msg.suggestion, remove)
|
||||
case ((add, remove), msg: Api.SuggestionsDatabaseUpdate.Remove) =>
|
||||
(add, remove :+ msg.suggestion)
|
||||
val (addCmds, removeCmds, cleanCmds) = msg.updates
|
||||
.foldLeft(
|
||||
(Vector[Suggestion](), Vector[Suggestion](), Vector[String]())
|
||||
) {
|
||||
case ((add, remove, clean), m: Api.SuggestionsDatabaseUpdate.Add) =>
|
||||
(add :+ m.suggestion, remove, clean)
|
||||
case ((add, remove, clean), m: Api.SuggestionsDatabaseUpdate.Remove) =>
|
||||
(add, remove :+ m.suggestion, clean)
|
||||
case ((add, remove, clean), m: Api.SuggestionsDatabaseUpdate.Clean) =>
|
||||
(add, remove, clean :+ m.module)
|
||||
}
|
||||
val fileVersion = versionCalculator.evalDigest(msg.contents)
|
||||
log.debug(
|
||||
s"Applying suggestion updates; added=${added
|
||||
.map(_.name)}; removed=${removed.map(_.name)}"
|
||||
s"Applying suggestion updates: Add(${addCmds.map(_.name).mkString(",")}); Remove(${removeCmds
|
||||
.map(_.name)
|
||||
.mkString(",")}); Clean(${cleanCmds.mkString(",")})"
|
||||
)
|
||||
for {
|
||||
(_, removedIds) <- suggestionsRepo.removeAll(removed)
|
||||
(version, addedIds) <- suggestionsRepo.insertAll(added)
|
||||
(_, cleanedIds) <- suggestionsRepo.removeAllByModule(cleanCmds)
|
||||
(_, removedIds) <- suggestionsRepo.removeAll(removeCmds)
|
||||
(version, addedIds) <- suggestionsRepo.insertAll(addCmds)
|
||||
_ <- fileVersionsRepo.setVersion(msg.file, fileVersion)
|
||||
} yield {
|
||||
val updatesRemoved = removedIds.collect {
|
||||
case Some(id) => SuggestionsDatabaseUpdate.Remove(id)
|
||||
}
|
||||
val updatesCleaned = cleanedIds.map(SuggestionsDatabaseUpdate.Remove)
|
||||
val updatesRemoved =
|
||||
(removedIds zip removeCmds).flatMap {
|
||||
case (Some(id), _) =>
|
||||
Some(SuggestionsDatabaseUpdate.Remove(id))
|
||||
case (None, suggestion) =>
|
||||
log.error("Failed to remove suggestion: {}", suggestion)
|
||||
None
|
||||
}
|
||||
val updatesAdded =
|
||||
(addedIds zip added).flatMap {
|
||||
(addedIds zip addCmds).flatMap {
|
||||
case (Some(id), suggestion) =>
|
||||
Some(SuggestionsDatabaseUpdate.Add(id, suggestion))
|
||||
case (None, suggestion) =>
|
||||
@ -416,7 +343,7 @@ final class SuggestionsHandler(
|
||||
}
|
||||
SuggestionsDatabaseUpdateNotification(
|
||||
version,
|
||||
updatesRemoved :++ updatesAdded
|
||||
updatesCleaned :++ updatesRemoved :++ updatesAdded
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,9 @@ class SuggestionsHandlerSpec
|
||||
expectMsg(CapabilityAcquired)
|
||||
|
||||
// receive updates
|
||||
handler ! Api.SuggestionsDatabaseUpdateNotification(
|
||||
handler ! Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File("/tmp/foo"),
|
||||
"",
|
||||
Suggestions.all.map(Api.SuggestionsDatabaseUpdate.Add)
|
||||
)
|
||||
|
||||
@ -105,7 +107,9 @@ class SuggestionsHandlerSpec
|
||||
expectMsg(CapabilityAcquired)
|
||||
|
||||
// receive updates
|
||||
handler ! Api.SuggestionsDatabaseUpdateNotification(
|
||||
handler ! Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File("/tmp/foo"),
|
||||
"",
|
||||
Suggestions.all.map(Api.SuggestionsDatabaseUpdate.Add) ++
|
||||
Suggestions.all.map(Api.SuggestionsDatabaseUpdate.Remove)
|
||||
)
|
||||
@ -178,7 +182,7 @@ class SuggestionsHandlerSpec
|
||||
|
||||
"search entries by empty search query" taggedAs Retry in withDb {
|
||||
(config, repo, _, _, handler) =>
|
||||
Await.ready(repo.insertAll(Suggestions.all), Timeout)
|
||||
val (_, inserted) = Await.result(repo.insertAll(Suggestions.all), Timeout)
|
||||
handler ! SearchProtocol.Completion(
|
||||
file = mkModulePath(config, "Foo", "Main.enso"),
|
||||
position = Position(0, 0),
|
||||
@ -187,7 +191,12 @@ class SuggestionsHandlerSpec
|
||||
tags = None
|
||||
)
|
||||
|
||||
expectMsg(SearchProtocol.CompletionResult(4L, Seq()))
|
||||
expectMsg(
|
||||
SearchProtocol.CompletionResult(
|
||||
4L,
|
||||
Seq(inserted(0).get, inserted(1).get)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"search entries by self type" taggedAs Retry in withDb {
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.enso.languageserver.websocket.json
|
||||
|
||||
import java.io.File
|
||||
|
||||
import io.circe.literal._
|
||||
import org.enso.languageserver.refactoring.ProjectNameChangedEvent
|
||||
import org.enso.languageserver.search.Suggestions
|
||||
@ -20,7 +22,9 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
|
||||
// add atom
|
||||
system.eventStream.publish(
|
||||
Api.SuggestionsDatabaseUpdateNotification(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File("/tmp/foo"),
|
||||
"",
|
||||
Seq(Api.SuggestionsDatabaseUpdate.Add(Suggestions.atom))
|
||||
)
|
||||
)
|
||||
@ -56,7 +60,9 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
|
||||
// add method
|
||||
system.eventStream.publish(
|
||||
Api.SuggestionsDatabaseUpdateNotification(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File("/tmp/foo"),
|
||||
"",
|
||||
Seq(Api.SuggestionsDatabaseUpdate.Add(Suggestions.method))
|
||||
)
|
||||
)
|
||||
@ -102,7 +108,9 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
|
||||
// add function
|
||||
system.eventStream.publish(
|
||||
Api.SuggestionsDatabaseUpdateNotification(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File("/tmp/foo"),
|
||||
"",
|
||||
Seq(Api.SuggestionsDatabaseUpdate.Add(Suggestions.function))
|
||||
)
|
||||
)
|
||||
@ -142,7 +150,9 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
|
||||
// add local
|
||||
system.eventStream.publish(
|
||||
Api.SuggestionsDatabaseUpdateNotification(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File("/tmp/foo"),
|
||||
"",
|
||||
Seq(Api.SuggestionsDatabaseUpdate.Add(Suggestions.local))
|
||||
)
|
||||
)
|
||||
@ -281,7 +291,9 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
|
||||
// remove items
|
||||
system.eventStream.publish(
|
||||
Api.SuggestionsDatabaseUpdateNotification(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File("/tmp/foo"),
|
||||
"",
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Remove(Suggestions.method),
|
||||
Api.SuggestionsDatabaseUpdate.Remove(Suggestions.function)
|
||||
|
@ -28,8 +28,10 @@ import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo}
|
||||
)
|
||||
sealed trait Suggestion {
|
||||
|
||||
def name: String
|
||||
def externalId: Option[Suggestion.ExternalId]
|
||||
def module: String
|
||||
def name: String
|
||||
def returnType: String
|
||||
}
|
||||
|
||||
object Suggestion {
|
||||
|
@ -165,12 +165,8 @@ object Runtime {
|
||||
name = "runtimeServerShutDown"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Api.SuggestionsDatabaseUpdateNotification],
|
||||
name = "suggestionsDatabaseUpdateNotification"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Api.SuggestionsDatabaseReIndexNotification],
|
||||
name = "suggestionsDatabaseReindexNotification"
|
||||
value = classOf[Api.SuggestionsDatabaseModuleUpdateNotification],
|
||||
name = "suggestionsDatabaseModuleUpdateNotification"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Api.InvalidateModulesIndexRequest],
|
||||
@ -179,10 +175,6 @@ object Runtime {
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Api.InvalidateModulesIndexResponse],
|
||||
name = "invalidateModulesIndexResponse"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Api.SuggestionsDatabaseIndexUpdateNotification],
|
||||
name = "suggestionsDatabaseIndexUpdateNotification"
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -343,24 +335,37 @@ object Runtime {
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[SuggestionsDatabaseUpdate.Remove],
|
||||
name = "suggestionsDatabaseUpdateRemove"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[SuggestionsDatabaseUpdate.Clean],
|
||||
name = "suggestionsDatabaseUpdateClean"
|
||||
)
|
||||
)
|
||||
)
|
||||
sealed trait SuggestionsDatabaseUpdate
|
||||
object SuggestionsDatabaseUpdate {
|
||||
|
||||
/** Create or replace the database entry.
|
||||
/**
|
||||
* Create or replace the database entry.
|
||||
*
|
||||
* @param suggestion the new suggestion
|
||||
*/
|
||||
case class Add(suggestion: Suggestion) extends SuggestionsDatabaseUpdate
|
||||
|
||||
/** Remove the database entry.
|
||||
/**
|
||||
* Remove the database entry.
|
||||
*
|
||||
* @param suggestion the suggestion to remove
|
||||
*/
|
||||
case class Remove(suggestion: Suggestion)
|
||||
extends SuggestionsDatabaseUpdate
|
||||
|
||||
/**
|
||||
* Remove all module entries from the database.
|
||||
*
|
||||
* @param module the module name
|
||||
*/
|
||||
case class Clean(module: String) extends SuggestionsDatabaseUpdate
|
||||
}
|
||||
|
||||
/**
|
||||
@ -711,53 +716,24 @@ object Runtime {
|
||||
case class ProjectRenamed(newName: String) extends ApiResponse
|
||||
|
||||
/**
|
||||
* A notification about the change in the suggestions database.
|
||||
* A notification about the changes in the suggestions database.
|
||||
*
|
||||
* @param updates the list of database updates
|
||||
* @param file the module file path
|
||||
* @param contents the module source
|
||||
* @param updates the list of suggestions extracted from module
|
||||
*/
|
||||
case class SuggestionsDatabaseUpdateNotification(
|
||||
case class SuggestionsDatabaseModuleUpdateNotification(
|
||||
file: File,
|
||||
contents: String,
|
||||
updates: Seq[SuggestionsDatabaseUpdate]
|
||||
) extends ApiNotification
|
||||
|
||||
/**
|
||||
* A notification about the re-indexed module updates.
|
||||
*
|
||||
* @param moduleName the name of re-indexed module
|
||||
* @param updates the list of database updates
|
||||
*/
|
||||
case class SuggestionsDatabaseReIndexNotification(
|
||||
moduleName: String,
|
||||
updates: Seq[SuggestionsDatabaseUpdate.Add]
|
||||
) extends ApiNotification
|
||||
|
||||
/** A request to invalidate the indexed flag of the modules. */
|
||||
case class InvalidateModulesIndexRequest() extends ApiRequest
|
||||
|
||||
/** Signals that the module indexes has been invalidated. */
|
||||
case class InvalidateModulesIndexResponse() extends ApiResponse
|
||||
|
||||
/**
|
||||
* An indexed module.
|
||||
*
|
||||
* @param file the module file path
|
||||
* @param contents the module source
|
||||
* @param updates the list of suggestions extracted from module
|
||||
*/
|
||||
case class IndexedModule(
|
||||
file: File,
|
||||
contents: String,
|
||||
updates: Seq[SuggestionsDatabaseUpdate.Add]
|
||||
) extends ApiNotification
|
||||
|
||||
/**
|
||||
* A notification about new indexed modules.
|
||||
*
|
||||
* @param updates the list of suggestions database updates
|
||||
*/
|
||||
case class SuggestionsDatabaseIndexUpdateNotification(
|
||||
updates: Iterable[IndexedModule]
|
||||
) extends ApiNotification
|
||||
|
||||
private lazy val mapper = {
|
||||
val factory = new CBORFactory()
|
||||
val mapper = new ObjectMapper(factory) with ScalaObjectMapper
|
||||
|
@ -92,33 +92,40 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
ctx.executionService.getContext.getTopScope.getModules.asScala
|
||||
ctx.executionService.getLogger
|
||||
.finest(s"Modules in scope: ${modulesInScope.map(_.getName)}")
|
||||
val updates = modulesInScope.flatMap { module =>
|
||||
modulesInScope.foreach { module =>
|
||||
compile(module)
|
||||
analyzeModuleInScope(module)
|
||||
}
|
||||
sendIndexUpdateNotification(
|
||||
Api.SuggestionsDatabaseIndexUpdateNotification(updates)
|
||||
)
|
||||
}
|
||||
|
||||
private def analyzeImport(
|
||||
module: Module
|
||||
)(implicit ctx: RuntimeContext): Unit = {
|
||||
if (!module.isIndexed && module.getLiteralSource != null) {
|
||||
if (
|
||||
!module.isIndexed &&
|
||||
module.getLiteralSource != null &&
|
||||
module.getPath != null
|
||||
) {
|
||||
ctx.executionService.getLogger
|
||||
.finest(s"Analyzing imported ${module.getName}")
|
||||
val moduleName = module.getName.toString
|
||||
val addedSuggestions = SuggestionBuilder(module.getLiteralSource)
|
||||
.build(module.getName.toString, module.getIr)
|
||||
.filter(isSuggestionGlobal)
|
||||
sendReIndexNotification(moduleName, addedSuggestions)
|
||||
val update = Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File(module.getPath),
|
||||
module.getLiteralSource.toString,
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName) +:
|
||||
addedSuggestions.map(Api.SuggestionsDatabaseUpdate.Add)
|
||||
)
|
||||
sendModuleUpdate(update)
|
||||
module.setIndexed(true)
|
||||
}
|
||||
}
|
||||
|
||||
private def analyzeModuleInScope(module: Module)(implicit
|
||||
ctx: RuntimeContext
|
||||
): Option[Api.IndexedModule] = {
|
||||
): Unit = {
|
||||
try module.getSource
|
||||
catch {
|
||||
case e: IOException =>
|
||||
@ -139,16 +146,14 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
val addedSuggestions = SuggestionBuilder(module.getLiteralSource)
|
||||
.build(moduleName, module.getIr)
|
||||
.filter(isSuggestionGlobal)
|
||||
module.setIndexed(true)
|
||||
Some(
|
||||
Api.IndexedModule(
|
||||
new File(module.getPath),
|
||||
module.getSource.getCharacters.toString,
|
||||
addedSuggestions.map(Api.SuggestionsDatabaseUpdate.Add)
|
||||
)
|
||||
val update = Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File(module.getPath),
|
||||
module.getLiteralSource.toString,
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName) +:
|
||||
addedSuggestions.map(Api.SuggestionsDatabaseUpdate.Add)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
sendModuleUpdate(update)
|
||||
module.setIndexed(true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,17 +170,30 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
val addedSuggestions =
|
||||
SuggestionBuilder(module.getLiteralSource)
|
||||
.build(moduleName, module.getIr)
|
||||
sendSuggestionsUpdateNotification(
|
||||
removedSuggestions diff addedSuggestions,
|
||||
addedSuggestions diff removedSuggestions
|
||||
val update = Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File(module.getPath),
|
||||
module.getLiteralSource.toString,
|
||||
removedSuggestions
|
||||
.diff(addedSuggestions)
|
||||
.map(Api.SuggestionsDatabaseUpdate.Remove) :++
|
||||
addedSuggestions
|
||||
.diff(removedSuggestions)
|
||||
.map(Api.SuggestionsDatabaseUpdate.Add)
|
||||
)
|
||||
sendModuleUpdate(update)
|
||||
} else {
|
||||
ctx.executionService.getLogger
|
||||
.finest(s"Analyzing not-indexed module ${module.getName}")
|
||||
val addedSuggestions =
|
||||
SuggestionBuilder(module.getLiteralSource)
|
||||
.build(moduleName, module.getIr)
|
||||
sendReIndexNotification(moduleName, addedSuggestions)
|
||||
val update = Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
new File(module.getPath),
|
||||
module.getLiteralSource.toString,
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName) +:
|
||||
addedSuggestions.map(Api.SuggestionsDatabaseUpdate.Add)
|
||||
)
|
||||
sendModuleUpdate(update)
|
||||
module.setIndexed(true)
|
||||
}
|
||||
}
|
||||
@ -209,7 +227,7 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
val prevStage = module.getCompilationStage
|
||||
module.compileScope(ctx.executionService.getContext).getModule
|
||||
if (prevStage != module.getCompilationStage) {
|
||||
ctx.executionService.getLogger.finer(
|
||||
ctx.executionService.getLogger.finest(
|
||||
s"Compiled ${module.getName} $prevStage->${module.getCompilationStage}"
|
||||
)
|
||||
}
|
||||
@ -289,58 +307,18 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification about the suggestions database updates.
|
||||
* Send notification about module updates.
|
||||
*
|
||||
* @param removed the list of suggestions to remove
|
||||
* @param added the list of suggestions to add
|
||||
* @param payload the module update
|
||||
* @param ctx the runtime context
|
||||
*/
|
||||
private def sendSuggestionsUpdateNotification(
|
||||
removed: Seq[Suggestion],
|
||||
added: Seq[Suggestion]
|
||||
private def sendModuleUpdate(
|
||||
payload: Api.SuggestionsDatabaseModuleUpdateNotification
|
||||
)(implicit ctx: RuntimeContext): Unit =
|
||||
if (added.nonEmpty || removed.nonEmpty) {
|
||||
ctx.endpoint.sendToClient(
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseUpdateNotification(
|
||||
removed.map(Api.SuggestionsDatabaseUpdate.Remove) :++
|
||||
added.map(Api.SuggestionsDatabaseUpdate.Add)
|
||||
)
|
||||
)
|
||||
)
|
||||
if (payload.updates.nonEmpty) {
|
||||
ctx.endpoint.sendToClient(Api.Response(payload))
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification about the re-indexed module updates.
|
||||
*
|
||||
* @param moduleName the name of re-indexed module
|
||||
* @param added the list of suggestions to add
|
||||
* @param ctx the runtime context
|
||||
*/
|
||||
private def sendReIndexNotification(
|
||||
moduleName: String,
|
||||
added: Seq[Suggestion]
|
||||
)(implicit ctx: RuntimeContext): Unit = {
|
||||
ctx.endpoint.sendToClient(
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
added.map(Api.SuggestionsDatabaseUpdate.Add)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private def sendIndexUpdateNotification(
|
||||
msg: Api.SuggestionsDatabaseIndexUpdateNotification
|
||||
)(implicit
|
||||
ctx: RuntimeContext
|
||||
): Unit = {
|
||||
if (msg.updates.nonEmpty) {
|
||||
ctx.endpoint.sendToClient(Api.Response(msg))
|
||||
}
|
||||
}
|
||||
|
||||
private def isSuggestionGlobal(suggestion: Suggestion): Boolean =
|
||||
suggestion match {
|
||||
case _: Suggestion.Atom => true
|
||||
|
@ -454,9 +454,11 @@ class RuntimeServerTest
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
contents,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
@ -557,9 +559,11 @@ class RuntimeServerTest
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
contents,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
@ -660,9 +664,11 @@ class RuntimeServerTest
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
contents,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
Some(idMain),
|
||||
@ -761,9 +767,11 @@ class RuntimeServerTest
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
contents,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
Some(idMain),
|
||||
@ -862,9 +870,11 @@ class RuntimeServerTest
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
contents,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
@ -946,9 +956,11 @@ class RuntimeServerTest
|
||||
context.Main.Update.mainZ(contextId),
|
||||
idMainUpdate,
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
contents,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
Some(idMain),
|
||||
@ -1136,9 +1148,11 @@ class RuntimeServerTest
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
contents,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
Some(idMain),
|
||||
@ -1270,9 +1284,11 @@ class RuntimeServerTest
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
contents,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
Some(idMain),
|
||||
@ -1621,9 +1637,11 @@ class RuntimeServerTest
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
contents,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
Some(idMain),
|
||||
@ -1877,6 +1895,7 @@ class RuntimeServerTest
|
||||
Api.Response(requestId, Api.CreateContextResponse(contextId))
|
||||
)
|
||||
|
||||
val moduleName = "Test.Main"
|
||||
val code =
|
||||
"""from Builtins import all
|
||||
|
|
||||
@ -1899,7 +1918,7 @@ class RuntimeServerTest
|
||||
contextId,
|
||||
Api.StackItem
|
||||
.ExplicitCall(
|
||||
Api.MethodPointer("Test.Main", "Main", "main"),
|
||||
Api.MethodPointer(moduleName, "Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
@ -1909,9 +1928,11 @@ class RuntimeServerTest
|
||||
context.receive(3) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
"Test.Main",
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
code,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
@ -1953,11 +1974,12 @@ class RuntimeServerTest
|
||||
}
|
||||
|
||||
it should "support file modification operations with attached ids" in {
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val metadata = new Metadata
|
||||
val idMain = metadata.addItem(7, 2)
|
||||
val code = metadata.appendToCode("main = 84")
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val moduleName = "Test.Main"
|
||||
val metadata = new Metadata
|
||||
val idMain = metadata.addItem(7, 2)
|
||||
val code = metadata.appendToCode("main = 84")
|
||||
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
context.receive shouldEqual Some(
|
||||
@ -1987,7 +2009,7 @@ class RuntimeServerTest
|
||||
contextId,
|
||||
Api.StackItem
|
||||
.ExplicitCall(
|
||||
Api.MethodPointer("Test.Main", "Main", "main"),
|
||||
Api.MethodPointer(moduleName, "Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
@ -2005,9 +2027,11 @@ class RuntimeServerTest
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
"Test.Main",
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
code,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
Some(idMain),
|
||||
@ -2043,9 +2067,10 @@ class RuntimeServerTest
|
||||
}
|
||||
|
||||
it should "send suggestion notifications when file is executed" in {
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val idMain = context.Main.metadata.addItem(33, 47)
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val moduleName = "Test.Main"
|
||||
val idMain = context.Main.metadata.addItem(33, 47)
|
||||
val idMainUpdate =
|
||||
Api.Response(
|
||||
Api.ExpressionValuesComputed(
|
||||
@ -2076,7 +2101,7 @@ class RuntimeServerTest
|
||||
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer("Test.Main", "Main", "main"),
|
||||
Api.MethodPointer(moduleName, "Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
@ -2090,13 +2115,15 @@ class RuntimeServerTest
|
||||
context.Main.Update.mainZ(contextId),
|
||||
idMainUpdate,
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
"Test.Main",
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
context.Main.code,
|
||||
List(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
Some(idMain),
|
||||
"Test.Main",
|
||||
moduleName,
|
||||
"main",
|
||||
List(Suggestion.Argument("this", "Any", false, false, None)),
|
||||
"Main",
|
||||
@ -2107,7 +2134,7 @@ class RuntimeServerTest
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Main",
|
||||
moduleName,
|
||||
"foo",
|
||||
List(
|
||||
Suggestion.Argument("this", "Any", false, false, None),
|
||||
@ -2121,7 +2148,7 @@ class RuntimeServerTest
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Local(
|
||||
Some(context.Main.idMainX),
|
||||
"Test.Main",
|
||||
moduleName,
|
||||
"x",
|
||||
"Any",
|
||||
Suggestion
|
||||
@ -2131,7 +2158,7 @@ class RuntimeServerTest
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Local(
|
||||
Some(context.Main.idMainY),
|
||||
"Test.Main",
|
||||
moduleName,
|
||||
"y",
|
||||
"Any",
|
||||
Suggestion
|
||||
@ -2141,7 +2168,7 @@ class RuntimeServerTest
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Local(
|
||||
Some(context.Main.idMainZ),
|
||||
"Test.Main",
|
||||
moduleName,
|
||||
"z",
|
||||
"Any",
|
||||
Suggestion
|
||||
@ -2151,7 +2178,7 @@ class RuntimeServerTest
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Local(
|
||||
Some(context.Main.idFooY),
|
||||
"Test.Main",
|
||||
moduleName,
|
||||
"y",
|
||||
"Any",
|
||||
Suggestion
|
||||
@ -2161,7 +2188,7 @@ class RuntimeServerTest
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Local(
|
||||
Some(context.Main.idFooZ),
|
||||
"Test.Main",
|
||||
moduleName,
|
||||
"z",
|
||||
"Any",
|
||||
Suggestion
|
||||
@ -2219,8 +2246,10 @@ class RuntimeServerTest
|
||||
}
|
||||
|
||||
it should "send suggestion notifications when file is modified" in {
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val moduleName = "Test.Main"
|
||||
val newline = System.lineSeparator()
|
||||
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
context.receive shouldEqual Some(
|
||||
@ -2237,15 +2266,7 @@ class RuntimeServerTest
|
||||
val mainFile = context.writeMain(code)
|
||||
|
||||
// Open the new file
|
||||
context.send(
|
||||
Api.Request(
|
||||
Api.OpenFileNotification(
|
||||
mainFile,
|
||||
code,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
context.send(Api.Request(Api.OpenFileNotification(mainFile, code, false)))
|
||||
context.receiveNone shouldEqual None
|
||||
context.consumeOut shouldEqual List()
|
||||
|
||||
@ -2257,7 +2278,7 @@ class RuntimeServerTest
|
||||
contextId,
|
||||
Api.StackItem
|
||||
.ExplicitCall(
|
||||
Api.MethodPointer("Test.Main", "Main", "main"),
|
||||
Api.MethodPointer(moduleName, "Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
@ -2267,13 +2288,15 @@ class RuntimeServerTest
|
||||
context.receive(3) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
"Test.Main",
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
code,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Main",
|
||||
moduleName,
|
||||
"main",
|
||||
Seq(Suggestion.Argument("this", "Any", false, false, None)),
|
||||
"Main",
|
||||
@ -2300,20 +2323,29 @@ class RuntimeServerTest
|
||||
),
|
||||
TextEdit(
|
||||
model.Range(model.Position(2, 0), model.Position(2, 0)),
|
||||
"Number.lucky = 42\n\n"
|
||||
s"Number.lucky = 42$newline$newline"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
val codeModified =
|
||||
"""from Builtins import all
|
||||
|
|
||||
|Number.lucky = 42
|
||||
|
|
||||
|main = IO.println "I'm a modified!"
|
||||
|""".stripMargin
|
||||
context.receive(2) should contain theSameElementsAs Seq(
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseUpdateNotification(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
codeModified,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Main",
|
||||
moduleName,
|
||||
"lucky",
|
||||
Seq(Suggestion.Argument("this", "Any", false, false, None)),
|
||||
"Number",
|
||||
@ -2335,10 +2367,11 @@ class RuntimeServerTest
|
||||
}
|
||||
|
||||
it should "recompute expressions without invalidation" in {
|
||||
val contents = context.Main.code
|
||||
val mainFile = context.writeMain(contents)
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val contents = context.Main.code
|
||||
val mainFile = context.writeMain(contents)
|
||||
val moduleName = "Test.Main"
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
@ -2354,7 +2387,7 @@ class RuntimeServerTest
|
||||
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer("Test.Main", "Main", "main"),
|
||||
Api.MethodPointer(moduleName, "Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
@ -2380,10 +2413,11 @@ class RuntimeServerTest
|
||||
}
|
||||
|
||||
it should "recompute expressions invalidating all" in {
|
||||
val contents = context.Main.code
|
||||
val mainFile = context.writeMain(contents)
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val contents = context.Main.code
|
||||
val mainFile = context.writeMain(contents)
|
||||
val moduleName = "Test.Main"
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
@ -2399,7 +2433,7 @@ class RuntimeServerTest
|
||||
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer("Test.Main", "Main", "main"),
|
||||
Api.MethodPointer(moduleName, "Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
@ -2431,10 +2465,11 @@ class RuntimeServerTest
|
||||
}
|
||||
|
||||
it should "recompute expressions invalidating some" in {
|
||||
val contents = context.Main.code
|
||||
val mainFile = context.writeMain(contents)
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val contents = context.Main.code
|
||||
val mainFile = context.writeMain(contents)
|
||||
val moduleName = "Test.Main"
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
@ -2450,7 +2485,7 @@ class RuntimeServerTest
|
||||
context.receiveNone shouldEqual None
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer("Test.Main", "Main", "main"),
|
||||
Api.MethodPointer(moduleName, "Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
@ -2849,8 +2884,9 @@ class RuntimeServerTest
|
||||
}
|
||||
|
||||
it should "emit visualisation update when expression is evaluated" in {
|
||||
val contents = context.Main.code
|
||||
val mainFile = context.writeMain(context.Main.code)
|
||||
val contents = context.Main.code
|
||||
val mainFile = context.writeMain(context.Main.code)
|
||||
val moduleName = "Test.Main"
|
||||
val visualisationFile =
|
||||
context.writeInSrcDir("Visualisation", context.Visualisation.code)
|
||||
|
||||
@ -2882,7 +2918,7 @@ class RuntimeServerTest
|
||||
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer("Test.Main", "Main", "main"),
|
||||
Api.MethodPointer(moduleName, "Main", "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
@ -2895,40 +2931,37 @@ class RuntimeServerTest
|
||||
context.Main.Update.mainY(contextId),
|
||||
context.Main.Update.mainZ(contextId),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseIndexUpdateNotification(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
visualisationFile,
|
||||
context.Visualisation.code,
|
||||
Seq(
|
||||
Api.IndexedModule(
|
||||
visualisationFile,
|
||||
context.Visualisation.code,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Visualisation",
|
||||
"encode",
|
||||
List(
|
||||
Suggestion.Argument("this", "Any", false, false, None),
|
||||
Suggestion.Argument("x", "Any", false, false, None)
|
||||
),
|
||||
"Visualisation",
|
||||
"Any",
|
||||
None
|
||||
)
|
||||
Api.SuggestionsDatabaseUpdate.Clean("Test.Visualisation"),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Visualisation",
|
||||
"encode",
|
||||
List(
|
||||
Suggestion.Argument("this", "Any", false, false, None),
|
||||
Suggestion.Argument("x", "Any", false, false, None)
|
||||
),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Visualisation",
|
||||
"incAndEncode",
|
||||
List(
|
||||
Suggestion.Argument("this", "Any", false, false, None),
|
||||
Suggestion.Argument("x", "Any", false, false, None)
|
||||
),
|
||||
"Visualisation",
|
||||
"Any",
|
||||
None
|
||||
)
|
||||
)
|
||||
"Visualisation",
|
||||
"Any",
|
||||
None
|
||||
)
|
||||
),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Visualisation",
|
||||
"incAndEncode",
|
||||
List(
|
||||
Suggestion.Argument("this", "Any", false, false, None),
|
||||
Suggestion.Argument("x", "Any", false, false, None)
|
||||
),
|
||||
"Visualisation",
|
||||
"Any",
|
||||
None
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -3066,49 +3099,48 @@ class RuntimeServerTest
|
||||
context.Main.Update.mainY(contextId),
|
||||
context.Main.Update.mainZ(contextId),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseIndexUpdateNotification(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
visualisationFile,
|
||||
context.Visualisation.code,
|
||||
Seq(
|
||||
Api.IndexedModule(
|
||||
visualisationFile,
|
||||
context.Visualisation.code,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Visualisation",
|
||||
"encode",
|
||||
List(
|
||||
Suggestion.Argument("this", "Any", false, false, None),
|
||||
Suggestion.Argument("x", "Any", false, false, None)
|
||||
),
|
||||
"Visualisation",
|
||||
"Any",
|
||||
None
|
||||
)
|
||||
Api.SuggestionsDatabaseUpdate.Clean("Test.Visualisation"),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Visualisation",
|
||||
"encode",
|
||||
List(
|
||||
Suggestion.Argument("this", "Any", false, false, None),
|
||||
Suggestion.Argument("x", "Any", false, false, None)
|
||||
),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Visualisation",
|
||||
"incAndEncode",
|
||||
List(
|
||||
Suggestion.Argument("this", "Any", false, false, None),
|
||||
Suggestion.Argument("x", "Any", false, false, None)
|
||||
),
|
||||
"Visualisation",
|
||||
"Any",
|
||||
None
|
||||
)
|
||||
)
|
||||
"Visualisation",
|
||||
"Any",
|
||||
None
|
||||
)
|
||||
),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
"Test.Visualisation",
|
||||
"incAndEncode",
|
||||
List(
|
||||
Suggestion.Argument("this", "Any", false, false, None),
|
||||
Suggestion.Argument("x", "Any", false, false, None)
|
||||
),
|
||||
"Visualisation",
|
||||
"Any",
|
||||
None
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseReIndexNotification(
|
||||
moduleName,
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
mainFile,
|
||||
contents,
|
||||
Seq(
|
||||
Api.SuggestionsDatabaseUpdate.Clean(moduleName),
|
||||
Api.SuggestionsDatabaseUpdate.Add(
|
||||
Suggestion.Method(
|
||||
None,
|
||||
|
@ -112,6 +112,17 @@ class StdlibRuntimeServerTest
|
||||
Iterator.continually(receive(timeout)).take(n).flatten.toList
|
||||
}
|
||||
|
||||
def receiveAllUntil(
|
||||
msg: Api.Response,
|
||||
timeout: Long
|
||||
): List[Api.Response] = {
|
||||
Iterator
|
||||
.continually(receive(timeout))
|
||||
.takeWhile(received => received.isDefined && received != Some(msg))
|
||||
.flatten
|
||||
.toList
|
||||
}
|
||||
|
||||
def consumeOut: List[String] = {
|
||||
val result = out.toString
|
||||
out.reset()
|
||||
@ -169,22 +180,22 @@ class StdlibRuntimeServerTest
|
||||
)
|
||||
)
|
||||
)
|
||||
val response = context.receiveN(3, timeout = 30)
|
||||
response.length shouldEqual 3
|
||||
response should contain allOf (
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
context.executionSuccessful(contextId)
|
||||
val response =
|
||||
context.receiveAllUntil(
|
||||
context.executionSuccessful(contextId),
|
||||
timeout = 30
|
||||
)
|
||||
response should contain (
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId))
|
||||
)
|
||||
response.collect {
|
||||
val collected = response.collect {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.SuggestionsDatabaseIndexUpdateNotification(xs)
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(_, _, xs)
|
||||
) =>
|
||||
xs.nonEmpty shouldBe true
|
||||
xs.flatMap(
|
||||
_.updates.headOption.map(_.suggestion.module)
|
||||
) should not contain "Test.Main"
|
||||
} should have length 1
|
||||
}
|
||||
collected.nonEmpty shouldBe true
|
||||
|
||||
context.consumeOut shouldEqual List("Hello World!")
|
||||
}
|
||||
|
@ -28,6 +28,12 @@ trait FileVersionsRepo[F[_]] {
|
||||
*/
|
||||
def updateVersion(file: File, digest: Array[Byte]): F[Boolean]
|
||||
|
||||
/** Update the versions in batch.
|
||||
*
|
||||
* @param versions files with corresponding digests
|
||||
*/
|
||||
def updateVersions(versions: Seq[(File, Array[Byte])]): F[Unit]
|
||||
|
||||
/** Remove the version record.
|
||||
*
|
||||
* @param file the file path
|
||||
|
@ -269,6 +269,7 @@ final class SqlSuggestionsRepo(db: SqlDatabase)(implicit ec: ExecutionContext)
|
||||
private def removeQuery(suggestion: Suggestion): DBIO[Option[Long]] = {
|
||||
val (raw, _) = toSuggestionRow(suggestion)
|
||||
val selectQuery = Suggestions
|
||||
.filter(_.module === raw.module)
|
||||
.filter(_.kind === raw.kind)
|
||||
.filter(_.name === raw.name)
|
||||
.filter(_.scopeStartLine === raw.scopeStartLine)
|
||||
@ -306,13 +307,18 @@ final class SqlSuggestionsRepo(db: SqlDatabase)(implicit ec: ExecutionContext)
|
||||
private def removeAllByModuleQuery(
|
||||
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)
|
||||
deleteQuery
|
||||
if (modules.nonEmpty) {
|
||||
val selectQuery = Suggestions.filter(_.module inSet modules)
|
||||
for {
|
||||
rows <- selectQuery.result
|
||||
n <- selectQuery.delete
|
||||
version <- if (n > 0) incrementVersionQuery else currentVersionQuery
|
||||
} yield version -> rows.flatMap(_.id)
|
||||
} else {
|
||||
for {
|
||||
version <- currentVersionQuery
|
||||
} yield (version, Seq())
|
||||
}
|
||||
}
|
||||
|
||||
/** The query to remove a list of suggestions.
|
||||
@ -418,6 +424,9 @@ final class SqlSuggestionsRepo(db: SqlDatabase)(implicit ec: ExecutionContext)
|
||||
}
|
||||
|
||||
/** Create a search query by the provided parameters.
|
||||
*
|
||||
* Even if the module is specified, the response includes all available
|
||||
* global symbols (atoms and method).
|
||||
*
|
||||
* @param module the module name search parameter
|
||||
* @param selfType the selfType search parameter
|
||||
@ -435,7 +444,8 @@ final class SqlSuggestionsRepo(db: SqlDatabase)(implicit ec: ExecutionContext)
|
||||
): Query[SuggestionsTable, SuggestionRow, Seq] = {
|
||||
Suggestions
|
||||
.filterOpt(module) {
|
||||
case (row, value) => row.module === value
|
||||
case (row, value) =>
|
||||
row.scopeStartLine === ScopeColumn.EMPTY || row.module === value
|
||||
}
|
||||
.filterOpt(selfType) {
|
||||
case (row, value) => row.selfType === value
|
||||
|
@ -30,6 +30,12 @@ final class SqlVersionsRepo(db: SqlDatabase)(implicit ec: ExecutionContext)
|
||||
override def updateVersion(file: File, digest: Array[Byte]): Future[Boolean] =
|
||||
db.run(updateVersionQuery(file, digest))
|
||||
|
||||
/** @inheritdoc */
|
||||
override def updateVersions(
|
||||
versions: Seq[(File, Array[Byte])]
|
||||
): Future[Unit] =
|
||||
db.run(updateVersionsQuery(versions))
|
||||
|
||||
/** @inheritdoc */
|
||||
override def remove(file: File): Future[Unit] =
|
||||
db.run(removeQuery(file))
|
||||
@ -77,11 +83,10 @@ final class SqlVersionsRepo(db: SqlDatabase)(implicit ec: ExecutionContext)
|
||||
): DBIO[Option[Array[Byte]]] = {
|
||||
val upsertQuery = FileVersions
|
||||
.insertOrUpdate(FileVersionRow(file.toString, version))
|
||||
val query = for {
|
||||
for {
|
||||
version <- getVersionQuery(file)
|
||||
_ <- upsertQuery
|
||||
} yield version
|
||||
query.transactionally
|
||||
}
|
||||
|
||||
/** The query to update the version if it differs from the recorded version.
|
||||
@ -102,6 +107,23 @@ final class SqlVersionsRepo(db: SqlDatabase)(implicit ec: ExecutionContext)
|
||||
else DBIO.successful(None)
|
||||
} yield !versionsEquals
|
||||
|
||||
|
||||
/** The query to update the versions in batch.
|
||||
*
|
||||
* @param versions files with corresponding digests
|
||||
*/
|
||||
private def updateVersionsQuery(
|
||||
versions: Seq[(File, Array[Byte])]
|
||||
): DBIO[Unit] =
|
||||
if (versions.nonEmpty) {
|
||||
def upsertQuery(file: File, version: Array[Byte]) = FileVersions
|
||||
.insertOrUpdate(FileVersionRow(file.toString, version))
|
||||
DBIO.sequence(versions.map(Function.tupled(upsertQuery))) >>
|
||||
DBIO.successful(())
|
||||
} else {
|
||||
DBIO.successful(())
|
||||
}
|
||||
|
||||
/** The query to remove the version record.
|
||||
*
|
||||
* @param file the file path
|
||||
|
@ -108,6 +108,28 @@ class FileVersionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
b4 shouldBe false
|
||||
}
|
||||
|
||||
"batch update digest" taggedAs Retry in withRepo { repo =>
|
||||
val file1 = new File("/foo/1")
|
||||
val file2 = new File("/foo/2")
|
||||
val digest0 = nextDigest()
|
||||
val digest1 = nextDigest()
|
||||
val digest2 = nextDigest()
|
||||
val input = Seq(file1 -> digest1, file2 -> digest2)
|
||||
val action =
|
||||
for {
|
||||
_ <- repo.setVersion(file1, digest0)
|
||||
_ <- repo.updateVersions(input)
|
||||
v1 <- repo.getVersion(file1)
|
||||
v2 <- repo.getVersion(file2)
|
||||
} yield (v1, v2)
|
||||
|
||||
val (v1, v2) = Await.result(action, Timeout)
|
||||
v1 shouldBe a[Some[_]]
|
||||
v2 shouldBe a[Some[_]]
|
||||
util.Arrays.equals(v1.get, digest1) shouldBe true
|
||||
util.Arrays.equals(v2.get, digest2) shouldBe true
|
||||
}
|
||||
|
||||
"delete digest" taggedAs Retry in withRepo { repo =>
|
||||
val file = new File("/foo/bar")
|
||||
val digest = nextDigest()
|
||||
|
@ -204,6 +204,23 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
inserted should contain theSameElementsAs removed
|
||||
}
|
||||
|
||||
"remove all suggestions by empty module" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
(_, idsIns) <- repo.insertAll(
|
||||
Seq(
|
||||
suggestion.atom,
|
||||
suggestion.method,
|
||||
suggestion.function,
|
||||
suggestion.local
|
||||
)
|
||||
)
|
||||
(_, idsRem) <- repo.removeAllByModule(Seq())
|
||||
} yield (idsIns.flatten, idsRem)
|
||||
|
||||
val (_, removed) = Await.result(action, Timeout)
|
||||
removed.isEmpty shouldBe true
|
||||
}
|
||||
|
||||
"remove all suggestions" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
(_, Seq(id1, _, _, id4)) <- repo.insertAll(
|
||||
@ -492,15 +509,15 @@ class SuggestionsRepoTest extends AnyWordSpec with Matchers with RetrySpec {
|
||||
|
||||
"search suggestion by empty module" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.atom)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
id1 <- repo.insert(suggestion.atom)
|
||||
id2 <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(Some(""), None, None, None, None)
|
||||
} yield res._2
|
||||
} yield (res._2, Seq(id1, id2))
|
||||
|
||||
val res = Await.result(action, Timeout)
|
||||
res.isEmpty shouldEqual true
|
||||
val (res, globals) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs globals.flatten
|
||||
}
|
||||
|
||||
"search suggestion by self type" taggedAs Retry in withRepo { repo =>
|
||||
|
Loading…
Reference in New Issue
Block a user