diff --git a/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellBreakpointHandler.kt b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellBreakpointHandler.kt new file mode 100644 index 00000000..2dc95415 --- /dev/null +++ b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellBreakpointHandler.kt @@ -0,0 +1,58 @@ +package com.intellij.plugin.powershell.ide.debugger + +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.MessageType +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.xdebugger.XDebugSession +import com.intellij.xdebugger.breakpoints.XBreakpointHandler +import com.intellij.xdebugger.breakpoints.XBreakpointProperties +import com.intellij.xdebugger.breakpoints.XBreakpointType +import com.intellij.xdebugger.breakpoints.XLineBreakpoint + +class PowerShellBreakpointHandler(powerShellDebugProcess: PowerShellDebugProcess, breakpointTypeClass: Class>, *>>): XBreakpointHandler>>( + breakpointTypeClass +) { + val myPowerShellDebugProcess = powerShellDebugProcess; + + override fun registerBreakpoint(breakpoint: XLineBreakpoint>) { + val sourcePosition = breakpoint.sourcePosition + if (sourcePosition == null || !sourcePosition.file.exists() || !sourcePosition.file.isValid) { + return + } + val file = sourcePosition.file + val project: Project = myPowerShellDebugProcess.getSession().getProject() + val fileURL: String = getFileURL(file) + val lineNumber: Int = breakpoint.line + if (lineNumber == -1) { + //myXsltDebugProcess.getSession().setBreakpointInvalid(breakpoint, "Unsupported breakpoint position") + return + } + /*try { + val manager: BreakpointManager = myPowerShellDebugProcess.getBreakpointManager() + var bp: Breakpoint + if ((manager.getBreakpoint(fileURL, lineNumber).also { bp = it }) != null) { + bp.setEnabled(true) + } else { + manager.setBreakpoint(fileURL, lineNumber) + } + } catch (ignore: DebuggerStoppedException) { + } catch (e: VMPausedException) { + val session: XDebugSession = myXsltDebugProcess.getSession() + session.reportMessage( + XsltDebuggerBundle.message("notification.content.target.vm.not.responding.breakpoint.can.not.be.set"), + MessageType.ERROR + ) + session.setBreakpointInvalid(breakpoint, "Target VM is not responding. Breakpoint can not be set") + }*/ + } + + override fun unregisterBreakpoint(breakpoint: XLineBreakpoint>, temporary: Boolean) { + TODO("Not yet implemented") + } + + fun getFileURL(file: VirtualFile?): String { + return VfsUtil.virtualToIoFile(file!!).toURI().toASCIIString() + } + +} diff --git a/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellBreakpointType.kt b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellBreakpointType.kt new file mode 100644 index 00000000..fda64a3c --- /dev/null +++ b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellBreakpointType.kt @@ -0,0 +1,28 @@ +package com.intellij.plugin.powershell.ide.debugger + +import com.intellij.ide.highlighter.XmlFileType +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.plugin.powershell.PowerShellFileType +import com.intellij.plugin.powershell.ide.MessagesBundle +import com.intellij.psi.PsiDocumentManager +import com.intellij.xdebugger.breakpoints.XBreakpointProperties +import com.intellij.xdebugger.breakpoints.XLineBreakpointType + +//XsltDebuggerBundle.message("title.xslt.breakpoints") +class PowerShellBreakpointType : XLineBreakpointType>("powershell", MessagesBundle.message("powershell.debugger.breakpoints.title")) { + override fun canPutAt(file: VirtualFile, line: Int, project: Project): Boolean { + val document = FileDocumentManager.getInstance().getDocument(file) ?: return false + + val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document) ?: return false + val fileType = psiFile.fileType + if (fileType != PowerShellFileType.INSTANCE) { + return false + } + return true + } + override fun createBreakpointProperties(file: VirtualFile, line: Int): XBreakpointProperties<*>? { + return null + } +} diff --git a/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellDebugProcess.kt b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellDebugProcess.kt new file mode 100644 index 00000000..3667e8ed --- /dev/null +++ b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellDebugProcess.kt @@ -0,0 +1,47 @@ +package com.intellij.plugin.powershell.ide.debugger + +import com.intellij.execution.ExecutionResult +import com.intellij.execution.ui.ExecutionConsole +import com.intellij.openapi.Disposable +import com.intellij.openapi.util.Key +import com.intellij.xdebugger.XDebugProcess +import com.intellij.xdebugger.XDebugSession +import com.intellij.xdebugger.breakpoints.XBreakpointHandler +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider + +class PowerShellDebugProcess(session: XDebugSession, executionResult: ExecutionResult) : XDebugProcess(session), Disposable { + + val KEY: Key = Key.create("com.intellij.plugin.powershell.ide.debugger.PowerShellDebugProcess") + + val myProcessHandler = executionResult.processHandler + init { + myProcessHandler.putUserData(KEY, this) + } + val myExecutionConsole = executionResult.executionConsole + val myEditorsProvider = PowerShellDebuggerEditorsProvider() + init { + com.intellij.openapi.util.Disposer.register(myExecutionConsole, this) + } + + private val myXBreakpointHandlers = arrayOf>( + PowerShellBreakpointHandler( + this, + PowerShellBreakpointType::class.java + ), + ) + + override fun createConsole(): ExecutionConsole { + return myExecutionConsole + } + override fun getEditorsProvider(): XDebuggerEditorsProvider { + return myEditorsProvider + } + + override fun getBreakpointHandlers(): Array> { + return myXBreakpointHandlers + } + + override fun dispose() { + TODO("Not yet implemented") + } +} diff --git a/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellDebuggerEditorsProvider.kt b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellDebuggerEditorsProvider.kt new file mode 100644 index 00000000..141e7521 --- /dev/null +++ b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellDebuggerEditorsProvider.kt @@ -0,0 +1,38 @@ +package com.intellij.plugin.powershell.ide.debugger + +import com.intellij.openapi.editor.Document +import com.intellij.openapi.fileTypes.FileType +import com.intellij.openapi.project.Project +import com.intellij.plugin.powershell.PowerShellFileType +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFileFactory +import com.intellij.util.LocalTimeCounter +import com.intellij.xdebugger.XExpression +import com.intellij.xdebugger.XSourcePosition +import com.intellij.xdebugger.evaluation.EvaluationMode +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider + +class PowerShellDebuggerEditorsProvider: XDebuggerEditorsProvider() { + private var myFileType: PowerShellFileType = PowerShellFileType.INSTANCE + + override fun getFileType(): FileType { + return myFileType + } + + override fun createDocument( + project: Project, + expression: XExpression, + sourcePosition: XSourcePosition?, + mode: EvaluationMode + ): Document { + val psiFile = PsiFileFactory.getInstance(project) + .createFileFromText( + "pwsh." + myFileType.getDefaultExtension(), myFileType, expression.expression, + LocalTimeCounter.currentTime(), true + ) + + val document = checkNotNull(PsiDocumentManager.getInstance(project).getDocument(psiFile)) + return document + } +} diff --git a/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellSourcePosition.kt b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellSourcePosition.kt new file mode 100644 index 00000000..b9b36a24 --- /dev/null +++ b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellSourcePosition.kt @@ -0,0 +1,8 @@ +package com.intellij.plugin.powershell.ide.debugger + +import com.intellij.xdebugger.XSourcePosition +import com.intellij.xdebugger.XSourcePositionWrapper + +class PowerShellSourcePosition(position: XSourcePosition) : XSourcePositionWrapper(position) { + +} diff --git a/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellProgramDebugRunner.kt b/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellProgramDebugRunner.kt new file mode 100644 index 00000000..5db80769 --- /dev/null +++ b/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellProgramDebugRunner.kt @@ -0,0 +1,60 @@ +package com.intellij.plugin.powershell.ide.run + +import com.intellij.execution.ExecutionException +import com.intellij.execution.ExecutionResult +import com.intellij.execution.configurations.RunProfile +import com.intellij.execution.configurations.RunProfileState +import com.intellij.execution.configurations.RunnerSettings +import com.intellij.execution.executors.DefaultDebugExecutor +import com.intellij.execution.runners.AsyncProgramRunner +import com.intellij.execution.runners.ExecutionEnvironment +import com.intellij.execution.ui.RunContentDescriptor +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.rd.util.toPromise +import com.intellij.openapi.rd.util.withUiContext +import com.intellij.plugin.powershell.ide.PluginProjectRoot +import com.intellij.plugin.powershell.ide.debugger.PowerShellDebugProcess +import com.intellij.xdebugger.XDebugProcess +import com.intellij.xdebugger.XDebugProcessStarter +import com.intellij.xdebugger.XDebugSession +import com.intellij.xdebugger.XDebuggerManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async +import org.jetbrains.concurrency.Promise +import java.lang.Boolean +import kotlin.OptIn +import kotlin.String +import kotlin.Throws + +/** + * The main purpose of this runner is to call [RunProfileState.execute] or a background thread instead of a foreground + * one, as our [RunProfileState] implementation requires FS access that's only possible from the background. + */ +class PowerShellProgramDebugRunner : AsyncProgramRunner() { + + override fun getRunnerId() = "com.intellij.plugin.powershell.ide.run.PowerShellProgramDebugRunner" + + override fun canRun(executorId: String, profile: RunProfile) = + executorId == DefaultDebugExecutor.EXECUTOR_ID && profile is PowerShellRunConfiguration + + @OptIn(ExperimentalCoroutinesApi::class) + override fun execute(environment: ExecutionEnvironment, state: RunProfileState): Promise = + PluginProjectRoot.getInstance(environment.project).coroutineScope.async(Dispatchers.Default) { + state as PowerShellScriptCommandLineState + withUiContext { + FileDocumentManager.getInstance().saveAllDocuments() + } + state.prepareExecution() + val executionResult = state.execute(environment.executor, this@PowerShellProgramDebugRunner) + val descriptor = withUiContext { + XDebuggerManager.getInstance(environment.project).startSession(environment, object : XDebugProcessStarter() { + @Throws(ExecutionException::class) + override fun start(session: XDebugSession): XDebugProcess { + return PowerShellDebugProcess(session, executionResult) + } + }).runContentDescriptor + } + descriptor + }.toPromise() +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 7710a239..ae6fbcc7 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -20,10 +20,6 @@ - - @@ -69,7 +65,10 @@ + + + diff --git a/src/main/resources/messages/MessagesBundle.properties b/src/main/resources/messages/MessagesBundle.properties index 7d8deb5d..b9c119a7 100644 --- a/src/main/resources/messages/MessagesBundle.properties +++ b/src/main/resources/messages/MessagesBundle.properties @@ -31,3 +31,5 @@ wrapping.attribute.argument=Attribute arguments wrapping.block.parameters=Block parameters wrapping.catch,type.list=Catch type list wrapping.pipeline=Pipeline + +powershell.debugger.breakpoints.title=Breakpoints