Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- Implicit class member information for providing completions
create table implicit_class_members(
class_symbol varchar not null,
param_type varchar not null,
method_symbol varchar not null,
method_name varchar not null,
start_line int not null,
start_character int not null,
end_line int not null,
end_character int not null,
path varchar not null,
jar int,
foreign key (jar) references indexed_jar (id) on delete cascade
);

create index implicit_class_members_jar on implicit_class_members(jar);
create index implicit_class_members_param_type on implicit_class_members(param_type);

alter table indexed_jar
add implicit_class_members_indexed bit;

Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ final class ImplementationProvider(
}

def addTypeHierarchy(results: List[IndexingResult]): Unit = for {
IndexingResult(path, _, overrides, _) <- results
IndexingResult(path, _, overrides, _, _) <- results
(overridesSymbol, overriddenSymbols) <- overrides
overridden <- overriddenSymbols
} addTypeHierarchyElement(path, overridesSymbol, overridden)
Expand Down
69 changes: 61 additions & 8 deletions metals/src/main/scala/scala/meta/internal/metals/Indexer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ case class Indexer(indexProviders: IndexProviders)(implicit rc: ReportContext) {
definitionIndex.addIndexedSourceJar(zip, Nil, dialect)
implementationProvider.addTypeHierarchyElements(overrides)
case None =>
val (_, overrides, _) = indexJar(zip, dialect)
val (_, overrides, _, _) = indexJar(zip, dialect)
sharedIndices.jvmTypeHierarchy.addTypeHierarchyInfo(
zip,
overrides,
Expand Down Expand Up @@ -582,6 +582,19 @@ case class Indexer(indexProviders: IndexProviders)(implicit rc: ReportContext) {
.foreach(identifiers =>
referencesProvider.addIdentifiers(source, identifiers)
)

optMtags.foreach { mtags =>
val implicitMembers = mtags.implicitClassMembers()
if (implicitMembers.nonEmpty) {
scribe.info(
s"[Indexer.indexSourceFile] Found ${implicitMembers.size} implicit class members in $source"
)
workspaceSymbols.addImplicitClassMembers(
Map(source -> implicitMembers)
)
}
}

workspaceSymbols.didChange(source, symbols.toSeq, methodSymbols.toSeq)

// Since the `symbols` here are toplevel symbols,
Expand Down Expand Up @@ -612,30 +625,51 @@ case class Indexer(indexProviders: IndexProviders)(implicit rc: ReportContext) {
case Some(toplevelMembersMap) =>
workspaceSymbols.addToplevelMembers(toplevelMembersMap)
case None =>
val (_, _, toplevelMembers) =
val (_, _, toplevelMembers, implicitClassMembers) =
indexJar(path, dialect, reindex = true)
if (toplevelMembers.nonEmpty) {
tables.jarSymbols.addToplevelMembersInfo(
path,
toplevelMembers,
)
}
if (implicitClassMembers.nonEmpty) {
tables.jarSymbols.addImplicitClassMembersInfo(
path,
implicitClassMembers,
)
}
}
tables.jarSymbols.getImplicitClassMembers(path) match {
case Some(implicitClassMembersMap) =>
workspaceSymbols.addImplicitClassMembers(
implicitClassMembersMap
)
case None => // already handled above if we had to reindex
}
case None =>
val (_, overrides, toplevelMembers) =
val (_, overrides, toplevelMembers, implicitClassMembers) =
indexJar(path, dialect, reindex = true)
tables.jarSymbols.addTypeHierarchyInfo(path, overrides)
if (toplevelMembers.nonEmpty) {
tables.jarSymbols.addToplevelMembersInfo(path, toplevelMembers)
}
if (implicitClassMembers.nonEmpty) {
tables.jarSymbols.addImplicitClassMembersInfo(
path,
implicitClassMembers,
)
}
}
case None =>
val (toplevels, overrides, toplevelMembers) = indexJar(path, dialect)
val (toplevels, overrides, toplevelMembers, implicitClassMembers) =
indexJar(path, dialect)
tables.jarSymbols.putJarIndexingInfo(
path,
toplevels,
overrides,
toplevelMembers,
implicitClassMembers,
)
}
}
Expand All @@ -645,25 +679,44 @@ case class Indexer(indexProviders: IndexProviders)(implicit rc: ReportContext) {
dialect: Dialect,
reindex: Boolean = false,
) = {
scribe.info(s"[Indexer.indexJar] Indexing JAR: $path")
val indexResult = definitionIndex.addSourceJar(path, dialect, reindex)
scribe.info(
s"[Indexer.indexJar] Got ${indexResult.size} indexing results from JAR"
)
val toplevels = indexResult.flatMap {
case IndexingResult(path, toplevels, _, _) =>
case IndexingResult(path, toplevels, _, _, _) =>
toplevels.map((_, path))
}
val overrides = indexResult.flatMap {
case IndexingResult(path, _, list, _) =>
case IndexingResult(path, _, list, _, _) =>
list.flatMap { case (symbol, overridden) =>
overridden.map((path, symbol, _))
}
}
val toplevelMembersMap = indexResult.collect {
case IndexingResult(path, _, _, toplevelMembers)
case IndexingResult(path, _, _, toplevelMembers, _)
if toplevelMembers.nonEmpty =>
path -> toplevelMembers
}.toMap
val implicitClassMembersMap = indexResult.collect {
case IndexingResult(path, _, _, _, implicitClassMembers)
if implicitClassMembers.nonEmpty =>
scribe.info(
s"[Indexer.indexJar] Found ${implicitClassMembers.size} implicit class members in $path"
)
path -> implicitClassMembers
}.toMap
scribe.info(
s"[Indexer.indexJar] Total implicit class members map size: ${implicitClassMembersMap.size} files"
)
implementationProvider.addTypeHierarchyElements(overrides)
workspaceSymbols.addToplevelMembers(toplevelMembersMap)
(toplevels, overrides, toplevelMembersMap)
workspaceSymbols.addImplicitClassMembers(implicitClassMembersMap)
scribe.info(
s"[Indexer.indexJar] Added implicit class members to workspace symbols"
)
(toplevels, overrides, toplevelMembersMap, implicitClassMembersMap)
}

