mirror of
https://github.com/enso-org/enso.git
synced 2024-12-28 14:11:56 +03:00
Set suggestion reexports when serializing the library (#6778)
close #6613 Changelog - feat: during the library serialization, build the exports map and set the reexport field of the suggestion # Important Notes IDE does not create additional imports for re-exported symbols. ![2023-05-18-192739_2019x828_scrot](https://github.com/enso-org/enso/assets/357683/5ef20dfe-d6a5-4935-a759-4af10b0817a5)
This commit is contained in:
parent
029b900335
commit
9ec7415ded
@ -533,6 +533,13 @@ class SuggestionsHandlerSpec
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestions.tpe,
|
||||
Api.SuggestionAction.Add()
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Api.SuggestionUpdate(
|
||||
Suggestions.constructor,
|
||||
@ -598,7 +605,11 @@ class SuggestionsHandlerSpec
|
||||
ExportedSymbol.Module(
|
||||
Suggestions.module.module
|
||||
),
|
||||
ExportedSymbol.Atom(
|
||||
ExportedSymbol.Type(
|
||||
Suggestions.tpe.module,
|
||||
Suggestions.tpe.name
|
||||
),
|
||||
ExportedSymbol.Constructor(
|
||||
Suggestions.constructor.module,
|
||||
Suggestions.constructor.name
|
||||
),
|
||||
@ -618,7 +629,7 @@ class SuggestionsHandlerSpec
|
||||
Tree.Root(Vector())
|
||||
)
|
||||
|
||||
val updates2 = Seq(1L, 2L, 3L).map { id =>
|
||||
val updates2 = Seq(1L, 2L, 3L, 4L).map { id =>
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Modify(
|
||||
id,
|
||||
reexport = Some(fieldUpdate(exportUpdateAdd.exports.module))
|
||||
@ -642,7 +653,11 @@ class SuggestionsHandlerSpec
|
||||
ExportedSymbol.Module(
|
||||
Suggestions.module.module
|
||||
),
|
||||
ExportedSymbol.Atom(
|
||||
ExportedSymbol.Type(
|
||||
Suggestions.tpe.module,
|
||||
Suggestions.tpe.name
|
||||
),
|
||||
ExportedSymbol.Constructor(
|
||||
Suggestions.constructor.module,
|
||||
Suggestions.constructor.name
|
||||
),
|
||||
@ -662,7 +677,7 @@ class SuggestionsHandlerSpec
|
||||
Tree.Root(Vector())
|
||||
)
|
||||
|
||||
val updates3 = Seq(1L, 2L, 3L).map { id =>
|
||||
val updates3 = Seq(1L, 2L, 3L, 4L).map { id =>
|
||||
SearchProtocol.SuggestionsDatabaseUpdate.Modify(
|
||||
id,
|
||||
reexport = Some(fieldRemove)
|
||||
|
@ -500,11 +500,14 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
ModuleExports(
|
||||
"Foo.Bar",
|
||||
ListSet(
|
||||
ExportedSymbol
|
||||
.Atom(
|
||||
Suggestions.constructor.module,
|
||||
Suggestions.constructor.name
|
||||
)
|
||||
ExportedSymbol.Type(
|
||||
Suggestions.tpe.module,
|
||||
Suggestions.tpe.name
|
||||
),
|
||||
ExportedSymbol.Constructor(
|
||||
Suggestions.constructor.module,
|
||||
Suggestions.constructor.name
|
||||
)
|
||||
)
|
||||
),
|
||||
Api.ExportsAction.Add()
|
||||
@ -519,6 +522,14 @@ class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
|
||||
"method" : "search/suggestionsDatabaseUpdates",
|
||||
"params" : {
|
||||
"updates" : [
|
||||
{
|
||||
"type" : "Modify",
|
||||
"id" : 1,
|
||||
"reexport" : {
|
||||
"tag" : "Set",
|
||||
"value" : "Foo.Bar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type" : "Modify",
|
||||
"id" : 2,
|
||||
|
@ -10,8 +10,12 @@ import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo}
|
||||
name = "exportedModule"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExportedSymbol.Atom],
|
||||
name = "exportedAtom"
|
||||
value = classOf[ExportedSymbol.Type],
|
||||
name = "exportedType"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExportedSymbol.Constructor],
|
||||
name = "exportedConstructor"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExportedSymbol.Method],
|
||||
@ -28,26 +32,65 @@ sealed trait ExportedSymbol {
|
||||
}
|
||||
object ExportedSymbol {
|
||||
|
||||
/** Create [[ExportedSymbol]] from [[Suggestion]].
|
||||
*
|
||||
* @param suggestion the suggestion to convert
|
||||
* @return the corresponding [[ExportedSymbol]]
|
||||
*/
|
||||
def fromSuggestion(suggestion: Suggestion): Option[ExportedSymbol] =
|
||||
suggestion match {
|
||||
case s: Suggestion.Module => Some(Module(s.module))
|
||||
case s: Suggestion.Type => Some(Type(s.module, s.name))
|
||||
case s: Suggestion.Constructor => Some(Constructor(s.module, s.name))
|
||||
case s: Suggestion.Method => Some(Method(s.module, s.name))
|
||||
case _: Suggestion.Conversion => None
|
||||
case _: Suggestion.Function => None
|
||||
case _: Suggestion.Local => None
|
||||
}
|
||||
|
||||
/** Create an exported symbol of the suggestion module.
|
||||
*
|
||||
* @param suggestion the suggestion to convert
|
||||
* @return the corresponding [[ExportedSymbol.Module]]
|
||||
*/
|
||||
def suggestionModule(suggestion: Suggestion): ExportedSymbol.Module =
|
||||
ExportedSymbol.Module(suggestion.module)
|
||||
|
||||
/** The module symbol.
|
||||
*
|
||||
* @param module the module name
|
||||
*/
|
||||
case class Module(module: String) extends ExportedSymbol {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def name: String =
|
||||
module
|
||||
|
||||
/** @inheritdoc */
|
||||
override def kind: Suggestion.Kind =
|
||||
Suggestion.Kind.Module
|
||||
}
|
||||
|
||||
/** The atom symbol.
|
||||
/** The type symbol.
|
||||
*
|
||||
* @param module the module defining this atom
|
||||
* @param name the atom name
|
||||
* @param name the type name
|
||||
*/
|
||||
case class Atom(module: String, name: String) extends ExportedSymbol {
|
||||
case class Type(module: String, name: String) extends ExportedSymbol {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def kind: Suggestion.Kind =
|
||||
Suggestion.Kind.Type
|
||||
}
|
||||
|
||||
/** The constructor symbol.
|
||||
*
|
||||
* @param module the module where this constructor is defined
|
||||
* @param name the constructor name
|
||||
*/
|
||||
case class Constructor(module: String, name: String) extends ExportedSymbol {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def kind: Suggestion.Kind =
|
||||
Suggestion.Kind.Constructor
|
||||
}
|
||||
@ -59,6 +102,7 @@ object ExportedSymbol {
|
||||
*/
|
||||
case class Method(module: String, name: String) extends ExportedSymbol {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def kind: Suggestion.Kind =
|
||||
Suggestion.Kind.Method
|
||||
}
|
||||
|
@ -46,6 +46,9 @@ sealed trait Suggestion extends ToLogString {
|
||||
def name: String
|
||||
def returnType: String
|
||||
def documentation: Option[String]
|
||||
|
||||
/** Set the reexport field of the suggestion. */
|
||||
def withReexport(reexport: Option[String]): Suggestion
|
||||
}
|
||||
|
||||
object Suggestion {
|
||||
@ -220,6 +223,10 @@ object Suggestion {
|
||||
override def returnType: String =
|
||||
module
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexport(reexport: Option[String]): Module =
|
||||
copy(reexport = reexport)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
s"Module(module=$module,name=$name,documentation=" +
|
||||
@ -250,6 +257,10 @@ object Suggestion {
|
||||
) extends Suggestion
|
||||
with ToLogString {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexport(reexport: Option[String]): Type =
|
||||
copy(reexport = reexport)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"Type(" +
|
||||
@ -285,6 +296,10 @@ object Suggestion {
|
||||
) extends Suggestion
|
||||
with ToLogString {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexport(reexport: Option[String]): Constructor =
|
||||
copy(reexport = reexport)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"Constructor(" +
|
||||
@ -323,6 +338,10 @@ object Suggestion {
|
||||
) extends Suggestion
|
||||
with ToLogString {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexport(reexport: Option[String]): Method =
|
||||
copy(reexport = reexport)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"Method(" +
|
||||
@ -357,6 +376,10 @@ object Suggestion {
|
||||
reexport: Option[String] = None
|
||||
) extends Suggestion {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexport(reexport: Option[String]): Conversion =
|
||||
copy(reexport = reexport)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def name: String =
|
||||
Kind.Conversion.From
|
||||
@ -394,6 +417,10 @@ object Suggestion {
|
||||
) extends Suggestion
|
||||
with ToLogString {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexport(reexport: Option[String]): Function =
|
||||
this
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"Function(" +
|
||||
@ -425,6 +452,10 @@ object Suggestion {
|
||||
documentation: Option[String]
|
||||
) extends Suggestion {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexport(reexport: Option[String]): Local =
|
||||
this
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
s"Local(" +
|
||||
|
@ -1014,7 +1014,7 @@ class RuntimeSuggestionUpdatesTest
|
||||
)
|
||||
)
|
||||
context.receiveNIgnoreExpressionUpdates(
|
||||
5
|
||||
6
|
||||
) should contain theSameElementsAs Seq(
|
||||
Api.Response(Api.BackgroundJobsStartedNotification()),
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
@ -1151,7 +1151,8 @@ class RuntimeSuggestionUpdatesTest
|
||||
ModuleExports(
|
||||
"Enso_Test.Test.Main",
|
||||
Set(
|
||||
ExportedSymbol.Atom("Enso_Test.Test.A", "MkA"),
|
||||
ExportedSymbol.Type("Enso_Test.Test.A", "MyType"),
|
||||
ExportedSymbol.Constructor("Enso_Test.Test.A", "MkA"),
|
||||
ExportedSymbol.Method("Enso_Test.Test.A", "hello")
|
||||
)
|
||||
),
|
||||
@ -1190,6 +1191,7 @@ class RuntimeSuggestionUpdatesTest
|
||||
)
|
||||
)
|
||||
),
|
||||
Api.Response(Api.AnalyzeModuleInScopeJobFinished()),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
context.consumeOut shouldEqual List("Hello World!")
|
||||
@ -1210,7 +1212,7 @@ class RuntimeSuggestionUpdatesTest
|
||||
)
|
||||
)
|
||||
context.receiveNIgnoreExpressionUpdates(
|
||||
3
|
||||
2
|
||||
) should contain theSameElementsAs Seq(
|
||||
Api.Response(
|
||||
Api.SuggestionsDatabaseModuleUpdateNotification(
|
||||
@ -1228,7 +1230,6 @@ class RuntimeSuggestionUpdatesTest
|
||||
updates = Tree.Root(Vector())
|
||||
)
|
||||
),
|
||||
Api.Response(Api.AnalyzeModuleInScopeJobFinished()),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
context.consumeOut shouldEqual List("Hello World!")
|
||||
@ -1259,7 +1260,10 @@ class RuntimeSuggestionUpdatesTest
|
||||
Api.ExportsUpdate(
|
||||
ModuleExports(
|
||||
"Enso_Test.Test.Main",
|
||||
Set(ExportedSymbol.Atom("Enso_Test.Test.A", "MkA"))
|
||||
Set(
|
||||
ExportedSymbol.Type("Enso_Test.Test.A", "MyType"),
|
||||
ExportedSymbol.Constructor("Enso_Test.Test.A", "MkA")
|
||||
)
|
||||
),
|
||||
Api.ExportsAction.Remove()
|
||||
)
|
||||
|
@ -0,0 +1,61 @@
|
||||
package org.enso.compiler.context;
|
||||
|
||||
import org.enso.pkg.QualifiedName;
|
||||
import org.enso.polyglot.ExportedSymbol;
|
||||
import org.enso.polyglot.ModuleExports;
|
||||
import org.enso.polyglot.Suggestion;
|
||||
import scala.Option;
|
||||
import scala.runtime.BoxedUnit;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class ExportsMap {
|
||||
|
||||
private static final String MODULE_MAIN = "Main";
|
||||
private static final String TYPE_SUFFIX = "type";
|
||||
|
||||
private final Map<ExportedSymbol, QualifiedName> exportsMap;
|
||||
|
||||
public ExportsMap() {
|
||||
this.exportsMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public ExportsMap(Map<ExportedSymbol, QualifiedName> exportsMap) {
|
||||
this.exportsMap = exportsMap;
|
||||
}
|
||||
|
||||
public void add(ExportedSymbol symbol, QualifiedName moduleName) {
|
||||
exportsMap.merge(symbol, moduleName, ExportsMap::getShortest);
|
||||
}
|
||||
|
||||
public void addAll(QualifiedName moduleName, ModuleExports moduleExports) {
|
||||
moduleExports
|
||||
.symbols()
|
||||
.foreach(
|
||||
symbol -> {
|
||||
add(symbol, moduleName);
|
||||
return BoxedUnit.UNIT;
|
||||
});
|
||||
}
|
||||
|
||||
public QualifiedName get(ExportedSymbol symbol) {
|
||||
return exportsMap.get(symbol);
|
||||
}
|
||||
|
||||
public QualifiedName get(Suggestion suggestion) {
|
||||
return ExportedSymbol.fromSuggestion(suggestion)
|
||||
.flatMap(symbol -> Option.apply(exportsMap.get(symbol)))
|
||||
.getOrElse(() -> exportsMap.get(ExportedSymbol.suggestionModule(suggestion)));
|
||||
}
|
||||
|
||||
private static QualifiedName getShortest(QualifiedName name1, QualifiedName name2) {
|
||||
return length(name1) <= length(name2) ? name1 : name2;
|
||||
}
|
||||
|
||||
private static int length(QualifiedName qualifiedName) {
|
||||
QualifiedName name =
|
||||
qualifiedName.item().equals(TYPE_SUFFIX) ? qualifiedName.getParent().get() : qualifiedName;
|
||||
return name.item().equals(MODULE_MAIN) ? name.path().length() : name.path().length() + 1;
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ package org.enso.compiler
|
||||
|
||||
import com.oracle.truffle.api.TruffleLogger
|
||||
import com.oracle.truffle.api.source.Source
|
||||
import org.enso.compiler.context.SuggestionBuilder
|
||||
import org.enso.compiler.context.{ExportsBuilder, ExportsMap, SuggestionBuilder}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.analyse.BindingAnalysis
|
||||
import org.enso.editions.LibraryName
|
||||
@ -233,44 +233,7 @@ final class SerializationManager(
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
val suggestions = new util.ArrayList[Suggestion]()
|
||||
compiler.packageRepository
|
||||
.getModulesForLibrary(libraryName)
|
||||
.flatMap { module =>
|
||||
SuggestionBuilder(module, compiler)
|
||||
.build(module.getName, module.getIr)
|
||||
.toVector
|
||||
.filter(Suggestion.isGlobal)
|
||||
}
|
||||
.foreach(suggestions.add)
|
||||
val cachedSuggestions =
|
||||
new SuggestionsCache.CachedSuggestions(
|
||||
libraryName,
|
||||
new SuggestionsCache.Suggestions(suggestions),
|
||||
compiler.packageRepository
|
||||
.getPackageForLibraryJava(libraryName)
|
||||
.map(_.listSourcesJava())
|
||||
)
|
||||
new SuggestionsCache(libraryName)
|
||||
.save(cachedSuggestions, compiler.context, useGlobalCacheLocations)
|
||||
.isPresent
|
||||
} catch {
|
||||
case e: NotSerializableException =>
|
||||
logger.log(
|
||||
Level.SEVERE,
|
||||
s"Could not serialize suggestions [$libraryName].",
|
||||
e
|
||||
)
|
||||
throw e
|
||||
case e: Throwable =>
|
||||
logger.log(
|
||||
Level.SEVERE,
|
||||
s"Serialization of suggestions `$libraryName` failed: ${e.getMessage}`",
|
||||
e
|
||||
)
|
||||
throw e
|
||||
}
|
||||
doSerializeLibrarySuggestions(libraryName, useGlobalCacheLocations)
|
||||
|
||||
result
|
||||
} finally {
|
||||
@ -278,6 +241,61 @@ final class SerializationManager(
|
||||
}
|
||||
}
|
||||
|
||||
private def doSerializeLibrarySuggestions(
|
||||
libraryName: LibraryName,
|
||||
useGlobalCacheLocations: Boolean
|
||||
): Boolean = {
|
||||
val exportsBuilder = new ExportsBuilder
|
||||
val exportsMap = new ExportsMap
|
||||
val suggestions = new util.ArrayList[Suggestion]()
|
||||
|
||||
try {
|
||||
val libraryModules =
|
||||
compiler.packageRepository.getModulesForLibrary(libraryName)
|
||||
libraryModules
|
||||
.flatMap { module =>
|
||||
val suggestions = SuggestionBuilder(module, compiler)
|
||||
.build(module.getName, module.getIr)
|
||||
.toVector
|
||||
.filter(Suggestion.isGlobal)
|
||||
val exports = exportsBuilder.build(module.getName, module.getIr)
|
||||
exportsMap.addAll(module.getName, exports)
|
||||
suggestions
|
||||
}
|
||||
.map { suggestion =>
|
||||
val reexport = Option(exportsMap.get(suggestion)).map(_.toString)
|
||||
suggestion.withReexport(reexport)
|
||||
}
|
||||
.foreach(suggestions.add)
|
||||
val cachedSuggestions =
|
||||
new SuggestionsCache.CachedSuggestions(
|
||||
libraryName,
|
||||
new SuggestionsCache.Suggestions(suggestions),
|
||||
compiler.packageRepository
|
||||
.getPackageForLibraryJava(libraryName)
|
||||
.map(_.listSourcesJava())
|
||||
)
|
||||
new SuggestionsCache(libraryName)
|
||||
.save(cachedSuggestions, compiler.context, useGlobalCacheLocations)
|
||||
.isPresent
|
||||
} catch {
|
||||
case e: NotSerializableException =>
|
||||
logger.log(
|
||||
Level.SEVERE,
|
||||
s"Could not serialize suggestions [$libraryName].",
|
||||
e
|
||||
)
|
||||
throw e
|
||||
case e: Throwable =>
|
||||
logger.log(
|
||||
Level.SEVERE,
|
||||
s"Serialization of suggestions `$libraryName` failed: ${e.getMessage}`",
|
||||
e
|
||||
)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
def deserializeSuggestions(
|
||||
libraryName: LibraryName
|
||||
): Option[SuggestionsCache.CachedSuggestions] = {
|
||||
|
@ -19,8 +19,10 @@ final class ExportsBuilder {
|
||||
.collect {
|
||||
case BindingsMap.ResolvedMethod(module, method) =>
|
||||
ExportedSymbol.Method(module.getName.toString, method.name)
|
||||
case BindingsMap.ResolvedType(module, tp) =>
|
||||
ExportedSymbol.Type(module.getName.toString, tp.name)
|
||||
case BindingsMap.ResolvedConstructor(tp, cons) =>
|
||||
ExportedSymbol.Atom(tp.module.getName.toString, cons.name)
|
||||
ExportedSymbol.Constructor(tp.module.getName.toString, cons.name)
|
||||
case BindingsMap.ResolvedModule(module) =>
|
||||
ExportedSymbol.Module(module.getName.toString)
|
||||
}
|
||||
|
@ -16,9 +16,7 @@ import scala.annotation.unused
|
||||
|
||||
/** A utility structure for resolving symbols in a given module.
|
||||
*
|
||||
* @param constructors the types defined in the current module
|
||||
* @param polyglotSymbols the polyglot symbols imported into the scope
|
||||
* @param moduleMethods the methods defined with current module as `this`
|
||||
* @param definedEntities the list of entities defined in the current module
|
||||
* @param currentModule the module holding these bindings
|
||||
*/
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user