Skip to content

Commit

Permalink
Fix for #262 - REPL does not start on windows (#264)
Browse files Browse the repository at this point in the history
* Fix for #262 - REPL does not start on windows

* Rewrite repl manager

* Accurate shutdown and restart
  • Loading branch information
elmot committed Jan 24, 2024
1 parent 130a8f8 commit 5131e6e
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 190 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ abstract class MicroPythonAction : AnAction() {
val project = e.project ?: return
val facet = project.firstMicroPythonFacet
if (facet != null) {
e.presentation.isEnabled = facet.checkValid() == ValidationResult.OK
e.presentation.isEnabledAndVisible = facet.checkValid() == ValidationResult.OK
} else {
e.presentation.isVisible = false
e.presentation.isEnabled = false
e.presentation.isEnabledAndVisible = false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,27 @@

package com.jetbrains.micropython.actions

import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.components.service
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.wm.ToolWindowManager
import com.jetbrains.micropython.repl.MicroPythonReplManager
import com.jetbrains.micropython.settings.firstMicroPythonFacet

class RunMicroReplAction : MicroPythonAction() {

override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT

override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
val editor = FileEditorManagerEx.getInstanceEx(project).selectedTextEditor ?: return

/*
Here we make best effort to find out module which is relevant to the current event.
Here we make our best to find out module which is relevant to the current event.
There are two cases to consider:
1) There is an open file present.
2) No files are opened.
Expand All @@ -48,9 +49,6 @@ class RunMicroReplAction : MicroPythonAction() {
project.firstMicroPythonFacet?.module
}

if (module != null) {
MicroPythonReplManager.getInstance(module).startREPL()
ToolWindowManager.getInstance(project).getToolWindow("MicroPython")?.show()
}
project.service<MicroPythonReplManager>().startOrRestartRepl()
}
}
116 changes: 14 additions & 102 deletions src/main/kotlin/com/jetbrains/micropython/repl/MicroPythonReplManager.kt
Original file line number Diff line number Diff line change
@@ -1,112 +1,24 @@
package com.jetbrains.micropython.repl

import com.intellij.openapi.components.Service
import com.intellij.openapi.module.Module
import com.jediterm.terminal.TtyConnector
import com.jetbrains.micropython.settings.MicroPythonFacet
import com.jetbrains.micropython.settings.microPythonFacet
import com.pty4j.PtyProcess
import org.jetbrains.plugins.terminal.LocalTerminalDirectRunner
import org.jetbrains.plugins.terminal.ShellStartupOptions
import com.intellij.openapi.project.Project
import com.intellij.util.messages.Topic

interface CommsEventListener {
fun onProcessStarted(ttyConnector: TtyConnector)
fun onProcessDestroyed()
fun onProcessCreationFailed(reason: String)
}

@Service
class MicroPythonReplManager(module: Module) {
private val currentModule: Module = module
private var listeners: MutableList<CommsEventListener> = mutableListOf()
private var currentProcess: Process? = null
private var currentConnector: TtyConnector? = null

companion object {
fun getInstance(module: Module): MicroPythonReplManager =
module.getService(MicroPythonReplManager::class.java)
}

fun startREPL() {
if (isRunning) {
stopREPL()
}

val facet = currentModule.microPythonFacet ?: return
val devicePath = facet.getOrDetectDevicePathSynchronously()

if (facet.pythonPath == null) {
notifyProcessCreationFailed("Valid Python interpreter is needed to start a REPL!")
return
}

if (devicePath == null) {
notifyProcessCreationFailed("Device path is not specified, please check settings.")
return
}

val initialShellCommand = mutableListOf(
facet.pythonPath!!,
"${MicroPythonFacet.scriptsPath}/microrepl.py",
devicePath
)

val terminalRunner = object : LocalTerminalDirectRunner(currentModule.project) {
override fun getInitialCommand(envs: MutableMap<String, String>): MutableList<String> {
return initialShellCommand
}

fun getTtyConnector(process: PtyProcess): TtyConnector {
return this.createTtyConnector(process)
}
}

synchronized(this) {
val terminalOptions = ShellStartupOptions.Builder()
.workingDirectory(devicePath)
.shellCommand(initialShellCommand)
.build()
val process = terminalRunner.createProcess(terminalOptions)
val ttyConnector = terminalRunner.getTtyConnector(process)

currentProcess = process
currentConnector = ttyConnector
notifyProcessStarted(ttyConnector)
}
}

fun stopREPL() {
synchronized(this) {
currentProcess?.let {
it.destroy()
notifyProcessDestroyed()
}
currentProcess = null
}
}

val isRunning: Boolean
get() = currentProcess?.isAlive ?: false

fun addListener(listener: CommsEventListener) {
listeners.add(listener)
interface MicroPythonReplControl {
fun stopRepl()
fun startOrRestartRepl()
}

currentConnector?.let { listener.onProcessStarted(it) }
}
@Service(Service.Level.PROJECT)
class MicroPythonReplManager(private val project: Project) : MicroPythonReplControl {
override fun stopRepl() =
project.messageBus.syncPublisher(MICROPYTHON_REPL_CONTROL).stopRepl()

fun removeListener(listener: CommsEventListener) {
listeners.remove(listener)
}

private fun notifyProcessStarted(connector: TtyConnector) {
listeners.forEach { it.onProcessStarted(connector) }
}
override fun startOrRestartRepl() =
project.messageBus.syncPublisher(MICROPYTHON_REPL_CONTROL).startOrRestartRepl()

private fun notifyProcessDestroyed() {
listeners.forEach { it.onProcessDestroyed() }
}
}

private fun notifyProcessCreationFailed(reason: String) {
listeners.forEach { it.onProcessCreationFailed(reason) }
}
}
val MICROPYTHON_REPL_CONTROL = Topic(MicroPythonReplControl::class.java)
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import com.intellij.execution.BeforeRunTaskProvider
import com.intellij.execution.configurations.RunConfiguration
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.LangDataKeys
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.components.service
import com.intellij.openapi.util.Key
import com.jetbrains.micropython.run.MicroPythonRunConfiguration
import com.jetbrains.micropython.settings.MicroPythonFacetType
Expand Down Expand Up @@ -38,21 +37,14 @@ class StopReplBeforeRunTaskProvider : BeforeRunTaskProvider<StopReplBeforeRunTas
}

override fun executeTask(
context: DataContext,
configuration: RunConfiguration,
environment: ExecutionEnvironment,
task: StopReplBeforeRunTask
context: DataContext,
configuration: RunConfiguration,
environment: ExecutionEnvironment,
task: StopReplBeforeRunTask
): Boolean {
if (configuration is MicroPythonRunConfiguration) {
ApplicationManager.getApplication().invokeLater {
configuration.module?.let {
ApplicationManager.getApplication().runWriteAction {
MicroPythonReplManager.getInstance(it).stopREPL()
}
}
}
environment.project.service<MicroPythonReplManager>().stopRepl()
}

return true
}

Expand Down
Loading

0 comments on commit 5131e6e

Please sign in to comment.