def reindexWorkspaceSources(
Expand Down
137 changes: 134 additions & 3 deletions metals/src/main/scala/scala/meta/internal/metals/JarTopLevels.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import scala.meta.internal.metals.JarTopLevels.getFileSystem
import scala.meta.internal.metals.JdbcEnrichments._
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.mtags.MD5
import scala.meta.internal.mtags.ImplicitClassMember
import scala.meta.internal.mtags.OverriddenSymbol
import scala.meta.internal.mtags.ResolvedOverriddenSymbol
import scala.meta.internal.mtags.ToplevelMember
Expand Down Expand Up @@ -77,19 +78,27 @@ final class JarTopLevels(conn: () => Connection) extends JarIndexingInfo(conn) {
toplevels: List[(String, AbsolutePath)],
type_hierarchy: List[(AbsolutePath, String, OverriddenSymbol)],
toplevelMembers: Map[AbsolutePath, List[ToplevelMember]] = Map.empty,
implicitClassMembers: Map[AbsolutePath, List[ImplicitClassMember]] =
Map.empty,
): Int = {
if (toplevels.isEmpty && type_hierarchy.isEmpty && toplevelMembers.isEmpty)
if (
toplevels.isEmpty && type_hierarchy.isEmpty && toplevelMembers.isEmpty && implicitClassMembers.isEmpty
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we reuse ToplevelMember for now? And jsut add different kind there?

)
0
else {
// Add jar to H2
addOrUpdateJar(path, "type_hierarchy_indexed")
val jar = addOrUpdateJar(path, "toplevel_members_indexed")
addOrUpdateJar(path, "toplevel_members_indexed")
val jar = addOrUpdateJar(path, "implicit_class_members_indexed")
jar
.map(jar =>
putToplevels(jar, toplevels) + putTypeHierarchyInfo(
jar,
type_hierarchy,
) + putToplevelMembersInfo(jar, toplevelMembers)
) + putToplevelMembersInfo(
jar,
toplevelMembers,
) + putImplicitClassMembersInfo(jar, implicitClassMembers)
)
.getOrElse(0)
}
Expand Down Expand Up @@ -402,6 +411,128 @@ class JarIndexingInfo(conn: () => Connection) {
}
} else 0

