From ea1085c2a5a66c31840f7986abadef5605badd16 Mon Sep 17 00:00:00 2001 From: Chris Simon Date: Sun, 10 Mar 2024 15:44:00 +1100 Subject: [PATCH] feat(intellij): automatically download the language server if it's not found --- src/intellij/contextive/build.gradle.kts | 6 +- .../ContextiveLspServerSupportProvider.kt | 14 ++- .../contextive/LanguageServerDownloader.kt | 90 +++++++++++++++++++ src/vscode/contextive/.gitignore | 3 +- 4 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 src/intellij/contextive/src/main/kotlin/tech/contextive/contextive/LanguageServerDownloader.kt diff --git a/src/intellij/contextive/build.gradle.kts b/src/intellij/contextive/build.gradle.kts index 85481145..ae9841e7 100644 --- a/src/intellij/contextive/build.gradle.kts +++ b/src/intellij/contextive/build.gradle.kts @@ -5,12 +5,16 @@ plugins { } group = "tech.contextive" -version = "1.11.0-beta" +version = "1.10.5" repositories { mavenCentral() } +dependencies { + implementation("net.lingala.zip4j:zip4j:2.11.5") +} + // Configure Gradle IntelliJ Plugin // Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html intellij { diff --git a/src/intellij/contextive/src/main/kotlin/tech/contextive/contextive/ContextiveLspServerSupportProvider.kt b/src/intellij/contextive/src/main/kotlin/tech/contextive/contextive/ContextiveLspServerSupportProvider.kt index f17b8ee2..28ee2f4e 100644 --- a/src/intellij/contextive/src/main/kotlin/tech/contextive/contextive/ContextiveLspServerSupportProvider.kt +++ b/src/intellij/contextive/src/main/kotlin/tech/contextive/contextive/ContextiveLspServerSupportProvider.kt @@ -1,17 +1,27 @@ package tech.contextive.contextive import com.intellij.execution.configurations.GeneralCommandLine +import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.platform.lsp.api.LspServerSupportProvider import com.intellij.platform.lsp.api.ProjectWideLspServerDescriptor +private val LOG = logger() + class ContextiveLspServerSupportProvider : LspServerSupportProvider { - override fun fileOpened(project: Project, file: VirtualFile, serverStarter: LspServerSupportProvider.LspServerStarter) { + override fun fileOpened( + project: Project, + file: VirtualFile, + serverStarter: LspServerSupportProvider.LspServerStarter + ) { serverStarter.ensureServerStarted(ContextiveLspServerDescriptor(project)) } + } + private class ContextiveLspServerDescriptor(project: Project) : ProjectWideLspServerDescriptor(project, "Contextive") { override fun isSupportedFile(file: VirtualFile) = true - override fun createCommandLine(): GeneralCommandLine = GeneralCommandLine("Contextive.LanguageServer") + override fun createCommandLine(): GeneralCommandLine = + downloadLanguageServerIfNotFound().run { GeneralCommandLine(this.toString()) } } \ No newline at end of file diff --git a/src/intellij/contextive/src/main/kotlin/tech/contextive/contextive/LanguageServerDownloader.kt b/src/intellij/contextive/src/main/kotlin/tech/contextive/contextive/LanguageServerDownloader.kt new file mode 100644 index 00000000..b95a71ea --- /dev/null +++ b/src/intellij/contextive/src/main/kotlin/tech/contextive/contextive/LanguageServerDownloader.kt @@ -0,0 +1,90 @@ +package tech.contextive.contextive + +import com.intellij.ide.plugins.PluginManagerCore +import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.extensions.PluginId +import net.lingala.zip4j.io.inputstream.ZipInputStream +import net.lingala.zip4j.model.LocalFileHeader +import java.io.FileOutputStream +import java.net.URI +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.attribute.PosixFilePermission +import kotlin.io.path.exists +import kotlin.io.path.getPosixFilePermissions +import kotlin.io.path.setPosixFilePermissions + + +private val LOG = logger() + +private const val LANGUAGE_SERVER_TEMPLATE = + "https://github.com/dev-cycles/contextive/releases/download/v%s/Contextive.LanguageServer-%s-%s-%1\$s.zip" +private const val CONTEXTIVE_ID = "tech.contextive.contextive" + +private fun getOsCode(): String = System.getProperty("os.name").lowercase().run { + when { + "win" in this -> "win" + "mac" in this -> "osx" + else -> "linux" + } +} + +private fun getArchCode(): String = System.getProperty("os.arch").lowercase().run { + when { + "aarch64" in this -> "arm64" + else -> "x64" + } +} + +private fun getLanguageServerFileName(): String = "Contextive.LanguageServer" + getOsCode().run { + when { + "win" in this -> ".exe" + else -> "" + } +} + +fun getLsPath(): Path = + PluginManagerCore + .getPlugin(PluginId.getId(CONTEXTIVE_ID))!!.run { + this.pluginPath.resolve("language-server") + .resolve(this.version).resolve(getLanguageServerFileName()) + } + + +private fun downloadLanguageServer(languageServerZipUrl: String, lsPath: Path) { + var localFileHeader: LocalFileHeader? + val readBuffer = ByteArray(4096) + var readLen: Int + + var destination = lsPath.parent + + LOG.info("Downloading LanguageServer from $languageServerZipUrl") + Files.createDirectories(destination) + val zipInputStream = ZipInputStream(URI(languageServerZipUrl).toURL().openStream()) + while (zipInputStream.nextEntry.also { localFileHeader = it } != null) { + val extractedFile = destination.resolve(localFileHeader!!.fileName).toFile() + LOG.info("Extracting `${localFileHeader!!.fileName}` to `${extractedFile.path}`") + FileOutputStream(extractedFile).use { outputStream -> + while (zipInputStream.read(readBuffer).also { readLen = it } != -1) { + outputStream.write(readBuffer, 0, readLen) + } + } + } + lsPath.setPosixFilePermissions(lsPath.getPosixFilePermissions().plus(PosixFilePermission.OWNER_EXECUTE)) + LOG.info("LanguageServer downloaded and extracted.") +} + +fun downloadLanguageServerIfNotFound(): Path { + val lsPath = getLsPath() + val plugin = PluginManagerCore.getPlugin(PluginId.getId(CONTEXTIVE_ID))!! + LOG.info("Looking for LanguageServer at `${lsPath}`") + if (!lsPath.exists()) { + val url = LANGUAGE_SERVER_TEMPLATE.format(plugin.version, getOsCode(), getArchCode()) + downloadLanguageServer(url, lsPath) + } else { + LOG.info("Found LanguageServer, not downloading.") + } + return lsPath +} + +class LanguageServerDownloader \ No newline at end of file diff --git a/src/vscode/contextive/.gitignore b/src/vscode/contextive/.gitignore index e875f242..ec405fa2 100644 --- a/src/vscode/contextive/.gitignore +++ b/src/vscode/contextive/.gitignore @@ -7,4 +7,5 @@ !test/index.js **/*.vsix dist -out \ No newline at end of file +out +.idea \ No newline at end of file