Skip to content

Commit

Permalink
debug: get to know how IJ debugger work
Browse files Browse the repository at this point in the history
  • Loading branch information
imkiva committed Sep 16, 2022
1 parent 94d2060 commit 090ca8a
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 7 deletions.
63 changes: 63 additions & 0 deletions src/main/java/org/aya/intellij/debug/AyaDebugProcess.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.aya.intellij.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.AyaFileType;
import org.aya.intellij.run.AyaProgramRunner;
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);
}
}
}
31 changes: 31 additions & 0 deletions src/main/java/org/aya/intellij/debug/AyaExecutionStack.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.aya.intellij.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);
}
}
}
10 changes: 10 additions & 0 deletions src/main/java/org/aya/intellij/debug/AyaSuspendState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.aya.intellij.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);
}
}
71 changes: 71 additions & 0 deletions src/main/java/org/aya/intellij/run/AyaProgramRunner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.aya.intellij.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.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<GenericDebuggerRunnerSettings> {
@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<RunContentDescriptor> 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");
}
}
}
9 changes: 2 additions & 7 deletions src/main/java/org/aya/intellij/run/TyckRunConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.aya.intellij.run;

import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.actions.LazyRunConfigurationProducer;
Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<runLineMarkerContributor
language="Aya"
implementationClass="org.aya.intellij.run.AyaRunLineMarkerContributor"/>
<programRunner implementation="org.aya.intellij.run.AyaProgramRunner"/>

<!-- settings -->
<applicationConfigurable
Expand Down

0 comments on commit 090ca8a

Please sign in to comment.