Skip to content

Commit

Permalink
[plugin] very very early version of project import mechanism
Browse files Browse the repository at this point in the history
* needs more love after andrzej's pr

* import mechanism improvements (#14)

Co-authored-by: Marcin Abramowicz <marcin.abramowicz@jetbrains.com>

* id does something xd

Co-authored-by: Andrzej Głuszak <andrzej.gluszak@jetbrains.com>
  • Loading branch information
abrams27 and agluszak committed Aug 9, 2022
1 parent 1b009ad commit 09bf62b
Show file tree
Hide file tree
Showing 24 changed files with 1,125 additions and 181 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ version = properties("pluginVersion")

dependencies {
implementation(project(":magicmetamodel"))
implementation(project(":protocol"))
implementation("ch.epfl.scala:bsp4j:2.0.0-M15")
implementation("com.google.code.gson:gson:2.9.0")
}
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ repositories {
}

dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0")
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.20.0")
implementation("org.jlleitschuh.gradle:ktlint-gradle:10.3.0")
}
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ platformDownloadSources = true

# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
platformPlugins =
platformPlugins = com.intellij.java

# Opt-out flag for bundling Kotlin standard library.
# See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details.
kotlin.stdlib.default.dependency = false

javaVersion = 11
kotlinVersion = 1.6
kotlinVersion = 1.7
12 changes: 12 additions & 0 deletions protocol/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
id("intellijbsp.kotlin-conventions")
}

