diff --git a/src/main/java/org/aya/intellij/actions/debug/AyaDebugProcess.java b/src/main/java/org/aya/intellij/actions/debug/AyaDebugProcess.java new file mode 100644 index 0000000..514fb49 --- /dev/null +++ b/src/main/java/org/aya/intellij/actions/debug/AyaDebugProcess.java @@ -0,0 +1,63 @@ +package org.aya.intellij.actions.debug; + +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.Project; +import com.intellij.psi.JavaCodeFragmentFactory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XSourcePosition; +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProviderBase; +import com.intellij.xdebugger.frame.XSuspendContext; +import org.aya.intellij.actions.run.AyaProgramRunner; +import org.aya.intellij.language.AyaFileType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class AyaDebugProcess extends XDebugProcess { + public AyaDebugProcess(@NotNull XDebugSession session, @NotNull AyaProgramRunner.AyaRunState state) { + super(session); + } + + @Override public void sessionInitialized() { + getSession().positionReached(new AyaSuspendState()); + } + + @Override public void resume(@Nullable XSuspendContext context) {} + + @Override public void stop() {} + + @Override public void startStepOut(@Nullable XSuspendContext context) { + getSession().positionReached(new AyaSuspendState()); + } + + @Override public void startStepInto(@Nullable XSuspendContext context) { + getSession().positionReached(new AyaSuspendState()); + } + + @Override public void startStepOver(@Nullable XSuspendContext context) { + getSession().positionReached(new AyaSuspendState()); + } + + @Override public void runToPosition(@NotNull XSourcePosition position, @Nullable XSuspendContext context) { + getSession().positionReached(new AyaSuspendState()); + } + + @Override public @NotNull XDebuggerEditorsProvider getEditorsProvider() { + return new AyaEditorsProvider(); + } + + private static final class AyaEditorsProvider extends XDebuggerEditorsProviderBase { + @Override public @NotNull FileType getFileType() { + return AyaFileType.INSTANCE; + } + + @Override + protected PsiFile createExpressionCodeFragment(@NotNull Project project, @NotNull String text, @Nullable PsiElement context, boolean isPhysical) { + // TODO: aya code fragment? + return JavaCodeFragmentFactory.getInstance(project).createExpressionCodeFragment(text, context, null, isPhysical); + } + } +} diff --git a/src/main/java/org/aya/intellij/actions/debug/AyaExecutionStack.java b/src/main/java/org/aya/intellij/actions/debug/AyaExecutionStack.java new file mode 100644 index 0000000..b29c592 --- /dev/null +++ b/src/main/java/org/aya/intellij/actions/debug/AyaExecutionStack.java @@ -0,0 +1,31 @@ +package org.aya.intellij.actions.debug; + +import com.intellij.ui.ColoredTextContainer; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.xdebugger.frame.XExecutionStack; +import com.intellij.xdebugger.frame.XStackFrame; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.List; + +public class AyaExecutionStack extends XExecutionStack { + protected AyaExecutionStack(@NotNull String displayName, @Nullable Icon icon) { + super(displayName, icon); + } + + @Override public @Nullable AyaStackFrame getTopFrame() { + return new AyaStackFrame(); + } + + @Override public void computeStackFrames(int firstFrameIndex, XStackFrameContainer container) { + container.addStackFrames(List.of(new AyaStackFrame(), new AyaStackFrame()), false); + } + + public static final class AyaStackFrame extends XStackFrame { + @Override public void customizePresentation(@NotNull ColoredTextContainer component) { + component.append("Aya stack frame", SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + } +} diff --git a/src/main/java/org/aya/intellij/actions/debug/AyaSuspendState.java b/src/main/java/org/aya/intellij/actions/debug/AyaSuspendState.java new file mode 100644 index 0000000..e336922 --- /dev/null +++ b/src/main/java/org/aya/intellij/actions/debug/AyaSuspendState.java @@ -0,0 +1,10 @@ +package org.aya.intellij.actions.debug; + +import com.intellij.xdebugger.frame.XSuspendContext; +import org.jetbrains.annotations.Nullable; + +public class AyaSuspendState extends XSuspendContext { + @Override public @Nullable AyaExecutionStack getActiveExecutionStack() { + return new AyaExecutionStack("Aya Execution Stack", null); + } +} diff --git a/src/main/java/org/aya/intellij/actions/run/AyaProgramRunner.java b/src/main/java/org/aya/intellij/actions/run/AyaProgramRunner.java new file mode 100644 index 0000000..7e9eb83 --- /dev/null +++ b/src/main/java/org/aya/intellij/actions/run/AyaProgramRunner.java @@ -0,0 +1,71 @@ +package org.aya.intellij.actions.run; + +import com.intellij.debugger.impl.GenericDebuggerRunnerSettings; +import com.intellij.execution.ExecutionException; +import com.intellij.execution.ExecutionResult; +import com.intellij.execution.Executor; +import com.intellij.execution.configurations.RunProfile; +import com.intellij.execution.configurations.RunProfileState; +import com.intellij.execution.executors.DefaultDebugExecutor; +import com.intellij.execution.executors.DefaultRunExecutor; +import com.intellij.execution.runners.AsyncProgramRunner; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.runners.ProgramRunner; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebugProcessStarter; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import org.aya.intellij.actions.debug.AyaDebugProcess; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.concurrency.Promise; +import org.jetbrains.concurrency.Promises; + +/** + * highly inspired from {@link com.intellij.javascript.debugger.execution.DebuggableProgramRunner} + */ +public class AyaProgramRunner extends AsyncProgramRunner { + @Override public @NotNull @NonNls String getRunnerId() { + return AyaProgramRunner.class.getSimpleName(); + } + + @Override public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) { + return (executorId.equals(DefaultDebugExecutor.EXECUTOR_ID) // make "Debug" button clickable + || executorId.equals(DefaultRunExecutor.EXECUTOR_ID) // make "Run" button clickable + ) && profile instanceof TyckRunConfig; + } + + public static @Nullable AyaRunState prepare(@NotNull Executor executor, @NotNull TyckRunConfig config) { + // TODO: what if user just want normal typechecking (rather than traced/debugger)? + if (executor.getId().equals(DefaultRunExecutor.EXECUTOR_ID)) return null; + // Now user want to trace the typechecking process. + return new AyaRunState(config, true); + } + + @Override + protected @NotNull Promise execute(@NotNull ExecutionEnvironment env, @NotNull RunProfileState state) throws ExecutionException { + if (!(state instanceof AyaRunState aya)) + return Promises.rejectedPromise("Trying to run non-aya program with AyaProgramRunner"); + var session = startDebug(env, aya); + return Promises.resolvedPromise(session.getRunContentDescriptor()); + } + + private static @NotNull XDebugSession startDebug(@NotNull ExecutionEnvironment env, @NotNull AyaRunState state) throws ExecutionException { + return XDebuggerManager.getInstance(env.getProject()) + .startSession(env, new XDebugProcessStarter() { + @Override public @NotNull XDebugProcess start(@NotNull XDebugSession session) { + return new AyaDebugProcess(session, state); + } + }); + } + + /** @implNote put aya compiler cmdline arguments here */ + public record AyaRunState(@NotNull TyckRunConfig config, boolean debug) implements RunProfileState { + @Override public @Nullable ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) { + // We do not need to execute here. AyaProgramRunner handles everything. + throw new IllegalStateException("unreachable"); + } + } +} diff --git a/src/main/java/org/aya/intellij/actions/run/TyckRunConfig.java b/src/main/java/org/aya/intellij/actions/run/TyckRunConfig.java index df8ced6..e20be41 100644 --- a/src/main/java/org/aya/intellij/actions/run/TyckRunConfig.java +++ b/src/main/java/org/aya/intellij/actions/run/TyckRunConfig.java @@ -1,6 +1,5 @@ package org.aya.intellij.actions.run; -import com.intellij.execution.ExecutionException; import com.intellij.execution.Executor; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.actions.LazyRunConfigurationProducer; @@ -38,12 +37,8 @@ protected TyckRunConfig(@NotNull Project project, @NotNull ConfigurationFactory } @Override - public @Nullable RunProfileState getState( - @NotNull Executor executor, - @NotNull ExecutionEnvironment environment - ) throws ExecutionException { - // TODO: call Aya tycker - return null; + public @Nullable RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) { + return AyaProgramRunner.prepare(executor, this); } @Override public @Nullable RefactoringElementListener getRefactoringElementListener(PsiElement element) { diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 37876cd..dc99d9c 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -64,6 +64,7 @@ +