diff --git a/src/main/java/com/sourcegraph/cody/PromptPanel.kt b/src/main/java/com/sourcegraph/cody/PromptPanel.kt index 5d0e9cf576..3081e3ea74 100644 --- a/src/main/java/com/sourcegraph/cody/PromptPanel.kt +++ b/src/main/java/com/sourcegraph/cody/PromptPanel.kt @@ -5,6 +5,7 @@ import com.intellij.openapi.actionSystem.CustomShortcutSet import com.intellij.openapi.actionSystem.KeyboardShortcut import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.DumbAwareAction +import com.intellij.openapi.project.Project import com.intellij.ui.DocumentAdapter import com.intellij.ui.components.JBList import com.intellij.ui.components.JBScrollPane @@ -27,9 +28,7 @@ import javax.swing.event.AncestorEvent import javax.swing.event.AncestorListener import javax.swing.event.DocumentEvent -class PromptPanel( - private val chatSession: ChatSession, -) : JLayeredPane() { +class PromptPanel(project: Project, private val chatSession: ChatSession) : JLayeredPane() { /** View components */ private val autoGrowingTextArea = AutoGrowingTextArea(5, 9, this) @@ -45,7 +44,7 @@ class PromptPanel( /** Related components */ private val promptMessageHistory = - CodyChatMessageHistory(CHAT_MESSAGE_HISTORY_CAPACITY, chatSession) + CodyChatMessageHistory(project, CHAT_MESSAGE_HISTORY_CAPACITY, chatSession) init { /** Initialize view */ diff --git a/src/main/java/com/sourcegraph/cody/agent/WebviewPostMessageParams.kt b/src/main/java/com/sourcegraph/cody/agent/WebviewPostMessageParams.kt index 24dcc9fc1b..cb759e2ae3 100644 --- a/src/main/java/com/sourcegraph/cody/agent/WebviewPostMessageParams.kt +++ b/src/main/java/com/sourcegraph/cody/agent/WebviewPostMessageParams.kt @@ -3,8 +3,7 @@ package com.sourcegraph.cody.agent import com.sourcegraph.cody.agent.protocol.ChatError import com.sourcegraph.cody.agent.protocol.ChatMessage import com.sourcegraph.cody.agent.protocol.ContextFile -import kotlinx.serialization.* -import kotlinx.serialization.modules.* +import com.sourcegraph.cody.agent.protocol.Repo /** * A message sent from the webview to the extension host. See vscode/src/chat/protocol.ts for the @@ -18,6 +17,8 @@ data class WebviewMessage( val contextFiles: List? = null, val error: ChatError? = null, val query: String? = null, + val explicitRepos: List? = null, + val repoId: String? = null ) data class WebviewReceiveMessageParams(val id: String, val message: WebviewMessage) diff --git a/src/main/java/com/sourcegraph/vcs/RepoUtil.java b/src/main/java/com/sourcegraph/vcs/RepoUtil.java index b1f71ac6d3..444badc748 100644 --- a/src/main/java/com/sourcegraph/vcs/RepoUtil.java +++ b/src/main/java/com/sourcegraph/vcs/RepoUtil.java @@ -159,7 +159,7 @@ private static String doReplacements( .thenCompose( agent -> agent.getServer().convertGitCloneURLToCodebaseName(new CloneURL(cloneURL))) - .completeOnTimeout(null, 4, TimeUnit.SECONDS) + .completeOnTimeout(null, 15, TimeUnit.SECONDS) .get(); if (codebaseName == null) { diff --git a/src/main/kotlin/com/sourcegraph/cody/CodyToolWindowContent.kt b/src/main/kotlin/com/sourcegraph/cody/CodyToolWindowContent.kt index c64d63e9f2..042e37d176 100644 --- a/src/main/kotlin/com/sourcegraph/cody/CodyToolWindowContent.kt +++ b/src/main/kotlin/com/sourcegraph/cody/CodyToolWindowContent.kt @@ -32,7 +32,7 @@ class CodyToolWindowContent(private val project: Project) { private var codyOnboardingGuidancePanel: CodyOnboardingGuidancePanel? = null private val signInWithSourcegraphPanel = SignInWithSourcegraphPanel(project) - private val historyTree = HistoryTree(::selectChat, ::removeChat, ::removeAllChats) + private val historyTree = HistoryTree(project, ::selectChat, ::removeChat, ::removeAllChats) private val tabbedPane = JBTabbedPane() private val currentChatSession: AtomicReference = AtomicReference(null) @@ -137,7 +137,7 @@ class CodyToolWindowContent(private val project: Project) { } private fun removeChat(state: ChatState) { - HistoryService.getInstance().remove(state.internalId) + HistoryService.getInstance(project).remove(state.internalId) if (AgentChatSessionService.getInstance(project).removeSession(state)) { val isVisible = currentChatSession.get()?.getInternalId() == state.internalId if (isVisible) { @@ -148,7 +148,7 @@ class CodyToolWindowContent(private val project: Project) { private fun removeAllChats() { AgentChatSessionService.getInstance(project).removeAllSessions() - HistoryService.getInstance().removeAll() + HistoryService.getInstance(project).removeAll() switchToChatSession(AgentChatSession.createNew(project)) } diff --git a/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgentCodebase.kt b/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgentCodebase.kt index d0212d1c00..a6f2aafc3d 100644 --- a/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgentCodebase.kt +++ b/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgentCodebase.kt @@ -8,22 +8,28 @@ import com.intellij.openapi.vfs.VirtualFile import com.sourcegraph.cody.config.CodyProjectSettings import com.sourcegraph.config.ConfigUtil import com.sourcegraph.vcs.RepoUtil +import java.util.concurrent.CompletableFuture @Service(Service.Level.PROJECT) class CodyAgentCodebase(val project: Project) { // TODO: Support list of repository names instead of just one. - private val application = ApplicationManager.getApplication() private val settings = CodyProjectSettings.getInstance(project) - private var inferredUrl: String? = null + private var inferredUrl: CompletableFuture = CompletableFuture() - fun getUrl(): String? = settings.remoteUrl ?: inferredUrl + init { + onFileOpened(project, null) + } + + fun getUrl(): CompletableFuture = + if (settings.remoteUrl != null) CompletableFuture.completedFuture(settings.remoteUrl) + else inferredUrl fun onFileOpened(project: Project, file: VirtualFile?) { - application.executeOnPooledThread { + ApplicationManager.getApplication().executeOnPooledThread { val repositoryName = RepoUtil.findRepositoryName(project, file) - if (repositoryName != null && inferredUrl != repositoryName) { - inferredUrl = repositoryName + if (repositoryName != null && inferredUrl.getNow(null) != repositoryName) { + inferredUrl.complete(repositoryName) CodyAgentService.applyAgentOnBackgroundThread(project) { it.server.configurationDidChange(ConfigUtil.getAgentConfiguration(project)) } diff --git a/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgentServer.kt b/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgentServer.kt index 8a4fa21fe4..a42052ce62 100644 --- a/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgentServer.kt +++ b/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgentServer.kt @@ -1,6 +1,7 @@ package com.sourcegraph.cody.agent import com.sourcegraph.cody.agent.protocol.* +import com.sourcegraph.cody.agent.protocol.util.ChatRemoteReposResponse import java.util.concurrent.CompletableFuture import org.eclipse.lsp4j.jsonrpc.services.JsonNotification import org.eclipse.lsp4j.jsonrpc.services.JsonRequest @@ -35,6 +36,9 @@ interface CodyAgentServer { @JsonRequest("graphql/getRepoId") fun getRepoId(repoName: GetRepoIDResponse): CompletableFuture + @JsonRequest("graphql/getRepoIds") + fun getRepoIds(repoName: GetRepoIdsParam): CompletableFuture + @JsonRequest("git/codebaseName") fun convertGitCloneURLToCodebaseName(cloneURL: CloneURL): CompletableFuture @@ -105,4 +109,6 @@ interface CodyAgentServer { fun attributionSearch( params: AttributionSearchParams ): CompletableFuture + + @JsonRequest("chat/remoteRepos") fun chatRemoteRepos(): CompletableFuture } diff --git a/src/main/kotlin/com/sourcegraph/cody/agent/protocol/GetRepoIdsParam.kt b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/GetRepoIdsParam.kt new file mode 100644 index 0000000000..7e0ac1a80f --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/GetRepoIdsParam.kt @@ -0,0 +1,3 @@ +package com.sourcegraph.cody.agent.protocol + +data class GetRepoIdsParam(val names: List, val first: Int) diff --git a/src/main/kotlin/com/sourcegraph/cody/agent/protocol/GetRepoIdsResponse.kt b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/GetRepoIdsResponse.kt new file mode 100644 index 0000000000..edb84c54b2 --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/GetRepoIdsResponse.kt @@ -0,0 +1,3 @@ +package com.sourcegraph.cody.agent.protocol + +data class GetRepoIdsResponse(val repos: List) diff --git a/src/main/kotlin/com/sourcegraph/cody/agent/protocol/Repo.kt b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/Repo.kt new file mode 100644 index 0000000000..c3a0afa97d --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/Repo.kt @@ -0,0 +1,3 @@ +package com.sourcegraph.cody.agent.protocol + +data class Repo(val name: String, val id: String) diff --git a/src/main/kotlin/com/sourcegraph/cody/agent/protocol/util/ChatRemoteReposResponse.kt b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/util/ChatRemoteReposResponse.kt new file mode 100644 index 0000000000..fddc2dc8e5 --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/util/ChatRemoteReposResponse.kt @@ -0,0 +1,5 @@ +package com.sourcegraph.cody.agent.protocol.util + +import com.sourcegraph.cody.agent.protocol.Repo + +data class ChatRemoteReposResponse(val remoteRepos: List) diff --git a/src/main/kotlin/com/sourcegraph/cody/chat/AgentChatSession.kt b/src/main/kotlin/com/sourcegraph/cody/chat/AgentChatSession.kt index 9d3cf04e6a..e7eabeaed5 100644 --- a/src/main/kotlin/com/sourcegraph/cody/chat/AgentChatSession.kt +++ b/src/main/kotlin/com/sourcegraph/cody/chat/AgentChatSession.kt @@ -179,7 +179,7 @@ private constructor( } messages.add(message) chatPanel.addOrUpdateMessage(message) - HistoryService.getInstance().update(internalId, messages) + HistoryService.getInstance(project).updateChatMessages(internalId, messages) } @RequiresEdt @@ -220,12 +220,7 @@ private constructor( val chatSession = AgentChatSession(project, sessionId) chatSession.createCancellationToken( - onCancel = { - CodyAgentService.applyAgentOnBackgroundThread(project) { agent -> - agent.server.webviewReceiveMessage( - WebviewReceiveMessageParams(sessionId.get(), WebviewMessage(command = "abort"))) - } - }, + onCancel = { chatSession.sendWebviewMessage(WebviewMessage(command = "abort")) }, onFinish = { GraphQlLogger.logCodyEvent(project, "command:${commandId.displayName}", "executed") }) diff --git a/src/main/kotlin/com/sourcegraph/cody/chat/CodyChatMessageHistory.kt b/src/main/kotlin/com/sourcegraph/cody/chat/CodyChatMessageHistory.kt index 6980ff3274..60791d1e69 100644 --- a/src/main/kotlin/com/sourcegraph/cody/chat/CodyChatMessageHistory.kt +++ b/src/main/kotlin/com/sourcegraph/cody/chat/CodyChatMessageHistory.kt @@ -1,11 +1,16 @@ package com.sourcegraph.cody.chat +import com.intellij.openapi.project.Project import com.intellij.ui.components.JBTextArea import com.sourcegraph.cody.history.HistoryService import com.sourcegraph.cody.history.state.MessageState import java.util.* -class CodyChatMessageHistory(private val capacity: Int, chatSession: ChatSession) { +class CodyChatMessageHistory( + private val project: Project, + private val capacity: Int, + chatSession: ChatSession +) { var currentValue: String = "" private var upperStack: Stack = Stack() private var lowerStack: Stack = Stack() @@ -53,8 +58,8 @@ class CodyChatMessageHistory(private val capacity: Int, chatSession: ChatSession } private fun preloadHistoricalMessages(chatSession: ChatSession) { - HistoryService.getInstance() - .state + HistoryService.getInstance(project) + .getHistoryReadOnly() .chats .find { it.internalId == chatSession.getInternalId() } ?.messages diff --git a/src/main/kotlin/com/sourcegraph/cody/chat/ui/ChatPanel.kt b/src/main/kotlin/com/sourcegraph/cody/chat/ui/ChatPanel.kt index f8c4ccf1be..2374de69f0 100644 --- a/src/main/kotlin/com/sourcegraph/cody/chat/ui/ChatPanel.kt +++ b/src/main/kotlin/com/sourcegraph/cody/chat/ui/ChatPanel.kt @@ -18,12 +18,12 @@ import javax.swing.BorderFactory import javax.swing.JButton import javax.swing.JPanel -class ChatPanel(project: Project, val chatSession: ChatSession) : +class ChatPanel(project: Project, chatSession: ChatSession) : JPanel(VerticalFlowLayout(VerticalFlowLayout.CENTER, 0, 0, true, false)) { private val messagesPanel = MessagesPanel(project, chatSession) private val chatPanel = ChatScrollPane(messagesPanel) - val promptPanel: PromptPanel = PromptPanel(chatSession) - private val contextView: EnhancedContextPanel = EnhancedContextPanel(project) + val promptPanel: PromptPanel = PromptPanel(project, chatSession) + private val contextView: EnhancedContextPanel = EnhancedContextPanel(project, chatSession) private val stopGeneratingButton = object : JButton("Stop generating", IconUtil.desaturate(AllIcons.Actions.Suspend)) { diff --git a/src/main/kotlin/com/sourcegraph/cody/config/CodyAccountDetailsProvider.kt b/src/main/kotlin/com/sourcegraph/cody/config/CodyAccountDetailsProvider.kt index 0a7ff83753..c46de17618 100644 --- a/src/main/kotlin/com/sourcegraph/cody/config/CodyAccountDetailsProvider.kt +++ b/src/main/kotlin/com/sourcegraph/cody/config/CodyAccountDetailsProvider.kt @@ -22,12 +22,15 @@ class CodyAccountDetailsProvider( account: CodyAccount, indicator: ProgressIndicator ): CompletableFuture> { - val token = - accountsModel.newCredentials.getOrElse(account) { accountManager.findCredentials(account) } - ?: return CompletableFuture.completedFuture(noToken()) - val executor = service().create(token) + return ProgressManager.getInstance() .submitIOTask(indicator) { indicator -> + val token = + accountsModel.newCredentials.getOrElse(account) { + accountManager.findCredentials(account) + } ?: return@submitIOTask noToken() + val executor = service().create(token) + if (account.isCodyApp()) { val details = CodyAccountDetails(account.id, account.name, account.name, null) DetailsLoadingResult(details, IconUtil.toBufferedImage(defaultIcon), null, false) diff --git a/src/main/kotlin/com/sourcegraph/cody/config/DialogValidationUtils.kt b/src/main/kotlin/com/sourcegraph/cody/config/DialogValidationUtils.kt index 6053c3f4db..9943fc5944 100644 --- a/src/main/kotlin/com/sourcegraph/cody/config/DialogValidationUtils.kt +++ b/src/main/kotlin/com/sourcegraph/cody/config/DialogValidationUtils.kt @@ -2,6 +2,7 @@ package com.sourcegraph.cody.config import com.intellij.openapi.ui.ValidationInfo import com.intellij.openapi.util.NlsContexts +import javax.swing.JComponent import javax.swing.JTextField object DialogValidationUtils { @@ -12,11 +13,11 @@ object DialogValidationUtils { /** Returns [ValidationInfo] with [message] if [isValid] returns false */ fun custom( - textField: JTextField, + component: JComponent, @NlsContexts.DialogMessage message: String, isValid: () -> Boolean ): ValidationInfo? { - return if (!isValid()) ValidationInfo(message, textField) else null + return if (!isValid()) ValidationInfo(message, component) else null } /** diff --git a/src/main/kotlin/com/sourcegraph/cody/context/RemoteRepoUtils.kt b/src/main/kotlin/com/sourcegraph/cody/context/RemoteRepoUtils.kt new file mode 100644 index 0000000000..4f5bc20fdb --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/context/RemoteRepoUtils.kt @@ -0,0 +1,23 @@ +package com.sourcegraph.cody.context + +import com.intellij.openapi.project.Project +import com.sourcegraph.cody.agent.CodyAgentService +import com.sourcegraph.cody.agent.protocol.GetRepoIdsParam +import com.sourcegraph.cody.agent.protocol.Repo +import java.util.concurrent.CompletableFuture + +object RemoteRepoUtils { + fun getRepository(project: Project, url: String): CompletableFuture { + val result = CompletableFuture>() + CodyAgentService.applyAgentOnBackgroundThread(project) { agent -> + try { + agent.server.getRepoIds(GetRepoIdsParam(listOf(url), 1)).thenApply { + result.complete(it?.repos) + } + } catch (e: Exception) { + result.complete(null) + } + } + return result.thenApply { it?.firstOrNull() } + } +} diff --git a/src/main/kotlin/com/sourcegraph/cody/context/ui/AddRepositoryDialog.kt b/src/main/kotlin/com/sourcegraph/cody/context/ui/AddRepositoryDialog.kt new file mode 100644 index 0000000000..1334ff6b1c --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/context/ui/AddRepositoryDialog.kt @@ -0,0 +1,94 @@ +package com.sourcegraph.cody.context.ui + +import com.intellij.openapi.editor.event.DocumentListener +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.ui.ValidationInfo +import com.intellij.ui.TextFieldWithAutoCompletion +import com.intellij.ui.TextFieldWithAutoCompletionListProvider +import com.intellij.util.Alarm +import com.sourcegraph.cody.config.DialogValidationUtils +import com.sourcegraph.cody.context.RemoteRepoUtils +import java.net.URL +import java.util.concurrent.TimeUnit +import javax.swing.JComponent +import javax.swing.JLabel +import javax.swing.JPanel +import org.jetbrains.annotations.NotNull + +class AddRepositoryDialog(private val project: Project, private val addAction: (String) -> Unit) : + DialogWrapper(project) { + + private val repoUrlInputField = TextFieldWithAutoCompletion.create(project, listOf(), false, null) + + init { + init() + title = "Add Remote Repository" + setOKButtonText("Add") + setValidationDelay(100) + } + + override fun doValidateAll(): List { + fun validateNonEmpty() = + DialogValidationUtils.custom(repoUrlInputField, "Remote repository URL cannot be empty") { + repoUrlInputField.text.isNotBlank() + } + + fun validateValidUrl() = + DialogValidationUtils.custom(repoUrlInputField, "Remote repository URL must be valid") { + val url = + if (repoUrlInputField.text.startsWith("http")) repoUrlInputField.text + else "http://" + repoUrlInputField.text + runCatching { URL(url) }.isSuccess + } + + fun validateRepoExists() = + DialogValidationUtils.custom( + repoUrlInputField, "Remote repository not found on the server") { + val repo = + RemoteRepoUtils.getRepository(project, repoUrlInputField.text) + .completeOnTimeout(null, 2, TimeUnit.SECONDS) + .get() + repo != null + } + + return listOfNotNull(validateNonEmpty() ?: validateValidUrl() ?: validateRepoExists()) + } + + override fun getValidationThreadToUse(): Alarm.ThreadToUse { + return Alarm.ThreadToUse.POOLED_THREAD + } + + override fun doOKAction() { + addAction(repoUrlInputField.text) + close(OK_EXIT_CODE, true) + } + + override fun createCenterPanel(): JComponent { + val panel = JPanel() + val label = JLabel("Repository URL: ") + panel.add(label) + + // TODO: we can provide repository suggestions using `provider.setItems` method + val completionProvider: TextFieldWithAutoCompletionListProvider = + object : TextFieldWithAutoCompletionListProvider(listOf()) { + @NotNull + override fun getLookupString(@NotNull s: String): String { + return s + } + } + + repoUrlInputField.setPreferredWidth(300) + repoUrlInputField.installProvider(completionProvider) + repoUrlInputField.addDocumentListener( + object : DocumentListener { + override fun documentChanged(event: com.intellij.openapi.editor.event.DocumentEvent) { + initValidation() + } + }) + + panel.add((repoUrlInputField)) + + return panel + } +} diff --git a/src/main/kotlin/com/sourcegraph/cody/context/ui/ContextRepositoriesCheckboxRenderer.kt b/src/main/kotlin/com/sourcegraph/cody/context/ui/ContextRepositoriesCheckboxRenderer.kt index a12d55486e..1dd310012b 100644 --- a/src/main/kotlin/com/sourcegraph/cody/context/ui/ContextRepositoriesCheckboxRenderer.kt +++ b/src/main/kotlin/com/sourcegraph/cody/context/ui/ContextRepositoriesCheckboxRenderer.kt @@ -1,34 +1,38 @@ package com.sourcegraph.cody.context.ui -import com.intellij.openapi.project.Project import com.intellij.ui.CheckboxTree import com.intellij.ui.CheckedTreeNode import com.intellij.ui.SimpleTextAttributes +import java.io.File import javax.swing.JTree class ContextRepositoriesCheckboxRenderer : CheckboxTree.CheckboxTreeCellRenderer() { override fun customizeRenderer( tree: JTree?, - value: Any?, + node: Any?, selected: Boolean, expanded: Boolean, leaf: Boolean, row: Int, hasFocus: Boolean ) { - when (value) { + when (node) { + is ContextTreeRemoteRepoNode -> { + val repoName = node.repoUrl.split(File.separator).lastOrNull() ?: node.repoUrl + textRenderer.appendHTML( + "${repoName} ${node.repoUrl}", + SimpleTextAttributes.REGULAR_ATTRIBUTES) + } + is ContextTreeLocalRepoNode -> { + val projectPath = node.project.basePath?.replace(System.getProperty("user.home"), "~") + textRenderer.appendHTML( + "${node.project.name} ${projectPath}", + SimpleTextAttributes.REGULAR_ATTRIBUTES) + } is CheckedTreeNode -> { - when (val userObject = value.userObject) { - is Project -> { - textRenderer.appendHTML( - "${userObject.name} - ${userObject.basePath}", - SimpleTextAttributes.REGULAR_ATTRIBUTES) - } - is String -> { - textRenderer.appendHTML(userObject, SimpleTextAttributes.REGULAR_ATTRIBUTES) - } - } + textRenderer.appendHTML( + "${node.userObject}", SimpleTextAttributes.REGULAR_ATTRIBUTES) } } } diff --git a/src/main/kotlin/com/sourcegraph/cody/context/ui/ContextToolbarButton.kt b/src/main/kotlin/com/sourcegraph/cody/context/ui/ContextToolbarButton.kt new file mode 100644 index 0000000000..bd5e63fcda --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/context/ui/ContextToolbarButton.kt @@ -0,0 +1,17 @@ +package com.sourcegraph.cody.context.ui + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.ui.ToolbarDecorator +import javax.swing.Icon + +open class ContextToolbarButton( + name: String, + icon: Icon, + private val buttonAction: () -> Unit = {} +) : ToolbarDecorator.ElementActionButton(name, icon) { + override fun isDumbAware(): Boolean = true + + override fun actionPerformed(p0: AnActionEvent) { + buttonAction() + } +} diff --git a/src/main/kotlin/com/sourcegraph/cody/context/ui/ContextTreeNode.kt b/src/main/kotlin/com/sourcegraph/cody/context/ui/ContextTreeNode.kt new file mode 100644 index 0000000000..70e9aa933d --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/context/ui/ContextTreeNode.kt @@ -0,0 +1,38 @@ +package com.sourcegraph.cody.context.ui + +import com.intellij.openapi.project.Project +import com.intellij.ui.CheckedTreeNode + +open class ContextTreeNode(value: T, private val onSetChecked: (Boolean) -> Unit) : + CheckedTreeNode(value) { + override fun setChecked(checked: Boolean) { + super.setChecked(checked) + onSetChecked(checked) + } +} + +class ContextTreeRootNode( + val text: String, + isEnabled: Boolean = true, + onSetChecked: (Boolean) -> Unit = {} +) : ContextTreeNode(text, onSetChecked) { + init { + this.isEnabled = isEnabled + } +} + +class ContextTreeRemoteRepoNode( + val repoUrl: String, + isChecked: Boolean, + onSetChecked: (Boolean) -> Unit +) : ContextTreeNode(repoUrl, onSetChecked) { + init { + this.isChecked = isChecked + } +} + +class ContextTreeLocalRepoNode(val project: Project) : ContextTreeNode(project, {}) { + init { + this.isEnabled = false + } +} diff --git a/src/main/kotlin/com/sourcegraph/cody/context/ui/EnhancedContextPanel.kt b/src/main/kotlin/com/sourcegraph/cody/context/ui/EnhancedContextPanel.kt index 0b971c3104..c2ca837356 100644 --- a/src/main/kotlin/com/sourcegraph/cody/context/ui/EnhancedContextPanel.kt +++ b/src/main/kotlin/com/sourcegraph/cody/context/ui/EnhancedContextPanel.kt @@ -1,58 +1,257 @@ package com.sourcegraph.cody.context.ui -import com.intellij.openapi.actionSystem.ActionToolbarPosition +import com.intellij.icons.AllIcons +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project import com.intellij.openapi.ui.VerticalFlowLayout import com.intellij.ui.CheckboxTree +import com.intellij.ui.CheckboxTreeBase import com.intellij.ui.CheckedTreeNode -import com.intellij.ui.ToolbarDecorator -import com.sourcegraph.cody.agent.CodyAgentService +import com.intellij.ui.ToolbarDecorator.createDecorator +import com.intellij.util.concurrency.annotations.RequiresEdt +import com.sourcegraph.cody.agent.CodyAgentCodebase +import com.sourcegraph.cody.agent.WebviewMessage +import com.sourcegraph.cody.agent.protocol.Repo +import com.sourcegraph.cody.chat.ChatSession +import com.sourcegraph.cody.config.CodyAuthenticationManager +import com.sourcegraph.cody.context.RemoteRepoUtils +import com.sourcegraph.cody.history.HistoryService +import com.sourcegraph.cody.history.state.EnhancedContextState +import com.sourcegraph.cody.history.state.RemoteRepositoryState import com.sourcegraph.common.CodyBundle import java.awt.Dimension import java.util.concurrent.atomic.AtomicBoolean +import java.util.function.Consumer import javax.swing.BorderFactory import javax.swing.JPanel +import javax.swing.event.TreeExpansionEvent +import javax.swing.event.TreeExpansionListener +import javax.swing.tree.DefaultTreeModel -class EnhancedContextPanel(private val project: Project) : JPanel() { +class EnhancedContextPanel(private val project: Project, private val chatSession: ChatSession) : + JPanel() { val isEnhancedContextEnabled = AtomicBoolean(true) - private val reindexButton = ReindexButton(project) + private val treeRoot = CheckedTreeNode(CodyBundle.getString("context-panel.tree.root")) + private val enhancedContextNode = + ContextTreeRootNode(CodyBundle.getString("context-panel.tree.node-chat-context")) { isChecked + -> + isEnhancedContextEnabled.set(isChecked) + updateContextState { it.isEnabled = isChecked } + } + private val localContextNode = + ContextTreeRootNode( + CodyBundle.getString("context-panel.tree.node-local-project"), isEnabled = false) + private val remoteContextNode = + ContextTreeRootNode(CodyBundle.getString("context-panel.tree.node-remote-repos")) + private val localProjectNode = ContextTreeLocalRepoNode(project) - private val helpButton = HelpButton() + private val treeModel = DefaultTreeModel(treeRoot) private val tree = run { - val treeRoot = CheckedTreeNode(CodyBundle.getString("context-panel.tree-root")) - val chatContext = CheckedTreeNode(CodyBundle.getString("context-panel.tree-chat-context")) - val currentRepo = - object : CheckedTreeNode(project) { - override fun isChecked(): Boolean = isEnhancedContextEnabled.get() - - override fun setChecked(checked: Boolean) { - isEnhancedContextEnabled.set(checked) - CodyAgentService.getInstance(project).restartAgent(project) - } + val checkboxPropagationPolicy = + CheckboxTreeBase.CheckPolicy( + /* checkChildrenWithCheckedParent = */ true, + /* uncheckChildrenWithUncheckedParent = */ true, + /* checkParentWithCheckedChild = */ false, + /* uncheckParentWithUncheckedChild = */ false) + CheckboxTree(ContextRepositoriesCheckboxRenderer(), treeRoot, checkboxPropagationPolicy) + } + + @RequiresEdt + private fun prepareTree() { + treeRoot.add(enhancedContextNode) + localContextNode.add(localProjectNode) + enhancedContextNode.add(localContextNode) + + val contextState = getContextState() + + ApplicationManager.getApplication().invokeLater { + enhancedContextNode.isChecked = contextState.isEnabled + localContextNode.isChecked = contextState.isEnabled + localProjectNode.isChecked = contextState.isEnabled + } + + if (!isDotComAccount()) { + enhancedContextNode.add(remoteContextNode) + if (contextState.remoteRepositories.isNotEmpty()) { + contextState.remoteRepositories.forEach { repo -> + repo.remoteUrl?.let { remoteUrl -> addRemoteRepository(remoteUrl, repo.isEnabled) } + } + } else { + CodyAgentCodebase.getInstance(project).getUrl().thenApply { repo -> + addRemoteRepository(repo) + } + } + } + + treeModel.reload() + } + + private fun getContextState(): EnhancedContextState { + val historyService = HistoryService.getInstance(project) + + return historyService.getOrCreateChatReadOnly(chatSession.getInternalId()).enhancedContext + ?: historyService.getHistoryReadOnly().defaultEnhancedContext + ?: EnhancedContextState() + } + + private fun updateContextState(consumer: Consumer) { + val contextState = getContextState() + consumer.accept(contextState) + HistoryService.getInstance(project) + .updateContextState(chatSession.getInternalId(), contextState) + } + + private fun isDotComAccount() = + CodyAuthenticationManager.instance.getActiveAccount(project)?.isDotcomAccount() ?: false + + private fun getRepoByUrlAndRun(repoUrl: String, consumer: Consumer) { + RemoteRepoUtils.getRepository(project, repoUrl).thenApply { + it?.let { repo -> consumer.accept(repo) } + } + } + + private fun enableRemote(repoUrl: String) { + updateContextState { contextState -> + contextState.remoteRepositories.find { it.remoteUrl == repoUrl }?.isEnabled = true + } + getRepoByUrlAndRun(repoUrl) { repo -> + chatSession.sendWebviewMessage( + WebviewMessage( + command = "context/choose-remote-search-repo", explicitRepos = listOf(repo))) + } + } + + private fun disableRemote(repoUrl: String) { + updateContextState { contextState -> + contextState.remoteRepositories.find { it.remoteUrl == repoUrl }?.isEnabled = false + } + getRepoByUrlAndRun(repoUrl) { repo -> + chatSession.sendWebviewMessage( + WebviewMessage(command = "context/remove-remote-search-repo", repoId = repo.id)) + } + } + + @RequiresEdt + private fun removeRemoteRepository(node: ContextTreeRemoteRepoNode) { + updateContextState { contextState -> + contextState.remoteRepositories.removeIf { it.remoteUrl == node.repoUrl } + } + remoteContextNode.remove(node) + treeModel.reload() + disableRemote(node.repoUrl) + } + + @RequiresEdt + private fun addRemoteRepository(repoUrl: String, isCheckedInitially: Boolean = true) { + val existingRemote = getContextState().remoteRepositories.find { it.remoteUrl == repoUrl } + if (existingRemote == null) { + updateContextState { contextState -> + contextState.remoteRepositories.add( + RemoteRepositoryState.create(repoUrl, isCheckedInitially)) + } + } else { + existingRemote.isEnabled = isCheckedInitially + existingRemote.remoteUrl = repoUrl + } + + val remoteRepoNode = + ContextTreeRemoteRepoNode(repoUrl, isChecked = isCheckedInitially) { isChecked -> + if (isChecked) enableRemote(repoUrl) else disableRemote(repoUrl) } - chatContext.add(currentRepo) - treeRoot.add(chatContext) - CheckboxTree(ContextRepositoriesCheckboxRenderer(), treeRoot) - } - - private val toolbarPanel = - ToolbarDecorator.createDecorator(tree) - .disableUpDownActions() - .addExtraAction(reindexButton) - .addExtraAction(helpButton) - .setPreferredSize(Dimension(0, 30)) - .setToolbarPosition(ActionToolbarPosition.LEFT) - .setScrollPaneBorder(BorderFactory.createEmptyBorder()) - .setToolbarBorder(BorderFactory.createEmptyBorder()) - .createPanel() + + remoteContextNode.add(remoteRepoNode) + treeModel.reload() + } + + @RequiresEdt + private fun expandAllNodes(rowCount: Int = tree.rowCount) { + for (i in 0 until tree.rowCount) { + tree.expandRow(i) + } + + if (tree.getRowCount() != rowCount) { + expandAllNodes(tree.rowCount) + } + } init { - layout = VerticalFlowLayout(VerticalFlowLayout.BOTTOM, 0, 5, true, false) - tree.expandRow(0) + layout = VerticalFlowLayout(VerticalFlowLayout.BOTTOM, 0, 0, true, false) + tree.setModel(treeModel) + prepareTree() + + val toolbarDecorator = + createDecorator(tree) + .disableUpDownActions() + .setVisibleRowCount(1) + .setScrollPaneBorder(BorderFactory.createEmptyBorder()) + .setToolbarBorder(BorderFactory.createEmptyBorder()) + + if (!isDotComAccount()) { + toolbarDecorator.setAddActionName( + CodyBundle.getString("context-panel.button.add-remote-repo")) + toolbarDecorator.setAddAction { + AddRepositoryDialog(project) { repoUrl -> addRemoteRepository(repoUrl) }.show() + expandAllNodes() + } + + toolbarDecorator.setRemoveActionName( + CodyBundle.getString("context-panel.button.remove-remote-repo")) + toolbarDecorator.setRemoveActionUpdater { + tree.selectionPath?.lastPathComponent is ContextTreeRemoteRepoNode + } + toolbarDecorator.setRemoveAction { + (tree.selectionPath?.lastPathComponent as? ContextTreeRemoteRepoNode)?.let { node -> + removeRemoteRepository(node) + expandAllNodes() + } + } + } + + toolbarDecorator.addExtraAction(ReindexButton(project)) + + toolbarDecorator.addExtraAction( + ContextToolbarButton( + CodyBundle.getString("context-panel.button.save-default"), + AllIcons.Actions.SetDefault) { + HistoryService.getInstance(project).updateDefaultContextState(getContextState()) + }) + + toolbarDecorator.addExtraAction( + ContextToolbarButton( + CodyBundle.getString("context-panel.button.restore-default"), AllIcons.General.Reset) { + HistoryService.getInstance(project) + .updateContextState(chatSession.getInternalId(), contextState = null) + treeRoot.removeAllChildren() + prepareTree() + }) + + toolbarDecorator.addExtraAction(HelpButton()) + + val panel = toolbarDecorator.createPanel() + + tree.addTreeExpansionListener( + object : TreeExpansionListener { + private fun resize() { + panel.preferredSize = Dimension(0, 30 + tree.rowCount * 20) + panel.parent.revalidate() + } + + override fun treeExpanded(event: TreeExpansionEvent) { + val component = event.path.lastPathComponent + if (component is ContextTreeRootNode && component == enhancedContextNode) { + expandAllNodes() + } + resize() + } + + override fun treeCollapsed(event: TreeExpansionEvent) { + resize() + } + }) - add(toolbarPanel) + add(panel) } } diff --git a/src/main/kotlin/com/sourcegraph/cody/context/ui/HelpButton.kt b/src/main/kotlin/com/sourcegraph/cody/context/ui/HelpButton.kt index 6892ff1554..3ff6fb3eae 100644 --- a/src/main/kotlin/com/sourcegraph/cody/context/ui/HelpButton.kt +++ b/src/main/kotlin/com/sourcegraph/cody/context/ui/HelpButton.kt @@ -2,16 +2,10 @@ package com.sourcegraph.cody.context.ui import com.intellij.icons.AllIcons import com.intellij.ide.BrowserUtil -import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.ui.ToolbarDecorator import com.sourcegraph.common.CodyBundle class HelpButton : - ToolbarDecorator.ElementActionButton( - CodyBundle.getString("context-panel.help-button-name"), AllIcons.Actions.Help) { - override fun actionPerformed(p0: AnActionEvent) { - BrowserUtil.open("https://docs.sourcegraph.com/cody/core-concepts/keyword-search") - } - - override fun isDumbAware(): Boolean = true -} + ContextToolbarButton( + CodyBundle.getString("context-panel.button.help"), + AllIcons.Actions.Help, + { BrowserUtil.open("https://docs.sourcegraph.com/cody/core-concepts/keyword-search") }) diff --git a/src/main/kotlin/com/sourcegraph/cody/context/ui/ReindexButton.kt b/src/main/kotlin/com/sourcegraph/cody/context/ui/ReindexButton.kt index 4b851354e2..72d750d77f 100644 --- a/src/main/kotlin/com/sourcegraph/cody/context/ui/ReindexButton.kt +++ b/src/main/kotlin/com/sourcegraph/cody/context/ui/ReindexButton.kt @@ -7,7 +7,6 @@ import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project import com.intellij.openapi.ui.Messages -import com.intellij.ui.ToolbarDecorator import com.sourcegraph.cody.agent.CodyAgentService import com.sourcegraph.cody.agent.CommandExecuteParams import com.sourcegraph.common.CodyBundle @@ -15,8 +14,8 @@ import com.sourcegraph.common.CodyBundle.fmt import java.util.concurrent.atomic.AtomicBoolean class ReindexButton(private val project: Project) : - ToolbarDecorator.ElementActionButton( - CodyBundle.getString("context-panel.reindex-button-name"), AllIcons.Actions.Refresh) { + ContextToolbarButton( + CodyBundle.getString("context-panel.button.reindex"), AllIcons.Actions.Refresh) { override fun actionPerformed(p0: AnActionEvent) { CodyAgentService.applyAgentOnBackgroundThread(project) { agent -> ProgressManager.getInstance() @@ -45,7 +44,5 @@ class ReindexButton(private val project: Project) : override fun isEnabled(): Boolean = !isReindexingInProgress.get() - override fun isDumbAware(): Boolean = true - private val isReindexingInProgress = AtomicBoolean(false) } diff --git a/src/main/kotlin/com/sourcegraph/cody/history/HistoryService.kt b/src/main/kotlin/com/sourcegraph/cody/history/HistoryService.kt index 1927cffb85..1888ce2af4 100644 --- a/src/main/kotlin/com/sourcegraph/cody/history/HistoryService.kt +++ b/src/main/kotlin/com/sourcegraph/cody/history/HistoryService.kt @@ -1,9 +1,12 @@ package com.sourcegraph.cody.history import com.intellij.openapi.components.* +import com.intellij.openapi.project.Project +import com.jetbrains.rd.framework.base.deepClonePolymorphic import com.sourcegraph.cody.agent.protocol.ChatMessage import com.sourcegraph.cody.agent.protocol.Speaker import com.sourcegraph.cody.history.state.ChatState +import com.sourcegraph.cody.history.state.EnhancedContextState import com.sourcegraph.cody.history.state.HistoryState import com.sourcegraph.cody.history.state.MessageState import java.time.LocalDateTime @@ -18,8 +21,9 @@ class HistoryService : SimplePersistentStateComponent(HistoryState synchronized(listeners) { listeners += listener } } - fun update(internalId: String, chatMessages: List) { - val found = getChatOrCreate(internalId) + @Synchronized + fun updateChatMessages(internalId: String, chatMessages: List) { + val found = getOrCreateChat(internalId) found.messages = chatMessages.map(::convertToMessageState).toMutableList() if (chatMessages.lastOrNull()?.speaker == Speaker.HUMAN) { found.setUpdatedTimeAt(LocalDateTime.now()) @@ -27,6 +31,28 @@ class HistoryService : SimplePersistentStateComponent(HistoryState synchronized(listeners) { listeners.forEach { it(found) } } } + @Synchronized + fun updateContextState(internalId: String, contextState: EnhancedContextState?) { + val found = getOrCreateChat(internalId) + found.enhancedContext = contextState + } + + @Synchronized + fun updateDefaultContextState(contextState: EnhancedContextState) { + state.defaultEnhancedContext = contextState + } + + @Synchronized + fun getOrCreateChatReadOnly(internalId: String): ChatState { + return getOrCreateChat(internalId).deepClonePolymorphic() + } + + @Synchronized + fun getHistoryReadOnly(): HistoryState { + return state.deepClonePolymorphic() + } + + @Synchronized fun remove(internalId: String?) { state.chats.removeIf { it.internalId == internalId } } @@ -46,7 +72,7 @@ class HistoryService : SimplePersistentStateComponent(HistoryState return message } - private fun getChatOrCreate(internalId: String): ChatState { + private fun getOrCreateChat(internalId: String): ChatState { val found = state.chats.find { it.internalId == internalId } if (found != null) return found val newChat = ChatState().also { it.internalId = internalId } @@ -55,7 +81,6 @@ class HistoryService : SimplePersistentStateComponent(HistoryState } companion object { - - @JvmStatic fun getInstance() = service() + @JvmStatic fun getInstance(project: Project): HistoryService = project.service() } } diff --git a/src/main/kotlin/com/sourcegraph/cody/history/HistoryTree.kt b/src/main/kotlin/com/sourcegraph/cody/history/HistoryTree.kt index 4181956ae4..f39b10a536 100644 --- a/src/main/kotlin/com/sourcegraph/cody/history/HistoryTree.kt +++ b/src/main/kotlin/com/sourcegraph/cody/history/HistoryTree.kt @@ -4,6 +4,7 @@ import com.intellij.icons.AllIcons import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.DefaultActionGroup +import com.intellij.openapi.project.Project import com.intellij.openapi.ui.SimpleToolWindowPanel import com.intellij.ui.PopupHandler import com.intellij.ui.ScrollPaneFactory @@ -27,6 +28,7 @@ import javax.swing.tree.TreePath import javax.swing.tree.TreeSelectionModel class HistoryTree( + private val project: Project, private val onSelect: (ChatState) -> Unit, private val onRemove: (ChatState) -> Unit, private val onRemoveAll: () -> Unit @@ -69,7 +71,7 @@ class HistoryTree( PopupHandler.installPopupMenu(tree, group, "ChatActionsPopup") EditSourceOnDoubleClickHandler.install(tree, ::selectLeaf) setContent(ScrollPaneFactory.createScrollPane(tree)) - HistoryService.getInstance().listenOnUpdate(::updatePresentation) + HistoryService.getInstance(project).listenOnUpdate(::updatePresentation) } private fun updatePresentation(chat: ChatState) { @@ -170,9 +172,10 @@ class HistoryTree( } private fun getChatsGroupedByPeriod(): Map> = - HistoryService.getInstance() - .state + HistoryService.getInstance(project) + .getHistoryReadOnly() .chats + .filter { it.messages.isNotEmpty() } .sortedByDescending { chat -> chat.getUpdatedTimeAt() } .groupBy { chat -> DurationGroupFormatter.format(chat.getUpdatedTimeAt()) } diff --git a/src/main/kotlin/com/sourcegraph/cody/history/state/ChatState.kt b/src/main/kotlin/com/sourcegraph/cody/history/state/ChatState.kt index dfdcfd200a..009b8a97ef 100644 --- a/src/main/kotlin/com/sourcegraph/cody/history/state/ChatState.kt +++ b/src/main/kotlin/com/sourcegraph/cody/history/state/ChatState.kt @@ -16,6 +16,9 @@ class ChatState : BaseState() { @get:OptionTag(tag = "updatedAt", nameAttribute = "") var updatedAt: String? by string() + @get:OptionTag(tag = "enhancedContext", nameAttribute = "") + var enhancedContext: EnhancedContextState? by property() + fun title(): String? = messages.firstOrNull()?.text fun setUpdatedTimeAt(date: LocalDateTime) { diff --git a/src/main/kotlin/com/sourcegraph/cody/history/state/EnhancedContextState.kt b/src/main/kotlin/com/sourcegraph/cody/history/state/EnhancedContextState.kt new file mode 100644 index 0000000000..ab06679c2f --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/history/state/EnhancedContextState.kt @@ -0,0 +1,13 @@ +package com.sourcegraph.cody.history.state + +import com.intellij.openapi.components.BaseState +import com.intellij.util.xmlb.annotations.OptionTag +import com.intellij.util.xmlb.annotations.Tag + +@Tag("enhancedContext") +class EnhancedContextState : BaseState() { + @get:OptionTag(tag = "isEnabled", nameAttribute = "") var isEnabled: Boolean by property(true) + + @get:OptionTag(tag = "remoteRepositories", nameAttribute = "") + var remoteRepositories: MutableList by list() +} diff --git a/src/main/kotlin/com/sourcegraph/cody/history/state/HistoryState.kt b/src/main/kotlin/com/sourcegraph/cody/history/state/HistoryState.kt index b65aea51f4..2726e887b4 100644 --- a/src/main/kotlin/com/sourcegraph/cody/history/state/HistoryState.kt +++ b/src/main/kotlin/com/sourcegraph/cody/history/state/HistoryState.kt @@ -6,4 +6,7 @@ import com.intellij.util.xmlb.annotations.OptionTag class HistoryState : BaseState() { @get:OptionTag(tag = "chats", nameAttribute = "") var chats: MutableList by list() + + @get:OptionTag(tag = "defaultEnhancedContext", nameAttribute = "") + var defaultEnhancedContext: EnhancedContextState? by property() } diff --git a/src/main/kotlin/com/sourcegraph/cody/history/state/RemoteRepositoryState.kt b/src/main/kotlin/com/sourcegraph/cody/history/state/RemoteRepositoryState.kt new file mode 100644 index 0000000000..5fdb13d297 --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/history/state/RemoteRepositoryState.kt @@ -0,0 +1,22 @@ +package com.sourcegraph.cody.history.state + +import com.intellij.openapi.components.BaseState +import com.intellij.util.xmlb.annotations.OptionTag +import com.intellij.util.xmlb.annotations.Tag + +@Tag("remoteRepository") +class RemoteRepositoryState : BaseState() { + + @get:OptionTag(tag = "isEnabled", nameAttribute = "") var isEnabled: Boolean by property(true) + + @get:OptionTag(tag = "remoteUrl", nameAttribute = "") var remoteUrl: String? by string() + + companion object { + fun create(remoteUrl: String, isEnabled: Boolean): RemoteRepositoryState { + val state = RemoteRepositoryState() + state.isEnabled = isEnabled + state.remoteUrl = remoteUrl + return state + } + } +} diff --git a/src/main/kotlin/com/sourcegraph/cody/initialization/PostStartupActivity.kt b/src/main/kotlin/com/sourcegraph/cody/initialization/PostStartupActivity.kt index 93e59457ac..1b671edc53 100644 --- a/src/main/kotlin/com/sourcegraph/cody/initialization/PostStartupActivity.kt +++ b/src/main/kotlin/com/sourcegraph/cody/initialization/PostStartupActivity.kt @@ -3,7 +3,6 @@ package com.sourcegraph.cody.initialization import com.intellij.openapi.project.Project import com.intellij.openapi.startup.StartupActivity import com.sourcegraph.cody.CodyFocusChangeListener -import com.sourcegraph.cody.agent.CodyAgentCodebase import com.sourcegraph.cody.agent.CodyAgentService import com.sourcegraph.cody.auth.SelectOneOfTheAccountsAsActive import com.sourcegraph.cody.config.SettingsMigration @@ -29,7 +28,6 @@ class PostStartupActivity : StartupActivity.DumbAware { if (ConfigUtil.isCodyEnabled()) CodyAgentService.getInstance(project).startAgent(project) CodyAutocompleteStatusService.resetApplication(project) CodyFocusChangeListener().runActivity(project) - CodyAgentCodebase.getInstance(project).onFileOpened(project, null) EndOfTrialNotificationScheduler.createAndStart(project) } } diff --git a/src/main/kotlin/com/sourcegraph/config/ConfigUtil.kt b/src/main/kotlin/com/sourcegraph/config/ConfigUtil.kt index 7b03ceccfc..4465684e3e 100644 --- a/src/main/kotlin/com/sourcegraph/config/ConfigUtil.kt +++ b/src/main/kotlin/com/sourcegraph/config/ConfigUtil.kt @@ -45,7 +45,7 @@ object ConfigUtil { UserLevelConfig.getAutocompleteProviderType()?.vscodeSettingString(), debug = isCodyDebugEnabled(), verboseDebug = isCodyVerboseDebugEnabled(), - codebase = CodyAgentCodebase.getInstance(project).getUrl(), + codebase = CodyAgentCodebase.getInstance(project).getUrl().getNow(null), ) } diff --git a/src/main/resources/CodyBundle.properties b/src/main/resources/CodyBundle.properties index fae1e66382..033477e6f9 100644 --- a/src/main/resources/CodyBundle.properties +++ b/src/main/resources/CodyBundle.properties @@ -51,7 +51,6 @@ chat.attribution.success.label=Guard Rails Check Passed chat.attribution.success.tooltip=Snippet not found on Sourcegraph.com chat.attribution.failure.label=Guard Rails Check Failed chat.attribution.failure.tooltip=Guard Rails Check Failed. Code found in {0} repositories: {1} - my-account-tab.chat-rate-limit-error=You've used all your chat messages and commands for the month. Upgrade to Pro for unlimited usage. my-account-tab.autocomplete-rate-limit-error=You've used all your autocompletions for the month. Upgrade to Pro for unlimited usage. my-account-tab.chat-and-autocomplete-rate-limit-error=You've used all your autocompletions and chats for this month. Upgrade to Cody Pro to get unlimited interactions. @@ -59,7 +58,6 @@ my-account-tab.cody-pro-label=Cody Pro my-account-tab.cody-free-label=Cody Free my-account-tab.loading-label=Loading... my-account-tab.already-pro=(Already upgraded to Pro? Restart your IDE for changes to take effect) - commands-tab.message-in-progress=Message generation in progress... UpgradeToCodyProNotification.title.upgrade=You've used up your autocompletes for the month UpgradeToCodyProNotification.title.explain=Thank you for using Cody so heavily today! @@ -70,31 +68,33 @@ UpgradeToCodyProNotification.content.upgrade=\ (Already upgraded to Pro? Restart your IDE for changes to take effect)\ UpgradeToCodyProNotification.content.explain=To ensure that Cody can stay operational for all Cody users, please come back tomorrow for more chats, commands, and autocompletes. -context-panel.panel-name=Chat Context -context-panel.reindex-button-name=Trigger Reindexing +tab.subscription.already-pro=(Already upgraded to Pro? Restart your IDE for changes to take effect) +context-panel.button.remove-remote-repo=Remove Remote Repository +context-panel.button.add-remote-repo=Add Remote Repository +context-panel.button.reindex=Reindex Local Project +context-panel.button.save-default=Save Current Context As Default +context-panel.button.restore-default=Restore Default Context +context-panel.button.help=Help context-panel.in-progress=Running Cody 'Keyword Search' indexer... context-panel.error-title=Cody Failure context-panel.error-message=Cody 'Keyword Search' indexer failed: {0} -context-panel.help-button-name=Help -context-panel.tree-root=repositories-root -context-panel.tree-chat-context=Chat Context +context-panel.tree.root=repositories-root +context-panel.tree.node-chat-context=Chat Context +context-panel.tree.node-local-project=Local Project +context-panel.tree.node-remote-repos=Remote Repo(s) messages-panel.welcome-text=Hello! I'm Cody. I can write code and answer questions for you. See [Cody documentation](https://sourcegraph.com/docs/cody) for help and tips. chat-session.error-title=Error while processing the message chat-session.error-message=Cody is not able to reply at the moment. This is a bug, please report an issue to https://github.com/sourcegraph/jetbrains/issues/new/choose and include as many details as possible to help troubleshoot the problem. - -EndOfTrialNotification.link-action-name= Setup Payment Info +EndOfTrialNotification.link-action-name=Setup Payment Info EndOfTrialNotification.do-not-show-again=Don't show again - TrialEndingSoonNotification.ignore=cody.ignore.notification.trial-ending-soon TrialEndingSoonNotification.ending-soon.title=Your Cody Pro trial is ending soon TrialEndingSoonNotification.ending-soon.content=Setup your payment information to continue using Cody Pro, you won't be charged until February 15. TrialEndingSoonNotification.ending-soon.link=https://sourcegraph.com/cody/subscription?on-trial=true - TrialEndedNotification.ignore=cody.ignore.notification.trial-ended TrialEndedNotification.ended.title=Your Cody Pro trial has ended TrialEndedNotification.ended.content=Thank you for trying Cody Pro! Your trial period has ended. To keep enjoying all the features of Cody Pro, subscribe to our monthly plan. You can cancel anytime. TrialEndedNotification.ended.link=https://sourcegraph.com/cody/subscription - duration.today=Today duration.yesterday=Yesterday duration.x-days-ago={0} days ago @@ -105,7 +105,7 @@ duration.x-months-ago={0} months ago duration.last-year=Last year duration.x-years-ago={0} years ago duration.x-ago={0} ago - popup.select-chat=Select Chat popup.remove-chat=Remove Chat popup.remove-all-chats=Remove All Chats +