protected def putImplicitClassMembersInfo(
jar: Int,
implicitClassMemberMap: Map[AbsolutePath, List[ImplicitClassMember]],
): Int =
if (implicitClassMemberMap.nonEmpty) {
val totalMembers = implicitClassMemberMap.values.map(_.size).sum
scribe.info(
s"[JarTopLevels] Storing $totalMembers implicit class members from ${implicitClassMemberMap.size} files to database"
)

// Add implicit class members for jar to H2
var implicitClassMemberStmt: PreparedStatement = null
try {
implicitClassMemberStmt = conn().prepareStatement(
s"insert into implicit_class_members (class_symbol, param_type, method_symbol, method_name, start_line, start_character, end_line, end_character, path, jar) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
)

implicitClassMemberMap.foreach { case (path, implicitClassMembers) =>
implicitClassMembers.foreach { member =>
scribe.debug(
s"[JarTopLevels] Storing: paramType=${member.paramType}, " +
s"method=${member.methodName}, methodSymbol=${member.methodSymbol}"
)
implicitClassMemberStmt.setString(1, member.classSymbol)
implicitClassMemberStmt.setString(2, member.paramType)
implicitClassMemberStmt.setString(3, member.methodSymbol)
implicitClassMemberStmt.setString(4, member.methodName)
implicitClassMemberStmt.setInt(5, member.range.startLine)
implicitClassMemberStmt.setInt(6, member.range.startCharacter)
implicitClassMemberStmt.setInt(7, member.range.endLine)
implicitClassMemberStmt.setInt(8, member.range.endCharacter)
implicitClassMemberStmt.setString(
9,
path.toString,
)
implicitClassMemberStmt.setInt(10, jar)
implicitClassMemberStmt.addBatch()
}
}
// Return number of rows inserted
val inserted = implicitClassMemberStmt.executeBatch().sum
scribe.info(
s"[JarTopLevels] Successfully stored $inserted implicit class members"
)
inserted
} catch {
case e: JdbcBatchUpdateException =>
scribe.error(s"failed to insert implicit class members", e)
0
case e: JdbcSQLIntegrityConstraintViolationException =>
scribe.error(s"failed to insert implicit class members", e)
0
} finally {
if (implicitClassMemberStmt != null) implicitClassMemberStmt.close()
}
} else 0

def addImplicitClassMembersInfo(
path: AbsolutePath,
implicitClassMembers: Map[AbsolutePath, List[ImplicitClassMember]],
): Int = {
val jar = addOrUpdateJar(path, "implicit_class_members_indexed")
jar.map(putImplicitClassMembersInfo(_, implicitClassMembers)).getOrElse(0)
}

def getImplicitClassMembers(
jar: AbsolutePath
): Option[Map[AbsolutePath, List[ImplicitClassMember]]] =
try {
scribe.info(
s"[JarTopLevels] Reading implicit class members from database for JAR: $jar"
)
val fs = getFileSystem(jar)
val implicitClassMembers =
List.newBuilder[(AbsolutePath, ImplicitClassMember)]
conn()
.query(
"""select icm.class_symbol, icm.param_type, icm.method_symbol, icm.method_name, icm.start_line, icm.start_character, icm.end_line, icm.end_character, icm.path
|from indexed_jar ij
|left join implicit_class_members icm
|on ij.id=icm.jar
|where ij.implicit_class_members_indexed=true and ij.md5=?""".stripMargin
) { _.setString(1, getMD5Digest(jar)) } { rs =>
if (rs.getString(1) != null) {
val classSymbol = rs.getString(1)
val paramType = rs.getString(2)
val methodSymbol = rs.getString(3)
val methodName = rs.getString(4)
val startLine = rs.getInt(5)
val startChar = rs.getInt(6)
val endLine = rs.getInt(7)
val endChar = rs.getInt(8)
val path = AbsolutePath(fs.getPath(rs.getString(9)))
import scala.meta.internal.semanticdb.Range
val range = Range(startLine, startChar, endLine, endChar)
implicitClassMembers += (path -> ImplicitClassMember(
classSymbol,
paramType,
methodSymbol,
methodName,
range,
))
}
}
.headOption
.map { _ =>
val result = implicitClassMembers.result().groupBy(_._1).map {
case (path, members) =>
(path, members.map(_._2))
}
val totalMembers = result.values.map(_.size).sum
scribe.info(
s"[JarTopLevels] Retrieved $totalMembers implicit class members from ${result.size} files from database"
)
result
}
} catch {
case error @ (_: ZipError | _: ZipException) =>
scribe.warn(s"corrupted jar $jar: $error")
None
}

def getMD5Digest(path: AbsolutePath): String = {
val attributes = Files
.getFileAttributeView(path.toNIO, classOf[BasicFileAttributeView])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ abstract class MetalsLspService(
implementationProvider,
testProvider,
buildTargetClasses,
workspaceSymbols,
),
buildTargets,
folder,
Expand Down
Loading
Loading