From 312be8e95af5237f45f5dc492384a167f751dc5e Mon Sep 17 00:00:00 2001 From: apple_developer <1@icloud.com> Date: Tue, 17 Mar 2026 18:58:27 +0800 Subject: [PATCH 1/2] fix(idea): avoid ui freeze during method execution Co-Authored-By: Claude Sonnet 4.5 --- .../idea/ui/combobox/ClassLoaderComboBox.java | 63 ++++++++--- .../ui/main/InvokeMethodRecordDialog.java | 75 ++++++------- .../ui/main/InvokeMethodRecordQuickPanel.java | 5 +- .../debug/tools/idea/ui/main/MainDialog.java | 4 + .../tools/idea/ui/main/MainJsonEditor.java | 75 ++++++++++++- .../debug/tools/idea/ui/main/MainPanel.java | 5 +- .../idea/ui/tab/ExceptionTabbedPane.java | 31 +++-- .../tools/idea/ui/tab/ResultTabbedPane.java | 106 ++++++++++++++---- .../idea/ui/tree/ResultDebugTreePanel.java | 46 +++++++- 9 files changed, 300 insertions(+), 110 deletions(-) diff --git a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/combobox/ClassLoaderComboBox.java b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/combobox/ClassLoaderComboBox.java index 57837b73..c0810980 100644 --- a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/combobox/ClassLoaderComboBox.java +++ b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/combobox/ClassLoaderComboBox.java @@ -22,23 +22,25 @@ import com.intellij.openapi.ui.ComboBox; import io.github.future0923.debug.tools.base.utils.DebugToolsThreadUtils; import io.github.future0923.debug.tools.common.protocal.http.AllClassLoaderRes; -import io.github.future0923.debug.tools.idea.action.ExecuteLastWithDefaultClassLoaderEditorPopupMenuAction; import io.github.future0923.debug.tools.idea.client.http.HttpClientUtils; import io.github.future0923.debug.tools.idea.utils.StateUtils; import javax.swing.*; import java.awt.*; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; /** * @author future0923 */ public class ClassLoaderComboBox extends ComboBox { - private static final Logger logger = Logger.getInstance(ExecuteLastWithDefaultClassLoaderEditorPopupMenuAction.class); + private static final Logger logger = Logger.getInstance(ClassLoaderComboBox.class); private final Project project; + private final AtomicInteger refreshVersion = new AtomicInteger(); + public ClassLoaderComboBox(Project project) { this(project, -1, true); } @@ -75,11 +77,48 @@ public Component getListCellRendererComponent(JList list, Object value, int i * @param changeDefaultClassLoader 是否修改默认ClassLoader */ public void refreshClassLoaderLater(boolean changeDefaultClassLoader) { - ApplicationManager.getApplication().invokeLater(() -> refreshClassLoader(changeDefaultClassLoader)); + refreshClassLoader(changeDefaultClassLoader, null); } public void refreshClassLoader(boolean changeDefaultClassLoader) { - removeAllItems(); + refreshClassLoader(changeDefaultClassLoader, null); + } + + public void refreshClassLoader(boolean changeDefaultClassLoader, Runnable successUiCallback) { + int currentRefresh = refreshVersion.incrementAndGet(); + ApplicationManager.getApplication().invokeLater(() -> { + removeAllItems(); + setEnabled(false); + }, project.getDisposed()); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + AllClassLoaderRes allClassLoaderRes = loadAllClassLoader(); + ApplicationManager.getApplication().invokeLater(() -> { + if (refreshVersion.get() != currentRefresh) { + return; + } + removeAllItems(); + if (allClassLoaderRes != null) { + AllClassLoaderRes.Item defaultClassLoader = null; + for (AllClassLoaderRes.Item item : allClassLoaderRes.getItemList()) { + if (item.getIdentity().equals(allClassLoaderRes.getDefaultIdentity())) { + defaultClassLoader = item; + } + addItem(item); + } + if (changeDefaultClassLoader && defaultClassLoader != null) { + setSelectedItem(defaultClassLoader); + StateUtils.setProjectDefaultClassLoader(project, defaultClassLoader); + } + if (successUiCallback != null) { + successUiCallback.run(); + } + } + setEnabled(true); + }, project.getDisposed()); + }); + } + + private AllClassLoaderRes loadAllClassLoader() { AllClassLoaderRes allClassLoaderRes = null; int retryCount = 0; while (!Thread.currentThread().isInterrupted() && retryCount < 20) { @@ -90,23 +129,13 @@ public void refreshClassLoader(boolean changeDefaultClassLoader) { retryCount++; } if (!DebugToolsThreadUtils.sleep(1, TimeUnit.SECONDS)) { - return; + return null; } } if (allClassLoaderRes == null) { - return; - } - AllClassLoaderRes.Item defaultClassLoader = null; - for (AllClassLoaderRes.Item item : allClassLoaderRes.getItemList()) { - if (item.getIdentity().equals(allClassLoaderRes.getDefaultIdentity())) { - defaultClassLoader = item; - } - addItem(item); - } - if (changeDefaultClassLoader && defaultClassLoader != null) { - setSelectedItem(defaultClassLoader); - StateUtils.setProjectDefaultClassLoader(project, defaultClassLoader); + logger.warn("Failed to load classloader list after retries"); } + return allClassLoaderRes; } public void setSelectedClassLoader(AllClassLoaderRes.Item identity) { diff --git a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/InvokeMethodRecordDialog.java b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/InvokeMethodRecordDialog.java index dc29fab8..e1e196e5 100644 --- a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/InvokeMethodRecordDialog.java +++ b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/InvokeMethodRecordDialog.java @@ -20,19 +20,17 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.Messages; import io.github.future0923.debug.tools.base.hutool.core.io.FileUtil; import io.github.future0923.debug.tools.base.hutool.core.util.StrUtil; import io.github.future0923.debug.tools.base.utils.DebugToolsDigestUtil; import io.github.future0923.debug.tools.common.dto.RunContentDTO; import io.github.future0923.debug.tools.common.dto.RunDTO; import io.github.future0923.debug.tools.common.dto.TraceMethodDTO; -import io.github.future0923.debug.tools.common.exception.SocketCloseException; import io.github.future0923.debug.tools.common.protocal.http.AllClassLoaderRes; import io.github.future0923.debug.tools.common.protocal.packet.request.RunTargetMethodRequestPacket; import io.github.future0923.debug.tools.common.utils.DebugToolsJsonUtils; import io.github.future0923.debug.tools.idea.bundle.DebugToolsBundle; -import io.github.future0923.debug.tools.idea.client.ApplicationProjectHolder; +import io.github.future0923.debug.tools.idea.client.socket.utils.SocketSendUtils; import io.github.future0923.debug.tools.idea.constant.IdeaPluginProjectConstants; import io.github.future0923.debug.tools.idea.model.InvokeMethodRecordDTO; import io.github.future0923.debug.tools.idea.model.ParamCache; @@ -91,6 +89,10 @@ protected void doOKAction() { Map itemHeaderMap = mainPanel.getItemHeaderMap(); AllClassLoaderRes.Item classLoaderRes = (AllClassLoaderRes.Item) mainPanel.getClassLoaderComboBox().getSelectedItem(); MainJsonEditor editor = mainPanel.getEditor(); + if (editor.isGenerating()) { + DebugToolsNotifierUtil.notifyError(project, "参数正在生成,请稍后再试"); + return; + } String text = DebugToolsJsonUtils.compress(editor.getText()); String xxlJobParam = mainPanel.getXxlJobParamField().getText(); TraceMethodPanel traceMethodPanel = mainPanel.getTraceMethodPanel(); @@ -137,48 +139,35 @@ protected void doOKAction() { } } RunTargetMethodRequestPacket packet = new RunTargetMethodRequestPacket(runDTO); - ApplicationProjectHolder.Info info = ApplicationProjectHolder.getInfo(project); - if (info == null) { - Messages.showErrorDialog(DebugToolsBundle.message("dialog.error.run.attach.first"), DebugToolsBundle.message("dialog.title.execution.failed")); - DebugToolsToolWindowFactory.showWindow(project, null); - return; - } - try { - info.getClient().getHolder().send(packet); - } catch (SocketCloseException e) { - Messages.showErrorDialog(DebugToolsBundle.message("dialog.error.socket.close"), DebugToolsBundle.message("dialog.title.execution.failed")); - return; - } catch (Exception e) { - Messages.showErrorDialog(DebugToolsBundle.message("dialog.error.socket.send") + e.getMessage(), DebugToolsBundle.message("dialog.title.execution.failed")); - return; - } String runJsonStr = DebugToolsJsonUtils.toJsonStr(runDTO); - try { - String pathname = project.getBasePath() + IdeaPluginProjectConstants.PARAM_FILE; - File file = new File(pathname); - if (!file.exists()) { - FileUtils.touch(file); + SocketSendUtils.sendAsync(project, packet, () -> { + try { + String pathname = project.getBasePath() + IdeaPluginProjectConstants.PARAM_FILE; + File file = new File(pathname); + if (!file.exists()) { + FileUtils.touch(file); + } + FileUtil.writeUtf8String(runJsonStr, file); + } catch (IOException ex) { + log.error("参数写入json文件失败", ex); + DebugToolsNotifierUtil.notifyError(project, "参数写入json文件失败"); + return; } - FileUtil.writeUtf8String(runJsonStr, file); - } catch (IOException ex) { - log.error("参数写入json文件失败", ex); - DebugToolsNotifierUtil.notifyError(project, "参数写入json文件失败"); - return; - } - if (settingState.getInvokeMethodRecord()) { - InvokeMethodRecordDTO invokeMethodRecordDTO = new InvokeMethodRecordDTO(); - invokeMethodRecordDTO.setIdentity(recordRunDTO.getIdentity()); - invokeMethodRecordDTO.formatRunTime(); - invokeMethodRecordDTO.setClassName(runDTO.getTargetClassName()); - invokeMethodRecordDTO.setClassSimpleName(recordRunDTO.getClassSimpleName()); - invokeMethodRecordDTO.setMethodName(runDTO.getTargetMethodName()); - invokeMethodRecordDTO.setMethodSignature(recordRunDTO.getMethodSignature()); - invokeMethodRecordDTO.setMethodAroundName(methodAroundName); - invokeMethodRecordDTO.setMethodParamJson(text); - invokeMethodRecordDTO.setCacheKey(recordRunDTO.getCacheKey()); - invokeMethodRecordDTO.formatRunDTO(runDTO); - DebugToolsToolWindowFactory.consumerInvokeMethodRecordPanel(project, panel -> panel.addItem(invokeMethodRecordDTO)); - } + if (settingState.getInvokeMethodRecord()) { + InvokeMethodRecordDTO invokeMethodRecordDTO = new InvokeMethodRecordDTO(); + invokeMethodRecordDTO.setIdentity(recordRunDTO.getIdentity()); + invokeMethodRecordDTO.formatRunTime(); + invokeMethodRecordDTO.setClassName(runDTO.getTargetClassName()); + invokeMethodRecordDTO.setClassSimpleName(recordRunDTO.getClassSimpleName()); + invokeMethodRecordDTO.setMethodName(runDTO.getTargetMethodName()); + invokeMethodRecordDTO.setMethodSignature(recordRunDTO.getMethodSignature()); + invokeMethodRecordDTO.setMethodAroundName(methodAroundName); + invokeMethodRecordDTO.setMethodParamJson(text); + invokeMethodRecordDTO.setCacheKey(recordRunDTO.getCacheKey()); + invokeMethodRecordDTO.formatRunDTO(runDTO); + DebugToolsToolWindowFactory.consumerInvokeMethodRecordPanel(project, panel -> panel.addItem(invokeMethodRecordDTO)); + } + }); super.doOKAction(); } diff --git a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/InvokeMethodRecordQuickPanel.java b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/InvokeMethodRecordQuickPanel.java index a07e1d0c..fbc54fd1 100644 --- a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/InvokeMethodRecordQuickPanel.java +++ b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/InvokeMethodRecordQuickPanel.java @@ -113,9 +113,7 @@ private void initLayout(RunDTO formatRunDTO) { JPanel classLoaderJPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); getAllClassLoader(); refreshButton.addActionListener(e -> { - classLoaderComboBox.removeAllItems(); getAllClassLoader(); - classLoaderComboBox.setSelectedClassLoader(StateUtils.getProjectDefaultClassLoader(project)); }); classLoaderJPanel.add(classLoaderComboBox); classLoaderJPanel.add(refreshButton); @@ -195,8 +193,7 @@ private void initLayout(RunDTO formatRunDTO) { } private void getAllClassLoader() { - classLoaderComboBox.refreshClassLoader(false); - classLoaderComboBox.setSelectedClassLoader(StateUtils.getProjectDefaultClassLoader(project)); + classLoaderComboBox.refreshClassLoader(false, () -> classLoaderComboBox.setSelectedClassLoader(StateUtils.getProjectDefaultClassLoader(project))); } public Map getItemHeaderMap() { diff --git a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainDialog.java b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainDialog.java index 28e27618..fc724f8c 100644 --- a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainDialog.java +++ b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainDialog.java @@ -91,6 +91,10 @@ protected void doOKAction() { Map itemHeaderMap = mainPanel.getItemHeaderMap(); AllClassLoaderRes.Item classLoaderRes = (AllClassLoaderRes.Item) mainPanel.getClassLoaderComboBox().getSelectedItem(); MainJsonEditor editor = mainPanel.getEditor(); + if (editor.isGenerating()) { + DebugToolsNotifierUtil.notifyError(project, "参数正在生成,请稍后再试"); + return; + } String text = DebugToolsJsonUtils.compress(editor.getText()); String xxlJobParam = mainPanel.getXxlJobParamField().getText(); TraceMethodPanel traceMethodPanel = mainPanel.getTraceMethodPanel(); diff --git a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainJsonEditor.java b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainJsonEditor.java index 3ac4aaaa..34e8d890 100644 --- a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainJsonEditor.java +++ b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainJsonEditor.java @@ -16,38 +16,51 @@ */ package io.github.future0923.debug.tools.idea.ui.main; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiParameterList; +import com.intellij.util.concurrency.AppExecutorUtil; import io.github.future0923.debug.tools.common.utils.DebugToolsJsonUtils; import io.github.future0923.debug.tools.idea.setting.DebugToolsSettingState; import io.github.future0923.debug.tools.idea.setting.GenParamType; import io.github.future0923.debug.tools.idea.ui.editor.JsonEditor; +import io.github.future0923.debug.tools.idea.utils.DebugToolsNotifierUtil; import io.github.future0923.debug.tools.idea.utils.DebugToolsJsonElementUtil; import lombok.Getter; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.incremental.GlobalContextKey; +import java.util.concurrent.atomic.AtomicInteger; + /** * @author future0923 */ @Getter public class MainJsonEditor extends JsonEditor { + private static final String STATUS_FIELD_NAME = "_status"; + private final PsiParameterList psiParameterList; public static final String FILE_NAME = "DebugToolsContentEditFile.json"; public static final GlobalContextKey DEBUG_POWER_EDIT_CONTENT = GlobalContextKey.create("DebugToolsEditContent"); + private final AtomicInteger generationVersion = new AtomicInteger(); + + @Getter + private volatile boolean generating; + public MainJsonEditor(String cacheText, PsiParameterList psiParameterList, Project project) { super(project, ""); this.psiParameterList = psiParameterList; if (StringUtils.isBlank(cacheText)) { DebugToolsSettingState settingState = DebugToolsSettingState.getInstance(project); - setText(getJsonText(psiParameterList, settingState.getDefaultGenParamType())); + regenerateJsonText(settingState.getDefaultGenParamType(), true); } else { setText(cacheText); } @@ -58,17 +71,67 @@ public String getJsonText(@Nullable PsiParameterList psiParameterList, GenParamT } public void regenerateJsonText(GenParamType type) { - if (GenParamType.SIMPLE.equals(type)) { - setText(DebugToolsJsonElementUtil.getSimpleText(psiParameterList)); - } else { - setText(getJsonText(psiParameterList, type)); - } + regenerateJsonText(type, false); } public void prettyJsonText() { setText(DebugToolsJsonUtils.pretty(getText())); } + private void regenerateJsonText(GenParamType type, boolean preserveManualText) { + int currentGenerationVersion = generationVersion.incrementAndGet(); + String previousText = getText(); + String loadingText = statusText("Generating parameters..."); + generating = true; + setText(loadingText); + ReadAction.nonBlocking(() -> buildJsonResult(type)) + .withDocumentsCommitted(getProject()) + .finishOnUiThread(ModalityState.any(), result -> { + if (generationVersion.get() != currentGenerationVersion) { + return; + } + generating = false; + if (preserveManualText && !StringUtils.equals(getText(), loadingText)) { + return; + } + if (result.success()) { + setText(result.text()); + } else { + if (preserveManualText) { + setText("{}"); + } else { + setText(previousText); + } + DebugToolsNotifierUtil.notifyError(getProject(), result.errorMessage()); + } + }) + .submit(AppExecutorUtil.getAppExecutorService()); + } + + private GenerateResult buildJsonResult(GenParamType type) { + if (psiParameterList == null) { + return new GenerateResult(null, "当前记录缺少方法参数信息,无法重新生成参数", false); + } + try { + String text; + if (GenParamType.SIMPLE.equals(type)) { + text = DebugToolsJsonElementUtil.getSimpleText(psiParameterList); + } else { + text = getJsonText(psiParameterList, type); + } + return new GenerateResult(text, null, true); + } catch (Exception ex) { + return new GenerateResult(null, "参数生成失败: " + ex.getMessage(), false); + } + } + + private String statusText(String status) { + return DebugToolsJsonUtils.createJsonObject().set(STATUS_FIELD_NAME, status).toJSONString(4); + } + + private record GenerateResult(String text, String errorMessage, boolean success) { + } + @Override protected String fileName() { return FILE_NAME; diff --git a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainPanel.java b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainPanel.java index b7fe46b3..b973d6ce 100644 --- a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainPanel.java +++ b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/main/MainPanel.java @@ -126,9 +126,7 @@ private void initLayout() { JPanel classLoaderJPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); getAllClassLoader(); refreshButton.addActionListener(e -> { - classLoaderComboBox.removeAllItems(); getAllClassLoader(); - classLoaderComboBox.setSelectedClassLoader(StateUtils.getProjectDefaultClassLoader(project)); }); classLoaderJPanel.add(classLoaderComboBox); classLoaderJPanel.add(refreshButton); @@ -211,8 +209,7 @@ private void initLayout() { } private void getAllClassLoader() { - classLoaderComboBox.refreshClassLoader(false); - classLoaderComboBox.setSelectedClassLoader(StateUtils.getProjectDefaultClassLoader(project)); + classLoaderComboBox.refreshClassLoader(false, () -> classLoaderComboBox.setSelectedClassLoader(StateUtils.getProjectDefaultClassLoader(project))); } public Map getItemHeaderMap() { diff --git a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tab/ExceptionTabbedPane.java b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tab/ExceptionTabbedPane.java index 04577918..c4592867 100644 --- a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tab/ExceptionTabbedPane.java +++ b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tab/ExceptionTabbedPane.java @@ -17,6 +17,7 @@ package io.github.future0923.debug.tools.idea.ui.tab; import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.ui.components.JBPanel; @@ -51,6 +52,8 @@ public class ExceptionTabbedPane extends JBPanel { private boolean loadDebug = false; + private boolean loadingDebug = false; + public ExceptionTabbedPane(Project project, String throwable, String offsetPath) { this.project = project; this.throwable = throwable; @@ -81,19 +84,31 @@ private void initEvent() { int selectedIndex = tabPane.getSelectedIndex(); // 获取当前选中的选项卡标题 //String selectedTabTitle = tabPane.getTitleAt(selectedIndex); - if (selectedIndex == 1 && !loadDebug) { + if (selectedIndex == 1 && !loadDebug && !loadingDebug) { changeDebug(); } }); } private void changeDebug() { - String body = HttpClientUtils.resultType(project, offsetPath, PrintResultType.DEBUG.getType()); - if (DebugToolsStringUtils.isNotBlank(body)) { - debugTab.setRoot(new ResultDebugTreeNode(DebugToolsJsonUtils.toBean(body, RunResultDTO.class))); - loadDebug = true; - } else { - Messages.showErrorDialog(project, "The request failed, please try again later", "Exception Result"); - } + loadingDebug = true; + debugTab.setRoot(createStatusNode("Loading...")); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + String body = HttpClientUtils.resultType(project, offsetPath, PrintResultType.DEBUG.getType()); + ApplicationManager.getApplication().invokeLater(() -> { + loadingDebug = false; + if (DebugToolsStringUtils.isNotBlank(body)) { + debugTab.setRoot(new ResultDebugTreeNode(DebugToolsJsonUtils.toBean(body, RunResultDTO.class))); + loadDebug = true; + } else { + debugTab.setRoot(createStatusNode("Load failed")); + Messages.showErrorDialog(project, "The request failed, please try again later", "Exception Result"); + } + }, project.getDisposed()); + }); + } + + private ResultDebugTreeNode createStatusNode(String message) { + return new ResultDebugTreeNode(new RunResultDTO("result", message), true); } } diff --git a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tab/ResultTabbedPane.java b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tab/ResultTabbedPane.java index f9237c55..d3b3f7a7 100644 --- a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tab/ResultTabbedPane.java +++ b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tab/ResultTabbedPane.java @@ -16,12 +16,14 @@ */ package io.github.future0923.debug.tools.idea.ui.tab; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBTabbedPane; import io.github.future0923.debug.tools.base.hutool.core.collection.CollUtil; import io.github.future0923.debug.tools.base.hutool.core.util.StrUtil; +import io.github.future0923.debug.tools.base.trace.MethodTraceType; import io.github.future0923.debug.tools.base.trace.MethodTreeNode; import io.github.future0923.debug.tools.base.utils.DebugToolsStringUtils; import io.github.future0923.debug.tools.common.dto.RunResultDTO; @@ -71,6 +73,12 @@ public class ResultTabbedPane extends JBPanel { private boolean loadTrace = false; + private boolean loadingJson = false; + + private boolean loadingDebug = false; + + private boolean loadingTrace = false; + public ResultTabbedPane(Project project, String printResult, String offsetPath, String traceOffsetPath, ResultClassType resultClassType) { this.project = project; this.printResult = printResult; @@ -131,11 +139,11 @@ private void initEvent() { int selectedIndex = tabPane.getSelectedIndex(); // 获取当前选中的选项卡标题 String selectedTabTitle = tabPane.getTitleAt(selectedIndex); - if (Objects.equals(selectedTabTitle, "json") && !loadJson) { + if (Objects.equals(selectedTabTitle, "json") && !loadJson && !loadingJson) { changeJson(); - } else if (Objects.equals(selectedTabTitle, "debug") && !loadDebug) { + } else if (Objects.equals(selectedTabTitle, "debug") && !loadDebug && !loadingDebug) { changeDebug(); - } else if (Objects.equals(selectedTabTitle, "trace") && !loadTrace) { + } else if (Objects.equals(selectedTabTitle, "trace") && !loadTrace && !loadingTrace) { changeTrace(); } }); @@ -150,7 +158,17 @@ private void changeJson() { } else if (ResultClassType.SIMPLE.equals(resultClassType)) { text = "{\n \"result\": \"" + printResult + "\"\n}"; } else if (ResultClassType.OBJECT.equals(resultClassType)) { - text = HttpClientUtils.resultType(project, offsetPath, PrintResultType.JSON.getType()); + loadingJson = true; + jsonTab.setText(statusJson("Loading...")); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + String body = HttpClientUtils.resultType(project, offsetPath, PrintResultType.JSON.getType()); + ApplicationManager.getApplication().invokeLater(() -> { + loadingJson = false; + loadJson = true; + jsonTab.setText(body); + }, project.getDisposed()); + }); + return; } jsonTab.setText(text); loadJson = true; @@ -161,22 +179,30 @@ private void changeDebug() { return; } if (ResultClassType.VOID.equals(resultClassType)) { - Messages.showErrorDialog(project, "Void does not support viewing", "Debug Result"); + debugTab.setRoot(createDebugStatusNode("Void does not support viewing")); + loadDebug = true; } else if (ResultClassType.NULL.equals(resultClassType)) { - Messages.showErrorDialog(project, "Null does not support viewing", "Debug Result"); + debugTab.setRoot(createDebugStatusNode("Null does not support viewing")); + loadDebug = true; } else if (ResultClassType.SIMPLE.equals(resultClassType)) { - debugTab.setRoot(new ResultDebugTreeNode(new RunResultDTO("result", printResult) {{ - setLeaf(true); - }})); + debugTab.setRoot(createDebugStatusNode(printResult)); loadDebug = true; } else if (ResultClassType.OBJECT.equals(resultClassType)) { - String body = HttpClientUtils.resultType(project, offsetPath, PrintResultType.DEBUG.getType()); - if (DebugToolsStringUtils.isNotBlank(body)) { - debugTab.setRoot(new ResultDebugTreeNode(DebugToolsJsonUtils.toBean(body, RunResultDTO.class))); - loadDebug = true; - } else { - Messages.showErrorDialog(project, "The request failed, please try again later", "Request Result"); - } + loadingDebug = true; + debugTab.setRoot(createDebugStatusNode("Loading...")); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + String body = HttpClientUtils.resultType(project, offsetPath, PrintResultType.DEBUG.getType()); + ApplicationManager.getApplication().invokeLater(() -> { + loadingDebug = false; + if (DebugToolsStringUtils.isNotBlank(body)) { + debugTab.setRoot(new ResultDebugTreeNode(DebugToolsJsonUtils.toBean(body, RunResultDTO.class))); + loadDebug = true; + } else { + debugTab.setRoot(createDebugStatusNode("Load failed")); + Messages.showErrorDialog(project, "The request failed, please try again later", "Request Result"); + } + }, project.getDisposed()); + }); } } @@ -185,12 +211,48 @@ private void changeTrace() { return; } if (StrUtil.isBlank(traceOffsetPath)) { - Messages.showErrorDialog(project, "Trace does not support viewing", "Debug Result"); - } - List methodTreeNodes = HttpClientUtils.resultTrace(project, traceOffsetPath); - if (CollUtil.isNotEmpty(methodTreeNodes)) { - traceTab.setRoot(new ResultTraceTreeNode(CollUtil.getFirst(methodTreeNodes))); + traceTab.setRoot(createTraceStatusNode("Trace does not support viewing")); + loadTrace = true; + return; } - loadTrace = true; + loadingTrace = true; + traceTab.setRoot(createTraceStatusNode("Loading...")); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + try { + List methodTreeNodes = HttpClientUtils.resultTrace(project, traceOffsetPath); + ApplicationManager.getApplication().invokeLater(() -> { + loadingTrace = false; + if (CollUtil.isNotEmpty(methodTreeNodes)) { + traceTab.setRoot(new ResultTraceTreeNode(CollUtil.getFirst(methodTreeNodes))); + } else { + traceTab.setRoot(createTraceStatusNode("No trace data")); + } + loadTrace = true; + }, project.getDisposed()); + } catch (Exception ex) { + ApplicationManager.getApplication().invokeLater(() -> { + loadingTrace = false; + traceTab.setRoot(createTraceStatusNode("Load failed")); + Messages.showErrorDialog(project, "The request failed, please try again later", "Trace Result"); + }, project.getDisposed()); + } + }); + } + + private String statusJson(String result) { + return "{\n \"result\": \"" + result + "\"\n}"; + } + + private ResultDebugTreeNode createDebugStatusNode(String message) { + return new ResultDebugTreeNode(new RunResultDTO("result", message), true); + } + + private ResultTraceTreeNode createTraceStatusNode(String message) { + MethodTreeNode methodTreeNode = new MethodTreeNode(); + methodTreeNode.setTraceType(MethodTraceType.METHOD); + methodTreeNode.setClassSimpleName("status"); + methodTreeNode.setMethodSignature(message); + methodTreeNode.setDuration(0L); + return new ResultTraceTreeNode(methodTreeNode); } } diff --git a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tree/ResultDebugTreePanel.java b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tree/ResultDebugTreePanel.java index 2e5c578e..69d86428 100644 --- a/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tree/ResultDebugTreePanel.java +++ b/debug-tools-idea/src/main/java/io/github/future0923/debug/tools/idea/ui/tree/ResultDebugTreePanel.java @@ -16,7 +16,9 @@ */ package io.github.future0923.debug.tools.idea.ui.tree; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; import com.intellij.ui.border.CustomLineBorder; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.treeStructure.SimpleTree; @@ -48,6 +50,8 @@ @SuppressWarnings(value = {"unchecked", "rawtypes"}) public class ResultDebugTreePanel extends JBScrollPane { + private final Project project; + private final Tree tree; public ResultDebugTreePanel(Project project) { @@ -55,6 +59,7 @@ public ResultDebugTreePanel(Project project) { } public ResultDebugTreePanel(Project project, ResultDebugTreeNode root) { + this.project = project; this.tree = new SimpleTree(); // 可以拖动的Tree SimpleDnDAwareTree this.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); @@ -67,12 +72,7 @@ public ResultDebugTreePanel(Project project, ResultDebugTreeNode root) { public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException { if (event.getPath().getLastPathComponent() instanceof TreeNode node) { if (node.getChildCount() == 1 && node.getFirstChild() instanceof EmptyTreeNode) { - node.removeAllChildren(); - List runResultDTOList = HttpClientUtils.resultDetail(project, ((RunResultDTO)node.getUserObject()).getFiledOffset()); - for (RunResultDTO runResultDTO : runResultDTOList) { - node.add(new ResultDebugTreeNode(runResultDTO, runResultDTO.getLeaf())); - } - ((DefaultTreeModel) tree.getModel()).reload(node); + loadChildren(node); } } } @@ -135,6 +135,40 @@ private void copy(boolean value) { } } + private void loadChildren(TreeNode node) { + DefaultTreeModel model = (DefaultTreeModel) tree.getModel(); + node.removeAllChildren(); + node.add(createStatusNode("Loading...")); + model.reload(node); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + try { + List runResultDTOList = HttpClientUtils.resultDetail(project, node.getUserObject().getFiledOffset()); + ApplicationManager.getApplication().invokeLater(() -> { + node.removeAllChildren(); + if (runResultDTOList == null || runResultDTOList.isEmpty()) { + node.add(createStatusNode("No data")); + } else { + for (RunResultDTO runResultDTO : runResultDTOList) { + node.add(new ResultDebugTreeNode(runResultDTO, runResultDTO.getLeaf())); + } + } + model.reload(node); + }, project.getDisposed()); + } catch (Exception ex) { + ApplicationManager.getApplication().invokeLater(() -> { + node.removeAllChildren(); + node.add(new EmptyTreeNode()); + model.reload(node); + Messages.showErrorDialog(project, "The request failed, please try again later", "Debug Result"); + }, project.getDisposed()); + } + }); + } + + private ResultDebugTreeNode createStatusNode(String message) { + return new ResultDebugTreeNode(new RunResultDTO("status", message, RunResultDTO.Type.PROPERTY, null), true); + } + public void setRoot(ResultDebugTreeNode root) { DefaultTreeModel model = (DefaultTreeModel) tree.getModel(); tree.setRootVisible(true); From 6f8fd8cf416a285b4b93cd2b0828bc4b1bc25cb9 Mon Sep 17 00:00:00 2001 From: apple_developer <1@icloud.com> Date: Wed, 18 Mar 2026 10:29:19 +0800 Subject: [PATCH 2/2] chore: ignore local codex files --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4f81ad7f..afd3b3b7 100644 --- a/.gitignore +++ b/.gitignore @@ -73,10 +73,11 @@ build/ ### VS Code ### .vscode/ +.codex/ ### Mac OS ### .DS_Store ### Maven ### .flattened-pom.xml -dependency-reduced-pom.xml \ No newline at end of file +dependency-reduced-pom.xml