From 8d9916edc64e0a9dafc9472d852c4f458a6095d7 Mon Sep 17 00:00:00 2001 From: janithcd Date: Sun, 26 Jul 2026 11:21:50 +0530 Subject: [PATCH] fix: explain silent states and ambiguous navigation Closes #8 --- .../stacktale/idea/ReportNavigator.java | 40 ++++++++++++++++--- .../stacktale/idea/StacktalePanel.java | 26 ++++++++---- .../idea/StacktaleToolWindowFactory.java | 2 +- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/ReportNavigator.java b/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/ReportNavigator.java index 952dfc4..793b34b 100644 --- a/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/ReportNavigator.java +++ b/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/ReportNavigator.java @@ -3,11 +3,15 @@ import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; +import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; +import java.util.List; /** Opens the source location a report points at (its culprit / stack frame) in the editor. */ final class ReportNavigator { @@ -17,13 +21,39 @@ private ReportNavigator() { static boolean navigate(Project project, StFrame frame) { if (frame == null) return false; - // index queries need a read action; the file lives by simple name in the report + + // Index queries need a read action; the file lives by simple name in the report. Collection files = ReadAction.compute(() -> FilenameIndex.getVirtualFilesByName(frame.fileName(), GlobalSearchScope.projectScope(project))); + if (files.isEmpty()) return false; - VirtualFile target = files.iterator().next(); - // OpenFileDescriptor lines are 0-based; report lines are 1-based - new OpenFileDescriptor(project, target, Math.max(0, frame.line() - 1), 0).navigate(true); + + List candidates = new ArrayList<>(files); + candidates.sort(Comparator.comparing(VirtualFile::getPath)); + + if (candidates.size() > 1) { + JBPopupFactory.getInstance() + .createPopupChooserBuilder(candidates) + .setTitle("Choose Source File") + .setItemChosenCallback(target -> + openSource(project, target, frame.line())) + .createPopup() + .showCenteredInCurrentWindow(project); + + return true; + } + + openSource(project, candidates.get(0), frame.line()); return true; } -} + + private static void openSource(Project project, VirtualFile target, int line) { + // OpenFileDescriptor lines are 0-based; report lines are 1-based. + new OpenFileDescriptor( + project, + target, + Math.max(0, line - 1), + 0 + ).navigate(true); + } +} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/StacktalePanel.java b/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/StacktalePanel.java index f619692..99cb688 100644 --- a/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/StacktalePanel.java +++ b/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/StacktalePanel.java @@ -11,6 +11,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindow; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBSplitter; @@ -44,15 +45,17 @@ class StacktalePanel extends SimpleToolWindowPanel { private static final int POLL_MILLIS = 3000; private final Project project; + private final ToolWindow toolWindow; private final DefaultListModel model = new DefaultListModel<>(); private final JBList list = new JBList<>(model); private final JTextArea detail = new JTextArea(); private final Alarm alarm; - private String lastContent = ""; + private String lastContent; - StacktalePanel(Project project) { + StacktalePanel(Project project, ToolWindow toolWindow) { super(true, true); this.project = project; + this.toolWindow = toolWindow; this.alarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, project); list.setCellRenderer(new ReportCellRenderer()); @@ -82,7 +85,7 @@ private ActionToolbar buildToolbar() { group.add(new AnAction("Refresh", "Re-read errors-ai.log", AllIcons.Actions.Refresh) { @Override public void actionPerformed(@NotNull AnActionEvent e) { - lastContent = ""; // force a re-read + lastContent = null; // force a re-read refresh(); } }); @@ -113,12 +116,16 @@ private void schedulePoll() { private void refresh() { Path log = findLog(); if (log == null) { + toolWindow.setTitle("Stacktale"); model.clear(); detail.setText("No errors-ai.log found in this project yet.\n\n" + "Add the stacktale library and trigger an error — reports will appear here."); - lastContent = ""; + lastContent = null; return; } + + toolWindow.setTitle("Stacktale — " + log); + String content; try { content = Files.readString(log, StandardCharsets.UTF_8); @@ -136,7 +143,9 @@ private void refresh() { int keep = previouslySelected == null ? 0 : indexOfId(previouslySelected.id()); list.setSelectedIndex(Math.max(0, keep)); } else { - detail.setText(""); + detail.setText("No error reports yet — the file has none.\n\n" + + "Resolved log path:\n" + log); + detail.setCaretPosition(0); } } @@ -171,7 +180,10 @@ private void showSelected() { private void jumpToCulprit() { StReport report = list.getSelectedValue(); - if (report != null) ReportNavigator.navigate(project, report.culprit()); + if (report != null && !ReportNavigator.navigate(project, report.culprit())) { + detail.setText("Report #" + report.id() + " has no source frame to open."); + detail.setCaretPosition(0); + } } private void copySelected() { @@ -193,4 +205,4 @@ private static String escape(String s) { return s.replace("&", "&").replace("<", "<").replace(">", ">"); } } -} +} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/StacktaleToolWindowFactory.java b/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/StacktaleToolWindowFactory.java index 951d586..2cb6965 100644 --- a/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/StacktaleToolWindowFactory.java +++ b/plugin/src/main/java/io/github/gabrielbbaldez/stacktale/idea/StacktaleToolWindowFactory.java @@ -12,7 +12,7 @@ public class StacktaleToolWindowFactory implements ToolWindowFactory { @Override public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { - StacktalePanel panel = new StacktalePanel(project); + StacktalePanel panel = new StacktalePanel(project, toolWindow); Content content = ContentFactory.getInstance().createContent(panel, "", false); toolWindow.getContentManager().addContent(content); }