Skip to content

Commit 23bf144

Browse files
Fix multi module (#189)
* fix multi-module * fix multi-module
1 parent 77501f9 commit 23bf144

File tree

12 files changed

+132
-99
lines changed

12 files changed

+132
-99
lines changed

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ lazy val jbAnnotVersion = "26.0.2"
1313
// https://youtrack.jetbrains.com/articles/IDEA-A-2100661679/IntelliJ-IDEA-2023.3-Latest-Builds
1414
// NOTE: Latest-Builds 233
1515
lazy val intellijVersion = "252.25557.131"
16-
lazy val pluginVersion = s"0.8.2-$intellijVersion"
16+
lazy val pluginVersion = s"0.8.3-$intellijVersion"
1717

1818
ThisBuild / version := pluginVersion
1919

src/main/scala/bitlap/sbt/analyzer/SbtDependencyAnalyzerContributor.scala

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ final class SbtDependencyAnalyzerContributor(project: Project) extends Dependenc
4747
private var organization: String = scala.compiletime.uninitialized
4848

4949
@volatile
50-
private var ideaModuleIdSbtModules: Map[String, String] = Map.empty
50+
private var moduleIdArtifactIds: Map[String, String] = Map.empty
5151

5252
private lazy val projects: ConcurrentHashMap[DependencyAnalyzerProject, ModuleNode] =
5353
ConcurrentHashMap[DependencyAnalyzerProject, ModuleNode]()
@@ -164,7 +164,7 @@ final class SbtDependencyAnalyzerContributor(project: Project) extends Dependenc
164164
val root = DAModule(moduleData.getModuleName)
165165
root.putUserData(Module_Data, moduleData)
166166

167-
val rootDependency = DADependency(root, DefaultConfiguration, null, Collections.emptyList())
167+
val rootDependency = DADependency(root, DEFAULT_CONFIGURATION, null, Collections.emptyList())
168168
dependencies.append(rootDependency)
169169
for (scopeNode <- scopeNodes.asScala) {
170170
val scope = scopeNode.toScope
@@ -248,18 +248,17 @@ final class SbtDependencyAnalyzerContributor(project: Project) extends Dependenc
248248
organization
249249
end getOrganization
250250

251-
private def getIdeaModuleIdSbtModules(project: Project): Map[String, String] =
252-
if (ideaModuleIdSbtModules.nonEmpty) return ideaModuleIdSbtModules
251+
private def getModuleIdArtifactIds(project: Project): Map[String, String] =
252+
if (moduleIdArtifactIds.nonEmpty) return moduleIdArtifactIds
253253

254254
// we don't actively delete configurations
255-
256255
val settingsState = SettingsState.getSettings(project)
257256
if (!settingsState.sbtModules.isEmpty) return settingsState.sbtModules.asScala.toMap
258257

259-
ideaModuleIdSbtModules = SbtShellOutputAnalysisTask.sbtModuleNamesTask.executeCommand(project)
260-
settingsState.sbtModules = ideaModuleIdSbtModules.asJava
261-
ideaModuleIdSbtModules
262-
end getIdeaModuleIdSbtModules
258+
moduleIdArtifactIds = SbtShellOutputAnalysisTask.sbtModuleNamesTask.executeCommand(project)
259+
settingsState.sbtModules = moduleIdArtifactIds.asJava
260+
moduleIdArtifactIds
261+
end getModuleIdArtifactIds
263262

264263
private def getOrRefreshData(moduleData: ModuleData): JList[DependencyScopeNode] =
265264
// use to link dependencies between modules.
@@ -273,7 +272,7 @@ final class SbtDependencyAnalyzerContributor(project: Project) extends Dependenc
273272
project,
274273
getOrganization(project),
275274
projects.values().asScala.map(d => d.getModuleName -> d.getLinkedExternalProjectPath).toMap,
276-
getIdeaModuleIdSbtModules(project)
275+
getModuleIdArtifactIds(project)
277276
)
278277
)
279278
Option(result).getOrElse(Collections.emptyList())
@@ -368,8 +367,8 @@ object SbtDependencyAnalyzerContributor
368367
def loadDependencies(
369368
project: Project,
370369
organization: String,
371-
ideaModuleNamePaths: Map[String, String],
372-
ideaModuleIdSbtModules: Map[String, String]
370+
moduleNamePaths: Map[String, String],
371+
moduleIdArtifactIds: Map[String, String]
373372
): JList[DependencyScopeNode] =
374373
val module = findModule(project, moduleData)
375374
val moduleId = moduleData.getId.split(" ")(0)
@@ -394,16 +393,14 @@ object SbtDependencyAnalyzerContributor
394393
DependencyGraphFactory
395394
.getInstance(summon[DependencyGraphType])
396395
.buildDependencyTree(
397-
ModuleContext(
396+
AnalyzerContext(
398397
file,
399398
moduleId,
400399
scope,
401400
organization,
402-
ideaModuleNamePaths,
403-
module.isScalaJs,
404-
module.isScalaNative,
405-
if (ideaModuleIdSbtModules.isEmpty) Map(moduleId -> module.getName)
406-
else ideaModuleIdSbtModules
401+
moduleNamePaths,
402+
if (moduleIdArtifactIds.isEmpty) Map(moduleId -> module.getName)
403+
else moduleIdArtifactIds
407404
),
408405
createRootScopeNode(scope, project)
409406
)
@@ -414,8 +411,8 @@ object SbtDependencyAnalyzerContributor
414411
moduleData,
415412
scope,
416413
organization,
417-
ideaModuleNamePaths,
418-
ideaModuleIdSbtModules
414+
moduleNamePaths,
415+
moduleIdArtifactIds
419416
)
420417
}
421418
end executeCommandOrReadExistsFile
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package bitlap.sbt.analyzer.model
2+
3+
import bitlap.sbt.analyzer.DependencyScopeEnum
4+
5+
final case class AnalyzerContext(
6+
analysisFile: String,
7+
currentModuleId: String,
8+
scope: DependencyScopeEnum,
9+
organization: String,
10+
moduleNamePathsCache: Map[String, String] = Map.empty,
11+
moduleIdArtifactIdsCache: Map[String, String] = Map.empty, // sbt module name == sbt artifact name
12+
isTest: Boolean = false
13+
)