dependencies {
implementation("ch.epfl.scala:bsp4j:2.0.0-M15")
implementation("com.google.code.gson:gson:2.9.0")

testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")
testImplementation("io.kotest:kotest-assertions-core:5.3.0")
testImplementation(project(":test-utils"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.jetbrains.protocol.connection

import com.intellij.openapi.vfs.VirtualFile

public interface BspConnectionDetailsGenerator {

public fun name(): String

public fun canGenerateBspConnectionDetailsFile(projectPath: VirtualFile): Boolean

public fun generateBspConnectionDetailsFile(projectPath: VirtualFile): VirtualFile
}

public class BspConnectionDetailsGeneratorProvider(
private val projectPath: VirtualFile,
bspConnectionDetailsGenerators: List<BspConnectionDetailsGenerator>,
) {

private val availableBspConnectionDetailsGenerators by lazy {
bspConnectionDetailsGenerators.filter { it.canGenerateBspConnectionDetailsFile(projectPath) }
}

public fun canGenerateAnyBspConnectionDetailsFile(): Boolean =
availableBspConnectionDetailsGenerators.isNotEmpty()

public fun availableGeneratorsNames(): List<String> =
availableBspConnectionDetailsGenerators.map { it.name() }

public fun generateBspConnectionDetailFileForGeneratorWithName(generatorName: String): VirtualFile? =
availableBspConnectionDetailsGenerators
.find { it.name() == generatorName }
?.generateBspConnectionDetailsFile(projectPath)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.jetbrains.protocol.connection

import com.intellij.openapi.vfs.VirtualFile

public class BspConnectionDetailsProvider(
private val bspConnectionDetailsGenerators: List<BspConnectionDetailsGenerator>
) {

private lateinit var bspConnectionFilesProvider: BspConnectionFilesProvider

private lateinit var bspConnectionDetailsGeneratorProvider: BspConnectionDetailsGeneratorProvider

public fun canOpenBspProject(projectPath: VirtualFile): Boolean {
initProvidersIfNeeded(projectPath)

return bspConnectionFilesProvider.isAnyBspConnectionFileDefined()
}


private fun initProvidersIfNeeded(projectPath: VirtualFile) {
if (!::bspConnectionFilesProvider.isInitialized) {
bspConnectionFilesProvider = BspConnectionFilesProvider(projectPath)
}

if (!::bspConnectionDetailsGeneratorProvider.isInitialized) {
bspConnectionDetailsGeneratorProvider =
BspConnectionDetailsGeneratorProvider(projectPath, bspConnectionDetailsGenerators)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.jetbrains.protocol.connection

import com.intellij.openapi.vfs.VirtualFile

public class BspConnectionFilesProvider(projectPath: VirtualFile) {

public val connectionFiles: List<LocatedBspConnectionDetails> by lazy { calculateConnectionFile(projectPath) }

private fun calculateConnectionFile(projectPath: VirtualFile): List<LocatedBspConnectionDetails> =
projectPath.findChild(dotBspDir)
?.children
?.mapNotNull { LocatedBspConnectionDetailsParser.parseFromFile(it) }
.orEmpty()

public fun isAnyBspConnectionFileDefined(): Boolean = connectionFiles.isNotEmpty()

private companion object {
private const val dotBspDir = ".bsp"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.jetbrains.protocol.connection

import ch.epfl.scala.bsp4j.BspConnectionDetails
import com.google.gson.Gson
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile

public data class LocatedBspConnectionDetails(
val bspConnectionDetails: BspConnectionDetails,
val connectionFileLocation: VirtualFile,
)

// TODO visib??
public object LocatedBspConnectionDetailsParser {

private val log = logger<LocatedBspConnectionDetailsParser>()

public fun parseFromFile(file: VirtualFile): LocatedBspConnectionDetails? =
parseBspConnectionDetails(file)?.let {
LocatedBspConnectionDetails(
bspConnectionDetails = it,
connectionFileLocation = file,
)
}

private fun parseBspConnectionDetails(file: VirtualFile): BspConnectionDetails? =
try {
Gson().fromJson(VfsUtil.loadText(file), BspConnectionDetails::class.java)
} catch (e: Exception) {
log.info("Parsing file '$file' to BspConnectionDetails failed!", e)
null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package org.jetbrains.protocol.connection

import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.io.createDirectories
import com.intellij.util.io.createFile
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
import io.kotest.matchers.should
import io.kotest.matchers.shouldBe
import org.jetbrains.workspace.model.test.framework.MockProjectBaseTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.createTempDirectory

private object GeneratorWhichCantGenerate : BspConnectionDetailsGenerator {

override fun name(): String = "cant generate"

override fun canGenerateBspConnectionDetailsFile(projectPath: VirtualFile): Boolean = false

override fun generateBspConnectionDetailsFile(projectPath: VirtualFile): VirtualFile = projectPath
}

private class GeneratorWhichCanGenerate(private val name: String, private val generatedFilePath: VirtualFile) :
BspConnectionDetailsGenerator {

var hasGenerated = false

override fun name(): String = name

override fun canGenerateBspConnectionDetailsFile(projectPath: VirtualFile): Boolean = true

override fun generateBspConnectionDetailsFile(projectPath: VirtualFile): VirtualFile {
hasGenerated = true

return generatedFilePath
}
}

class BspConnectionDetailsGeneratorProviderTest : MockProjectBaseTest() {

private lateinit var projectPath: Path
private lateinit var generatedVirtualFile: VirtualFile
private lateinit var otherGeneratedVirtualFile: VirtualFile

@BeforeEach
override fun beforeEach() {
// given
super.beforeEach()

this.projectPath = createTempDirectory("project")
this.generatedVirtualFile = projectPath.resolve(".bsp").resolve("connection-file.json").createFile().toVirtualFile()
this.otherGeneratedVirtualFile = projectPath.resolve(".bsp").resolve("other-connection-file.json").createFile().toVirtualFile()
}

@AfterEach
fun afterEach() {
projectPath.toFile().deleteRecursively()
}

@Test
fun `should return false for canGenerateAnyBspConnectionDetailsFile(), empty list for availableGeneratorsNames() if there is no generator provided`() {
// given
val bspConnectionDetailsGenerators = emptyList<BspConnectionDetailsGenerator>()

// when
val provider = BspConnectionDetailsGeneratorProvider(projectPath.toVirtualFile(), bspConnectionDetailsGenerators)

// then
provider.canGenerateAnyBspConnectionDetailsFile() shouldBe false
provider.availableGeneratorsNames() shouldBe emptyList()
}

@Test
fun `should return false for canGenerateAnyBspConnectionDetailsFile(), empty list for availableGeneratorsNames() if there is no generator which can generate`() {
// given
val bspConnectionDetailsGenerators = listOf(GeneratorWhichCantGenerate)

// when
val provider = BspConnectionDetailsGeneratorProvider(projectPath.toVirtualFile(), bspConnectionDetailsGenerators)

// then
provider.canGenerateAnyBspConnectionDetailsFile() shouldBe false
provider.availableGeneratorsNames() shouldBe emptyList()
}

@Test
fun `should not generate if the generator name is wrong`() {
// given
val generator = GeneratorWhichCanGenerate("generator 1", generatedVirtualFile)
val bspConnectionDetailsGenerators = listOf(generator)

// when
val provider = BspConnectionDetailsGeneratorProvider(projectPath.toVirtualFile(), bspConnectionDetailsGenerators)

// then
provider.canGenerateAnyBspConnectionDetailsFile() shouldBe true
provider.availableGeneratorsNames() shouldContainExactlyInAnyOrder listOf("generator 1")

provider.generateBspConnectionDetailFileForGeneratorWithName("wrong name") shouldBe null
generator.hasGenerated shouldBe false
}

@Test
fun `should return true for canGenerateAnyBspConnectionDetailsFile(), list with one element for availableGeneratorsNames() and generate if there is one generator which can generate`() {
// given
val generator = GeneratorWhichCanGenerate("generator 1", generatedVirtualFile)
val bspConnectionDetailsGenerators = listOf(generator)

// when
val provider = BspConnectionDetailsGeneratorProvider(projectPath.toVirtualFile(), bspConnectionDetailsGenerators)

// then
provider.canGenerateAnyBspConnectionDetailsFile() shouldBe true
provider.availableGeneratorsNames() shouldContainExactlyInAnyOrder listOf("generator 1")

provider.generateBspConnectionDetailFileForGeneratorWithName("generator 1") shouldBe generatedVirtualFile
generator.hasGenerated shouldBe true
}

@Test
fun `should return true for canGenerateAnyBspConnectionDetailsFile(), list with generators which can generate for availableGeneratorsNames() and generate if there are multiple generators which can generate and few which cant`() {
// given
val generator1 = GeneratorWhichCanGenerate("generator 1", otherGeneratedVirtualFile)
val generator2 = GeneratorWhichCanGenerate("generator 2", generatedVirtualFile)
val generator3 = GeneratorWhichCanGenerate("generator 3", otherGeneratedVirtualFile)
val bspConnectionDetailsGenerators =
listOf(generator1, GeneratorWhichCantGenerate, generator2, generator3, GeneratorWhichCantGenerate)

// when
val provider = BspConnectionDetailsGeneratorProvider(projectPath.toVirtualFile(), bspConnectionDetailsGenerators)

// then
provider.canGenerateAnyBspConnectionDetailsFile() shouldBe true
provider.availableGeneratorsNames() shouldContainExactlyInAnyOrder listOf(
"generator 1",
"generator 2",
"generator 3"
)

provider.generateBspConnectionDetailFileForGeneratorWithName("generator 2") shouldBe generatedVirtualFile
generator1.hasGenerated shouldBe false
generator2.hasGenerated shouldBe true
generator3.hasGenerated shouldBe false
}

@Test
fun `should call first generator with the given name`() {
// given
val generator1 = GeneratorWhichCanGenerate("generator 1", otherGeneratedVirtualFile)
val generator21 = GeneratorWhichCanGenerate("generator 2", generatedVirtualFile)
val generator22 = GeneratorWhichCanGenerate("generator 2", otherGeneratedVirtualFile)
val generator3 = GeneratorWhichCanGenerate("generator 3", otherGeneratedVirtualFile)
val bspConnectionDetailsGenerators =
listOf(generator1, GeneratorWhichCantGenerate, generator21, generator3, generator22, GeneratorWhichCantGenerate)

// when
val provider = BspConnectionDetailsGeneratorProvider(projectPath.toVirtualFile(), bspConnectionDetailsGenerators)

// then
provider.canGenerateAnyBspConnectionDetailsFile() shouldBe true
provider.availableGeneratorsNames() shouldContainExactlyInAnyOrder listOf(
"generator 1",
"generator 2",
"generator 2",
"generator 3"
)

provider.generateBspConnectionDetailFileForGeneratorWithName("generator 2") shouldBe generatedVirtualFile
generator1.hasGenerated shouldBe false
generator21.hasGenerated shouldBe true
generator22.hasGenerated shouldBe false
generator3.hasGenerated shouldBe false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.jetbrains.protocol.connection

import org.junit.jupiter.api.Test

//class BspConnectionDetailsProviderTest {
//
// @Test
// fun `should return false for canOpenBspProject()
//}
Loading

0 comments on commit 09bf62b

Please sign in to comment.