generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: initial Intelij plugin #2564
Merged
+782
−0
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
plugins { | ||
id("java") | ||
id("org.jetbrains.kotlin.jvm") version "1.9.24" | ||
id("org.jetbrains.intellij") version "1.17.3" | ||
} | ||
|
||
group = "xyz.block.ftl" | ||
version = "1.0-SNAPSHOT" | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
// Configure Gradle IntelliJ Plugin | ||
// Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html | ||
intellij { | ||
version.set("2024.1.3") | ||
type.set("IU") // Target IDE Platform | ||
|
||
plugins.set(listOf(/* Plugin Dependencies */)) | ||
} | ||
|
||
tasks { | ||
// Set the JVM compatibility versions | ||
withType<JavaCompile> { | ||
sourceCompatibility = "17" | ||
targetCompatibility = "17" | ||
} | ||
withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> { | ||
kotlinOptions.jvmTarget = "17" | ||
} | ||
|
||
patchPluginXml { | ||
sinceBuild.set("241") | ||
untilBuild.set("242.*") | ||
} | ||
|
||
signPlugin { | ||
certificateChain.set(System.getenv("CERTIFICATE_CHAIN")) | ||
privateKey.set(System.getenv("PRIVATE_KEY")) | ||
password.set(System.getenv("PRIVATE_KEY_PASSWORD")) | ||
} | ||
|
||
publishPlugin { | ||
token.set(System.getenv("PUBLISH_TOKEN")) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib | ||
kotlin.stdlib.default.dependency = false | ||
|
||
# Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html | ||
org.gradle.configuration-cache = true | ||
|
||
# Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html | ||
org.gradle.caching = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
pluginManagement { | ||
repositories { | ||
mavenCentral() | ||
gradlePluginPortal() | ||
} | ||
} | ||
|
||
rootProject.name = "intellij" |
10 changes: 10 additions & 0 deletions
10
extensions/intellij/src/main/kotlin/xyz/block/ftl/intellij/CustomLsp4jClient.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package xyz.block.ftl.intellij | ||
|
||
import com.intellij.platform.lsp.api.Lsp4jClient | ||
import com.intellij.platform.lsp.api.LspServerNotificationsHandler | ||
|
||
class CustomLsp4jClient(handler: LspServerNotificationsHandler) : Lsp4jClient(handler) { | ||
override fun telemetryEvent(`object`: Any) { | ||
super.telemetryEvent(`object`) | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
extensions/intellij/src/main/kotlin/xyz/block/ftl/intellij/FTL.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package xyz.block.ftl.intellij | ||
|
||
import com.intellij.openapi.application.ApplicationManager | ||
|
||
fun runOnEDT(runnable: () -> Unit) { | ||
ApplicationManager.getApplication().invokeLater { | ||
runnable() | ||
} | ||
} |
86 changes: 86 additions & 0 deletions
86
extensions/intellij/src/main/kotlin/xyz/block/ftl/intellij/FTLLspServerDescriptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package xyz.block.ftl.intellij | ||
|
||
import com.intellij.execution.configurations.GeneralCommandLine | ||
import com.intellij.execution.process.OSProcessHandler | ||
import com.intellij.execution.process.ProcessAdapter | ||
import com.intellij.execution.process.ProcessEvent | ||
import com.intellij.ide.DataManager | ||
import com.intellij.openapi.project.Project | ||
import com.intellij.openapi.util.Key | ||
import com.intellij.openapi.vfs.VirtualFile | ||
import com.intellij.openapi.wm.ToolWindowManager | ||
import com.intellij.platform.lsp.api.LspServerNotificationsHandler | ||
import com.intellij.platform.lsp.api.ProjectWideLspServerDescriptor | ||
import com.intellij.tools.ToolsCustomizer | ||
import xyz.block.ftl.intellij.toolWindow.FTLMessagesToolWindowFactory | ||
import java.util.concurrent.CompletableFuture | ||
import java.util.regex.Pattern | ||
|
||
class FTLLspServerDescriptor(project: Project) : ProjectWideLspServerDescriptor(project, "FTL") { | ||
override fun isSupportedFile(file: VirtualFile) = file.extension == "go" | ||
|
||
override fun createLsp4jClient(handler: LspServerNotificationsHandler): CustomLsp4jClient { | ||
return CustomLsp4jClient(handler) | ||
} | ||
|
||
override fun createCommandLine(): GeneralCommandLine { | ||
val settings = AppSettings.getInstance().state | ||
val generalCommandLine = | ||
GeneralCommandLine(listOf(settings.lspServerPath) + settings.lspServerArguments.split(Pattern.compile("\\s+"))) | ||
generalCommandLine.setWorkDirectory(project.basePath) | ||
displayMessageInToolWindow("LSP Server Command: " + generalCommandLine.commandLineString) | ||
displayMessageInToolWindow("Working Directory: " + generalCommandLine.workDirectory) | ||
try { | ||
// Hermit support, we need to get the environment variables so we use the correct FTL | ||
val result = CompletableFuture<GeneralCommandLine>() | ||
runOnEDT { | ||
val toolWindow = ToolWindowManager.getInstance(project).getToolWindow("FTL") | ||
if (toolWindow != null) { | ||
val dataContext = DataManager.getInstance().getDataContext(toolWindow.component) | ||
val customizeCommandLine = | ||
ToolsCustomizer.customizeCommandLine(generalCommandLine, dataContext) | ||
result.complete(customizeCommandLine) | ||
} | ||
} | ||
val res = result.get() | ||
return if (res != null) res else generalCommandLine | ||
} catch (e: Exception) { | ||
displayMessageInToolWindow("Failed to customize LSP Server Command: " + e.message) | ||
} | ||
return generalCommandLine | ||
} | ||
|
||
override fun startServerProcess(): OSProcessHandler { | ||
displayMessageInToolWindow("Starting FTL LSP Server") | ||
val processHandler = super.startServerProcess() | ||
processHandler.addProcessListener(object : ProcessAdapter() { | ||
|
||
override fun startNotified(event: ProcessEvent) { | ||
super.startNotified(event) | ||
displayMessageInToolWindow("LSP Started") | ||
} | ||
|
||
override fun processTerminated(event: ProcessEvent) { | ||
super.processTerminated(event) | ||
displayMessageInToolWindow("LSP Terminated") | ||
} | ||
|
||
override fun processWillTerminate(event: ProcessEvent, willBeDestroyed: Boolean) { | ||
super.processWillTerminate(event, willBeDestroyed) | ||
displayMessageInToolWindow("LSP Will Terminate") | ||
} | ||
|
||
override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { | ||
val message = event.text.trim() | ||
if (message.isNotBlank()) { | ||
displayMessageInToolWindow(message) | ||
} | ||
} | ||
}) | ||
return processHandler | ||
} | ||
|
||
private fun displayMessageInToolWindow(message: String) { | ||
FTLMessagesToolWindowFactory.Util.displayMessageInToolWindow(project, message) | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
extensions/intellij/src/main/kotlin/xyz/block/ftl/intellij/FTLLspServerService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package xyz.block.ftl.intellij | ||
|
||
import com.intellij.openapi.components.Service | ||
import com.intellij.openapi.project.Project | ||
|
||
@Service(Service.Level.PROJECT) | ||
class FTLLspServerService(val project: Project) { | ||
val lspServerSupportProvider = FTLLspServerSupportProvider() | ||
|
||
companion object { | ||
fun getInstance(project: Project): FTLLspServerService { | ||
return project.getService(FTLLspServerService::class.java) | ||
} | ||
} | ||
} |
123 changes: 123 additions & 0 deletions
123
extensions/intellij/src/main/kotlin/xyz/block/ftl/intellij/FTLLspServerSupportProvider.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package xyz.block.ftl.intellij | ||
|
||
import com.intellij.execution.configurations.GeneralCommandLine | ||
import com.intellij.execution.process.OSProcessHandler | ||
import com.intellij.icons.AllIcons.Icons | ||
import com.intellij.openapi.project.Project | ||
import com.intellij.openapi.vfs.VirtualFile | ||
import com.intellij.platform.lsp.api.LspServer | ||
import com.intellij.platform.lsp.api.LspServerDescriptor.Companion.LOG | ||
import com.intellij.platform.lsp.api.LspServerManager | ||
import com.intellij.platform.lsp.api.LspServerManagerListener | ||
import com.intellij.platform.lsp.api.LspServerState | ||
import com.intellij.platform.lsp.api.LspServerSupportProvider | ||
import com.intellij.platform.lsp.api.lsWidget.LspServerWidgetItem | ||
import com.intellij.util.io.BaseOutputReader | ||
import com.intellij.util.messages.Topic | ||
import xyz.block.ftl.intellij.toolWindow.FTLMessagesToolWindowFactory.Util.displayMessageInToolWindow | ||
import java.util.regex.Pattern | ||
|
||
interface FTLLSPNotifier { | ||
fun lspServerStateChange(state: LspServerState) | ||
|
||
companion object { | ||
@Topic.ProjectLevel | ||
val SERVER_STATE_CHANGE_TOPIC: Topic<FTLLSPNotifier> = Topic.create( | ||
"FTL Server State Changed", | ||
FTLLSPNotifier::class.java | ||
) | ||
} | ||
} | ||
|
||
class FTLLspServerSupportProvider : LspServerSupportProvider { | ||
private var listenerAdded: Boolean = false | ||
|
||
override fun createLspServerWidgetItem(lspServer: LspServer, currentFile: VirtualFile?): LspServerWidgetItem = | ||
LspServerWidgetItem( | ||
lspServer = lspServer, | ||
currentFile = currentFile, | ||
settingsPageClass = FTLSettingsConfigurable::class.java, | ||
widgetMainActionBaseIcon = Icons.Ide.MenuArrow | ||
) | ||
|
||
override fun fileOpened( | ||
project: Project, | ||
file: VirtualFile, | ||
serverStarter: LspServerSupportProvider.LspServerStarter | ||
) { | ||
if (!listenerAdded) { | ||
try { | ||
listenerAdded = true | ||
val lspServerManager = LspServerManager.getInstance(project) | ||
lspServerManager.addLspServerManagerListener(listener = object : LspServerManagerListener { | ||
override fun serverStateChanged(lspServer: LspServer) { | ||
val publisher = project.messageBus.syncPublisher(FTLLSPNotifier.SERVER_STATE_CHANGE_TOPIC) | ||
publisher.lspServerStateChange(lspServer.state) | ||
} | ||
}, parentDisposable = { }, sendEventsForExistingServers = true) | ||
} catch (e: Exception) { | ||
listenerAdded = false | ||
} | ||
} | ||
|
||
val isFtlSupportLanguage = file.extension == "go" || file.extension == "kt" || file.extension == "java" | ||
if (isFtlSupportLanguage && hasFtlProjectFile(project)) { | ||
serverStarter.ensureServerStarted(FTLLspServerDescriptor(project)) | ||
} | ||
} | ||
|
||
private fun hasFtlProjectFile(project: Project): Boolean { | ||
val projectBaseDir = project.baseDir ?: return false | ||
val ftlProjectFile = projectBaseDir.findChild("ftl-project.toml") | ||
return ftlProjectFile != null && ftlProjectFile.exists() | ||
} | ||
|
||
fun startLspServer(project: Project) { | ||
val lspServerManager = LspServerManager.getInstance(project) | ||
lspServerManager.startServersIfNeeded(FTLLspServerSupportProvider::class.java) | ||
} | ||
|
||
fun stopLspServer(project: Project): OSProcessHandler? { | ||
return when (getLspServerStatus(project)) { | ||
LspServerState.ShutdownUnexpectedly -> { | ||
stopViaCommand(project) | ||
} | ||
|
||
else -> { | ||
val lspServerManager = LspServerManager.getInstance(project) | ||
lspServerManager.stopServers(FTLLspServerSupportProvider::class.java) | ||
null | ||
} | ||
} | ||
} | ||
|
||
private fun stopViaCommand(project: Project): OSProcessHandler { | ||
val settings = AppSettings.getInstance().state | ||
val generalCommandLine = | ||
GeneralCommandLine(listOf(settings.lspServerPath) + settings.lspServerStopArguments.split(Pattern.compile("\\s+"))).withCharset( | ||
Charsets.UTF_8 | ||
) | ||
generalCommandLine.setWorkDirectory(project.basePath) | ||
displayMessageInToolWindow(project, "LSP Server Command: " + generalCommandLine.commandLineString) | ||
displayMessageInToolWindow(project, "Working Directory: " + generalCommandLine.workDirectory) | ||
|
||
LOG.info("$this: stopping LSP server: $generalCommandLine") | ||
val process: OSProcessHandler = object : OSProcessHandler(generalCommandLine) { | ||
override fun readerOptions(): BaseOutputReader.Options = BaseOutputReader.Options.forMostlySilentProcess() | ||
} | ||
|
||
return process | ||
} | ||
|
||
fun restartLspServer(project: Project) { | ||
val lspServerManager = LspServerManager.getInstance(project) | ||
lspServerManager.stopAndRestartIfNeeded(FTLLspServerSupportProvider::class.java) | ||
} | ||
|
||
fun getLspServerStatus(project: Project): LspServerState { | ||
val lspServerManager = LspServerManager.getInstance(project) | ||
val server = lspServerManager.getServersForProvider(FTLLspServerSupportProvider::class.java).firstOrNull() | ||
|
||
return server?.state ?: LspServerState.ShutdownNormally | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
extensions/intellij/src/main/kotlin/xyz/block/ftl/intellij/FTLSettings.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package xyz.block.ftl.intellij | ||
|
||
import com.intellij.openapi.application.ApplicationManager | ||
import com.intellij.openapi.components.PersistentStateComponent | ||
import com.intellij.openapi.components.Service | ||
import com.intellij.openapi.components.State | ||
import com.intellij.openapi.components.Storage | ||
import org.jetbrains.annotations.NonNls | ||
|
||
@State( | ||
name = "org.intellij.sdk.settings.AppSettings", | ||
storages = [Storage("SdkSettingsPlugin.xml")] | ||
) | ||
@Service | ||
class AppSettings : PersistentStateComponent<AppSettings.State> { | ||
|
||
data class State( | ||
@NonNls var lspServerPath: String = "ftl", | ||
var lspServerArguments: String = "--recreate --lsp", | ||
var lspServerStopArguments: String = "serve --stop", | ||
var autoRestartLspServer: Boolean = false, | ||
) | ||
|
||
private var myState = State() | ||
|
||
companion object { | ||
fun getInstance(): AppSettings { | ||
return ApplicationManager.getApplication().getService(AppSettings::class.java) | ||
} | ||
} | ||
|
||
override fun getState(): State { | ||
return myState | ||
} | ||
|
||
override fun loadState(state: State) { | ||
myState = state | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How long does this take? Copy pasta the
if
statement from other jobs if this is going to take more than a minute or two, so we can keep the main PR loop fast.