feat: quick link navigation

This commit is contained in:
Carl-Robert Linnupuu 2025-10-02 00:13:49 +01:00
parent 2a0a9d9a51
commit 3011dba44d
31 changed files with 587 additions and 159 deletions

View file

@ -4,11 +4,14 @@ import static ee.carlrobert.codegpt.ui.UIUtil.createScrollPaneWithSmartScroller;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.JBColor;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.ui.JBUI;
import ee.carlrobert.codegpt.CodeGPTKeys;
import ee.carlrobert.codegpt.ReferencedFile;
@ -178,6 +181,7 @@ public class ChatToolWindowTabPanel implements Disposable {
.referencedFiles(getReferencedFiles(selectedTags))
.history(getHistory(getSelectedTags()))
.psiStructure(psiStructure)
.project(project)
.chatMode(userInputPanel.getChatMode());
findTagOfType(selectedTags, PersonaTagDetails.class)
@ -286,8 +290,15 @@ public class ChatToolWindowTabPanel implements Disposable {
public void includeFiles(List<VirtualFile> referencedFiles) {
userInputPanel.includeFiles(referencedFiles);
totalTokensPanel.updateReferencedFilesTokens(
referencedFiles.stream().map(it -> ReferencedFile.from(it).fileContent()).toList());
ReadAction.nonBlocking(() ->
referencedFiles.stream()
.map(it -> ReferencedFile.from(it).fileContent())
.toList()
)
.inSmartMode(project)
.expireWith(project)
.finishOnUiThread(ModalityState.any(), totalTokensPanel::updateReferencedFilesTokens)
.submit(AppExecutorUtil.getAppExecutorService());
}
private boolean hasReferencedFilePaths(Message message) {
@ -498,6 +509,7 @@ public class ChatToolWindowTabPanel implements Disposable {
userMessagePanel.addReloadAction(() -> reloadMessage(
ChatCompletionParameters.builder(conversation, message)
.conversationType(ConversationType.DEFAULT)
.project(project)
.chatMode(userInputPanel.getChatMode())
.build(),
userMessagePanel));

View file

@ -13,17 +13,21 @@ import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.AnimatedIcon;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.components.BorderLayoutPanel;
import ee.carlrobert.codegpt.CodeGPTBundle;
@ -58,6 +62,7 @@ import ee.carlrobert.codegpt.toolwindow.ui.WebpageList;
import ee.carlrobert.codegpt.ui.ThoughtProcessPanel;
import ee.carlrobert.codegpt.ui.UIUtil;
import ee.carlrobert.codegpt.util.EditorUtil;
import ee.carlrobert.codegpt.util.MarkdownUtil;
import java.awt.BorderLayout;
import java.util.Objects;
import java.util.stream.Stream;

View file

@ -3,16 +3,20 @@ package ee.carlrobert.codegpt.ui;
import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;
import com.intellij.ide.BrowserUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel;
import com.intellij.openapi.ui.panel.ComponentPanelBuilder;
import com.intellij.ui.JBColor;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.ui.components.JBTextArea;
import com.intellij.util.ui.HTMLEditorKitBuilder;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UI;
import ee.carlrobert.codegpt.CodeGPTBundle;
import ee.carlrobert.codegpt.toolwindow.chat.ui.SmartScroller;
import ee.carlrobert.codegpt.util.PsiLinkNavigator;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
@ -43,6 +47,8 @@ import javax.swing.text.DefaultCaret;
public class UIUtil {
private static final Logger LOG = Logger.getInstance(UIUtil.class);
public static JTextPane createTextPane(String text) {
return createTextPane(text, true);
}
@ -54,6 +60,7 @@ public class UIUtil {
public static JTextPane createTextPane(String text, boolean opaque, HyperlinkListener listener) {
var textPane = new JTextPane();
textPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true);
textPane.setEditorKit(HTMLEditorKitBuilder.simple());
textPane.addHyperlinkListener(listener);
textPane.setContentType("text/html");
textPane.setEditable(false);
@ -103,12 +110,23 @@ public class UIUtil {
}
public static void handleHyperlinkClicked(HyperlinkEvent event) {
if (!ACTIVATED.equals(event.getEventType())) {
return;
}
String desc = event.getDescription();
if (desc != null && PsiLinkNavigator.isValidNavigationLink(desc)) {
ApplicationManager.getApplication()
.executeOnPooledThread(() -> PsiLinkNavigator.handle(desc));
return;
}
var url = event.getURL();
if (ACTIVATED.equals(event.getEventType()) && url != null) {
if (url != null) {
try {
BrowserUtil.browse(url.toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
LOG.warn("Failed to browse URL: " + url, e);
}
}
}
@ -119,7 +137,6 @@ public class UIUtil {
textArea.getActionMap().put("text-submit", onSubmit);
}
public static JPanel createRadioButtonsPanel(List<JBRadioButton> radioButtons) {
var buttonGroup = new ButtonGroup();
var radioPanel = new JPanel();