Skip to content

Commit 56a2444

Browse files
committed
feat: Attach Qute debugger with inlay hint.
Signed-off-by: azerr <[email protected]>
1 parent 1e84f4d commit 56a2444

File tree

4 files changed

+138
-26
lines changed

4 files changed

+138
-26
lines changed

src/main/java/com/redhat/devtools/intellij/quarkus/run/AttachDebuggerProcessListener.java

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.intellij.execution.executors.DefaultDebugExecutor;
2020
import com.intellij.execution.process.ProcessEvent;
2121
import com.intellij.execution.process.ProcessListener;
22+
import com.intellij.execution.process.ProcessOutputType;
2223
import com.intellij.execution.remote.RemoteConfiguration;
2324
import com.intellij.execution.remote.RemoteConfigurationType;
2425
import com.intellij.execution.runners.ExecutionEnvironment;
@@ -48,7 +49,7 @@
4849
* ProcessListener which tracks the message Listening for transport dt_socket at address: $PORT' to start the
4950
* remote debugger of the given port $PORT.
5051
*/
51-
class AttachDebuggerProcessListener implements ProcessListener {
52+
public class AttachDebuggerProcessListener implements ProcessListener {
5253

5354
private final static Logger LOGGER = LoggerFactory.getLogger(AttachDebuggerProcessListener.class);
5455

@@ -73,30 +74,32 @@ class AttachDebuggerProcessListener implements ProcessListener {
7374

7475
@Override
7576
public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
76-
String message = event.getText();
77-
if (!connected && debugPort != null && message.startsWith(LISTENING_FOR_TRANSPORT_DT_SOCKET_AT_ADDRESS + debugPort)) {
78-
connected = true;
79-
ProgressManager.getInstance().run(new Task.Backgroundable(project, QUARKUS_CONFIGURATION, false) {
80-
@Override
81-
public void run(@NotNull ProgressIndicator indicator) {
82-
String name = env.getRunProfile().getName();
83-
createRemoteConfiguration(indicator, debugPort, name);
77+
if (ProcessOutputType.isStdout(outputType)) {
78+
String message = event.getText();
79+
if (!connected && debugPort != null && message.startsWith(LISTENING_FOR_TRANSPORT_DT_SOCKET_AT_ADDRESS + debugPort)) {
80+
connected = true;
81+
ProgressManager.getInstance().run(new Task.Backgroundable(project, QUARKUS_CONFIGURATION, true) {
82+
@Override
83+
public void run(@NotNull ProgressIndicator indicator) {
84+
String name = env.getRunProfile().getName();
85+
createRemoteConfiguration(indicator, debugPort, name);
86+
}
87+
});
88+
} else if (!quteConnected && message.startsWith(QUTE_LISTENING_ON_PORT)) {
89+
quteConnected = true;
90+
Integer quteDebugPort = getQuteDebugPort(message);
91+
if (quteDebugPort == null) {
92+
LOGGER.error("Cannot extract Qute debug port from the given message: {}", message);
93+
return;
8494
}
85-
});
86-
} else if (!quteConnected && message.startsWith(QUTE_LISTENING_ON_PORT)) {
87-
quteConnected = true;
88-
Integer quteDebugPort = getQuteDebugPort(message);
89-
if (quteDebugPort == null) {
90-
LOGGER.error("Cannot extract Qute debug port from the given message: {}", message);
91-
return;
95+
ProgressManager.getInstance().run(new Task.Backgroundable(project, QUARKUS_CONFIGURATION, true) {
96+
@Override
97+
public void run(@NotNull ProgressIndicator indicator) {
98+
String name = env.getRunProfile().getName();
99+
createQuteConfiguration(indicator, quteDebugPort, name);
100+
}
101+
});
92102
}
93-
ProgressManager.getInstance().run(new Task.Backgroundable(project, QUARKUS_CONFIGURATION, false) {
94-
@Override
95-
public void run(@NotNull ProgressIndicator indicator) {
96-
String name = env.getRunProfile().getName();
97-
createQuteConfiguration(indicator, quteDebugPort, name);
98-
}
99-
});
100103
}
101104
}
102105

@@ -131,9 +134,19 @@ private void createRemoteConfiguration(@NotNull ProgressIndicator indicator, int
131134
}
132135

133136
private void createQuteConfiguration(@NotNull ProgressIndicator indicator, int port, String name) {
137+
createQuteConfiguration(port, name, project, indicator, true);
138+
}
139+
140+
public static void createQuteConfiguration(int port,
141+
@NotNull String name,
142+
@NotNull Project project,
143+
@NotNull ProgressIndicator indicator,
144+
boolean waitForPortAvailable) {
134145
indicator.setText("Connecting Qute debugger to port " + port);
135146
try {
136-
waitForPortAvailable(port, indicator);
147+
if (waitForPortAvailable) {
148+
waitForPortAvailable(port, indicator);
149+
}
137150
RunnerAndConfigurationSettings settings = RunManager.getInstance(project).createConfiguration(name + " (Qute)", QuteConfigurationType.class);
138151
QuteRunConfiguration quteConfiguration = (QuteRunConfiguration) settings.getConfiguration();
139152
quteConfiguration.setAttachPort(Integer.toString(port));
@@ -145,7 +158,7 @@ private void createQuteConfiguration(@NotNull ProgressIndicator indicator, int p
145158
}
146159
}
147160

148-
private void waitForPortAvailable(int port, ProgressIndicator monitor) throws IOException {
161+
private static void waitForPortAvailable(int port, ProgressIndicator monitor) throws IOException {
149162
long start = System.currentTimeMillis();
150163
while (System.currentTimeMillis() - start < 120_000 && !monitor.isCanceled()) {
151164
try (Socket socket = new Socket("localhost", port)) {

src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfigurationManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public static QuarkusRunConfigurationManager getInstance(Project project) {
5858
public QuarkusRunConfigurationManager(Project project) {
5959
this.project = project;
6060
connection = addProjectImportListener(project);
61-
connection.subscribe(ExecutionManager.EXECUTION_TOPIC, new AttachDebuggerExecutionListener(project));
61+
//connection.subscribe(ExecutionManager.EXECUTION_TOPIC, new AttachDebuggerExecutionListener(project));
6262
}
6363

6464
public @Nullable RunnerAndConfigurationSettings findExistingConfigurationFor(@NotNull Module module) {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
package com.redhat.devtools.intellij.qute.run;
3+
4+
import com.intellij.codeInsight.hints.presentation.InlayPresentation;
5+
import com.intellij.codeInsight.hints.presentation.PresentationFactory;
6+
import com.intellij.codeInsight.hints.presentation.PresentationRenderer;
7+
import com.intellij.execution.filters.ConsoleFilterProvider;
8+
import com.intellij.execution.filters.Filter;
9+
import com.intellij.execution.impl.InlayProvider;
10+
import com.intellij.openapi.application.ApplicationManager;
11+
import com.intellij.openapi.application.ModalityState;
12+
import com.intellij.openapi.editor.Editor;
13+
import com.intellij.openapi.editor.EditorCustomElementRenderer;
14+
import com.intellij.openapi.progress.EmptyProgressIndicator;
15+
import com.intellij.openapi.project.Project;
16+
import com.intellij.openapi.util.registry.Registry;
17+
import com.redhat.devtools.intellij.quarkus.run.AttachDebuggerProcessListener;
18+
import org.jetbrains.annotations.NotNull;
19+
import org.jetbrains.annotations.Nullable;
20+
21+
import java.util.Arrays;
22+
23+
public final class QuteDebuggerConsoleFilterProvider implements ConsoleFilterProvider {
24+
25+
@Override
26+
public Filter @NotNull [] getDefaultFilters(@NotNull Project project) {
27+
return new Filter[]{new QuteDebuggerAttachFilter(project)};
28+
}
29+
30+
public static @Nullable Integer getConnectionMatcher(String line) {
31+
if (line.contains("Qute debugger server listening on port ")) {
32+
String port = line.substring("Qute debugger server listening on port ".length()).trim();
33+
return Integer.parseInt(port);
34+
}
35+
return null;
36+
}
37+
38+
private static class QuteDebuggerAttachFilter implements Filter {
39+
@NotNull Project myProject;
40+
41+
private QuteDebuggerAttachFilter(@NotNull Project project) {
42+
this.myProject = project;
43+
}
44+
45+
@Override
46+
public @Nullable Result applyFilter(@NotNull String line, int entireLength) {
47+
Integer port = getConnectionMatcher(line);
48+
if (port == null) {
49+
return null;
50+
}
51+
52+
if (Registry.is("debugger.auto.attach.from.any.console") && !isDebuggerAttached(port, myProject)) {
53+
ApplicationManager.getApplication().invokeLater(
54+
() -> AttachDebuggerProcessListener.createQuteConfiguration(port, "", myProject, new EmptyProgressIndicator(), true),//JavaAttachDebuggerProvider.attach(transport, address, null, myProject),
55+
ModalityState.any());
56+
}
57+
58+
int start = entireLength - line.length();
59+
// to trick the code unwrapping single results in com.intellij.execution.filters.CompositeFilter#createFinalResult
60+
return new Result(Arrays.asList(
61+
new AttachInlayResult(start, start + line.length() - 1, port),
62+
new ResultItem(0, 0, null)));
63+
}
64+
}
65+
66+
private static boolean isDebuggerAttached(Integer port, Project project) {
67+
/*return DebuggerManagerEx.getInstanceEx(project).getSessions()
68+
.stream()
69+
.map(s -> s.getDebugEnvironment().getRemoteConnection())
70+
.anyMatch(c -> address.equals(c.getApplicationAddress()) && "dt_shmem".equals(transport) != c.isUseSockets());
71+
72+
*/
73+
return false;
74+
}
75+
76+
private static class AttachInlayResult extends Filter.ResultItem implements InlayProvider {
77+
private final Integer port;
78+
79+
AttachInlayResult(int highlightStartOffset, int highlightEndOffset, Integer port) {
80+
super(highlightStartOffset, highlightEndOffset, null);
81+
this.port = port;
82+
}
83+
84+
@Override
85+
public EditorCustomElementRenderer createInlayRenderer(Editor editor) {
86+
PresentationFactory factory = new PresentationFactory(editor);
87+
InlayPresentation presentation = factory.referenceOnHover(
88+
factory.roundWithBackground(factory.smallText("Attach Qute debugger")),
89+
(event, point) -> {
90+
AttachDebuggerProcessListener.createQuteConfiguration(port, "", editor.getProject(), new EmptyProgressIndicator(), true);
91+
//JavaDebuggerActionsCollector.attachFromConsoleInlay.log();
92+
//JavaAttachDebuggerProvider.attach(myTransport, myAddress, null, editor.getProject());
93+
});
94+
return new PresentationRenderer(presentation);
95+
}
96+
}
97+
}

src/main/resources/META-INF/lsp4ij-qute.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
implementation="com.redhat.devtools.intellij.qute.run.QuteConfigurationType"/>
116116
<xdebugger.breakpointType
117117
implementation="com.redhat.devtools.intellij.qute.run.QuteBreakpointType"/>
118+
<consoleFilterProvider implementation="com.redhat.devtools.intellij.qute.run.QuteDebuggerConsoleFilterProvider"/>
118119
</extensions>
119120

120121
<extensions defaultExtensionNs="com.redhat.devtools.lsp4ij">
@@ -124,4 +125,5 @@
124125
factoryClass="com.redhat.devtools.intellij.qute.run.QuteDebugAdapterDescriptorFactory" />
125126
</extensions>
126127

128+
127129
</idea-plugin>

0 commit comments

Comments
 (0)