src/main/scala/bitlap/sbt/analyzer/model/ModuleContext.scala

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/main/scala/bitlap/sbt/analyzer/parsing/DependencyGraphParser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ trait DependencyGraphParser {
99
val dependencyGraphType: DependencyGraphType
1010

1111
def buildDependencyTree(
12-
context: ModuleContext,
12+
context: AnalyzerContext,
1313
root: DependencyScopeNode
1414
): DependencyScopeNode
1515

src/main/scala/bitlap/sbt/analyzer/parsing/DotDependencyGraphParser.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ final class DotDependencyGraphParser extends DependencyGraphParser:
6161

6262
/** build tree for dependency analyzer view
6363
*/
64-
override def buildDependencyTree(context: ModuleContext, root: DependencyScopeNode): DependencyScopeNode = {
64+
override def buildDependencyTree(context: AnalyzerContext, root: DependencyScopeNode): DependencyScopeNode = {
6565
val data = getDependencyRelations(context)
6666
val dependencies: Dependencies = data.orNull
6767
val depMap = data.map(_.dependencies.map(a => a.id.toString -> toDependencyNode(a)).toMap).getOrElse(Map.empty)
@@ -98,7 +98,7 @@ final class DotDependencyGraphParser extends DependencyGraphParser:
9898
parentChildrenMap: Map[String, List[Int]],
9999
depMap: Map[String, DependencyNode],
100100
relationLabelsMap: Map[String, String],
101-
context: ModuleContext,
101+
context: AnalyzerContext,
102102
relations: List[Relation]
103103
): Unit = {
104104
val childIds = parentChildrenMap
@@ -127,7 +127,7 @@ final class DotDependencyGraphParser extends DependencyGraphParser:
127127

128128
/** parse dot file, get graph data
129129
*/
130-
private def getDependencyRelations(context: ModuleContext): Option[Dependencies] =
130+
private def getDependencyRelations(context: AnalyzerContext): Option[Dependencies] =
131131
val mutableGraph: MutableGraph = DotUtils.parseAsGraph(context)
132132
if (mutableGraph == null) None
133133
else

src/main/scala/bitlap/sbt/analyzer/parsing/DotUtils.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import analyzer.util.Notifications
2020
import guru.nidi.graphviz.attribute.validate.ValidatorEngine
2121
import guru.nidi.graphviz.model.MutableGraph
2222
import guru.nidi.graphviz.parse.Parser
23-
import model.ModuleContext
23+
import model.AnalyzerContext
2424

2525
object DotUtils {
2626

@@ -32,7 +32,7 @@ object DotUtils {
3232
Try(parser.read(new File(file))).getOrElse(null)
3333
}
3434

35-
def parseAsGraph(context: ModuleContext): MutableGraph = {
35+
def parseAsGraph(context: AnalyzerContext): MutableGraph = {
3636
if (context.isTest) return parseAsGraphTestOnly(context.analysisFile)
3737
val file = context.analysisFile
3838
try {

src/main/scala/bitlap/sbt/analyzer/task/DependencyDotTask.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,12 @@ final class DependencyDotTask extends SbtShellDependencyAnalysisTask:
3636
DependencyGraphFactory
3737
.getInstance(dependencyGraphType)
3838
.buildDependencyTree(
39-
ModuleContext(
39+
AnalyzerContext(
4040
file,
4141
moduleId,
4242
scope,
4343
organization,
4444
moduleNamePaths,
45-
module.isScalaJs,
46-
module.isScalaNative,
4745
sbtModuleNameMap
4846
),
4947
createRootScopeNode(scope, project)

src/main/scala/bitlap/sbt/analyzer/util/DependencyUtils.scala

Lines changed: 31 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package util
66
import java.util.concurrent.atomic.AtomicLong
77

88
import scala.jdk.CollectionConverters.*
9+
import scala.util.matching.Regex
910

1011
import bitlap.sbt.analyzer.model.*
1112
import bitlap.sbt.analyzer.parsing.*
@@ -25,17 +26,14 @@ import com.intellij.openapi.util.text.StringUtil
2526

2627
object DependencyUtils {
2728

28-
final val DefaultConfiguration = toDAScope("default")
29+
final val DEFAULT_CONFIGURATION = toDAScope("default")
2930

30-
private final val rootId = new AtomicLong(0)
31-
private final val artifactId = new AtomicLong(0)
31+
private final val rootId = new AtomicLong(0)
32+
private final val artifactId = new AtomicLong(0)
33+
private val SBT_ARTIFACT_REGEX = "(.*):(.*):(.*)".r
3234

33-
private val SBT_ARTIFACT_REGEX = "(.*):(.*):(.*)".r
34-
private val SCALA_ARTIFACT_REGEX = "(.*)_(.*)".r
35-
private val NATIVE_ARTIFACT_PATTERN = "_native\\d(\\.\\d+)?_\\d(\\.\\d+)?"
36-
private val SJS_ARTIFACT_PATTERN = "_sjs\\d(\\.\\d+)?_\\d(\\.\\d+)?"
37-
private val NATIVE_ARTIFACT_REGEX = "(.*)(_native\\d(\\.\\d+)?)_(.*)".r
38-
private val SJS_ARTIFACT_REGEX = "(.*)(_sjs\\d(\\.\\d+)?)_(.*)".r
35+
val SCALA_VERSION_PATTERN: Regex =
36+
"""^([a-zA-Z0-9-]+?)(?:_(?:sjs\d+(?:\.\d+)?|native\d*(?:\.\d+)?|2\.1[123]|3))+$""".r
3937

4038
private final case class PlatformModule(
4139
module: String,
@@ -50,10 +48,10 @@ object DependencyUtils {
5048
* and applying filtering logic, which is critical for dependency graph/tree operations as this node serves as the
5149
* root of the structure.
5250
*/
53-
def isSelfNode(dn: DependencyNode, context: ModuleContext): Boolean = {
51+
def isSelfNode(dn: DependencyNode, context: AnalyzerContext): Boolean = {
5452
dn.getDisplayName match
5553
case SBT_ARTIFACT_REGEX(group, artifact, _) =>
56-
context.organization == group && isSelfArtifact(artifact, context)
54+
context.organization == group && isSubModule(artifact, context)
5755
case _ => false
5856
}
5957

@@ -96,7 +94,7 @@ object DependencyUtils {
9694
def appendChildrenAndFixProjectNodes[N <: DependencyNode](
9795
parentNode: N,
9896
nodes: Seq[DependencyNode],
99-
context: ModuleContext
97+
context: AnalyzerContext
10098
): Unit = {
10199
parentNode.getDependencies.addAll(nodes.asJava)
102100
val moduleDependencies = nodes.filter(d => isProjectModule(d, context))
@@ -112,8 +110,8 @@ object DependencyUtils {
112110
val group = artifact.map(_.group).getOrElse(Constants.EMPTY_STRING)
113111
// Use artifact to determine whether there are modules in the dependency.
114112
if (
115-
context.ideaModuleIdSbtModuleNames.values
116-
.exists(d => group == context.organization && toPlatformModule(artifactId).module == d)
113+
context.moduleIdArtifactIdsCache.values
114+
.exists(d => group == context.organization && getPlatformModule(artifactId) == d)
117115
) {
118116
appendChildrenAndFixProjectNodes(
119117
node,
@@ -124,51 +122,40 @@ object DependencyUtils {
124122
}
125123
}
126124

127-
private def isSelfArtifact(artifact: String, context: ModuleContext): Boolean = {
128-
// processing cross-platform, module name is not artifact!
125+
private def isSubModule(maybeModule: String, context: AnalyzerContext): Boolean = {
126+
// Handles the cross-platform, module name is not equals to artifact!
129127
val currentModuleName =
130-
context.ideaModuleIdSbtModuleNames.getOrElse(
128+
context.moduleIdArtifactIdsCache.getOrElse(
131129
context.currentModuleId,
132-
context.ideaModuleIdSbtModuleNames.getOrElse(
130+
context.moduleIdArtifactIdsCache.getOrElse(
133131
Constants.SINGLE_SBT_MODULE,
134-
context.ideaModuleIdSbtModuleNames.getOrElse(Constants.ROOT_SBT_MODULE, context.currentModuleId)
132+
context.moduleIdArtifactIdsCache.getOrElse(Constants.ROOT_SBT_MODULE, context.currentModuleId)
135133
)
136134
)
137135

138136
// NOTE: we don't determine the Scala version number.
139-
if (context.isScalaNative) {
140-
val module = artifact.replaceAll(NATIVE_ARTIFACT_PATTERN, Constants.EMPTY_STRING)
141-
currentModuleName.equalsIgnoreCase(module)
142-
} else if (context.isScalaJs) {
143-
val module = artifact.replaceAll(SJS_ARTIFACT_PATTERN, Constants.EMPTY_STRING)
144-
currentModuleName.equalsIgnoreCase(module)
145-
} else {
146-
artifact match
147-
case SCALA_ARTIFACT_REGEX(module, _) =>
148-
currentModuleName.equalsIgnoreCase(module)
149-
// it is a java project
150-
case _ => artifact.equalsIgnoreCase(currentModuleName)
137+
maybeModule match {
138+
case SCALA_VERSION_PATTERN(module) => module.equalsIgnoreCase(currentModuleName)
139+
case _ => maybeModule.equalsIgnoreCase(currentModuleName)
151140
}
152141
}
153142

154-
private def toPlatformModule(artifact: String): PlatformModule = {
143+
private def getPlatformModule(artifact: String): String = {
155144
artifact match
156-
case SJS_ARTIFACT_REGEX(module, _, _, scalaVer) => PlatformModule(module, scalaVer)
157-
case NATIVE_ARTIFACT_REGEX(module, _, _, scalaVer) => PlatformModule(module, scalaVer)
158-
case SCALA_ARTIFACT_REGEX(module, scalaVer) => PlatformModule(module, scalaVer)
159-
case _ => PlatformModule(artifact, Constants.EMPTY_STRING)
145+
case SCALA_VERSION_PATTERN(module) => module
146+
case _ => artifact
160147
}
161148

162-
private def toProjectDependencyNode(dn: DependencyNode, context: ModuleContext): Option[DependencyNode] = {
149+
private def toProjectDependencyNode(dn: DependencyNode, context: AnalyzerContext): Option[DependencyNode] = {
163150
val artifactInfo = getArtifactInfoFromDisplayName(dn.getDisplayName).orNull
164151
if (artifactInfo == null) return None
165-
val sbtModuleName = toPlatformModule(artifactInfo.artifact).module
166-
val ideaModuleName = context.ideaModuleIdSbtModuleNames.find(_._2 == sbtModuleName).map(_._1)
152+
val sbtModuleName = getPlatformModule(artifactInfo.artifact)
153+
val ideaModuleName = context.moduleIdArtifactIdsCache.find(_._2 == sbtModuleName).map(_._1)
167154

168155
// Processing cross-platform, module name is not artifact
169156
// This is a project node, we need a module not an artifact to get project path!
170157

171-
val fixedCustomName = context.ideaModuleNamePaths.map { case (name, path) =>
158+
val fixedCustomName = context.moduleNamePathsCache.map { case (name, path) =>
172159
if (name.exists(_ == ' '))
173160
name.toLowerCase.replace(' ', '-') -> path
174161
else
@@ -177,9 +164,9 @@ object DependencyUtils {
177164

178165
val projectPath =
179166
ideaModuleName
180-
.flatMap(m => context.ideaModuleNamePaths.get(m))
167+
.flatMap(m => context.moduleNamePathsCache.get(m))
181168
.getOrElse(
182-
context.ideaModuleNamePaths
169+
context.moduleNamePathsCache
183170
.getOrElse(sbtModuleName, fixedCustomName.getOrElse(sbtModuleName.toLowerCase, Constants.EMPTY_STRING))
184171
)
185172

@@ -201,14 +188,14 @@ object DependencyUtils {
201188
Some(p)
202189
}
203190

204-
private def isProjectModule(dn: DependencyNode, context: ModuleContext): Boolean = {
191+
private def isProjectModule(dn: DependencyNode, context: AnalyzerContext): Boolean = {
205192
// module dependency
206193
val artifactInfo = getArtifactInfoFromDisplayName(dn.getDisplayName).orNull
207194
if (artifactInfo == null) return false
208195
if (artifactInfo.group != context.organization) return false
209196
// Use artifacts to determine if there are dependent modules
210197
val matchModule =
211-
context.ideaModuleIdSbtModuleNames.values.filter(m => m == toPlatformModule(artifactInfo.artifact).module)
198+
context.moduleIdArtifactIdsCache.values.filter(m => m == getPlatformModule(artifactInfo.artifact))
212199

213200
matchModule.nonEmpty
214201

src/test/scala/bitlap/sbt/analyzer/DependencyGraphParserSpec.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package bitlap.sbt.analyzer
22

3-
import bitlap.sbt.analyzer.model.ModuleContext
3+
import bitlap.sbt.analyzer.model.AnalyzerContext
44
import bitlap.sbt.analyzer.parsing.{ DependencyGraphFactory, DependencyGraphType }
55

66
import org.scalatest.flatspec.AnyFlatSpec
@@ -21,13 +21,13 @@ class DependencyGraphParserSpec extends AnyFlatSpec {
2121
root.setResolutionState(ResolutionState.RESOLVED)
2222

2323
val ctx =
24-
ModuleContext(
24+
AnalyzerContext(
2525
getClass.getClassLoader.getResource("test.dot").getFile,
2626
"star-authority-protocol",
2727
DependencyScopeEnum.Compile,
2828
"fc.xuanwu.star",
29-
ideaModuleNamePaths = Map.empty,
30-
ideaModuleIdSbtModuleNames = Map.empty,
29+
moduleNamePathsCache = Map.empty,
30+
moduleIdArtifactIdsCache = Map.empty,
3131
isTest = true
3232
)
3333

0 commit comments

Comments
 (0)