Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to webview-based sign-in flow #2382

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import com.intellij.testFramework.fixtures.BasePlatformTestCase
import com.intellij.testFramework.runInEdtAndWait
import com.sourcegraph.cody.agent.CodyAgentService
import com.sourcegraph.cody.agent.protocol_generated.ProtocolCodeLens
import com.sourcegraph.cody.config.CodyPersistentAccountsHost
import com.sourcegraph.cody.config.SourcegraphServerPath
import com.sourcegraph.cody.auth.CodyAccount
import com.sourcegraph.cody.auth.SourcegraphServerPath
import com.sourcegraph.cody.edit.lenses.LensListener
import com.sourcegraph.cody.edit.lenses.LensesService
import com.sourcegraph.cody.edit.lenses.providers.EditAcceptCodeVisionProvider
Expand Down Expand Up @@ -94,13 +94,11 @@ open class CodyIntegrationTextFixture : BasePlatformTestCase(), LensListener {
// change anything.
private fun initCredentialsAndAgent() {
val credentials = TestingCredentials.dotcom
CodyPersistentAccountsHost(project)
.addAccount(
SourcegraphServerPath.from(credentials.serverEndpoint, ""),
login = "test_user",
displayName = "Test User",
token = credentials.token ?: credentials.redactedToken,
id = "random-unique-testing-id-1337")
val account = CodyAccount(SourcegraphServerPath.from(credentials.serverEndpoint, ""))
account.storeToken(
credentials.token ?: credentials.redactedToken,
)
CodyAccount.setActiveAccount(account)

assertNotNull(
"Unable to start agent in a timely fashion!",
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/com/sourcegraph/cody/CodyActionGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.intellij.openapi.actionSystem.ActionUpdateThread;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.sourcegraph.cody.auth.CodyAccount;
import com.sourcegraph.config.ConfigUtil;
import org.jetbrains.annotations.NotNull;

Expand All @@ -21,6 +22,7 @@ public boolean isDumbAware() {
@Override
public void update(@NotNull AnActionEvent e) {
super.update(e);
e.getPresentation().setVisible(ConfigUtil.isCodyEnabled());
e.getPresentation()
.setVisible(ConfigUtil.isCodyEnabled() && CodyAccount.Companion.hasActiveAccount());
}
}
2 changes: 0 additions & 2 deletions src/main/java/com/sourcegraph/cody/CodyToolWindowFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import com.intellij.ui.content.ContentFactory;
import com.sourcegraph.cody.config.actions.OpenCodySettingsEditorAction;
import com.sourcegraph.config.ConfigUtil;
import com.sourcegraph.config.OpenPluginSettingsAction;
import org.jetbrains.annotations.NotNull;

public class CodyToolWindowFactory implements ToolWindowFactory, DumbAware {
Expand All @@ -30,7 +29,6 @@ public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindo
content.setPreferredFocusableComponent(null);
toolWindow.getContentManager().addContent(content);
DefaultActionGroup customCodySettings = new DefaultActionGroup();
customCodySettings.add(new OpenPluginSettingsAction("Cody Settings..."));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Probably this is not an item for this PR but our settings seems to be orphaned now 😢 Can we still have them under Tools > Sourcegraph & Cody?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for spotting it!
It is definitely for this PR, in fact I removed too much (update channel selection).
I added it back now.

customCodySettings.add(new OpenCodySettingsEditorAction());
customCodySettings.addSeparator();

Expand Down
19 changes: 0 additions & 19 deletions src/main/java/com/sourcegraph/cody/api/Promises.java

This file was deleted.

6 changes: 0 additions & 6 deletions src/main/java/com/sourcegraph/cody/chat/ChatUIConstants.java

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,14 @@
import com.intellij.notification.Notifications;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowManager;
import com.sourcegraph.Icons;
import com.sourcegraph.cody.CodyToolWindowFactory;
import com.sourcegraph.cody.config.CodyAccount;
import com.sourcegraph.cody.config.CodyAccountManager;
import com.sourcegraph.cody.auth.CodyAccount;
import com.sourcegraph.cody.config.CodyApplicationSettings;
import com.sourcegraph.cody.config.CodyAuthenticationManager;
import com.sourcegraph.cody.initialization.Activity;
import com.sourcegraph.cody.statusbar.CodyManageAccountsAction;
import com.sourcegraph.common.NotificationGroups;
import com.sourcegraph.common.ui.DumbAwareEDTAction;
import org.jetbrains.annotations.NotNull;
Expand All @@ -25,19 +21,8 @@ public class CodyAuthNotificationActivity implements Activity {

@Override
public void runActivity(@NotNull Project project) {
CodyAccount activeAccount = CodyAuthenticationManager.getInstance().getAccount();
CodyAccountManager service =
ApplicationManager.getApplication().getService(CodyAccountManager.class);

if (activeAccount != null) {
String token = service.findCredentials(activeAccount);
if (token == null) {
showMissingTokenNotification();
}
}

if (!CodyApplicationSettings.getInstance().isGetStartedNotificationDismissed()
&& activeAccount == null) {
&& !CodyAccount.Companion.hasActiveAccount()) {
showOpenCodySidebarNotification(project);
}
}
Expand Down Expand Up @@ -79,15 +64,4 @@ public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
notification.addAction(neverShowAgainAction);
Notifications.Bus.notify(notification);
}

private void showMissingTokenNotification() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, I wonder... what if the token expires (or is removed)? 🤔 what will happen (with chat, with autocomplete)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will test this scenario today. In general I think we should then (if user try to call some action explicitly) show cody panel, and it should be showing login panel.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, VSCode has various APIs for showing views, we don't implement them. If we need to show the view, we should see if the extension is trying to do it anyway. Maybe we just need to implement a couple of those methods/properties in the VSCode shim.

// Display notification
Notification notification =
new Notification(
NotificationGroups.CODY_AUTH, "Missing access token", "", NotificationType.WARNING);

notification.setIcon(Icons.CodyLogo);
notification.addAction(new CodyManageAccountsAction());
Notifications.Bus.notify(notification);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.intellij.util.ui.JBDimension;
import com.intellij.util.ui.JBUI;
import com.sourcegraph.Icons;
import com.sourcegraph.cody.config.actions.OpenCodySettingsEditorAction;
import javax.swing.*;
import org.jetbrains.annotations.NotNull;

Expand All @@ -16,7 +17,7 @@ public class GoToPluginSettingsButtonFactory {
public static ActionButton createGoToPluginSettingsButton() {
JBDimension actionButtonSize = JBUI.size(22, 22);

AnAction action = new OpenPluginSettingsAction();
AnAction action = new OpenCodySettingsEditorAction();
Presentation presentation = new Presentation("Open Plugin Settings");

ActionButton button =
Expand Down
25 changes: 0 additions & 25 deletions src/main/java/com/sourcegraph/config/OpenPluginSettingsAction.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import com.intellij.ui.components.JBPanelWithEmptyText;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.StatusText;
import com.sourcegraph.cody.config.ui.AccountConfigurable;
import com.sourcegraph.cody.config.ui.CodyConfigurable;
import java.awt.*;
import javax.swing.*;
import org.apache.commons.lang.WordUtils;
Expand Down Expand Up @@ -69,9 +69,7 @@ private void refreshUI() {
emptyText.appendLine(
"Click here to configure your Sourcegraph Cody + Code Search settings.",
new SimpleTextAttributes(STYLE_PLAIN, JBUI.CurrentTheme.Link.Foreground.ENABLED),
__ ->
ShowSettingsUtil.getInstance()
.showSettingsDialog(project, AccountConfigurable.class));
__ -> ShowSettingsUtil.getInstance().showSettingsDialog(project, CodyConfigurable.class));

} else if (errorMessage != null) {
String wrappedText = WordUtils.wrap("Error: " + errorMessage, 100);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.intellij.openapi.util.Disposer;
import com.intellij.ui.jcef.JBCefBrowser;
import com.sourcegraph.cody.config.notification.AccountSettingChangeListener;
import com.sourcegraph.cody.config.notification.CodySettingChangeListener;
import com.sourcegraph.config.ThemeUtil;
import javax.swing.*;
Expand All @@ -24,11 +23,6 @@ public SourcegraphJBCefBrowser(@NotNull JSToJavaBridgeRequestHandler requestHand
Disposer.register(this, jsToJavaBridge);
javaToJSBridge = new JavaToJSBridge(this);

requestHandler
.getProject()
.getService(AccountSettingChangeListener.class)
.setJavaToJSBridge(javaToJSBridge);

requestHandler
.getProject()
.getService(CodySettingChangeListener.class)
Expand Down
75 changes: 13 additions & 62 deletions src/main/kotlin/com/sourcegraph/cody/CodyToolWindowContent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,15 @@ package com.sourcegraph.cody
import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.ToolWindowManager
import com.intellij.ui.components.JBLabel
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.ui.UIUtil
import com.sourcegraph.cody.chat.SignInWithSourcegraphPanel
import com.sourcegraph.cody.chat.ui.CodyOnboardingGuidancePanel
import com.sourcegraph.cody.chat.ui.ErrorPanel
import com.sourcegraph.cody.chat.ui.MissingJcefPanel
import com.sourcegraph.cody.config.CodyAccount
import com.sourcegraph.cody.config.CodyApplicationSettings
import com.sourcegraph.cody.config.CodyAuthenticationManager
import com.sourcegraph.cody.initialization.VerifyJavaBootRuntimeVersion.Companion.isCurrentRuntimeMissingJcef
import com.sourcegraph.cody.ui.web.CodyToolWindowContentWebviewHost
import com.sourcegraph.cody.ui.web.WebUIService
import java.awt.CardLayout
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import java.awt.GridLayout
Expand All @@ -25,81 +20,43 @@ import javax.swing.JPanel

@Service(Service.Level.PROJECT)
class CodyToolWindowContent(val project: Project) {
private val cardLayout = CardLayout()
private val cardPanel = JPanel(cardLayout)
val allContentPanel: JComponent = JPanel(GridLayout(1, 1))
private var webview: CodyToolWindowContentWebviewHost? = null

init {
cardPanel.add(SignInWithSourcegraphPanel(project), SIGN_IN_PANEL, SIGN_IN_PANEL_INDEX)
val codyOnboardingGuidancePanel = CodyOnboardingGuidancePanel(project)
codyOnboardingGuidancePanel.addMainButtonActionListener {
CodyApplicationSettings.instance.isOnboardingGuidanceDismissed = true
refreshPanelsVisibility()
}
cardPanel.add(codyOnboardingGuidancePanel, ONBOARDING_PANEL, ONBOARDING_PANEL_INDEX)

// Because the webview may be created lazily, populate a placeholder control.
val spinnerPlaceholder = JPanel(GridBagLayout())
val spinnerLabel =
JBLabel("Starting Cody...", Icons.StatusBar.CompletionInProgress, JBLabel.CENTER)
spinnerPlaceholder.add(spinnerLabel, GridBagConstraints())
cardPanel.add(spinnerPlaceholder, LOADING_PANEL, LOADING_PANEL_INDEX)
cardPanel.add(MissingJcefPanel(), CHANGE_RUNTIME_PANEL, CHANGE_RUNTIME_PANEL_INDEX)
cardPanel.add(ErrorPanel(), ERROR_PANEL, ERROR_INDEX)
allContentPanel.add(spinnerLabel, GridBagConstraints())

WebUIService.getInstance(project).views.provideCodyToolWindowContent(this)

refreshPanelsVisibility()
}

@RequiresEdt
fun refreshPanelsVisibility() {
val codyAuthenticationManager = CodyAuthenticationManager.getInstance()
if (codyAuthenticationManager.hasNoActiveAccount() ||
codyAuthenticationManager.showInvalidAccessTokenError()) {
cardLayout.show(cardPanel, SIGN_IN_PANEL)
showView(cardPanel)
return
}
val activeAccount = codyAuthenticationManager.account
if (!CodyApplicationSettings.instance.isOnboardingGuidanceDismissed) {
val displayName = activeAccount?.let(CodyAccount::displayName)
cardPanel.getComponent(ONBOARDING_PANEL_INDEX)?.let {
(it as CodyOnboardingGuidancePanel).updateDisplayName(displayName)
}
cardLayout.show(cardPanel, ONBOARDING_PANEL)
showView(cardPanel)
return
}
val errorOnProxyCreation = WebUIService.getInstance(project).proxyCreationException.get()
if (errorOnProxyCreation != null) {
if (errorOnProxyCreation == null) {
webview?.proxy?.component?.let { showView(it) }
} else {
if (isCurrentRuntimeMissingJcef()) {
cardLayout.show(cardPanel, CHANGE_RUNTIME_PANEL)
showView(cardPanel)
showView(MissingJcefPanel())
} else {
cardLayout.show(cardPanel, ERROR_PANEL)
showView(cardPanel)
showView(ErrorPanel())
logger.error(errorOnProxyCreation)
}
return
}
cardLayout.show(cardPanel, LOADING_PANEL)
showView(webview?.proxy?.component ?: cardPanel)
}

// Flips the sidebar view to the specified top level component. We do it this way
// because JetBrains Remote does not display webviews inside a component using
// CardLayout.
private fun showView(component: JComponent) {
if (allContentPanel.components.isEmpty() || allContentPanel.getComponent(0) != component) {
allContentPanel.removeAll()
allContentPanel.add(component)
}
}

/** Sets the webview component to display, if any. */
@RequiresEdt
internal fun setWebviewComponent(host: CodyToolWindowContentWebviewHost?) {
webview = host
if (host != null && host.proxy?.component == null) {
Expand All @@ -113,20 +70,14 @@ class CodyToolWindowContent(val project: Project) {
}

companion object {
const val ONBOARDING_PANEL = "onboardingPanel"
const val SIGN_IN_PANEL = "signInWithSourcegraphPanel"
const val LOADING_PANEL = "loadingPanel"
const val CHANGE_RUNTIME_PANEL = "changeRuntime"
const val ERROR_PANEL = "error"

const val SIGN_IN_PANEL_INDEX = 0
const val ONBOARDING_PANEL_INDEX = 1
const val LOADING_PANEL_INDEX = 2
const val CHANGE_RUNTIME_PANEL_INDEX = 3
const val ERROR_INDEX = 4

var logger = Logger.getInstance(CodyToolWindowContent::class.java)

fun show(project: Project) {
ToolWindowManager.getInstance(project)
.getToolWindow(CodyToolWindowFactory.TOOL_WINDOW_ID)
?.show()
}

fun executeOnInstanceIfNotDisposed(
project: Project,
myAction: CodyToolWindowContent.() -> Unit
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/com/sourcegraph/cody/agent/CodyAgent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ private constructor(
extensionConfiguration = ConfigUtil.getAgentConfiguration(project),
capabilities =
ClientCapabilities(
authentication = ClientCapabilities.AuthenticationEnum.Enabled,
edit = ClientCapabilities.EditEnum.Enabled,
editWorkspace = ClientCapabilities.EditWorkspaceEnum.Enabled,
codeLenses = ClientCapabilities.CodeLensesEnum.Enabled,
Expand All @@ -128,6 +129,7 @@ private constructor(
untitledDocuments = ClientCapabilities.UntitledDocumentsEnum.Enabled,
codeActions = ClientCapabilities.CodeActionsEnum.Enabled,
globalState = ClientCapabilities.GlobalStateEnum.`Server-managed`,
secrets = ClientCapabilities.SecretsEnum.`Client-managed`,
webview = ClientCapabilities.WebviewEnum.Native,
webviewNativeConfig =
WebviewNativeConfig(
Expand Down
Loading
Loading