mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 17:51:57 +00:00
Cody JetBrains: enable Java formatting and add CI workflow (#52446)
## Test plan Green CI with the new cody-jetbrains workflow. <!-- All pull requests REQUIRE a test plan: https://docs.sourcegraph.com/dev/background-information/testing_principles -->
This commit is contained in:
parent
e02a5f4b2d
commit
ea9ef9cc64
25
.github/workflows/cody-jetbrains.yml
vendored
Normal file
25
.github/workflows/cody-jetbrains.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: Cody JetBrains Tests
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- client/cody-jetbrains/**
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: client/cody-jetbrains
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Gradle Wrapper Validation
|
||||
uses: gradle/wrapper-validation-action@v1.0.4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 11
|
||||
cache: gradle
|
||||
- run: ./gradlew test
|
||||
- run: ./gradlew buildPlugin
|
||||
- run: ./gradlew spotlessCheck
|
||||
@ -21,6 +21,10 @@ New issues and feature requests can be filed through our [issue tracker](https:/
|
||||
- Clone `https://github.com/sourcegraph/sourcegraph` (on Windows, you'll need to use WSL2)
|
||||
- Go to `client/cody-jetbrains/` and run the plugin in a sandboxed IDE by running `./gradlew :runIde`. This will start the platform with the versions defined in `gradle.properties`, [here](https://github.com/sourcegraph/sourcegraph/blob/main/client/cody-jetbrains/gradle.properties#L14-L16).
|
||||
- Build a deployable plugin artifact by running `./gradlew buildPlugin`. The output file is `build/distributions/Cody.zip`.
|
||||
- Reformat the codebase with `./gradlew spotlessApply`.
|
||||
- Install the google-java-format plugin
|
||||
https://plugins.jetbrains.com/plugin/8527-google-java-format and configure
|
||||
IntelliJ's file save actions to format.
|
||||
|
||||
## Publishing a new version
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ plugins {
|
||||
id("org.jetbrains.kotlin.jvm") version "1.7.0"
|
||||
id("org.jetbrains.intellij") version "1.13.3"
|
||||
id("org.jetbrains.changelog") version "1.3.1"
|
||||
id("com.diffplug.spotless") version "6.19.0"
|
||||
}
|
||||
|
||||
group = properties("pluginGroup")
|
||||
@ -37,6 +38,24 @@ dependencies {
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
target("src/*/java/**/*.java")
|
||||
importOrder()
|
||||
removeUnusedImports()
|
||||
googleJavaFormat()
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
// Always compile the codebase with Java 11 regardless of what Java
|
||||
// version is installed on the computer. Gradle will download Java 11
|
||||
// even if you already have it installed on your computer.
|
||||
languageVersion.set(JavaLanguageVersion.of(11))
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
// Set the JVM compatibility versions
|
||||
properties("javaVersion").let {
|
||||
|
||||
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
178
client/cody-jetbrains/gradlew.bat
vendored
178
client/cody-jetbrains/gradlew.bat
vendored
@ -1,89 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
||||
@ -9,19 +9,20 @@ import com.intellij.openapi.project.DumbAwareAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CodyAction extends DumbAwareAction {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Notification notification = new Notification("Cody errors", "Cody",
|
||||
"Test", NotificationType.WARNING);
|
||||
AnAction dismissAction = new DumbAwareAction("Dismiss") {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
|
||||
notification.expire();
|
||||
}
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Notification notification =
|
||||
new Notification("Cody errors", "Cody", "Test", NotificationType.WARNING);
|
||||
AnAction dismissAction =
|
||||
new DumbAwareAction("Dismiss") {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
|
||||
notification.expire();
|
||||
}
|
||||
};
|
||||
notification.setIcon(Icons.CodyLogo);
|
||||
notification.addAction(dismissAction);
|
||||
Notifications.Bus.notify(notification);
|
||||
notification.notify(e.getProject());
|
||||
}
|
||||
notification.setIcon(Icons.CodyLogo);
|
||||
notification.addAction(dismissAction);
|
||||
Notifications.Bus.notify(notification);
|
||||
notification.notify(e.getProject());
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,154 +14,179 @@ import com.sourcegraph.cody.config.ConfigUtil;
|
||||
import com.sourcegraph.cody.config.SettingsComponent;
|
||||
import com.sourcegraph.cody.editor.EditorContextGetter;
|
||||
import com.sourcegraph.cody.recipes.RecipeRunner;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.AdjustmentListener;
|
||||
import java.util.ArrayList;
|
||||
import javax.swing.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class CodyToolWindowContent implements UpdatableChat {
|
||||
private final @NotNull JBTabbedPane tabbedPane = new JBTabbedPane();
|
||||
private final @NotNull JPanel contentPanel = new JPanel();
|
||||
private final @NotNull JPanel recipesPanel = new JPanel();
|
||||
private final @NotNull JPanel messagesPanel = new JPanel();
|
||||
private final @NotNull JTextField messageField;
|
||||
private boolean needScrollingDown = true;
|
||||
private final @NotNull JBTabbedPane tabbedPane = new JBTabbedPane();
|
||||
|
||||
public CodyToolWindowContent(@NotNull Project project) {
|
||||
// Tabs
|
||||
tabbedPane.insertTab("Chat", null, contentPanel, null, 0);
|
||||
tabbedPane.insertTab("Recipes", null, recipesPanel, null, 1);
|
||||
private final @NotNull JPanel contentPanel = new JPanel();
|
||||
private final @NotNull JPanel recipesPanel = new JPanel();
|
||||
private final @NotNull JPanel messagesPanel = new JPanel();
|
||||
private final @NotNull JTextField messageField;
|
||||
private boolean needScrollingDown = true;
|
||||
|
||||
// Recipes panel
|
||||
RecipeRunner recipeRunner = new RecipeRunner(project, this);
|
||||
JButton explainCodeDetailedButton = new JButton("Explain selected code (detailed)");
|
||||
explainCodeDetailedButton.addActionListener(e -> recipeRunner.runExplainCodeDetailed());
|
||||
JButton explainCodeHighLevelButton = new JButton("Explain selected code (high level)");
|
||||
explainCodeHighLevelButton.addActionListener(e -> recipeRunner.runExplainCodeHighLevel());
|
||||
JButton generateUnitTestButton = new JButton("Generate a unit test");
|
||||
generateUnitTestButton.addActionListener(e -> recipeRunner.runGenerateUnitTest());
|
||||
JButton generateDocstringButton = new JButton("Generate a docstring");
|
||||
generateDocstringButton.addActionListener(e -> recipeRunner.runGenerateDocstring());
|
||||
JButton improveVariableNamesButton = new JButton("Improve variable names");
|
||||
improveVariableNamesButton.addActionListener(e -> recipeRunner.runImproveVariableNames());
|
||||
JButton translateToLanguageButton = new JButton("Translate to different language");
|
||||
translateToLanguageButton.addActionListener(e -> recipeRunner.runTranslateToLanguage());
|
||||
JButton gitHistoryButton = new JButton("Summarize recent code changes");
|
||||
gitHistoryButton.addActionListener(e -> recipeRunner.runGitHistory());
|
||||
JButton findCodeSmellsButton = new JButton("Smell code");
|
||||
findCodeSmellsButton.addActionListener(e -> recipeRunner.runFindCodeSmells());
|
||||
JButton fixupButton = new JButton("Fixup code from inline instructions");
|
||||
fixupButton.addActionListener(e -> recipeRunner.runFixup());
|
||||
JButton contextSearchButton = new JButton("Codebase context search");
|
||||
contextSearchButton.addActionListener(e -> recipeRunner.runContextSearch());
|
||||
JButton releaseNotesButton = new JButton("Generate release notes");
|
||||
releaseNotesButton.addActionListener(e -> recipeRunner.runReleaseNotes());
|
||||
recipesPanel.add(explainCodeDetailedButton);
|
||||
recipesPanel.add(explainCodeHighLevelButton);
|
||||
recipesPanel.add(generateUnitTestButton);
|
||||
recipesPanel.add(generateDocstringButton);
|
||||
recipesPanel.add(improveVariableNamesButton);
|
||||
recipesPanel.add(translateToLanguageButton);
|
||||
recipesPanel.add(gitHistoryButton);
|
||||
recipesPanel.add(findCodeSmellsButton);
|
||||
recipesPanel.add(fixupButton);
|
||||
recipesPanel.add(contextSearchButton);
|
||||
recipesPanel.add(releaseNotesButton);
|
||||
public CodyToolWindowContent(@NotNull Project project) {
|
||||
// Tabs
|
||||
tabbedPane.insertTab("Chat", null, contentPanel, null, 0);
|
||||
tabbedPane.insertTab("Recipes", null, recipesPanel, null, 1);
|
||||
|
||||
// Chat panel
|
||||
messagesPanel.setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 10, true, true));
|
||||
JBScrollPane chatPanel = new JBScrollPane(messagesPanel, JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JBScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
// Recipes panel
|
||||
RecipeRunner recipeRunner = new RecipeRunner(project, this);
|
||||
JButton explainCodeDetailedButton = new JButton("Explain selected code (detailed)");
|
||||
explainCodeDetailedButton.addActionListener(e -> recipeRunner.runExplainCodeDetailed());
|
||||
JButton explainCodeHighLevelButton = new JButton("Explain selected code (high level)");
|
||||
explainCodeHighLevelButton.addActionListener(e -> recipeRunner.runExplainCodeHighLevel());
|
||||
JButton generateUnitTestButton = new JButton("Generate a unit test");
|
||||
generateUnitTestButton.addActionListener(e -> recipeRunner.runGenerateUnitTest());
|
||||
JButton generateDocstringButton = new JButton("Generate a docstring");
|
||||
generateDocstringButton.addActionListener(e -> recipeRunner.runGenerateDocstring());
|
||||
JButton improveVariableNamesButton = new JButton("Improve variable names");
|
||||
improveVariableNamesButton.addActionListener(e -> recipeRunner.runImproveVariableNames());
|
||||
JButton translateToLanguageButton = new JButton("Translate to different language");
|
||||
translateToLanguageButton.addActionListener(e -> recipeRunner.runTranslateToLanguage());
|
||||
JButton gitHistoryButton = new JButton("Summarize recent code changes");
|
||||
gitHistoryButton.addActionListener(e -> recipeRunner.runGitHistory());
|
||||
JButton findCodeSmellsButton = new JButton("Smell code");
|
||||
findCodeSmellsButton.addActionListener(e -> recipeRunner.runFindCodeSmells());
|
||||
JButton fixupButton = new JButton("Fixup code from inline instructions");
|
||||
fixupButton.addActionListener(e -> recipeRunner.runFixup());
|
||||
JButton contextSearchButton = new JButton("Codebase context search");
|
||||
contextSearchButton.addActionListener(e -> recipeRunner.runContextSearch());
|
||||
JButton releaseNotesButton = new JButton("Generate release notes");
|
||||
releaseNotesButton.addActionListener(e -> recipeRunner.runReleaseNotes());
|
||||
recipesPanel.add(explainCodeDetailedButton);
|
||||
recipesPanel.add(explainCodeHighLevelButton);
|
||||
recipesPanel.add(generateUnitTestButton);
|
||||
recipesPanel.add(generateDocstringButton);
|
||||
recipesPanel.add(improveVariableNamesButton);
|
||||
recipesPanel.add(translateToLanguageButton);
|
||||
recipesPanel.add(gitHistoryButton);
|
||||
recipesPanel.add(findCodeSmellsButton);
|
||||
recipesPanel.add(fixupButton);
|
||||
recipesPanel.add(contextSearchButton);
|
||||
recipesPanel.add(releaseNotesButton);
|
||||
|
||||
// Scroll all the way down after each message
|
||||
AdjustmentListener scrollAdjustmentListener = e -> {
|
||||
if (needScrollingDown) {
|
||||
e.getAdjustable().setValue(e.getAdjustable().getMaximum());
|
||||
needScrollingDown = false;
|
||||
}
|
||||
// Chat panel
|
||||
messagesPanel.setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 10, true, true));
|
||||
JBScrollPane chatPanel =
|
||||
new JBScrollPane(
|
||||
messagesPanel,
|
||||
JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
|
||||
JBScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
|
||||
// Scroll all the way down after each message
|
||||
AdjustmentListener scrollAdjustmentListener =
|
||||
e -> {
|
||||
if (needScrollingDown) {
|
||||
e.getAdjustable().setValue(e.getAdjustable().getMaximum());
|
||||
needScrollingDown = false;
|
||||
}
|
||||
};
|
||||
chatPanel.getVerticalScrollBar().addAdjustmentListener(scrollAdjustmentListener);
|
||||
chatPanel.getVerticalScrollBar().addAdjustmentListener(scrollAdjustmentListener);
|
||||
|
||||
// Controls panel
|
||||
JPanel controlsPanel = new JPanel();
|
||||
controlsPanel.setLayout(new BoxLayout(controlsPanel, BoxLayout.X_AXIS));
|
||||
messageField = new JTextField();
|
||||
controlsPanel.add(messageField);
|
||||
messageField.addActionListener(e -> sendMessage(project)); // TODO: Disable the button while sending, then re-enable it
|
||||
JButton sendButton = new JButton("Send");
|
||||
sendButton.addActionListener(e -> sendMessage(project));
|
||||
controlsPanel.add(sendButton);
|
||||
// Controls panel
|
||||
JPanel controlsPanel = new JPanel();
|
||||
controlsPanel.setLayout(new BoxLayout(controlsPanel, BoxLayout.X_AXIS));
|
||||
messageField = new JTextField();
|
||||
controlsPanel.add(messageField);
|
||||
messageField.addActionListener(
|
||||
e -> sendMessage(project)); // TODO: Disable the button while sending, then re-enable it
|
||||
JButton sendButton = new JButton("Send");
|
||||
sendButton.addActionListener(e -> sendMessage(project));
|
||||
controlsPanel.add(sendButton);
|
||||
|
||||
// Main content panel
|
||||
contentPanel.setLayout(new BorderLayout(0, 20));
|
||||
contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
contentPanel.add(chatPanel, BorderLayout.CENTER);
|
||||
contentPanel.add(controlsPanel, BorderLayout.SOUTH);
|
||||
// Main content panel
|
||||
contentPanel.setLayout(new BorderLayout(0, 20));
|
||||
contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
contentPanel.add(chatPanel, BorderLayout.CENTER);
|
||||
contentPanel.add(controlsPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Add welcome message
|
||||
var welcomeText = "Hello! I'm Cody. I can write code and answer questions for you. See [Cody documentation](https://docs.sourcegraph.com/cody) for help and tips.";
|
||||
addMessage(ChatMessage.createAssistantMessage(welcomeText));
|
||||
}
|
||||
// Add welcome message
|
||||
var welcomeText =
|
||||
"Hello! I'm Cody. I can write code and answer questions for you. See [Cody documentation](https://docs.sourcegraph.com/cody) for help and tips.";
|
||||
addMessage(ChatMessage.createAssistantMessage(welcomeText));
|
||||
}
|
||||
|
||||
public synchronized void addMessage(@NotNull ChatMessage message) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
boolean isHuman = message.getSpeaker() == Speaker.HUMAN;
|
||||
public synchronized void addMessage(@NotNull ChatMessage message) {
|
||||
ApplicationManager.getApplication()
|
||||
.invokeLater(
|
||||
() -> {
|
||||
boolean isHuman = message.getSpeaker() == Speaker.HUMAN;
|
||||
|
||||
// Bubble panel
|
||||
var bubblePanel = new JPanel();
|
||||
bubblePanel.setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 0, true, false));
|
||||
bubblePanel.setBorder(BorderFactory.createEmptyBorder(0, isHuman ? JBUIScale.scale(20) : 0, 0, !isHuman ? JBUIScale.scale(20) : 0));
|
||||
// Bubble panel
|
||||
var bubblePanel = new JPanel();
|
||||
bubblePanel.setLayout(
|
||||
new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 0, true, false));
|
||||
bubblePanel.setBorder(
|
||||
BorderFactory.createEmptyBorder(
|
||||
0, isHuman ? JBUIScale.scale(20) : 0, 0, !isHuman ? JBUIScale.scale(20) : 0));
|
||||
|
||||
// Chat bubble
|
||||
ChatBubble bubble = new ChatBubble(10, message);
|
||||
bubblePanel.add(bubble, VerticalFlowLayout.TOP);
|
||||
messagesPanel.add(bubblePanel);
|
||||
messagesPanel.revalidate();
|
||||
messagesPanel.repaint();
|
||||
// Chat bubble
|
||||
ChatBubble bubble = new ChatBubble(10, message);
|
||||
bubblePanel.add(bubble, VerticalFlowLayout.TOP);
|
||||
messagesPanel.add(bubblePanel);
|
||||
messagesPanel.revalidate();
|
||||
messagesPanel.repaint();
|
||||
|
||||
// Need this hacky solution to scroll all the way down after each message
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
needScrollingDown = true;
|
||||
messagesPanel.revalidate();
|
||||
messagesPanel.repaint();
|
||||
// Need this hacky solution to scroll all the way down after each message
|
||||
ApplicationManager.getApplication()
|
||||
.invokeLater(
|
||||
() -> {
|
||||
needScrollingDown = true;
|
||||
messagesPanel.revalidate();
|
||||
messagesPanel.repaint();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateLastMessage(@NotNull ChatMessage message) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
if (messagesPanel.getComponentCount() > 0) {
|
||||
JPanel lastBubblePanel = (JPanel) messagesPanel.getComponent(messagesPanel.getComponentCount() - 1);
|
||||
public synchronized void updateLastMessage(@NotNull ChatMessage message) {
|
||||
ApplicationManager.getApplication()
|
||||
.invokeLater(
|
||||
() -> {
|
||||
if (messagesPanel.getComponentCount() > 0) {
|
||||
JPanel lastBubblePanel =
|
||||
(JPanel) messagesPanel.getComponent(messagesPanel.getComponentCount() - 1);
|
||||
ChatBubble lastBubble = (ChatBubble) lastBubblePanel.getComponent(0);
|
||||
lastBubble.updateText(message.getDisplayText());
|
||||
messagesPanel.revalidate();
|
||||
messagesPanel.repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendMessage(@NotNull Project project) {
|
||||
// Build message
|
||||
boolean isEnterprise = ConfigUtil.getInstanceType(project).equals(SettingsComponent.InstanceType.ENTERPRISE);
|
||||
String instanceUrl = isEnterprise ? ConfigUtil.getEnterpriseUrl(project) : "https://sourcegraph.com/";
|
||||
String accessToken = isEnterprise ? ConfigUtil.getEnterpriseAccessToken(project) : ConfigUtil.getDotcomAccessToken(project);
|
||||
private void sendMessage(@NotNull Project project) {
|
||||
// Build message
|
||||
boolean isEnterprise =
|
||||
ConfigUtil.getInstanceType(project).equals(SettingsComponent.InstanceType.ENTERPRISE);
|
||||
String instanceUrl =
|
||||
isEnterprise ? ConfigUtil.getEnterpriseUrl(project) : "https://sourcegraph.com/";
|
||||
String accessToken =
|
||||
isEnterprise
|
||||
? ConfigUtil.getEnterpriseAccessToken(project)
|
||||
: ConfigUtil.getDotcomAccessToken(project);
|
||||
|
||||
var chat = new Chat("", instanceUrl, accessToken != null ? accessToken : "");
|
||||
ArrayList<String> contextFiles = EditorContextGetter.getEditorContext(project).getCurrentFileContentAsArrayList();
|
||||
ChatMessage humanMessage = ChatMessage.createHumanMessage(messageField.getText(), contextFiles);
|
||||
addMessage(humanMessage);
|
||||
var chat = new Chat("", instanceUrl, accessToken != null ? accessToken : "");
|
||||
ArrayList<String> contextFiles =
|
||||
EditorContextGetter.getEditorContext(project).getCurrentFileContentAsArrayList();
|
||||
ChatMessage humanMessage = ChatMessage.createHumanMessage(messageField.getText(), contextFiles);
|
||||
addMessage(humanMessage);
|
||||
|
||||
// Get assistant message
|
||||
// Note: A separate thread is needed because it's a long-running task. If we did the back-end call
|
||||
// in the main thread and then waited, we wouldn't see the messages streamed back to us.
|
||||
new Thread(() -> {
|
||||
chat.sendMessage(humanMessage, "", this); // TODO: Use prefix
|
||||
}).start();
|
||||
}
|
||||
// Get assistant message
|
||||
// Note: A separate thread is needed because it's a long-running task. If we did the back-end
|
||||
// call
|
||||
// in the main thread and then waited, we wouldn't see the messages streamed back to us.
|
||||
new Thread(
|
||||
() -> {
|
||||
chat.sendMessage(humanMessage, "", this); // TODO: Use prefix
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
public @NotNull JComponent getContentPanel() {
|
||||
return tabbedPane;
|
||||
}
|
||||
public @NotNull JComponent getContentPanel() {
|
||||
return tabbedPane;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,16 +9,18 @@ import com.intellij.ui.content.ContentFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CodyToolWindowFactory implements ToolWindowFactory, DumbAware {
|
||||
@Override
|
||||
public boolean isApplicable(@NotNull Project project) {
|
||||
return ToolWindowFactory.super.isApplicable(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
|
||||
CodyToolWindowContent toolWindowContent = new CodyToolWindowContent(project);
|
||||
Content content = ContentFactory.SERVICE.getInstance().createContent(toolWindowContent.getContentPanel(), "", false);
|
||||
toolWindow.getContentManager().addContent(content);
|
||||
}
|
||||
@Override
|
||||
public boolean isApplicable(@NotNull Project project) {
|
||||
return ToolWindowFactory.super.isApplicable(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
|
||||
CodyToolWindowContent toolWindowContent = new CodyToolWindowContent(project);
|
||||
Content content =
|
||||
ContentFactory.SERVICE
|
||||
.getInstance()
|
||||
.createContent(toolWindowContent.getContentPanel(), "", false);
|
||||
toolWindow.getContentManager().addContent(content);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
package com.sourcegraph.cody;
|
||||
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public interface Icons {
|
||||
Icon CodyLogo = IconLoader.getIcon("/icons/codyLogo.svg", Icons.class);
|
||||
Icon CodyLogo = IconLoader.getIcon("/icons/codyLogo.svg", Icons.class);
|
||||
}
|
||||
|
||||
@ -4,32 +4,28 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class TruncationUtils {
|
||||
|
||||
public static final int MAX_PROMPT_TOKEN_LENGTH = 7000;
|
||||
public static final int SOLUTION_TOKEN_LENGTH = 1000;
|
||||
public static final int MAX_HUMAN_INPUT_TOKENS = 1000;
|
||||
public static final int MAX_RECIPE_INPUT_TOKENS = 2000;
|
||||
public static final int MAX_CURRENT_FILE_TOKENS = 1000;
|
||||
public static final int MAX_RECIPE_SURROUNDING_TOKENS = 500;
|
||||
public static final int MAX_AVAILABLE_PROMPT_LENGTH = MAX_PROMPT_TOKEN_LENGTH - SOLUTION_TOKEN_LENGTH;
|
||||
public static final int CHARS_PER_TOKEN = 4;
|
||||
/**
|
||||
* The number of code lines to include in the preceding and following texts near the selection
|
||||
*/
|
||||
public static final int SURROUNDING_LINES = 50;
|
||||
public static final int MAX_PROMPT_TOKEN_LENGTH = 7000;
|
||||
public static final int SOLUTION_TOKEN_LENGTH = 1000;
|
||||
public static final int MAX_HUMAN_INPUT_TOKENS = 1000;
|
||||
public static final int MAX_RECIPE_INPUT_TOKENS = 2000;
|
||||
public static final int MAX_CURRENT_FILE_TOKENS = 1000;
|
||||
public static final int MAX_RECIPE_SURROUNDING_TOKENS = 500;
|
||||
public static final int MAX_AVAILABLE_PROMPT_LENGTH =
|
||||
MAX_PROMPT_TOKEN_LENGTH - SOLUTION_TOKEN_LENGTH;
|
||||
public static final int CHARS_PER_TOKEN = 4;
|
||||
|
||||
/**
|
||||
* Truncates text to the given number of tokens, keeping the start of the text.
|
||||
*/
|
||||
public static String truncateText(@NotNull String text, int maxTokens) {
|
||||
int maxLength = maxTokens * CHARS_PER_TOKEN;
|
||||
return text.length() <= maxLength ? text : text.substring(0, maxLength);
|
||||
}
|
||||
/** The number of code lines to include in the preceding and following texts near the selection */
|
||||
public static final int SURROUNDING_LINES = 50;
|
||||
|
||||
/**
|
||||
* Truncates text to the given number of tokens, keeping the end of the text.
|
||||
*/
|
||||
public static String truncateTextStart(@NotNull String text, int maxTokens) {
|
||||
int maxLength = maxTokens * CHARS_PER_TOKEN;
|
||||
return text.length() <= maxLength ? text : text.substring(text.length() - maxLength);
|
||||
}
|
||||
/** Truncates text to the given number of tokens, keeping the start of the text. */
|
||||
public static String truncateText(@NotNull String text, int maxTokens) {
|
||||
int maxLength = maxTokens * CHARS_PER_TOKEN;
|
||||
return text.length() <= maxLength ? text : text.substring(0, maxLength);
|
||||
}
|
||||
|
||||
/** Truncates text to the given number of tokens, keeping the end of the text. */
|
||||
public static String truncateTextStart(@NotNull String text, int maxTokens) {
|
||||
int maxLength = maxTokens * CHARS_PER_TOKEN;
|
||||
return text.length() <= maxLength ? text : text.substring(text.length() - maxLength);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import com.sourcegraph.cody.chat.ChatMessage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface UpdatableChat {
|
||||
void addMessage(@NotNull ChatMessage message);
|
||||
void addMessage(@NotNull ChatMessage message);
|
||||
|
||||
void updateLastMessage(@NotNull ChatMessage message);
|
||||
void updateLastMessage(@NotNull ChatMessage message);
|
||||
}
|
||||
|
||||
@ -3,44 +3,44 @@ package com.sourcegraph.cody.chat;
|
||||
import com.sourcegraph.cody.UpdatableChat;
|
||||
import com.sourcegraph.cody.completions.*;
|
||||
import com.sourcegraph.cody.prompts.Preamble;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Chat {
|
||||
private final @Nullable String codebase;
|
||||
private final @NotNull CompletionsService completionsService;
|
||||
private final @Nullable String codebase;
|
||||
private final @NotNull CompletionsService completionsService;
|
||||
|
||||
public Chat(@Nullable String codebase, @NotNull String instanceUrl, @NotNull String accessToken) {
|
||||
this.codebase = codebase;
|
||||
completionsService = new CompletionsService(instanceUrl, accessToken);
|
||||
public Chat(@Nullable String codebase, @NotNull String instanceUrl, @NotNull String accessToken) {
|
||||
this.codebase = codebase;
|
||||
completionsService = new CompletionsService(instanceUrl, accessToken);
|
||||
}
|
||||
|
||||
public void sendMessage(
|
||||
@NotNull ChatMessage humanMessage, @Nullable String prefix, @NotNull UpdatableChat chat) {
|
||||
List<Message> preamble = Preamble.getPreamble(codebase);
|
||||
|
||||
// TODO: Use the context getting logic from VS Code
|
||||
var codeContext = "";
|
||||
if (humanMessage.getContextFiles().size() == 0) {
|
||||
codeContext = "I have no file open in the editor right now.";
|
||||
} else {
|
||||
codeContext = "Here is my current file\n" + humanMessage.getContextFiles().get(0);
|
||||
}
|
||||
|
||||
public void sendMessage(@NotNull ChatMessage humanMessage, @Nullable String prefix, @NotNull UpdatableChat chat) {
|
||||
List<Message> preamble = Preamble.getPreamble(codebase);
|
||||
var input = new CompletionsInput(new ArrayList<>(), 0.5f, 1000, -1, -1);
|
||||
input.addMessages(preamble);
|
||||
input.addMessage(Speaker.HUMAN, codeContext);
|
||||
input.addMessage(Speaker.ASSISTANT, "Ok.");
|
||||
input.addMessage(Speaker.HUMAN, humanMessage.getText());
|
||||
input.addMessage(Speaker.ASSISTANT, "");
|
||||
|
||||
// TODO: Use the context getting logic from VS Code
|
||||
var codeContext = "";
|
||||
if (humanMessage.getContextFiles().size() == 0) {
|
||||
codeContext = "I have no file open in the editor right now.";
|
||||
} else {
|
||||
codeContext = "Here is my current file\n" + humanMessage.getContextFiles().get(0);
|
||||
}
|
||||
input.messages.forEach(System.out::println);
|
||||
|
||||
var input = new CompletionsInput(new ArrayList<>(), 0.5f, 1000, -1, -1);
|
||||
input.addMessages(preamble);
|
||||
input.addMessage(Speaker.HUMAN, codeContext);
|
||||
input.addMessage(Speaker.ASSISTANT, "Ok.");
|
||||
input.addMessage(Speaker.HUMAN, humanMessage.getText());
|
||||
input.addMessage(Speaker.ASSISTANT, "");
|
||||
// ConfigUtil.getAccessToken(project) TODO: Get the access token from the plugin config
|
||||
// TODO: Don't create this each time
|
||||
|
||||
input.messages.forEach(System.out::println);
|
||||
|
||||
// ConfigUtil.getAccessToken(project) TODO: Get the access token from the plugin config
|
||||
// TODO: Don't create this each time
|
||||
|
||||
completionsService.streamCompletion(input, new ChatUpdaterCallbacks(chat, prefix));
|
||||
}
|
||||
completionsService.streamCompletion(input, new ChatUpdaterCallbacks(chat, prefix));
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,60 +4,62 @@ import com.intellij.ui.JBColor;
|
||||
import com.intellij.util.ui.JBInsets;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.sourcegraph.cody.completions.Speaker;
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import org.commonmark.node.Node;
|
||||
import org.commonmark.parser.Parser;
|
||||
import org.commonmark.renderer.html.HtmlRenderer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import java.awt.*;
|
||||
|
||||
public class ChatBubble extends JPanel {
|
||||
private final int radius;
|
||||
private final int radius;
|
||||
|
||||
public ChatBubble(int radius, @NotNull ChatMessage message) {
|
||||
super();
|
||||
public ChatBubble(int radius, @NotNull ChatMessage message) {
|
||||
super();
|
||||
|
||||
boolean isHuman = message.getSpeaker() == Speaker.HUMAN;
|
||||
JBColor background = isHuman ? JBColor.BLUE : JBColor.GRAY;
|
||||
this.radius = radius;
|
||||
this.setBackground(background);
|
||||
this.setLayout(new BorderLayout());
|
||||
this.setBorder(new EmptyBorder(new JBInsets(10, 10, 10, 10)));
|
||||
boolean isHuman = message.getSpeaker() == Speaker.HUMAN;
|
||||
JBColor background = isHuman ? JBColor.BLUE : JBColor.GRAY;
|
||||
this.radius = radius;
|
||||
this.setBackground(background);
|
||||
this.setLayout(new BorderLayout());
|
||||
this.setBorder(new EmptyBorder(new JBInsets(10, 10, 10, 10)));
|
||||
|
||||
JEditorPane pane = new JEditorPane();
|
||||
pane.setContentType("text/html");
|
||||
pane.setText(convertToHtml(message.getDisplayText()));
|
||||
pane.setEditable(false);
|
||||
pane.setFont(UIUtil.getLabelFont());
|
||||
pane.setBackground(background);
|
||||
pane.setForeground(isHuman ? JBColor.WHITE : JBColor.BLACK);
|
||||
pane.setComponentOrientation(isHuman ? ComponentOrientation.RIGHT_TO_LEFT : ComponentOrientation.LEFT_TO_RIGHT);
|
||||
pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
|
||||
this.add(pane, BorderLayout.CENTER);
|
||||
}
|
||||
JEditorPane pane = new JEditorPane();
|
||||
pane.setContentType("text/html");
|
||||
pane.setText(convertToHtml(message.getDisplayText()));
|
||||
pane.setEditable(false);
|
||||
pane.setFont(UIUtil.getLabelFont());
|
||||
pane.setBackground(background);
|
||||
pane.setForeground(isHuman ? JBColor.WHITE : JBColor.BLACK);
|
||||
pane.setComponentOrientation(
|
||||
isHuman ? ComponentOrientation.RIGHT_TO_LEFT : ComponentOrientation.LEFT_TO_RIGHT);
|
||||
pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
|
||||
this.add(pane, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public void updateText(@NotNull String newMarkdownText) {
|
||||
JEditorPane pane = (JEditorPane) this.getComponent(0);
|
||||
pane.setText(convertToHtml(newMarkdownText));
|
||||
}
|
||||
public void updateText(@NotNull String newMarkdownText) {
|
||||
JEditorPane pane = (JEditorPane) this.getComponent(0);
|
||||
pane.setText(convertToHtml(newMarkdownText));
|
||||
}
|
||||
|
||||
private @NotNull String convertToHtml(@NotNull String markdown) {
|
||||
// Parse markdown
|
||||
Parser parser = Parser.builder().build();
|
||||
Node node = parser.parse(markdown);
|
||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
||||
String messageAsHtml = renderer.render(node);
|
||||
private @NotNull String convertToHtml(@NotNull String markdown) {
|
||||
// Parse markdown
|
||||
Parser parser = Parser.builder().build();
|
||||
Node node = parser.parse(markdown);
|
||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
||||
String messageAsHtml = renderer.render(node);
|
||||
|
||||
// Build HTML
|
||||
return "<html data-gramm=\"false\"><head><style>p { margin:0; }</style></head><body>" + messageAsHtml + "</body></html>";
|
||||
}
|
||||
// Build HTML
|
||||
return "<html data-gramm=\"false\"><head><style>p { margin:0; }</style></head><body>"
|
||||
+ messageAsHtml
|
||||
+ "</body></html>";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(@NotNull Graphics g) {
|
||||
final Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setColor(getBackground());
|
||||
g2d.fillRoundRect(0, 0, this.getWidth() - 1, this.getHeight() - 1, this.radius, this.radius);
|
||||
}
|
||||
@Override
|
||||
protected void paintComponent(@NotNull Graphics g) {
|
||||
final Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setColor(getBackground());
|
||||
g2d.fillRoundRect(0, 0, this.getWidth() - 1, this.getHeight() - 1, this.radius, this.radius);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,35 +2,39 @@ package com.sourcegraph.cody.chat;
|
||||
|
||||
import com.sourcegraph.cody.completions.Message;
|
||||
import com.sourcegraph.cody.completions.Speaker;
|
||||
import java.util.ArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ChatMessage extends Message {
|
||||
private final @NotNull String displayText;
|
||||
private final @NotNull ArrayList<String> contextFiles;
|
||||
private final @NotNull String displayText;
|
||||
private final @NotNull ArrayList<String> contextFiles;
|
||||
|
||||
private ChatMessage(@NotNull Speaker speaker, @NotNull String text, @NotNull String displayText, @NotNull ArrayList<String> contextFiles) {
|
||||
super(speaker, text);
|
||||
this.displayText = displayText;
|
||||
this.contextFiles = contextFiles;
|
||||
}
|
||||
private ChatMessage(
|
||||
@NotNull Speaker speaker,
|
||||
@NotNull String text,
|
||||
@NotNull String displayText,
|
||||
@NotNull ArrayList<String> contextFiles) {
|
||||
super(speaker, text);
|
||||
this.displayText = displayText;
|
||||
this.contextFiles = contextFiles;
|
||||
}
|
||||
|
||||
public static @NotNull ChatMessage createAssistantMessage(@NotNull String text) {
|
||||
return new ChatMessage(Speaker.ASSISTANT, text, text, new ArrayList<>());
|
||||
}
|
||||
public static @NotNull ChatMessage createAssistantMessage(@NotNull String text) {
|
||||
return new ChatMessage(Speaker.ASSISTANT, text, text, new ArrayList<>());
|
||||
}
|
||||
|
||||
public static @NotNull ChatMessage createHumanMessage(@NotNull String text, @NotNull ArrayList<String> contextFiles) {
|
||||
return new ChatMessage(Speaker.HUMAN, text, text, contextFiles);
|
||||
}
|
||||
public static @NotNull ChatMessage createHumanMessage(
|
||||
@NotNull String text, @NotNull ArrayList<String> contextFiles) {
|
||||
return new ChatMessage(Speaker.HUMAN, text, text, contextFiles);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDisplayText() {
|
||||
return displayText;
|
||||
}
|
||||
@NotNull
|
||||
public String getDisplayText() {
|
||||
return displayText;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ArrayList<String> getContextFiles() {
|
||||
return contextFiles;
|
||||
}
|
||||
@NotNull
|
||||
public ArrayList<String> getContextFiles() {
|
||||
return contextFiles;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,71 +2,75 @@ package com.sourcegraph.cody.completions;
|
||||
|
||||
import com.sourcegraph.cody.UpdatableChat;
|
||||
import com.sourcegraph.cody.chat.ChatMessage;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ChatUpdaterCallbacks implements CompletionsCallbacks {
|
||||
private final UpdatableChat chat;
|
||||
private final String prefix;
|
||||
private boolean gotFirstMessage = false;
|
||||
private final UpdatableChat chat;
|
||||
private final String prefix;
|
||||
private boolean gotFirstMessage = false;
|
||||
|
||||
public ChatUpdaterCallbacks(@NotNull UpdatableChat chat, @Nullable String prefix) {
|
||||
this.chat = chat;
|
||||
this.prefix = prefix;
|
||||
public ChatUpdaterCallbacks(@NotNull UpdatableChat chat, @Nullable String prefix) {
|
||||
this.chat = chat;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribed() {
|
||||
System.out.println("Subscribed to completions.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(@Nullable String data) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
// print date/time and msg
|
||||
// System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd
|
||||
// HH:mm:ss.SSS").format(LocalDateTime.now()) + " Data received by callback: " + data);
|
||||
if (!gotFirstMessage) {
|
||||
chat.addMessage(ChatMessage.createAssistantMessage(reformatBotMessage(data, prefix)));
|
||||
gotFirstMessage = true;
|
||||
} else {
|
||||
chat.updateLastMessage(ChatMessage.createAssistantMessage(reformatBotMessage(data, prefix)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NotNull Throwable error) {
|
||||
if (error.getMessage().equals("Connection refused")) {
|
||||
chat.addMessage(
|
||||
ChatMessage.createAssistantMessage(
|
||||
"I'm sorry, I can't connect to the server. Please make sure that the server is running and try again."));
|
||||
} else {
|
||||
chat.addMessage(
|
||||
ChatMessage.createAssistantMessage(
|
||||
"I'm sorry, something wet wrong. Please try again. The error message I got was: \""
|
||||
+ error.getMessage()
|
||||
+ "\"."));
|
||||
}
|
||||
System.err.println("Error: " + error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
System.out.println("Streaming completed.");
|
||||
}
|
||||
|
||||
private static @NotNull String reformatBotMessage(@NotNull String text, @Nullable String prefix) {
|
||||
String STOP_SEQUENCE_REGEXP = "(H|Hu|Hum|Huma|Human|Human:)$";
|
||||
Pattern stopSequencePattern = Pattern.compile(STOP_SEQUENCE_REGEXP);
|
||||
|
||||
String reformattedMessage = (prefix != null ? prefix : "") + text.stripTrailing();
|
||||
|
||||
Matcher stopSequenceMatcher = stopSequencePattern.matcher(reformattedMessage);
|
||||
|
||||
if (stopSequenceMatcher.find()) {
|
||||
reformattedMessage = reformattedMessage.substring(0, stopSequenceMatcher.start());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribed() {
|
||||
System.out.println("Subscribed to completions.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(@Nullable String data) {
|
||||
if(data == null) {
|
||||
return;
|
||||
}
|
||||
// print date/time and msg
|
||||
// System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(LocalDateTime.now()) + " Data received by callback: " + data);
|
||||
if (!gotFirstMessage) {
|
||||
chat.addMessage(ChatMessage.createAssistantMessage(reformatBotMessage(data, prefix)));
|
||||
gotFirstMessage = true;
|
||||
} else {
|
||||
chat.updateLastMessage(ChatMessage.createAssistantMessage(reformatBotMessage(data, prefix)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NotNull Throwable error) {
|
||||
if (error.getMessage().equals("Connection refused")) {
|
||||
chat.addMessage(ChatMessage.createAssistantMessage("I'm sorry, I can't connect to the server. Please make sure that the server is running and try again."));
|
||||
} else {
|
||||
chat.addMessage(ChatMessage.createAssistantMessage("I'm sorry, something wet wrong. Please try again. The error message I got was: \"" + error.getMessage() + "\"."));
|
||||
}
|
||||
System.err.println("Error: " + error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
System.out.println("Streaming completed.");
|
||||
}
|
||||
|
||||
private static @NotNull String reformatBotMessage(@NotNull String text, @Nullable String prefix) {
|
||||
String STOP_SEQUENCE_REGEXP = "(H|Hu|Hum|Huma|Human|Human:)$";
|
||||
Pattern stopSequencePattern = Pattern.compile(STOP_SEQUENCE_REGEXP);
|
||||
|
||||
String reformattedMessage = (prefix != null ? prefix : "") + text.stripTrailing();
|
||||
|
||||
Matcher stopSequenceMatcher = stopSequencePattern.matcher(reformattedMessage);
|
||||
|
||||
if (stopSequenceMatcher.find()) {
|
||||
reformattedMessage = reformattedMessage.substring(0, stopSequenceMatcher.start());
|
||||
}
|
||||
|
||||
return reformattedMessage;
|
||||
}
|
||||
return reformattedMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,11 +2,11 @@ package com.sourcegraph.cody.completions;
|
||||
|
||||
// Define a callback interface to handle events
|
||||
public interface CompletionsCallbacks {
|
||||
void onSubscribed();
|
||||
void onSubscribed();
|
||||
|
||||
void onData(String data);
|
||||
void onData(String data);
|
||||
|
||||
void onError(Throwable error);
|
||||
void onError(Throwable error);
|
||||
|
||||
void onComplete();
|
||||
void onComplete();
|
||||
}
|
||||
|
||||
@ -1,32 +1,34 @@
|
||||
package com.sourcegraph.cody.completions;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Input for the completions request.
|
||||
*/
|
||||
/** Input for the completions request. */
|
||||
public class CompletionsInput {
|
||||
public @NotNull List<Message> messages;
|
||||
public float temperature;
|
||||
public int maxTokensToSample;
|
||||
public int topK;
|
||||
public int topP;
|
||||
public @NotNull List<Message> messages;
|
||||
public float temperature;
|
||||
public int maxTokensToSample;
|
||||
public int topK;
|
||||
public int topP;
|
||||
|
||||
public CompletionsInput(@NotNull List<Message> messages, float temperature, int maxTokensToSample, int topK, int topP) {
|
||||
this.messages = messages;
|
||||
this.temperature = temperature;
|
||||
this.maxTokensToSample = maxTokensToSample;
|
||||
this.topK = topK;
|
||||
this.topP = topP;
|
||||
}
|
||||
public CompletionsInput(
|
||||
@NotNull List<Message> messages,
|
||||
float temperature,
|
||||
int maxTokensToSample,
|
||||
int topK,
|
||||
int topP) {
|
||||
this.messages = messages;
|
||||
this.temperature = temperature;
|
||||
this.maxTokensToSample = maxTokensToSample;
|
||||
this.topK = topK;
|
||||
this.topP = topP;
|
||||
}
|
||||
|
||||
public void addMessage(@NotNull Speaker speaker, @NotNull String text) {
|
||||
messages.add(new Message(speaker, text));
|
||||
}
|
||||
public void addMessage(@NotNull Speaker speaker, @NotNull String text) {
|
||||
messages.add(new Message(speaker, text));
|
||||
}
|
||||
|
||||
public void addMessages(@NotNull List<Message> messages) {
|
||||
this.messages.addAll(messages);
|
||||
}
|
||||
public void addMessages(@NotNull List<Message> messages) {
|
||||
this.messages.addAll(messages);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,49 +4,52 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.sourcegraph.api.GraphQlClient;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class CompletionsService {
|
||||
private final String instanceUrl;
|
||||
private final String accessToken;
|
||||
private final String instanceUrl;
|
||||
private final String accessToken;
|
||||
|
||||
public CompletionsService(@NotNull String instanceUrl, @NotNull String accessToken) {
|
||||
this.instanceUrl = instanceUrl;
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
public CompletionsService(@NotNull String instanceUrl, @NotNull String accessToken) {
|
||||
this.instanceUrl = instanceUrl;
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a completions request to the Sourcegraph instance, and returns the response.
|
||||
*/
|
||||
public String getCompletion(@NotNull CompletionsInput input) throws IOException, InterruptedException {
|
||||
Gson gson = new GsonBuilder()
|
||||
/** Sends a completions request to the Sourcegraph instance, and returns the response. */
|
||||
public String getCompletion(@NotNull CompletionsInput input)
|
||||
throws IOException, InterruptedException {
|
||||
Gson gson =
|
||||
new GsonBuilder()
|
||||
.registerTypeAdapter(Speaker.class, new SpeakerLowercaseSerializer())
|
||||
.create();
|
||||
|
||||
String query = "query completions($input: CompletionsInput!) { completions(input: $input) }";
|
||||
var variables = new JsonObject();
|
||||
variables.add("input", gson.toJsonTree(input));
|
||||
String query = "query completions($input: CompletionsInput!) { completions(input: $input) }";
|
||||
var variables = new JsonObject();
|
||||
variables.add("input", gson.toJsonTree(input));
|
||||
|
||||
var response = GraphQlClient.callGraphQLService(instanceUrl, accessToken, null, query, variables);
|
||||
return response
|
||||
.getBodyAsJson()
|
||||
.getAsJsonObject("data")
|
||||
.getAsJsonPrimitive("completions")
|
||||
.getAsString();
|
||||
}
|
||||
var response =
|
||||
GraphQlClient.callGraphQLService(instanceUrl, accessToken, null, query, variables);
|
||||
return response
|
||||
.getBodyAsJson()
|
||||
.getAsJsonObject("data")
|
||||
.getAsJsonPrimitive("completions")
|
||||
.getAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a completions request to the Sourcegraph instance, and receives the response in a streaming fashion.
|
||||
*/
|
||||
public void streamCompletion(@NotNull CompletionsInput input, @NotNull CompletionsCallbacks cb) {
|
||||
Gson gson = new GsonBuilder()
|
||||
/**
|
||||
* Sends a completions request to the Sourcegraph instance, and receives the response in a
|
||||
* streaming fashion.
|
||||
*/
|
||||
public void streamCompletion(@NotNull CompletionsInput input, @NotNull CompletionsCallbacks cb) {
|
||||
Gson gson =
|
||||
new GsonBuilder()
|
||||
.registerTypeAdapter(Speaker.class, new SpeakerLowercaseSerializer())
|
||||
.create();
|
||||
|
||||
String body = gson.toJsonTree(input).getAsJsonObject().toString();
|
||||
SSEClient sseClient = new SSEClient(instanceUrl + ".api/completions/stream", accessToken, body, cb);
|
||||
sseClient.start();
|
||||
}
|
||||
String body = gson.toJsonTree(input).getAsJsonObject().toString();
|
||||
SSEClient sseClient =
|
||||
new SSEClient(instanceUrl + ".api/completions/stream", accessToken, body, cb);
|
||||
sseClient.start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +1,27 @@
|
||||
package com.sourcegraph.cody.completions;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Wrapper for GraphQL requests that has a query and a list of variables.
|
||||
*/
|
||||
/** Wrapper for GraphQL requests that has a query and a list of variables. */
|
||||
class GraphQLWrapper {
|
||||
@NotNull
|
||||
public String query;
|
||||
@NotNull
|
||||
public Map<String, Object> variables = new HashMap<>();
|
||||
@NotNull public String query;
|
||||
@NotNull public Map<String, Object> variables = new HashMap<>();
|
||||
|
||||
public GraphQLWrapper(@NotNull String query) {
|
||||
this.query = query;
|
||||
}
|
||||
public GraphQLWrapper(@NotNull String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public GraphQLWrapper withVariable(@NotNull String key, @NotNull Object variable) {
|
||||
this.variables.put(key, variable);
|
||||
return this;
|
||||
}
|
||||
public GraphQLWrapper withVariable(@NotNull String key, @NotNull Object variable) {
|
||||
this.variables.put(key, variable);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String toJsonString() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
@NotNull
|
||||
public String toJsonString() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,24 +3,24 @@ package com.sourcegraph.cody.completions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class Message {
|
||||
protected final @NotNull Speaker speaker;
|
||||
protected final @NotNull String text;
|
||||
protected final @NotNull Speaker speaker;
|
||||
protected final @NotNull String text;
|
||||
|
||||
public Message(@NotNull Speaker speaker, @NotNull String text) {
|
||||
this.speaker = speaker;
|
||||
this.text = text;
|
||||
}
|
||||
public Message(@NotNull Speaker speaker, @NotNull String text) {
|
||||
this.speaker = speaker;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public @NotNull Speaker getSpeaker() {
|
||||
return speaker;
|
||||
}
|
||||
public @NotNull Speaker getSpeaker() {
|
||||
return speaker;
|
||||
}
|
||||
|
||||
public @NotNull String getText() {
|
||||
return text;
|
||||
}
|
||||
public @NotNull String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
return String.format("Message { speaker=%s, text='%s'}", speaker, text);
|
||||
}
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
return String.format("Message { speaker=%s, text='%s'}", speaker, text);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,6 @@ package com.sourcegraph.cody.completions;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
@ -16,114 +12,124 @@ import java.net.http.HttpResponse.BodyHandlers;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SSEClient {
|
||||
private final String url;
|
||||
private final String accessToken;
|
||||
private final String body;
|
||||
private final CompletionsCallbacks cb;
|
||||
private final String url;
|
||||
private final String accessToken;
|
||||
private final String body;
|
||||
private final CompletionsCallbacks cb;
|
||||
|
||||
private InputStream inputStream;
|
||||
private InputStream inputStream;
|
||||
|
||||
public SSEClient(@NotNull String url, @NotNull String accessToken, @NotNull String body, @NotNull CompletionsCallbacks cb) {
|
||||
this.url = url;
|
||||
this.body = body;
|
||||
this.accessToken = accessToken;
|
||||
this.cb = cb;
|
||||
}
|
||||
public SSEClient(
|
||||
@NotNull String url,
|
||||
@NotNull String accessToken,
|
||||
@NotNull String body,
|
||||
@NotNull CompletionsCallbacks cb) {
|
||||
this.url = url;
|
||||
this.body = body;
|
||||
this.accessToken = accessToken;
|
||||
this.cb = cb;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
||||
.uri(new URI(url))
|
||||
.version(HttpClient.Version.HTTP_2)
|
||||
.header("Content-Type", "application/json; charset=utf-8")
|
||||
.header("X-Sourcegraph-Should-Trace", "false")
|
||||
.header("Accept", "text/event-stream")
|
||||
.header("Cache-Control", "no-cache")
|
||||
.header("Authorization", "token " + accessToken)
|
||||
.timeout(Duration.ofSeconds(30))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(body));
|
||||
HttpRequest request = requestBuilder.build();
|
||||
public void start() {
|
||||
try {
|
||||
HttpRequest.Builder requestBuilder =
|
||||
HttpRequest.newBuilder()
|
||||
.uri(new URI(url))
|
||||
.version(HttpClient.Version.HTTP_2)
|
||||
.header("Content-Type", "application/json; charset=utf-8")
|
||||
.header("X-Sourcegraph-Should-Trace", "false")
|
||||
.header("Accept", "text/event-stream")
|
||||
.header("Cache-Control", "no-cache")
|
||||
.header("Authorization", "token " + accessToken)
|
||||
.timeout(Duration.ofSeconds(30))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(body));
|
||||
HttpRequest request = requestBuilder.build();
|
||||
|
||||
HttpResponse<InputStream> response = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(30))
|
||||
.build()
|
||||
.send(request, BodyHandlers.ofInputStream());
|
||||
HttpResponse<InputStream> response =
|
||||
HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(30))
|
||||
.build()
|
||||
.send(request, BodyHandlers.ofInputStream());
|
||||
|
||||
if (response.statusCode() == HttpStatus.SC_OK) {
|
||||
cb.onSubscribed();
|
||||
inputStream = response.body();
|
||||
handleResponse(inputStream);
|
||||
} else {
|
||||
String result;
|
||||
try (InputStream ignored = response.body()) {
|
||||
result = IOUtils.toString(response.body(), StandardCharsets.UTF_8);
|
||||
}
|
||||
cb.onError(new Error("Got error response " + response.statusCode() + ": " + result));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e.getCause() instanceof InterruptedException) {
|
||||
cb.onError(e); // TODO: Handle interruptions
|
||||
} else {
|
||||
cb.onError(e);
|
||||
}
|
||||
if (response.statusCode() == HttpStatus.SC_OK) {
|
||||
cb.onSubscribed();
|
||||
inputStream = response.body();
|
||||
handleResponse(inputStream);
|
||||
} else {
|
||||
String result;
|
||||
try (InputStream ignored = response.body()) {
|
||||
result = IOUtils.toString(response.body(), StandardCharsets.UTF_8);
|
||||
}
|
||||
cb.onError(new Error("Got error response " + response.statusCode() + ": " + result));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e.getCause() instanceof InterruptedException) {
|
||||
cb.onError(e); // TODO: Handle interruptions
|
||||
} else {
|
||||
cb.onError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopCurrentRequest() {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Got error stopCurrentRequest: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
public void stopCurrentRequest() {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Got error stopCurrentRequest: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleResponse(@NotNull InputStream inputStream) {
|
||||
/*
|
||||
* Streams go like this:
|
||||
* event: completion
|
||||
* data: Hello
|
||||
*
|
||||
* event: completion
|
||||
* data: Hello, there
|
||||
*
|
||||
* event: done
|
||||
* data:
|
||||
*/
|
||||
String eventName = null;
|
||||
try (BufferedInputStream in = IOUtils.buffer(inputStream)) {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
StringBuilder messageBuilder = new StringBuilder();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("data:")) {
|
||||
messageBuilder.append(line.substring(5));
|
||||
} else if (line.startsWith("event:")) {
|
||||
eventName = line.substring(6).trim();
|
||||
}
|
||||
if (line.trim().isEmpty() && messageBuilder.length() > 0) {
|
||||
String message = messageBuilder.toString();
|
||||
if (Objects.equals(eventName, "completion")) { // Completion
|
||||
JsonObject json = new Gson().fromJson(message, JsonObject.class);
|
||||
JsonPrimitive completion = json.getAsJsonPrimitive("completion");
|
||||
cb.onData(completion != null ? completion.getAsString().trim() : null);
|
||||
} else if (Objects.equals(eventName, "done")) { // Done
|
||||
stopCurrentRequest();
|
||||
cb.onComplete();
|
||||
return;
|
||||
}
|
||||
messageBuilder = new StringBuilder();
|
||||
}
|
||||
}
|
||||
if (messageBuilder.length() > 0) {
|
||||
System.out.println("Non-processed data: {}" + messageBuilder);
|
||||
}
|
||||
private void handleResponse(@NotNull InputStream inputStream) {
|
||||
/*
|
||||
* Streams go like this:
|
||||
* event: completion
|
||||
* data: Hello
|
||||
*
|
||||
* event: completion
|
||||
* data: Hello, there
|
||||
*
|
||||
* event: done
|
||||
* data:
|
||||
*/
|
||||
String eventName = null;
|
||||
try (BufferedInputStream in = IOUtils.buffer(inputStream)) {
|
||||
try (BufferedReader reader =
|
||||
new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
StringBuilder messageBuilder = new StringBuilder();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("data:")) {
|
||||
messageBuilder.append(line.substring(5));
|
||||
} else if (line.startsWith("event:")) {
|
||||
eventName = line.substring(6).trim();
|
||||
}
|
||||
if (line.trim().isEmpty() && messageBuilder.length() > 0) {
|
||||
String message = messageBuilder.toString();
|
||||
if (Objects.equals(eventName, "completion")) { // Completion
|
||||
JsonObject json = new Gson().fromJson(message, JsonObject.class);
|
||||
JsonPrimitive completion = json.getAsJsonPrimitive("completion");
|
||||
cb.onData(completion != null ? completion.getAsString().trim() : null);
|
||||
} else if (Objects.equals(eventName, "done")) { // Done
|
||||
stopCurrentRequest();
|
||||
cb.onComplete();
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
cb.onError(e);
|
||||
messageBuilder = new StringBuilder();
|
||||
}
|
||||
}
|
||||
if (messageBuilder.length() > 0) {
|
||||
System.out.println("Non-processed data: {}" + messageBuilder);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
cb.onError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package com.sourcegraph.cody.completions;
|
||||
|
||||
public enum Speaker {
|
||||
HUMAN,
|
||||
ASSISTANT
|
||||
HUMAN,
|
||||
ASSISTANT
|
||||
}
|
||||
|
||||
@ -4,12 +4,11 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class SpeakerLowercaseSerializer implements JsonSerializer<Speaker> {
|
||||
@Override
|
||||
public JsonElement serialize(Speaker src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.name().toLowerCase());
|
||||
}
|
||||
@Override
|
||||
public JsonElement serialize(Speaker src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.name().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,42 +6,52 @@ import com.google.gson.JsonSyntaxException;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.sourcegraph.api.GraphQlClient;
|
||||
import com.sourcegraph.api.GraphQlResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ApiAuthenticator {
|
||||
private final static Logger logger = Logger.getInstance(ApiAuthenticator.class);
|
||||
private static final Logger logger = Logger.getInstance(ApiAuthenticator.class);
|
||||
|
||||
public static void testConnection(@NotNull String instanceUrl, @Nullable String accessToken, @Nullable String customRequestHeaders, @NotNull Consumer<ConnectionStatus> callback) {
|
||||
new Thread(() -> {
|
||||
String query = "query {" +
|
||||
" currentUser {" +
|
||||
" id" +
|
||||
" }" +
|
||||
"}";
|
||||
public static void testConnection(
|
||||
@NotNull String instanceUrl,
|
||||
@Nullable String accessToken,
|
||||
@Nullable String customRequestHeaders,
|
||||
@NotNull Consumer<ConnectionStatus> callback) {
|
||||
new Thread(
|
||||
() -> {
|
||||
String query = "query { currentUser { id } }";
|
||||
|
||||
try {
|
||||
GraphQlResponse response = GraphQlClient.callGraphQLService(instanceUrl, accessToken, customRequestHeaders, query, new JsonObject());
|
||||
try {
|
||||
GraphQlResponse response =
|
||||
GraphQlClient.callGraphQLService(
|
||||
instanceUrl, accessToken, customRequestHeaders, query, new JsonObject());
|
||||
if (response.getStatusCode() == 200) {
|
||||
JsonElement id = response.getBodyAsJson().getAsJsonObject("data").getAsJsonObject("currentUser").get("id");
|
||||
callback.accept(id.isJsonNull() ? ConnectionStatus.COULD_CONNECT_BUT_NOT_AUTHENTICATED : ConnectionStatus.AUTHENTICATED);
|
||||
JsonElement id =
|
||||
response
|
||||
.getBodyAsJson()
|
||||
.getAsJsonObject("data")
|
||||
.getAsJsonObject("currentUser")
|
||||
.get("id");
|
||||
callback.accept(
|
||||
id.isJsonNull()
|
||||
? ConnectionStatus.COULD_CONNECT_BUT_NOT_AUTHENTICATED
|
||||
: ConnectionStatus.AUTHENTICATED);
|
||||
} else {
|
||||
callback.accept(ConnectionStatus.COULD_NOT_CONNECT);
|
||||
callback.accept(ConnectionStatus.COULD_NOT_CONNECT);
|
||||
}
|
||||
} catch (IOException | JsonSyntaxException e) {
|
||||
} catch (IOException | JsonSyntaxException e) {
|
||||
callback.accept(ConnectionStatus.COULD_NOT_CONNECT);
|
||||
logger.info(e);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum ConnectionStatus {
|
||||
AUTHENTICATED,
|
||||
COULD_NOT_CONNECT,
|
||||
COULD_CONNECT_BUT_NOT_AUTHENTICATED
|
||||
}
|
||||
enum ConnectionStatus {
|
||||
AUTHENTICATED,
|
||||
COULD_NOT_CONNECT,
|
||||
COULD_CONNECT_BUT_NOT_AUTHENTICATED
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,54 +1,50 @@
|
||||
package com.sourcegraph.cody.config;
|
||||
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import javax.swing.*;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Provides controller functionality for application settings.
|
||||
*/
|
||||
/** Provides controller functionality for application settings. */
|
||||
public class ApplicationSettingsConfigurable implements Configurable {
|
||||
private SettingsComponent mySettingsComponent;
|
||||
private SettingsComponent mySettingsComponent;
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Title)
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Cody";
|
||||
}
|
||||
@Nls(capitalization = Nls.Capitalization.Title)
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Cody";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return mySettingsComponent.getPreferredFocusedComponent();
|
||||
}
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return mySettingsComponent.getPreferredFocusedComponent();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
mySettingsComponent = new SettingsComponent();
|
||||
return mySettingsComponent.getPanel();
|
||||
}
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
mySettingsComponent = new SettingsComponent();
|
||||
return mySettingsComponent.getPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return SettingsConfigurableHelper.isModified(null, mySettingsComponent);
|
||||
}
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return SettingsConfigurableHelper.isModified(null, mySettingsComponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
SettingsConfigurableHelper.apply(null, mySettingsComponent);
|
||||
}
|
||||
@Override
|
||||
public void apply() {
|
||||
SettingsConfigurableHelper.apply(null, mySettingsComponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
SettingsConfigurableHelper.reset(null, mySettingsComponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
mySettingsComponent.dispose();
|
||||
mySettingsComponent = null;
|
||||
}
|
||||
@Override
|
||||
public void reset() {
|
||||
SettingsConfigurableHelper.reset(null, mySettingsComponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
mySettingsComponent.dispose();
|
||||
mySettingsComponent = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,134 +10,127 @@ import org.jetbrains.annotations.Nullable;
|
||||
@State(
|
||||
name = "ApplicationConfig",
|
||||
storages = {@Storage("cody.xml")})
|
||||
public class CodyApplicationService implements CodyService, PersistentStateComponent<CodyApplicationService> {
|
||||
@Nullable
|
||||
private String instanceType;
|
||||
@Nullable
|
||||
private String dotcomAccessToken;
|
||||
@Nullable
|
||||
private String enterpriseUrl;
|
||||
@Nullable
|
||||
private String enterpriseAccessToken;
|
||||
@Nullable
|
||||
private String customRequestHeaders;
|
||||
@Nullable
|
||||
private String codebase;
|
||||
@Nullable
|
||||
private String anonymousUserId;
|
||||
private boolean isInstallEventLogged;
|
||||
private boolean areChatPredictionsEnabled;
|
||||
@Nullable
|
||||
private Boolean authenticationFailedLastTime;
|
||||
public class CodyApplicationService
|
||||
implements CodyService, PersistentStateComponent<CodyApplicationService> {
|
||||
@Nullable private String instanceType;
|
||||
@Nullable private String dotcomAccessToken;
|
||||
@Nullable private String enterpriseUrl;
|
||||
@Nullable private String enterpriseAccessToken;
|
||||
@Nullable private String customRequestHeaders;
|
||||
@Nullable private String codebase;
|
||||
@Nullable private String anonymousUserId;
|
||||
private boolean isInstallEventLogged;
|
||||
private boolean areChatPredictionsEnabled;
|
||||
@Nullable private Boolean authenticationFailedLastTime;
|
||||
|
||||
@NotNull
|
||||
public static CodyApplicationService getInstance() {
|
||||
return ApplicationManager.getApplication().getService(CodyApplicationService.class);
|
||||
}
|
||||
@NotNull
|
||||
public static CodyApplicationService getInstance() {
|
||||
return ApplicationManager.getApplication().getService(CodyApplicationService.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getInstanceType() {
|
||||
return instanceType;
|
||||
}
|
||||
@Nullable
|
||||
public String getInstanceType() {
|
||||
return instanceType;
|
||||
}
|
||||
|
||||
public void setInstanceType(@Nullable String instanceType) {
|
||||
this.instanceType = instanceType;
|
||||
}
|
||||
public void setInstanceType(@Nullable String instanceType) {
|
||||
this.instanceType = instanceType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDotcomAccessToken() {
|
||||
return dotcomAccessToken;
|
||||
}
|
||||
@Nullable
|
||||
public String getDotcomAccessToken() {
|
||||
return dotcomAccessToken;
|
||||
}
|
||||
|
||||
public void setDotcomAccessToken(@Nullable String dotcomAccessToken) {
|
||||
this.dotcomAccessToken = dotcomAccessToken;
|
||||
}
|
||||
public void setDotcomAccessToken(@Nullable String dotcomAccessToken) {
|
||||
this.dotcomAccessToken = dotcomAccessToken;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getEnterpriseUrl() {
|
||||
return enterpriseUrl;
|
||||
}
|
||||
@Nullable
|
||||
public String getEnterpriseUrl() {
|
||||
return enterpriseUrl;
|
||||
}
|
||||
|
||||
public void setEnterpriseUrl(@Nullable String enterpriseUrl) {
|
||||
this.enterpriseUrl = enterpriseUrl;
|
||||
}
|
||||
public void setEnterpriseUrl(@Nullable String enterpriseUrl) {
|
||||
this.enterpriseUrl = enterpriseUrl;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getEnterpriseAccessToken() {
|
||||
return enterpriseAccessToken;
|
||||
}
|
||||
@Nullable
|
||||
public String getEnterpriseAccessToken() {
|
||||
return enterpriseAccessToken;
|
||||
}
|
||||
|
||||
public void setEnterpriseAccessToken(@Nullable String enterpriseAccessToken) {
|
||||
this.enterpriseAccessToken = enterpriseAccessToken;
|
||||
}
|
||||
public void setEnterpriseAccessToken(@Nullable String enterpriseAccessToken) {
|
||||
this.enterpriseAccessToken = enterpriseAccessToken;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getCustomRequestHeaders() {
|
||||
return customRequestHeaders;
|
||||
}
|
||||
@Nullable
|
||||
public String getCustomRequestHeaders() {
|
||||
return customRequestHeaders;
|
||||
}
|
||||
|
||||
public void setCustomRequestHeaders(@Nullable String customRequestHeaders) {
|
||||
this.customRequestHeaders = customRequestHeaders;
|
||||
}
|
||||
public void setCustomRequestHeaders(@Nullable String customRequestHeaders) {
|
||||
this.customRequestHeaders = customRequestHeaders;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getCodebase() {
|
||||
return codebase;
|
||||
}
|
||||
@Nullable
|
||||
public String getCodebase() {
|
||||
return codebase;
|
||||
}
|
||||
|
||||
public void setCodebase(@Nullable String codebase) {
|
||||
this.codebase = codebase;
|
||||
}
|
||||
public void setCodebase(@Nullable String codebase) {
|
||||
this.codebase = codebase;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getAnonymousUserId() {
|
||||
return anonymousUserId;
|
||||
}
|
||||
@Nullable
|
||||
public String getAnonymousUserId() {
|
||||
return anonymousUserId;
|
||||
}
|
||||
|
||||
public void setAnonymousUserId(@Nullable String anonymousUserId) {
|
||||
this.anonymousUserId = anonymousUserId;
|
||||
}
|
||||
public void setAnonymousUserId(@Nullable String anonymousUserId) {
|
||||
this.anonymousUserId = anonymousUserId;
|
||||
}
|
||||
|
||||
public boolean isInstallEventLogged() {
|
||||
return isInstallEventLogged;
|
||||
}
|
||||
public boolean isInstallEventLogged() {
|
||||
return isInstallEventLogged;
|
||||
}
|
||||
|
||||
public void setInstallEventLogged(boolean installEventLogged) {
|
||||
isInstallEventLogged = installEventLogged;
|
||||
}
|
||||
public void setInstallEventLogged(boolean installEventLogged) {
|
||||
isInstallEventLogged = installEventLogged;
|
||||
}
|
||||
|
||||
public Boolean areChatPredictionsEnabled() {
|
||||
return areChatPredictionsEnabled;
|
||||
}
|
||||
public Boolean areChatPredictionsEnabled() {
|
||||
return areChatPredictionsEnabled;
|
||||
}
|
||||
|
||||
public void setChatPredictionsEnabled(Boolean areChatPredictionsEnabled) {
|
||||
this.areChatPredictionsEnabled = areChatPredictionsEnabled;
|
||||
}
|
||||
public void setChatPredictionsEnabled(Boolean areChatPredictionsEnabled) {
|
||||
this.areChatPredictionsEnabled = areChatPredictionsEnabled;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getAuthenticationFailedLastTime() {
|
||||
return authenticationFailedLastTime;
|
||||
}
|
||||
@Nullable
|
||||
public Boolean getAuthenticationFailedLastTime() {
|
||||
return authenticationFailedLastTime;
|
||||
}
|
||||
|
||||
public void setAuthenticationFailedLastTime(@Nullable Boolean authenticationFailedLastTime) {
|
||||
this.authenticationFailedLastTime = authenticationFailedLastTime;
|
||||
}
|
||||
public void setAuthenticationFailedLastTime(@Nullable Boolean authenticationFailedLastTime) {
|
||||
this.authenticationFailedLastTime = authenticationFailedLastTime;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CodyApplicationService getState() {
|
||||
return this;
|
||||
}
|
||||
@Nullable
|
||||
@Override
|
||||
public CodyApplicationService getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(@NotNull CodyApplicationService settings) {
|
||||
this.instanceType = settings.instanceType;
|
||||
this.enterpriseUrl = settings.enterpriseUrl;
|
||||
this.enterpriseAccessToken = settings.enterpriseAccessToken;
|
||||
this.customRequestHeaders = settings.customRequestHeaders;
|
||||
this.codebase = settings.codebase;
|
||||
this.anonymousUserId = settings.anonymousUserId;
|
||||
this.areChatPredictionsEnabled = settings.areChatPredictionsEnabled;
|
||||
this.authenticationFailedLastTime = settings.authenticationFailedLastTime;
|
||||
}
|
||||
@Override
|
||||
public void loadState(@NotNull CodyApplicationService settings) {
|
||||
this.instanceType = settings.instanceType;
|
||||
this.enterpriseUrl = settings.enterpriseUrl;
|
||||
this.enterpriseAccessToken = settings.enterpriseAccessToken;
|
||||
this.customRequestHeaders = settings.customRequestHeaders;
|
||||
this.codebase = settings.codebase;
|
||||
this.anonymousUserId = settings.anonymousUserId;
|
||||
this.areChatPredictionsEnabled = settings.areChatPredictionsEnabled;
|
||||
this.authenticationFailedLastTime = settings.authenticationFailedLastTime;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,107 +10,101 @@ import org.jetbrains.annotations.Nullable;
|
||||
@State(
|
||||
name = "Config",
|
||||
storages = {@Storage("cody.xml")})
|
||||
public class CodyProjectService implements CodyService, PersistentStateComponent<CodyProjectService> {
|
||||
@Nullable
|
||||
private String instanceType;
|
||||
@Nullable
|
||||
private String dotcomAccessToken;
|
||||
@Nullable
|
||||
private String enterpriseUrl;
|
||||
@Nullable
|
||||
private String enterpriseAccessToken;
|
||||
@Nullable
|
||||
private String customRequestHeaders;
|
||||
@Nullable
|
||||
private String defaultBranch;
|
||||
@Nullable
|
||||
private String codebase;
|
||||
@Nullable
|
||||
private Boolean areChatPredictionsEnabled;
|
||||
public class CodyProjectService
|
||||
implements CodyService, PersistentStateComponent<CodyProjectService> {
|
||||
@Nullable private String instanceType;
|
||||
@Nullable private String dotcomAccessToken;
|
||||
@Nullable private String enterpriseUrl;
|
||||
@Nullable private String enterpriseAccessToken;
|
||||
@Nullable private String customRequestHeaders;
|
||||
@Nullable private String defaultBranch;
|
||||
@Nullable private String codebase;
|
||||
@Nullable private Boolean areChatPredictionsEnabled;
|
||||
|
||||
@NotNull
|
||||
public static CodyProjectService getInstance(@NotNull Project project) {
|
||||
return project.getService(CodyProjectService.class);
|
||||
}
|
||||
@NotNull
|
||||
public static CodyProjectService getInstance(@NotNull Project project) {
|
||||
|
||||
public void setInstanceType(@Nullable String instanceType) {
|
||||
this.instanceType = instanceType;
|
||||
}
|
||||
return project.getService(CodyProjectService.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getInstanceType() {
|
||||
return instanceType;
|
||||
}
|
||||
public void setInstanceType(@Nullable String instanceType) {
|
||||
this.instanceType = instanceType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDotcomAccessToken() {
|
||||
return dotcomAccessToken;
|
||||
}
|
||||
@Nullable
|
||||
public String getInstanceType() {
|
||||
return instanceType;
|
||||
}
|
||||
|
||||
public void setDotcomAccessToken(@Nullable String dotcomAccessToken) {
|
||||
this.dotcomAccessToken = dotcomAccessToken;
|
||||
}
|
||||
@Nullable
|
||||
public String getDotcomAccessToken() {
|
||||
return dotcomAccessToken;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getEnterpriseUrl() {
|
||||
return enterpriseUrl;
|
||||
}
|
||||
public void setDotcomAccessToken(@Nullable String dotcomAccessToken) {
|
||||
this.dotcomAccessToken = dotcomAccessToken;
|
||||
}
|
||||
|
||||
public void setEnterpriseUrl(@Nullable String enterpriseUrl) {
|
||||
this.enterpriseUrl = enterpriseUrl;
|
||||
}
|
||||
@Nullable
|
||||
public String getEnterpriseUrl() {
|
||||
return enterpriseUrl;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getEnterpriseAccessToken() {
|
||||
return enterpriseAccessToken;
|
||||
}
|
||||
public void setEnterpriseUrl(@Nullable String enterpriseUrl) {
|
||||
this.enterpriseUrl = enterpriseUrl;
|
||||
}
|
||||
|
||||
public void setEnterpriseAccessToken(@Nullable String enterpriseAccessToken) {
|
||||
this.enterpriseAccessToken = enterpriseAccessToken;
|
||||
}
|
||||
@Nullable
|
||||
public String getEnterpriseAccessToken() {
|
||||
return enterpriseAccessToken;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getCustomRequestHeaders() {
|
||||
return customRequestHeaders;
|
||||
}
|
||||
public void setEnterpriseAccessToken(@Nullable String enterpriseAccessToken) {
|
||||
this.enterpriseAccessToken = enterpriseAccessToken;
|
||||
}
|
||||
|
||||
public void setCustomRequestHeaders(@Nullable String customRequestHeaders) {
|
||||
this.customRequestHeaders = customRequestHeaders;
|
||||
}
|
||||
@Nullable
|
||||
public String getCustomRequestHeaders() {
|
||||
return customRequestHeaders;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getCodebase() {
|
||||
return codebase;
|
||||
}
|
||||
public void setCustomRequestHeaders(@Nullable String customRequestHeaders) {
|
||||
this.customRequestHeaders = customRequestHeaders;
|
||||
}
|
||||
|
||||
public void setCodebase(@Nullable String codebase) {
|
||||
this.codebase = codebase;
|
||||
}
|
||||
@Nullable
|
||||
public String getCodebase() {
|
||||
return codebase;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CodyProjectService getState() {
|
||||
return this;
|
||||
}
|
||||
public void setCodebase(@Nullable String codebase) {
|
||||
this.codebase = codebase;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean areChatPredictionsEnabled() {
|
||||
return areChatPredictionsEnabled;
|
||||
}
|
||||
@Nullable
|
||||
@Override
|
||||
public CodyProjectService getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setChatPredictionsEnabled(@Nullable Boolean areChatPredictionsEnabled) {
|
||||
this.areChatPredictionsEnabled = areChatPredictionsEnabled;
|
||||
}
|
||||
@Nullable
|
||||
public Boolean areChatPredictionsEnabled() {
|
||||
return areChatPredictionsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(@NotNull CodyProjectService settings) {
|
||||
this.instanceType = settings.instanceType;
|
||||
this.dotcomAccessToken = settings.dotcomAccessToken;
|
||||
this.enterpriseUrl = settings.enterpriseUrl;
|
||||
this.enterpriseAccessToken = settings.enterpriseAccessToken;
|
||||
this.customRequestHeaders = settings.customRequestHeaders;
|
||||
this.defaultBranch = settings.defaultBranch;
|
||||
this.codebase = settings.codebase;
|
||||
this.areChatPredictionsEnabled = settings.areChatPredictionsEnabled;
|
||||
}
|
||||
public void setChatPredictionsEnabled(@Nullable Boolean areChatPredictionsEnabled) {
|
||||
this.areChatPredictionsEnabled = areChatPredictionsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(@NotNull CodyProjectService settings) {
|
||||
this.instanceType = settings.instanceType;
|
||||
this.dotcomAccessToken = settings.dotcomAccessToken;
|
||||
this.enterpriseUrl = settings.enterpriseUrl;
|
||||
this.enterpriseAccessToken = settings.enterpriseAccessToken;
|
||||
this.customRequestHeaders = settings.customRequestHeaders;
|
||||
this.defaultBranch = settings.defaultBranch;
|
||||
this.codebase = settings.codebase;
|
||||
this.areChatPredictionsEnabled = settings.areChatPredictionsEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,37 +3,38 @@ package com.sourcegraph.cody.config;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface CodyService {
|
||||
@Nullable
|
||||
String getInstanceType();
|
||||
@Nullable
|
||||
String getInstanceType();
|
||||
|
||||
void setInstanceType(@Nullable String instanceType);
|
||||
void setInstanceType(@Nullable String instanceType);
|
||||
|
||||
@Nullable
|
||||
String getDotcomAccessToken();
|
||||
@Nullable
|
||||
String getDotcomAccessToken();
|
||||
|
||||
void setDotcomAccessToken(@Nullable String dotcomAccessToken);
|
||||
void setDotcomAccessToken(@Nullable String dotcomAccessToken);
|
||||
|
||||
@Nullable
|
||||
String getEnterpriseUrl();
|
||||
@Nullable
|
||||
String getEnterpriseUrl();
|
||||
|
||||
void setEnterpriseUrl(@Nullable String enterpriseUrl);
|
||||
void setEnterpriseUrl(@Nullable String enterpriseUrl);
|
||||
|
||||
@Nullable
|
||||
String getEnterpriseAccessToken();
|
||||
@Nullable
|
||||
String getEnterpriseAccessToken();
|
||||
|
||||
void setEnterpriseAccessToken(@Nullable String enterpriseAccessToken);
|
||||
void setEnterpriseAccessToken(@Nullable String enterpriseAccessToken);
|
||||
|
||||
@Nullable
|
||||
String getCustomRequestHeaders();
|
||||
@Nullable
|
||||
String getCustomRequestHeaders();
|
||||
|
||||
void setCustomRequestHeaders(@Nullable String customRequestHeaders);
|
||||
void setCustomRequestHeaders(@Nullable String customRequestHeaders);
|
||||
|
||||
@Nullable
|
||||
String getCodebase();
|
||||
@Nullable
|
||||
String getCodebase();
|
||||
|
||||
void setCodebase(@Nullable String codebase);
|
||||
void setCodebase(@Nullable String codebase);
|
||||
|
||||
@Nullable Boolean areChatPredictionsEnabled();
|
||||
@Nullable
|
||||
Boolean areChatPredictionsEnabled();
|
||||
|
||||
void setChatPredictionsEnabled(@Nullable Boolean areChatPredictionsEnabled);
|
||||
void setChatPredictionsEnabled(@Nullable Boolean areChatPredictionsEnabled);
|
||||
}
|
||||
|
||||
@ -1,170 +1,183 @@
|
||||
package com.sourcegraph.cody.config;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import java.util.Objects;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConfigUtil {
|
||||
public static final String DOTCOM_URL = "https://sourcegraph.com/";
|
||||
public static final String DOTCOM_URL = "https://sourcegraph.com/";
|
||||
|
||||
@NotNull
|
||||
public static SettingsComponent.InstanceType getInstanceType(@Nullable Project project) {
|
||||
// Project level
|
||||
if (project != null) {
|
||||
String projectLevelSetting = getProjectLevelConfig(project).getInstanceType();
|
||||
if (projectLevelSetting != null && !projectLevelSetting.isEmpty()) {
|
||||
return projectLevelSetting.equals(SettingsComponent.InstanceType.ENTERPRISE.name())
|
||||
? SettingsComponent.InstanceType.ENTERPRISE : SettingsComponent.InstanceType.DOTCOM;
|
||||
}
|
||||
}
|
||||
|
||||
// Application level
|
||||
String applicationLevelSetting = getApplicationLevelConfig().getInstanceType();
|
||||
if (applicationLevelSetting != null && !applicationLevelSetting.isEmpty()) {
|
||||
return applicationLevelSetting.equals(SettingsComponent.InstanceType.ENTERPRISE.name())
|
||||
? SettingsComponent.InstanceType.ENTERPRISE : SettingsComponent.InstanceType.DOTCOM;
|
||||
}
|
||||
|
||||
// Use default
|
||||
String enterpriseUrl = getEnterpriseUrl(project);
|
||||
return (enterpriseUrl.equals("") || enterpriseUrl.startsWith(DOTCOM_URL))
|
||||
? SettingsComponent.InstanceType.DOTCOM : SettingsComponent.InstanceType.ENTERPRISE;
|
||||
@NotNull
|
||||
public static SettingsComponent.InstanceType getInstanceType(@Nullable Project project) {
|
||||
// Project level
|
||||
if (project != null) {
|
||||
String projectLevelSetting = getProjectLevelConfig(project).getInstanceType();
|
||||
if (projectLevelSetting != null && !projectLevelSetting.isEmpty()) {
|
||||
return projectLevelSetting.equals(SettingsComponent.InstanceType.ENTERPRISE.name())
|
||||
? SettingsComponent.InstanceType.ENTERPRISE
|
||||
: SettingsComponent.InstanceType.DOTCOM;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getSourcegraphUrl(@Nullable Project project) {
|
||||
if (getInstanceType(project) == SettingsComponent.InstanceType.DOTCOM) {
|
||||
return DOTCOM_URL;
|
||||
} else {
|
||||
String enterpriseUrl = getEnterpriseUrl(project);
|
||||
return !enterpriseUrl.isEmpty() ? enterpriseUrl : DOTCOM_URL;
|
||||
}
|
||||
// Application level
|
||||
String applicationLevelSetting = getApplicationLevelConfig().getInstanceType();
|
||||
if (applicationLevelSetting != null && !applicationLevelSetting.isEmpty()) {
|
||||
return applicationLevelSetting.equals(SettingsComponent.InstanceType.ENTERPRISE.name())
|
||||
? SettingsComponent.InstanceType.ENTERPRISE
|
||||
: SettingsComponent.InstanceType.DOTCOM;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getEnterpriseUrl(@Nullable Project project) {
|
||||
// Project level
|
||||
if (project != null) {
|
||||
String projectLevelUrl = getProjectLevelConfig(project).getEnterpriseUrl();
|
||||
if (projectLevelUrl != null && projectLevelUrl.length() > 0) {
|
||||
return addSlashIfNeeded(projectLevelUrl);
|
||||
}
|
||||
}
|
||||
// Use default
|
||||
String enterpriseUrl = getEnterpriseUrl(project);
|
||||
return (enterpriseUrl.equals("") || enterpriseUrl.startsWith(DOTCOM_URL))
|
||||
? SettingsComponent.InstanceType.DOTCOM
|
||||
: SettingsComponent.InstanceType.ENTERPRISE;
|
||||
}
|
||||
|
||||
// Application level
|
||||
String applicationLevelUrl = getApplicationLevelConfig().getEnterpriseUrl();
|
||||
if (applicationLevelUrl != null && applicationLevelUrl.length() > 0) {
|
||||
return addSlashIfNeeded(applicationLevelUrl);
|
||||
}
|
||||
@NotNull
|
||||
public static String getSourcegraphUrl(@Nullable Project project) {
|
||||
if (getInstanceType(project) == SettingsComponent.InstanceType.DOTCOM) {
|
||||
return DOTCOM_URL;
|
||||
} else {
|
||||
String enterpriseUrl = getEnterpriseUrl(project);
|
||||
return !enterpriseUrl.isEmpty() ? enterpriseUrl : DOTCOM_URL;
|
||||
}
|
||||
}
|
||||
|
||||
// Use default
|
||||
return "";
|
||||
@NotNull
|
||||
public static String getEnterpriseUrl(@Nullable Project project) {
|
||||
// Project level
|
||||
if (project != null) {
|
||||
String projectLevelUrl = getProjectLevelConfig(project).getEnterpriseUrl();
|
||||
if (projectLevelUrl != null && projectLevelUrl.length() > 0) {
|
||||
return addSlashIfNeeded(projectLevelUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getDotcomAccessToken(@Nullable Project project) {
|
||||
// Project level → application level
|
||||
String accessToken = project != null ? getProjectLevelConfig(project).getDotcomAccessToken() : null;
|
||||
return accessToken != null ? accessToken : getApplicationLevelConfig().getDotcomAccessToken();
|
||||
// Application level
|
||||
String applicationLevelUrl = getApplicationLevelConfig().getEnterpriseUrl();
|
||||
if (applicationLevelUrl != null && applicationLevelUrl.length() > 0) {
|
||||
return addSlashIfNeeded(applicationLevelUrl);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getEnterpriseAccessToken(@Nullable Project project) {
|
||||
// Project level → application level
|
||||
String accessToken = project != null ? getProjectLevelConfig(project).getEnterpriseAccessToken() : null;
|
||||
return accessToken != null ? accessToken : getApplicationLevelConfig().getEnterpriseAccessToken();
|
||||
// Use default
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getDotcomAccessToken(@Nullable Project project) {
|
||||
// Project level → application level
|
||||
String accessToken =
|
||||
project != null ? getProjectLevelConfig(project).getDotcomAccessToken() : null;
|
||||
return accessToken != null ? accessToken : getApplicationLevelConfig().getDotcomAccessToken();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getEnterpriseAccessToken(@Nullable Project project) {
|
||||
// Project level → application level
|
||||
String accessToken =
|
||||
project != null ? getProjectLevelConfig(project).getEnterpriseAccessToken() : null;
|
||||
return accessToken != null
|
||||
? accessToken
|
||||
: getApplicationLevelConfig().getEnterpriseAccessToken();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getCustomRequestHeaders(@Nullable Project project) {
|
||||
// Project level
|
||||
if (project != null) {
|
||||
String projectLevelCustomRequestHeaders =
|
||||
getProjectLevelConfig(project).getCustomRequestHeaders();
|
||||
if (projectLevelCustomRequestHeaders != null
|
||||
&& projectLevelCustomRequestHeaders.length() > 0) {
|
||||
return projectLevelCustomRequestHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getCustomRequestHeaders(@Nullable Project project) {
|
||||
// Project level
|
||||
if (project != null) {
|
||||
String projectLevelCustomRequestHeaders = getProjectLevelConfig(project).getCustomRequestHeaders();
|
||||
if (projectLevelCustomRequestHeaders != null && projectLevelCustomRequestHeaders.length() > 0) {
|
||||
return projectLevelCustomRequestHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
// Application level
|
||||
String applicationLevelCustomRequestHeaders = getApplicationLevelConfig().getCustomRequestHeaders();
|
||||
if (applicationLevelCustomRequestHeaders != null && applicationLevelCustomRequestHeaders.length() > 0) {
|
||||
return applicationLevelCustomRequestHeaders;
|
||||
}
|
||||
|
||||
// Default
|
||||
return "";
|
||||
// Application level
|
||||
String applicationLevelCustomRequestHeaders =
|
||||
getApplicationLevelConfig().getCustomRequestHeaders();
|
||||
if (applicationLevelCustomRequestHeaders != null
|
||||
&& applicationLevelCustomRequestHeaders.length() > 0) {
|
||||
return applicationLevelCustomRequestHeaders;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getCodebase(@Nullable Project project) {
|
||||
// Project level
|
||||
if (project != null) {
|
||||
String projectLevelCodebase = getProjectLevelConfig(project).getCodebase();
|
||||
if (projectLevelCodebase != null && projectLevelCodebase.length() > 0) {
|
||||
return projectLevelCodebase;
|
||||
}
|
||||
}
|
||||
// Default
|
||||
return "";
|
||||
}
|
||||
|
||||
// Application level
|
||||
String applicationLevelCodebase = getApplicationLevelConfig().getCodebase();
|
||||
if (applicationLevelCodebase != null && applicationLevelCodebase.length() > 0) {
|
||||
return applicationLevelCodebase;
|
||||
}
|
||||
|
||||
// Use default
|
||||
return "";
|
||||
@NotNull
|
||||
public static String getCodebase(@Nullable Project project) {
|
||||
// Project level
|
||||
if (project != null) {
|
||||
String projectLevelCodebase = getProjectLevelConfig(project).getCodebase();
|
||||
if (projectLevelCodebase != null && projectLevelCodebase.length() > 0) {
|
||||
return projectLevelCodebase;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getAnonymousUserId() {
|
||||
return getApplicationLevelConfig().getAnonymousUserId();
|
||||
// Application level
|
||||
String applicationLevelCodebase = getApplicationLevelConfig().getCodebase();
|
||||
if (applicationLevelCodebase != null && applicationLevelCodebase.length() > 0) {
|
||||
return applicationLevelCodebase;
|
||||
}
|
||||
|
||||
public static void setAnonymousUserId(@Nullable String anonymousUserId) {
|
||||
getApplicationLevelConfig().setAnonymousUserId(anonymousUserId);
|
||||
}
|
||||
// Use default
|
||||
return "";
|
||||
}
|
||||
|
||||
public static boolean isInstallEventLogged() {
|
||||
return getApplicationLevelConfig().isInstallEventLogged();
|
||||
}
|
||||
@Nullable
|
||||
public static String getAnonymousUserId() {
|
||||
return getApplicationLevelConfig().getAnonymousUserId();
|
||||
}
|
||||
|
||||
public static void setInstallEventLogged(boolean value) {
|
||||
getApplicationLevelConfig().setInstallEventLogged(value);
|
||||
}
|
||||
public static void setAnonymousUserId(@Nullable String anonymousUserId) {
|
||||
getApplicationLevelConfig().setAnonymousUserId(anonymousUserId);
|
||||
}
|
||||
|
||||
public static boolean areChatPredictionsEnabled(@Nullable Project project) {
|
||||
// Project level → application level
|
||||
Boolean areChatPredictionsEnabled = project != null ? getProjectLevelConfig(project).areChatPredictionsEnabled() : null;
|
||||
return areChatPredictionsEnabled != null ? areChatPredictionsEnabled : Boolean.TRUE.equals(getApplicationLevelConfig().areChatPredictionsEnabled());
|
||||
}
|
||||
public static boolean isInstallEventLogged() {
|
||||
return getApplicationLevelConfig().isInstallEventLogged();
|
||||
}
|
||||
|
||||
public static void setAreChatPredictionsEnabled(boolean value) {
|
||||
getApplicationLevelConfig().setChatPredictionsEnabled(value);
|
||||
}
|
||||
public static void setInstallEventLogged(boolean value) {
|
||||
getApplicationLevelConfig().setInstallEventLogged(value);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String addSlashIfNeeded(@NotNull String url) {
|
||||
return url.endsWith("/") ? url : url + "/";
|
||||
}
|
||||
public static boolean areChatPredictionsEnabled(@Nullable Project project) {
|
||||
// Project level → application level
|
||||
Boolean areChatPredictionsEnabled =
|
||||
project != null ? getProjectLevelConfig(project).areChatPredictionsEnabled() : null;
|
||||
return areChatPredictionsEnabled != null
|
||||
? areChatPredictionsEnabled
|
||||
: Boolean.TRUE.equals(getApplicationLevelConfig().areChatPredictionsEnabled());
|
||||
}
|
||||
|
||||
public static boolean didAuthenticationFailLastTime() {
|
||||
Boolean failedLastTime = getApplicationLevelConfig().getAuthenticationFailedLastTime();
|
||||
return failedLastTime != null ? failedLastTime : true;
|
||||
}
|
||||
public static void setAreChatPredictionsEnabled(boolean value) {
|
||||
getApplicationLevelConfig().setChatPredictionsEnabled(value);
|
||||
}
|
||||
|
||||
public static void setAuthenticationFailedLastTime(boolean value) {
|
||||
CodyApplicationService.getInstance().setAuthenticationFailedLastTime(value);
|
||||
}
|
||||
@NotNull
|
||||
private static String addSlashIfNeeded(@NotNull String url) {
|
||||
return url.endsWith("/") ? url : url + "/";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static CodyApplicationService getApplicationLevelConfig() {
|
||||
return Objects.requireNonNull(CodyApplicationService.getInstance());
|
||||
}
|
||||
public static boolean didAuthenticationFailLastTime() {
|
||||
Boolean failedLastTime = getApplicationLevelConfig().getAuthenticationFailedLastTime();
|
||||
return failedLastTime != null ? failedLastTime : true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static CodyProjectService getProjectLevelConfig(@NotNull Project project) {
|
||||
return Objects.requireNonNull(CodyProjectService.getInstance(project));
|
||||
}
|
||||
public static void setAuthenticationFailedLastTime(boolean value) {
|
||||
CodyApplicationService.getInstance().setAuthenticationFailedLastTime(value);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static CodyApplicationService getApplicationLevelConfig() {
|
||||
return Objects.requireNonNull(CodyApplicationService.getInstance());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static CodyProjectService getProjectLevelConfig(@NotNull Project project) {
|
||||
return Objects.requireNonNull(CodyProjectService.getInstance(project));
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,36 +12,42 @@ import com.sourcegraph.cody.Icons;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NotificationActivity implements StartupActivity.DumbAware {
|
||||
@Override
|
||||
public void runActivity(@NotNull Project project) {
|
||||
String url = ConfigUtil.getEnterpriseUrl(project);
|
||||
if (url.length() == 0 || url.startsWith(ConfigUtil.DOTCOM_URL)) {
|
||||
notifyAboutSourcegraphUrl();
|
||||
}
|
||||
@Override
|
||||
public void runActivity(@NotNull Project project) {
|
||||
String url = ConfigUtil.getEnterpriseUrl(project);
|
||||
if (url.length() == 0 || url.startsWith(ConfigUtil.DOTCOM_URL)) {
|
||||
notifyAboutSourcegraphUrl();
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyAboutSourcegraphUrl() {
|
||||
// Display notification
|
||||
Notification notification = new Notification("Sourcegraph access", "Cody",
|
||||
"A custom Sourcegraph URL is not set. Cody can only access public repos. Do you want to set your custom URL?", NotificationType.INFORMATION);
|
||||
AnAction setUrlAction = new OpenPluginSettingsAction("Set URL");
|
||||
AnAction cancelAction = new DumbAwareAction("Do Not Set") {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
|
||||
notification.expire();
|
||||
}
|
||||
private void notifyAboutSourcegraphUrl() {
|
||||
// Display notification
|
||||
Notification notification =
|
||||
new Notification(
|
||||
"Sourcegraph access",
|
||||
"Cody",
|
||||
"A custom Sourcegraph URL is not set. Cody can only access public repos. Do you want to set your custom URL?",
|
||||
NotificationType.INFORMATION);
|
||||
AnAction setUrlAction = new OpenPluginSettingsAction("Set URL");
|
||||
AnAction cancelAction =
|
||||
new DumbAwareAction("Do Not Set") {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
|
||||
notification.expire();
|
||||
}
|
||||
};
|
||||
AnAction neverShowAgainAction = new DumbAwareAction("Never Show Again") {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
|
||||
notification.expire();
|
||||
ConfigUtil.setAreChatPredictionsEnabled(true);
|
||||
}
|
||||
AnAction neverShowAgainAction =
|
||||
new DumbAwareAction("Never Show Again") {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
|
||||
notification.expire();
|
||||
ConfigUtil.setAreChatPredictionsEnabled(true);
|
||||
}
|
||||
};
|
||||
notification.setIcon(Icons.CodyLogo);
|
||||
notification.addAction(setUrlAction);
|
||||
notification.addAction(cancelAction);
|
||||
notification.addAction(neverShowAgainAction);
|
||||
Notifications.Bus.notify(notification);
|
||||
}
|
||||
notification.setIcon(Icons.CodyLogo);
|
||||
notification.addAction(setUrlAction);
|
||||
notification.addAction(cancelAction);
|
||||
notification.addAction(neverShowAgainAction);
|
||||
Notifications.Bus.notify(notification);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,16 +8,17 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenPluginSettingsAction extends DumbAwareAction {
|
||||
public OpenPluginSettingsAction() {
|
||||
super();
|
||||
}
|
||||
public OpenPluginSettingsAction() {
|
||||
super();
|
||||
}
|
||||
|
||||
public OpenPluginSettingsAction(@Nullable @NlsActions.ActionText String text) {
|
||||
super(text);
|
||||
}
|
||||
public OpenPluginSettingsAction(@Nullable @NlsActions.ActionText String text) {
|
||||
super(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent event) {
|
||||
ShowSettingsUtil.getInstance().showSettingsDialog(event.getProject(), ProjectSettingsConfigurable.class);
|
||||
}
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent event) {
|
||||
ShowSettingsUtil.getInstance()
|
||||
.showSettingsDialog(event.getProject(), ProjectSettingsConfigurable.class);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,10 +5,10 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface PluginSettingChangeActionNotifier {
|
||||
|
||||
Topic<PluginSettingChangeActionNotifier> TOPIC = Topic.create("Cody plugin settings have changed", PluginSettingChangeActionNotifier.class);
|
||||
Topic<PluginSettingChangeActionNotifier> TOPIC =
|
||||
Topic.create("Cody plugin settings have changed", PluginSettingChangeActionNotifier.class);
|
||||
|
||||
void beforeAction(@NotNull PluginSettingChangeContext context);
|
||||
void beforeAction(@NotNull PluginSettingChangeContext context);
|
||||
|
||||
void afterAction(@NotNull PluginSettingChangeContext context);
|
||||
void afterAction(@NotNull PluginSettingChangeContext context);
|
||||
}
|
||||
|
||||
|
||||
@ -3,40 +3,34 @@ package com.sourcegraph.cody.config;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PluginSettingChangeContext {
|
||||
@Nullable
|
||||
public final String oldDotcomAccessToken;
|
||||
@Nullable public final String oldDotcomAccessToken;
|
||||
|
||||
@Nullable
|
||||
public final String oldEnterpriseUrl;
|
||||
@Nullable public final String oldEnterpriseUrl;
|
||||
|
||||
@Nullable
|
||||
public final String oldEnterpriseAccessToken;
|
||||
@Nullable public final String oldEnterpriseAccessToken;
|
||||
|
||||
@Nullable
|
||||
public final String newDotcomAccessToken;
|
||||
@Nullable public final String newDotcomAccessToken;
|
||||
|
||||
@Nullable
|
||||
public final String newEnterpriseUrl;
|
||||
@Nullable public final String newEnterpriseUrl;
|
||||
|
||||
@Nullable
|
||||
public final String newEnterpriseAccessToken;
|
||||
@Nullable public final String newEnterpriseAccessToken;
|
||||
|
||||
@Nullable
|
||||
public final String newCustomRequestHeaders;
|
||||
@Nullable public final String newCustomRequestHeaders;
|
||||
|
||||
public PluginSettingChangeContext(@Nullable String oldDotcomAccessToken,
|
||||
@Nullable String oldEnterpriseUrl,
|
||||
@Nullable String oldEnterpriseAccessToken,
|
||||
@Nullable String newDotcomAccessToken,
|
||||
@Nullable String newEnterpriseUrl,
|
||||
@Nullable String newEnterpriseAccessToken,
|
||||
@Nullable String newCustomRequestHeaders) {
|
||||
this.oldDotcomAccessToken = oldDotcomAccessToken;
|
||||
this.oldEnterpriseUrl = oldEnterpriseUrl;
|
||||
this.oldEnterpriseAccessToken = oldEnterpriseAccessToken;
|
||||
this.newDotcomAccessToken = newDotcomAccessToken;
|
||||
this.newEnterpriseUrl = newEnterpriseUrl;
|
||||
this.newEnterpriseAccessToken = newEnterpriseAccessToken;
|
||||
this.newCustomRequestHeaders = newCustomRequestHeaders;
|
||||
}
|
||||
public PluginSettingChangeContext(
|
||||
@Nullable String oldDotcomAccessToken,
|
||||
@Nullable String oldEnterpriseUrl,
|
||||
@Nullable String oldEnterpriseAccessToken,
|
||||
@Nullable String newDotcomAccessToken,
|
||||
@Nullable String newEnterpriseUrl,
|
||||
@Nullable String newEnterpriseAccessToken,
|
||||
@Nullable String newCustomRequestHeaders) {
|
||||
this.oldDotcomAccessToken = oldDotcomAccessToken;
|
||||
this.oldEnterpriseUrl = oldEnterpriseUrl;
|
||||
this.oldEnterpriseAccessToken = oldEnterpriseAccessToken;
|
||||
this.newDotcomAccessToken = newDotcomAccessToken;
|
||||
this.newEnterpriseUrl = newEnterpriseUrl;
|
||||
this.newEnterpriseAccessToken = newEnterpriseAccessToken;
|
||||
this.newCustomRequestHeaders = newCustomRequestHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,59 +2,55 @@ package com.sourcegraph.cody.config;
|
||||
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import javax.swing.*;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Provides controller functionality for application settings.
|
||||
*/
|
||||
/** Provides controller functionality for application settings. */
|
||||
public class ProjectSettingsConfigurable implements Configurable {
|
||||
private final Project project;
|
||||
private SettingsComponent mySettingsComponent;
|
||||
private final Project project;
|
||||
private SettingsComponent mySettingsComponent;
|
||||
|
||||
public ProjectSettingsConfigurable(Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
public ProjectSettingsConfigurable(Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Title)
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Cody (Project)";
|
||||
}
|
||||
@Nls(capitalization = Nls.Capitalization.Title)
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Cody (Project)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return mySettingsComponent.getPreferredFocusedComponent();
|
||||
}
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return mySettingsComponent.getPreferredFocusedComponent();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
mySettingsComponent = new SettingsComponent();
|
||||
return mySettingsComponent.getPanel();
|
||||
}
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
mySettingsComponent = new SettingsComponent();
|
||||
return mySettingsComponent.getPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return SettingsConfigurableHelper.isModified(project, mySettingsComponent);
|
||||
}
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return SettingsConfigurableHelper.isModified(project, mySettingsComponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
SettingsConfigurableHelper.apply(project, mySettingsComponent);
|
||||
}
|
||||
@Override
|
||||
public void apply() {
|
||||
SettingsConfigurableHelper.apply(project, mySettingsComponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
SettingsConfigurableHelper.reset(project, mySettingsComponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
mySettingsComponent.dispose();
|
||||
mySettingsComponent = null;
|
||||
}
|
||||
@Override
|
||||
public void reset() {
|
||||
SettingsConfigurableHelper.reset(project, mySettingsComponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
mySettingsComponent.dispose();
|
||||
mySettingsComponent = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,68 +11,81 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.sourcegraph.cody.telemetry.GraphQlLogger;
|
||||
import java.util.Objects;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Listens to changes in the plugin settings and:
|
||||
* - logs install/uninstall events.
|
||||
* - notifies the user about a successful connection.
|
||||
* Listens to changes in the plugin settings and: - logs install/uninstall events. - notifies the
|
||||
* user about a successful connection.
|
||||
*/
|
||||
public class SettingsChangeListener implements Disposable {
|
||||
private final MessageBusConnection connection;
|
||||
private final MessageBusConnection connection;
|
||||
|
||||
public SettingsChangeListener(@NotNull Project project) {
|
||||
MessageBus bus = project.getMessageBus();
|
||||
public SettingsChangeListener(@NotNull Project project) {
|
||||
MessageBus bus = project.getMessageBus();
|
||||
|
||||
connection = bus.connect();
|
||||
connection.subscribe(PluginSettingChangeActionNotifier.TOPIC, new PluginSettingChangeActionNotifier() {
|
||||
@Override
|
||||
public void beforeAction(@NotNull PluginSettingChangeContext context) {
|
||||
if (!Objects.equals(context.oldEnterpriseUrl, context.newEnterpriseUrl)) {
|
||||
GraphQlLogger.logUninstallEvent(project);
|
||||
ConfigUtil.setInstallEventLogged(false);
|
||||
}
|
||||
connection = bus.connect();
|
||||
connection.subscribe(
|
||||
PluginSettingChangeActionNotifier.TOPIC,
|
||||
new PluginSettingChangeActionNotifier() {
|
||||
@Override
|
||||
public void beforeAction(@NotNull PluginSettingChangeContext context) {
|
||||
if (!Objects.equals(context.oldEnterpriseUrl, context.newEnterpriseUrl)) {
|
||||
GraphQlLogger.logUninstallEvent(project);
|
||||
ConfigUtil.setInstallEventLogged(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterAction(@NotNull PluginSettingChangeContext context) {
|
||||
// Log install events
|
||||
if (!Objects.equals(context.oldEnterpriseUrl, context.newEnterpriseUrl)) {
|
||||
GraphQlLogger.logInstallEvent(project, ConfigUtil::setInstallEventLogged);
|
||||
} else if (!Objects.equals(
|
||||
context.oldEnterpriseAccessToken, context.newEnterpriseAccessToken)
|
||||
&& !ConfigUtil.isInstallEventLogged()) {
|
||||
GraphQlLogger.logInstallEvent(project, ConfigUtil::setInstallEventLogged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterAction(@NotNull PluginSettingChangeContext context) {
|
||||
// Log install events
|
||||
if (!Objects.equals(context.oldEnterpriseUrl, context.newEnterpriseUrl)) {
|
||||
GraphQlLogger.logInstallEvent(project, ConfigUtil::setInstallEventLogged);
|
||||
} else if (!Objects.equals(context.oldEnterpriseAccessToken, context.newEnterpriseAccessToken) && !ConfigUtil.isInstallEventLogged()) {
|
||||
GraphQlLogger.logInstallEvent(project, ConfigUtil::setInstallEventLogged);
|
||||
}
|
||||
|
||||
// Notify user about a successful connection
|
||||
if (context.newEnterpriseUrl != null) {
|
||||
ApiAuthenticator.testConnection(context.newEnterpriseUrl, context.newEnterpriseAccessToken, context.newCustomRequestHeaders, (status) -> {
|
||||
if (ConfigUtil.didAuthenticationFailLastTime() && status == ApiAuthenticator.ConnectionStatus.AUTHENTICATED) {
|
||||
notifyAboutSuccessfulConnection();
|
||||
}
|
||||
ConfigUtil.setAuthenticationFailedLastTime(status != ApiAuthenticator.ConnectionStatus.AUTHENTICATED);
|
||||
});
|
||||
}
|
||||
// Notify user about a successful connection
|
||||
if (context.newEnterpriseUrl != null) {
|
||||
ApiAuthenticator.testConnection(
|
||||
context.newEnterpriseUrl,
|
||||
context.newEnterpriseAccessToken,
|
||||
context.newCustomRequestHeaders,
|
||||
(status) -> {
|
||||
if (ConfigUtil.didAuthenticationFailLastTime()
|
||||
&& status == ApiAuthenticator.ConnectionStatus.AUTHENTICATED) {
|
||||
notifyAboutSuccessfulConnection();
|
||||
}
|
||||
ConfigUtil.setAuthenticationFailedLastTime(
|
||||
status != ApiAuthenticator.ConnectionStatus.AUTHENTICATED);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyAboutSuccessfulConnection() {
|
||||
Notification notification = new Notification("Cody Sourcegraph access", "Cody authentication success",
|
||||
"Cody successfully connected to your Sourcegraph account.", NotificationType.INFORMATION);
|
||||
AnAction dismissAction = new DumbAwareAction("Dismiss") {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
|
||||
notification.expire();
|
||||
}
|
||||
private void notifyAboutSuccessfulConnection() {
|
||||
Notification notification =
|
||||
new Notification(
|
||||
"Cody Sourcegraph access",
|
||||
"Cody authentication success",
|
||||
"Cody successfully connected to your Sourcegraph account.",
|
||||
NotificationType.INFORMATION);
|
||||
AnAction dismissAction =
|
||||
new DumbAwareAction("Dismiss") {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
|
||||
notification.expire();
|
||||
}
|
||||
};
|
||||
notification.addAction(dismissAction);
|
||||
Notifications.Bus.notify(notification);
|
||||
}
|
||||
notification.addAction(dismissAction);
|
||||
Notifications.Bus.notify(notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
connection.disconnect();
|
||||
}
|
||||
@Override
|
||||
public void dispose() {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,181 +11,218 @@ import com.intellij.util.ui.FormBuilder;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.jetbrains.jsonSchema.settings.mappings.JsonSchemaConfigurable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.Enumeration;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Supports creating and managing a {@link JPanel} for the Settings Dialog.
|
||||
*/
|
||||
/** Supports creating and managing a {@link JPanel} for the Settings Dialog. */
|
||||
public class SettingsComponent implements Disposable {
|
||||
private final JPanel panel;
|
||||
private ButtonGroup instanceTypeButtonGroup;
|
||||
private JBLabel dotcomAccessTokenLinkComment;
|
||||
private JBTextField dotcomAccessTokenTextField;
|
||||
private JBTextField enterpriseUrlTextField;
|
||||
private JBTextField enterpriseAccessTokenTextField;
|
||||
private JBLabel userDocsLinkComment;
|
||||
private JBLabel enterpriseAccessTokenLinkComment;
|
||||
private JBTextField customRequestHeadersTextField;
|
||||
private JBTextField codebaseTextField;
|
||||
private JBCheckBox areChatPredictionsEnabledCheckBox;
|
||||
private final JPanel panel;
|
||||
private ButtonGroup instanceTypeButtonGroup;
|
||||
private JBLabel dotcomAccessTokenLinkComment;
|
||||
private JBTextField dotcomAccessTokenTextField;
|
||||
private JBTextField enterpriseUrlTextField;
|
||||
private JBTextField enterpriseAccessTokenTextField;
|
||||
private JBLabel userDocsLinkComment;
|
||||
private JBLabel enterpriseAccessTokenLinkComment;
|
||||
private JBTextField customRequestHeadersTextField;
|
||||
private JBTextField codebaseTextField;
|
||||
private JBCheckBox areChatPredictionsEnabledCheckBox;
|
||||
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return codebaseTextField;
|
||||
}
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return codebaseTextField;
|
||||
}
|
||||
|
||||
public SettingsComponent() {
|
||||
JPanel userAuthenticationPanel = createAuthenticationPanel();
|
||||
JPanel navigationSettingsPanel = createOtherSettingsPanel();
|
||||
public SettingsComponent() {
|
||||
JPanel userAuthenticationPanel = createAuthenticationPanel();
|
||||
JPanel navigationSettingsPanel = createOtherSettingsPanel();
|
||||
|
||||
panel = FormBuilder.createFormBuilder()
|
||||
panel =
|
||||
FormBuilder.createFormBuilder()
|
||||
.addComponent(userAuthenticationPanel)
|
||||
.addComponent(navigationSettingsPanel)
|
||||
.addComponentFillVertically(new JPanel(), 0)
|
||||
.getPanel();
|
||||
}
|
||||
|
||||
public JPanel getPanel() {
|
||||
return panel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public InstanceType getInstanceType() {
|
||||
return instanceTypeButtonGroup
|
||||
.getSelection()
|
||||
.getActionCommand()
|
||||
.equals(InstanceType.DOTCOM.name())
|
||||
? InstanceType.DOTCOM
|
||||
: InstanceType.ENTERPRISE;
|
||||
}
|
||||
|
||||
public void setInstanceType(@NotNull InstanceType instanceType) {
|
||||
for (Enumeration<AbstractButton> buttons = instanceTypeButtonGroup.getElements();
|
||||
buttons.hasMoreElements(); ) {
|
||||
AbstractButton button = buttons.nextElement();
|
||||
|
||||
button.setSelected(button.getActionCommand().equals(instanceType.name()));
|
||||
}
|
||||
|
||||
public JPanel getPanel() {
|
||||
return panel;
|
||||
}
|
||||
setDotcomSettingsEnabled(instanceType == InstanceType.DOTCOM);
|
||||
setEnterpriseSettingsEnabled(instanceType == InstanceType.ENTERPRISE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public InstanceType getInstanceType() {
|
||||
return instanceTypeButtonGroup.getSelection().getActionCommand().equals(InstanceType.DOTCOM.name()) ? InstanceType.DOTCOM : InstanceType.ENTERPRISE;
|
||||
}
|
||||
@NotNull
|
||||
private JPanel createAuthenticationPanel() {
|
||||
|
||||
public void setInstanceType(@NotNull InstanceType instanceType) {
|
||||
for (Enumeration<AbstractButton> buttons = instanceTypeButtonGroup.getElements(); buttons.hasMoreElements(); ) {
|
||||
AbstractButton button = buttons.nextElement();
|
||||
|
||||
button.setSelected(button.getActionCommand().equals(instanceType.name()));
|
||||
}
|
||||
|
||||
setDotcomSettingsEnabled(instanceType == InstanceType.DOTCOM);
|
||||
setEnterpriseSettingsEnabled(instanceType == InstanceType.ENTERPRISE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JPanel createAuthenticationPanel() {
|
||||
|
||||
// Create dotcom access token field
|
||||
JBLabel dotcomAccessTokenLabel = new JBLabel("Access token:");
|
||||
dotcomAccessTokenTextField = new JBTextField();
|
||||
dotcomAccessTokenTextField.getEmptyText().setText("Paste your access token here");
|
||||
addValidation(dotcomAccessTokenTextField, () ->
|
||||
// Create dotcom access token field
|
||||
JBLabel dotcomAccessTokenLabel = new JBLabel("Access token:");
|
||||
dotcomAccessTokenTextField = new JBTextField();
|
||||
dotcomAccessTokenTextField.getEmptyText().setText("Paste your access token here");
|
||||
addValidation(
|
||||
dotcomAccessTokenTextField,
|
||||
() ->
|
||||
isValidAccessToken(dotcomAccessTokenTextField.getText())
|
||||
? null
|
||||
: new ValidationInfo("Invalid access token", dotcomAccessTokenTextField));
|
||||
dotcomAccessTokenLinkComment = new JBLabel("<html><body>Have no token yet? Create one <a href=\"https://sourcegraph.com/user/settings/tokens/new\">here</a>.</body></html>", UIUtil.ComponentStyle.SMALL, UIUtil.FontColor.BRIGHTER);
|
||||
//
|
||||
dotcomAccessTokenLinkComment =
|
||||
new JBLabel(
|
||||
"<html><body>Have no token yet? Create one <a href=\"https://sourcegraph.com/user/settings/tokens/new\">here</a>.</body></html>",
|
||||
UIUtil.ComponentStyle.SMALL,
|
||||
UIUtil.FontColor.BRIGHTER);
|
||||
//
|
||||
|
||||
// Create URL field for the enterprise section
|
||||
JBLabel urlLabel = new JBLabel("Sourcegraph URL:");
|
||||
enterpriseUrlTextField = new JBTextField();
|
||||
//noinspection DialogTitleCapitalization
|
||||
enterpriseUrlTextField.getEmptyText().setText("https://sourcegraph.example.com");
|
||||
enterpriseUrlTextField.setToolTipText("The default is \"" + ConfigUtil.DOTCOM_URL + "\".");
|
||||
addValidation(enterpriseUrlTextField, () ->
|
||||
enterpriseUrlTextField.getText().length() == 0 ? new ValidationInfo("Missing URL", enterpriseUrlTextField)
|
||||
: (!JsonSchemaConfigurable.isValidURL(enterpriseUrlTextField.getText()) ? new ValidationInfo("This is an invalid URL", enterpriseUrlTextField)
|
||||
: null));
|
||||
addDocumentListener(enterpriseUrlTextField, e -> updateAccessTokenLinkCommentText());
|
||||
// Create URL field for the enterprise section
|
||||
JBLabel urlLabel = new JBLabel("Sourcegraph URL:");
|
||||
enterpriseUrlTextField = new JBTextField();
|
||||
//noinspection DialogTitleCapitalization
|
||||
enterpriseUrlTextField.getEmptyText().setText("https://sourcegraph.example.com");
|
||||
enterpriseUrlTextField.setToolTipText("The default is \"" + ConfigUtil.DOTCOM_URL + "\".");
|
||||
addValidation(
|
||||
enterpriseUrlTextField,
|
||||
() ->
|
||||
enterpriseUrlTextField.getText().length() == 0
|
||||
? new ValidationInfo("Missing URL", enterpriseUrlTextField)
|
||||
: (!JsonSchemaConfigurable.isValidURL(enterpriseUrlTextField.getText())
|
||||
? new ValidationInfo("This is an invalid URL", enterpriseUrlTextField)
|
||||
: null));
|
||||
addDocumentListener(enterpriseUrlTextField, e -> updateAccessTokenLinkCommentText());
|
||||
|
||||
// Create enterprise access token field
|
||||
JBLabel enterpriseAccessTokenLabel = new JBLabel("Access token:");
|
||||
enterpriseAccessTokenTextField = new JBTextField();
|
||||
enterpriseAccessTokenTextField.getEmptyText().setText("Paste your access token here");
|
||||
addValidation(enterpriseAccessTokenTextField, () ->
|
||||
// Create enterprise access token field
|
||||
JBLabel enterpriseAccessTokenLabel = new JBLabel("Access token:");
|
||||
enterpriseAccessTokenTextField = new JBTextField();
|
||||
enterpriseAccessTokenTextField.getEmptyText().setText("Paste your access token here");
|
||||
addValidation(
|
||||
enterpriseAccessTokenTextField,
|
||||
() ->
|
||||
isValidAccessToken(enterpriseAccessTokenTextField.getText())
|
||||
? null
|
||||
: new ValidationInfo("Invalid access token", enterpriseAccessTokenTextField));
|
||||
|
||||
// Create comments
|
||||
userDocsLinkComment = new JBLabel("<html><body>You will need an access token to sign in. See our <a href=\"https://docs.sourcegraph.com/cli/how-tos/creating_an_access_token\">user docs</a> for a video guide</body></html>", UIUtil.ComponentStyle.SMALL, UIUtil.FontColor.BRIGHTER);
|
||||
userDocsLinkComment.setBorder(JBUI.Borders.emptyLeft(10));
|
||||
enterpriseAccessTokenLinkComment = new JBLabel("", UIUtil.ComponentStyle.SMALL, UIUtil.FontColor.BRIGHTER);
|
||||
enterpriseAccessTokenLinkComment.setBorder(JBUI.Borders.emptyLeft(10));
|
||||
// Create comments
|
||||
userDocsLinkComment =
|
||||
new JBLabel(
|
||||
"<html><body>You will need an access token to sign in. See our <a href=\"https://docs.sourcegraph.com/cli/how-tos/creating_an_access_token\">user docs</a> for a video guide</body></html>",
|
||||
UIUtil.ComponentStyle.SMALL,
|
||||
UIUtil.FontColor.BRIGHTER);
|
||||
userDocsLinkComment.setBorder(JBUI.Borders.emptyLeft(10));
|
||||
enterpriseAccessTokenLinkComment =
|
||||
new JBLabel("", UIUtil.ComponentStyle.SMALL, UIUtil.FontColor.BRIGHTER);
|
||||
enterpriseAccessTokenLinkComment.setBorder(JBUI.Borders.emptyLeft(10));
|
||||
|
||||
// Set up radio buttons
|
||||
ActionListener actionListener = event -> {
|
||||
String actionCommand = event.getActionCommand();
|
||||
setDotcomSettingsEnabled(actionCommand.equals(InstanceType.DOTCOM.name()));
|
||||
setEnterpriseSettingsEnabled(actionCommand.equals(InstanceType.ENTERPRISE.name()));
|
||||
// Set up radio buttons
|
||||
ActionListener actionListener =
|
||||
event -> {
|
||||
String actionCommand = event.getActionCommand();
|
||||
setDotcomSettingsEnabled(actionCommand.equals(InstanceType.DOTCOM.name()));
|
||||
setEnterpriseSettingsEnabled(actionCommand.equals(InstanceType.ENTERPRISE.name()));
|
||||
};
|
||||
JRadioButton sourcegraphDotComRadioButton = new JRadioButton("Use sourcegraph.com");
|
||||
sourcegraphDotComRadioButton.setMnemonic(KeyEvent.VK_C);
|
||||
sourcegraphDotComRadioButton.setActionCommand(InstanceType.DOTCOM.name());
|
||||
sourcegraphDotComRadioButton.addActionListener(actionListener);
|
||||
JRadioButton enterpriseInstanceRadioButton = new JRadioButton("Use an enterprise instance");
|
||||
enterpriseInstanceRadioButton.setMnemonic(KeyEvent.VK_E);
|
||||
enterpriseInstanceRadioButton.setActionCommand(InstanceType.ENTERPRISE.name());
|
||||
enterpriseInstanceRadioButton.addActionListener(actionListener);
|
||||
instanceTypeButtonGroup = new ButtonGroup();
|
||||
instanceTypeButtonGroup.add(sourcegraphDotComRadioButton);
|
||||
instanceTypeButtonGroup.add(enterpriseInstanceRadioButton);
|
||||
JRadioButton sourcegraphDotComRadioButton = new JRadioButton("Use sourcegraph.com");
|
||||
sourcegraphDotComRadioButton.setMnemonic(KeyEvent.VK_C);
|
||||
sourcegraphDotComRadioButton.setActionCommand(InstanceType.DOTCOM.name());
|
||||
sourcegraphDotComRadioButton.addActionListener(actionListener);
|
||||
JRadioButton enterpriseInstanceRadioButton = new JRadioButton("Use an enterprise instance");
|
||||
enterpriseInstanceRadioButton.setMnemonic(KeyEvent.VK_E);
|
||||
enterpriseInstanceRadioButton.setActionCommand(InstanceType.ENTERPRISE.name());
|
||||
enterpriseInstanceRadioButton.addActionListener(actionListener);
|
||||
instanceTypeButtonGroup = new ButtonGroup();
|
||||
instanceTypeButtonGroup.add(sourcegraphDotComRadioButton);
|
||||
instanceTypeButtonGroup.add(enterpriseInstanceRadioButton);
|
||||
|
||||
// Assemble the two main panels
|
||||
JBLabel dotComComment = new JBLabel("Cody for open source code is available to all users with a Sourcegraph.com account",
|
||||
UIUtil.ComponentStyle.SMALL, UIUtil.FontColor.BRIGHTER);
|
||||
dotComComment.setBorder(JBUI.Borders.emptyLeft(20));
|
||||
JPanel dotcomPanelContent = FormBuilder.createFormBuilder()
|
||||
// Assemble the two main panels
|
||||
JBLabel dotComComment =
|
||||
new JBLabel(
|
||||
"Cody for open source code is available to all users with a Sourcegraph.com account",
|
||||
UIUtil.ComponentStyle.SMALL,
|
||||
UIUtil.FontColor.BRIGHTER);
|
||||
dotComComment.setBorder(JBUI.Borders.emptyLeft(20));
|
||||
JPanel dotcomPanelContent =
|
||||
FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent(dotcomAccessTokenLabel, dotcomAccessTokenTextField, 1)
|
||||
.addComponentToRightColumn(dotcomAccessTokenLinkComment, 1)
|
||||
.getPanel();
|
||||
dotcomPanelContent.setBorder(IdeBorderFactory.createEmptyBorder(JBUI.insets(1, 30, 0, 0)));
|
||||
JPanel dotComPanel = FormBuilder.createFormBuilder()
|
||||
dotcomPanelContent.setBorder(IdeBorderFactory.createEmptyBorder(JBUI.insets(1, 30, 0, 0)));
|
||||
JPanel dotComPanel =
|
||||
FormBuilder.createFormBuilder()
|
||||
.addComponent(sourcegraphDotComRadioButton, 1)
|
||||
.addComponentToRightColumn(dotComComment, 2)
|
||||
.addComponent(dotcomPanelContent, 1)
|
||||
.getPanel();
|
||||
JPanel enterprisePanelContent = FormBuilder.createFormBuilder()
|
||||
JPanel enterprisePanelContent =
|
||||
FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent(urlLabel, enterpriseUrlTextField, 1)
|
||||
.addTooltip("If your company uses a private Sourcegraph instance, set its URL here")
|
||||
.addLabeledComponent(enterpriseAccessTokenLabel, enterpriseAccessTokenTextField, 1)
|
||||
.addComponentToRightColumn(userDocsLinkComment, 1)
|
||||
.addComponentToRightColumn(enterpriseAccessTokenLinkComment, 1)
|
||||
.getPanel();
|
||||
enterprisePanelContent.setBorder(IdeBorderFactory.createEmptyBorder(JBUI.insets(1, 30, 0, 0)));
|
||||
JPanel enterprisePanel = FormBuilder.createFormBuilder()
|
||||
enterprisePanelContent.setBorder(IdeBorderFactory.createEmptyBorder(JBUI.insets(1, 30, 0, 0)));
|
||||
JPanel enterprisePanel =
|
||||
FormBuilder.createFormBuilder()
|
||||
.addComponent(enterpriseInstanceRadioButton, 1)
|
||||
.addComponent(enterprisePanelContent, 1)
|
||||
.getPanel();
|
||||
|
||||
// Create the "Request headers" text box
|
||||
JBLabel customRequestHeadersLabel = new JBLabel("Custom headers:");
|
||||
customRequestHeadersTextField = new JBTextField();
|
||||
customRequestHeadersTextField.getEmptyText().setText("Client-ID, client-one, X-Extra, some metadata");
|
||||
customRequestHeadersTextField.setToolTipText("You can even overwrite \"Authorization\" that Access token sets above.");
|
||||
addValidation(customRequestHeadersTextField, () -> {
|
||||
if (customRequestHeadersTextField.getText().length() == 0) {
|
||||
return null;
|
||||
}
|
||||
String[] pairs = customRequestHeadersTextField.getText().split(",");
|
||||
if (pairs.length % 2 != 0) {
|
||||
return new ValidationInfo("Must be a comma-separated list of pairs", customRequestHeadersTextField);
|
||||
}
|
||||
|
||||
for (int i = 0; i < pairs.length; i += 2) {
|
||||
String headerName = pairs[i].trim();
|
||||
if (!headerName.matches("[\\w-]+")) {
|
||||
return new ValidationInfo("Invalid HTTP header name: " + headerName, customRequestHeadersTextField);
|
||||
}
|
||||
}
|
||||
// Create the "Request headers" text box
|
||||
JBLabel customRequestHeadersLabel = new JBLabel("Custom headers:");
|
||||
customRequestHeadersTextField = new JBTextField();
|
||||
customRequestHeadersTextField
|
||||
.getEmptyText()
|
||||
.setText("Client-ID, client-one, X-Extra, some metadata");
|
||||
customRequestHeadersTextField.setToolTipText(
|
||||
"You can even overwrite \"Authorization\" that Access token sets above.");
|
||||
addValidation(
|
||||
customRequestHeadersTextField,
|
||||
() -> {
|
||||
if (customRequestHeadersTextField.getText().length() == 0) {
|
||||
return null;
|
||||
}
|
||||
String[] pairs = customRequestHeadersTextField.getText().split(",");
|
||||
if (pairs.length % 2 != 0) {
|
||||
return new ValidationInfo(
|
||||
"Must be a comma-separated list of pairs", customRequestHeadersTextField);
|
||||
}
|
||||
|
||||
for (int i = 0; i < pairs.length; i += 2) {
|
||||
String headerName = pairs[i].trim();
|
||||
if (!headerName.matches("[\\w-]+")) {
|
||||
return new ValidationInfo(
|
||||
"Invalid HTTP header name: " + headerName, customRequestHeadersTextField);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// Assemble the main panel
|
||||
JPanel userAuthenticationPanel = FormBuilder.createFormBuilder()
|
||||
// Assemble the main panel
|
||||
JPanel userAuthenticationPanel =
|
||||
FormBuilder.createFormBuilder()
|
||||
.addComponent(dotComPanel)
|
||||
.addComponent(enterprisePanel, 5)
|
||||
.addLabeledComponent(customRequestHeadersLabel, customRequestHeadersTextField)
|
||||
@ -193,162 +230,177 @@ public class SettingsComponent implements Disposable {
|
||||
.addTooltip("Use any number of pairs: \"header1, value1, header2, value2, ...\".")
|
||||
.addTooltip("Whitespace around commas doesn't matter.")
|
||||
.getPanel();
|
||||
userAuthenticationPanel.setBorder(IdeBorderFactory.createTitledBorder("Authentication", true, JBUI.insetsTop(8)));
|
||||
userAuthenticationPanel.setBorder(
|
||||
IdeBorderFactory.createTitledBorder("Authentication", true, JBUI.insetsTop(8)));
|
||||
|
||||
return userAuthenticationPanel;
|
||||
return userAuthenticationPanel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDotcomAccessToken() {
|
||||
return dotcomAccessTokenTextField.getText();
|
||||
}
|
||||
|
||||
public void setDotcomAccessToken(@NotNull String value) {
|
||||
dotcomAccessTokenTextField.setText(value);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getEnterpriseUrl() {
|
||||
return enterpriseUrlTextField.getText();
|
||||
}
|
||||
|
||||
public void setEnterpriseUrl(@Nullable String value) {
|
||||
enterpriseUrlTextField.setText(value != null ? value : "");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getEnterpriseAccessToken() {
|
||||
return enterpriseAccessTokenTextField.getText();
|
||||
}
|
||||
|
||||
public void setEnterpriseAccessToken(@NotNull String value) {
|
||||
enterpriseAccessTokenTextField.setText(value);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getCustomRequestHeaders() {
|
||||
return customRequestHeadersTextField.getText();
|
||||
}
|
||||
|
||||
public void setCustomRequestHeaders(@NotNull String customRequestHeaders) {
|
||||
this.customRequestHeadersTextField.setText(customRequestHeaders);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getCodebase() {
|
||||
return codebaseTextField.getText();
|
||||
}
|
||||
|
||||
public void setCodebase(@NotNull String value) {
|
||||
codebaseTextField.setText(value);
|
||||
}
|
||||
|
||||
public boolean areChatPredictionsEnabled() {
|
||||
return areChatPredictionsEnabledCheckBox != null
|
||||
&& areChatPredictionsEnabledCheckBox.isSelected();
|
||||
}
|
||||
|
||||
public void setAreChatPredictionsEnabled(boolean value) {
|
||||
if (areChatPredictionsEnabledCheckBox != null) {
|
||||
areChatPredictionsEnabledCheckBox.setSelected(value);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDotcomAccessToken() {
|
||||
return dotcomAccessTokenTextField.getText();
|
||||
}
|
||||
private void setDotcomSettingsEnabled(boolean enable) {
|
||||
dotcomAccessTokenTextField.setEnabled(enable);
|
||||
dotcomAccessTokenLinkComment.setEnabled(enable);
|
||||
dotcomAccessTokenLinkComment.setCopyable(enable);
|
||||
}
|
||||
|
||||
public void setDotcomAccessToken(@NotNull String value) {
|
||||
dotcomAccessTokenTextField.setText(value);
|
||||
}
|
||||
private void setEnterpriseSettingsEnabled(boolean enable) {
|
||||
enterpriseUrlTextField.setEnabled(enable);
|
||||
enterpriseAccessTokenTextField.setEnabled(enable);
|
||||
userDocsLinkComment.setEnabled(enable);
|
||||
userDocsLinkComment.setCopyable(enable);
|
||||
enterpriseAccessTokenLinkComment.setEnabled(enable);
|
||||
enterpriseAccessTokenLinkComment.setCopyable(enable);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getEnterpriseUrl() {
|
||||
return enterpriseUrlTextField.getText();
|
||||
}
|
||||
@Override
|
||||
public void dispose() {
|
||||
instanceTypeButtonGroup = null;
|
||||
enterpriseUrlTextField = null;
|
||||
enterpriseAccessTokenTextField = null;
|
||||
customRequestHeadersTextField = null;
|
||||
codebaseTextField = null;
|
||||
areChatPredictionsEnabledCheckBox = null;
|
||||
userDocsLinkComment = null;
|
||||
enterpriseAccessTokenLinkComment = null;
|
||||
}
|
||||
|
||||
public void setEnterpriseUrl(@Nullable String value) {
|
||||
enterpriseUrlTextField.setText(value != null ? value : "");
|
||||
}
|
||||
public enum InstanceType {
|
||||
DOTCOM,
|
||||
ENTERPRISE,
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getEnterpriseAccessToken() {
|
||||
return enterpriseAccessTokenTextField.getText();
|
||||
}
|
||||
private void addValidation(
|
||||
@NotNull JTextComponent component, @NotNull Supplier<ValidationInfo> validator) {
|
||||
new ComponentValidator(this).withValidator(validator).installOn(component);
|
||||
addDocumentListener(
|
||||
component,
|
||||
e -> ComponentValidator.getInstance(component).ifPresent(ComponentValidator::revalidate));
|
||||
}
|
||||
|
||||
public void setEnterpriseAccessToken(@NotNull String value) {
|
||||
enterpriseAccessTokenTextField.setText(value);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getCustomRequestHeaders() {
|
||||
return customRequestHeadersTextField.getText();
|
||||
}
|
||||
|
||||
public void setCustomRequestHeaders(@NotNull String customRequestHeaders) {
|
||||
this.customRequestHeadersTextField.setText(customRequestHeaders);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getCodebase() {
|
||||
return codebaseTextField.getText();
|
||||
}
|
||||
|
||||
public void setCodebase(@NotNull String value) {
|
||||
codebaseTextField.setText(value);
|
||||
}
|
||||
|
||||
public boolean areChatPredictionsEnabled() {
|
||||
return areChatPredictionsEnabledCheckBox != null && areChatPredictionsEnabledCheckBox.isSelected();
|
||||
}
|
||||
|
||||
public void setAreChatPredictionsEnabled(boolean value) {
|
||||
if (areChatPredictionsEnabledCheckBox != null) {
|
||||
areChatPredictionsEnabledCheckBox.setSelected(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDotcomSettingsEnabled(boolean enable) {
|
||||
dotcomAccessTokenTextField.setEnabled(enable);
|
||||
dotcomAccessTokenLinkComment.setEnabled(enable);
|
||||
dotcomAccessTokenLinkComment.setCopyable(enable);
|
||||
}
|
||||
|
||||
private void setEnterpriseSettingsEnabled(boolean enable) {
|
||||
enterpriseUrlTextField.setEnabled(enable);
|
||||
enterpriseAccessTokenTextField.setEnabled(enable);
|
||||
userDocsLinkComment.setEnabled(enable);
|
||||
userDocsLinkComment.setCopyable(enable);
|
||||
enterpriseAccessTokenLinkComment.setEnabled(enable);
|
||||
enterpriseAccessTokenLinkComment.setCopyable(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
instanceTypeButtonGroup = null;
|
||||
enterpriseUrlTextField = null;
|
||||
enterpriseAccessTokenTextField = null;
|
||||
customRequestHeadersTextField = null;
|
||||
codebaseTextField = null;
|
||||
areChatPredictionsEnabledCheckBox = null;
|
||||
userDocsLinkComment = null;
|
||||
enterpriseAccessTokenLinkComment = null;
|
||||
|
||||
}
|
||||
|
||||
public enum InstanceType {
|
||||
DOTCOM,
|
||||
ENTERPRISE,
|
||||
}
|
||||
|
||||
private void addValidation(@NotNull JTextComponent component, @NotNull Supplier<ValidationInfo> validator) {
|
||||
new ComponentValidator(this).withValidator(validator).installOn(component);
|
||||
addDocumentListener(component, e -> ComponentValidator.getInstance(component).ifPresent(ComponentValidator::revalidate));
|
||||
}
|
||||
|
||||
private void addDocumentListener(@NotNull JTextComponent textComponent, @NotNull Consumer<ComponentValidator> function) {
|
||||
textComponent.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
private void addDocumentListener(
|
||||
@NotNull JTextComponent textComponent, @NotNull Consumer<ComponentValidator> function) {
|
||||
textComponent
|
||||
.getDocument()
|
||||
.addDocumentListener(
|
||||
new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
ComponentValidator.getInstance(textComponent).ifPresent(function);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
ComponentValidator.getInstance(textComponent).ifPresent(function);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
ComponentValidator.getInstance(textComponent).ifPresent(function);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateAccessTokenLinkCommentText() {
|
||||
String baseUrl = enterpriseUrlTextField.getText();
|
||||
String settingsUrl = (baseUrl.endsWith("/") ? baseUrl : baseUrl + "/") + "settings";
|
||||
enterpriseAccessTokenLinkComment.setText(isUrlValid(baseUrl)
|
||||
? "<html><body>or go to <a href=\"" + settingsUrl + "\">" + settingsUrl + "</a> | \"Access tokens\" to create one.</body></html>"
|
||||
private void updateAccessTokenLinkCommentText() {
|
||||
String baseUrl = enterpriseUrlTextField.getText();
|
||||
String settingsUrl = (baseUrl.endsWith("/") ? baseUrl : baseUrl + "/") + "settings";
|
||||
enterpriseAccessTokenLinkComment.setText(
|
||||
isUrlValid(baseUrl)
|
||||
? "<html><body>or go to <a href=\""
|
||||
+ settingsUrl
|
||||
+ "\">"
|
||||
+ settingsUrl
|
||||
+ "</a> | \"Access tokens\" to create one.</body></html>"
|
||||
: "");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidAccessToken(@NotNull String accessToken) {
|
||||
return accessToken.isEmpty() ||
|
||||
accessToken.length() == 40 ||
|
||||
(accessToken.startsWith("sgp_") && accessToken.length() == 44);
|
||||
}
|
||||
private boolean isValidAccessToken(@NotNull String accessToken) {
|
||||
return accessToken.isEmpty()
|
||||
|| accessToken.length() == 40
|
||||
|| (accessToken.startsWith("sgp_") && accessToken.length() == 44);
|
||||
}
|
||||
|
||||
private boolean isUrlValid(@NotNull String url) {
|
||||
return JsonSchemaConfigurable.isValidURL(url);
|
||||
}
|
||||
private boolean isUrlValid(@NotNull String url) {
|
||||
return JsonSchemaConfigurable.isValidURL(url);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JPanel createOtherSettingsPanel() {
|
||||
JBLabel codebaseLabel = new JBLabel("Codebase:");
|
||||
codebaseTextField = new JBTextField();
|
||||
//noinspection DialogTitleCapitalization
|
||||
codebaseTextField.getEmptyText().setText("github.com/sourcegraph/sourcegraph");
|
||||
@NotNull
|
||||
private JPanel createOtherSettingsPanel() {
|
||||
JBLabel codebaseLabel = new JBLabel("Codebase:");
|
||||
codebaseTextField = new JBTextField();
|
||||
//noinspection DialogTitleCapitalization
|
||||
codebaseTextField.getEmptyText().setText("github.com/sourcegraph/sourcegraph");
|
||||
|
||||
//// Always disabled for now
|
||||
//areChatPredictionsEnabledCheckBox = new JBCheckBox("Experimental: Chat predictions");
|
||||
//areChatPredictionsEnabledCheckBox.setEnabled(false);
|
||||
//// Always disabled for now
|
||||
// areChatPredictionsEnabledCheckBox = new JBCheckBox("Experimental: Chat predictions");
|
||||
// areChatPredictionsEnabledCheckBox.setEnabled(false);
|
||||
|
||||
//noinspection DialogTitleCapitalization
|
||||
JPanel otherSettingsPanel = FormBuilder.createFormBuilder()
|
||||
//noinspection DialogTitleCapitalization
|
||||
JPanel otherSettingsPanel =
|
||||
FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent(codebaseLabel, codebaseTextField)
|
||||
.addTooltip("The name of the embedded repository that Cody will use to gather context")
|
||||
.addTooltip("for its responses. This is automatically inferred from your Git metadata,")
|
||||
.addTooltip("but you can use this option if you need to override the default.")
|
||||
//.addComponent(areChatPredictionsEnabledCheckBox, 10)
|
||||
//.addTooltip("Adds suggestions of possible relevant messages in the chat window")
|
||||
// .addComponent(areChatPredictionsEnabledCheckBox, 10)
|
||||
// .addTooltip("Adds suggestions of possible relevant messages in the chat window")
|
||||
.getPanel();
|
||||
otherSettingsPanel.setBorder(IdeBorderFactory.createTitledBorder("Other Settings", true, JBUI.insetsTop(8)));
|
||||
return otherSettingsPanel;
|
||||
}
|
||||
otherSettingsPanel.setBorder(
|
||||
IdeBorderFactory.createTitledBorder("Other Settings", true, JBUI.insetsTop(8)));
|
||||
return otherSettingsPanel;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,73 +6,125 @@ import com.intellij.util.messages.MessageBus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Provides controller functionality for application settings.
|
||||
*/
|
||||
/** Provides controller functionality for application settings. */
|
||||
public class SettingsConfigurableHelper {
|
||||
public static boolean isModified(@Nullable Project project, @NotNull SettingsComponent newSettings) {
|
||||
CodyService oldSettings = project != null ? CodyProjectService.getInstance(project) :CodyApplicationService.getInstance();
|
||||
return !newSettings.getInstanceType().toString().equals(oldSettings.getInstanceType() != null ? oldSettings.getInstanceType() : SettingsComponent.InstanceType.DOTCOM.toString())
|
||||
|| !(newSettings.getDotcomAccessToken().equals(oldSettings.getDotcomAccessToken()) || newSettings.getDotcomAccessToken().isEmpty() && oldSettings.getDotcomAccessToken() == null)
|
||||
|| !newSettings.getEnterpriseUrl().equals(oldSettings.getEnterpriseUrl() != null ? oldSettings.getEnterpriseUrl() : "")
|
||||
|| !(newSettings.getEnterpriseAccessToken().equals(oldSettings.getEnterpriseAccessToken()) || newSettings.getEnterpriseAccessToken().isEmpty() && oldSettings.getEnterpriseAccessToken() == null)
|
||||
|| !newSettings.getCustomRequestHeaders().equals(oldSettings.getCustomRequestHeaders() != null ? oldSettings.getCustomRequestHeaders() : "")
|
||||
|| !newSettings.getCodebase().equals(oldSettings.getCodebase() != null ? oldSettings.getCodebase() : "")
|
||||
|| newSettings.areChatPredictionsEnabled() != Boolean.TRUE.equals(oldSettings.areChatPredictionsEnabled());
|
||||
}
|
||||
public static boolean isModified(
|
||||
@Nullable Project project, @NotNull SettingsComponent newSettings) {
|
||||
CodyService oldSettings =
|
||||
project != null
|
||||
? CodyProjectService.getInstance(project)
|
||||
: CodyApplicationService.getInstance();
|
||||
return !newSettings
|
||||
.getInstanceType()
|
||||
.toString()
|
||||
.equals(
|
||||
oldSettings.getInstanceType() != null
|
||||
? oldSettings.getInstanceType()
|
||||
: SettingsComponent.InstanceType.DOTCOM.toString())
|
||||
|| !(newSettings.getDotcomAccessToken().equals(oldSettings.getDotcomAccessToken())
|
||||
|| newSettings.getDotcomAccessToken().isEmpty()
|
||||
&& oldSettings.getDotcomAccessToken() == null)
|
||||
|| !newSettings
|
||||
.getEnterpriseUrl()
|
||||
.equals(oldSettings.getEnterpriseUrl() != null ? oldSettings.getEnterpriseUrl() : "")
|
||||
|| !(newSettings.getEnterpriseAccessToken().equals(oldSettings.getEnterpriseAccessToken())
|
||||
|| newSettings.getEnterpriseAccessToken().isEmpty()
|
||||
&& oldSettings.getEnterpriseAccessToken() == null)
|
||||
|| !newSettings
|
||||
.getCustomRequestHeaders()
|
||||
.equals(
|
||||
oldSettings.getCustomRequestHeaders() != null
|
||||
? oldSettings.getCustomRequestHeaders()
|
||||
: "")
|
||||
|| !newSettings
|
||||
.getCodebase()
|
||||
.equals(oldSettings.getCodebase() != null ? oldSettings.getCodebase() : "")
|
||||
|| newSettings.areChatPredictionsEnabled()
|
||||
!= Boolean.TRUE.equals(oldSettings.areChatPredictionsEnabled());
|
||||
}
|
||||
|
||||
public static void apply(@Nullable Project project, @NotNull SettingsComponent settings) {
|
||||
// Get message bus and publisher
|
||||
MessageBus bus = project != null ? project.getMessageBus() : ApplicationManager.getApplication().getMessageBus();
|
||||
PluginSettingChangeActionNotifier publisher = bus.syncPublisher(PluginSettingChangeActionNotifier.TOPIC);
|
||||
public static void apply(@Nullable Project project, @NotNull SettingsComponent settings) {
|
||||
// Get message bus and publisher
|
||||
MessageBus bus =
|
||||
project != null
|
||||
? project.getMessageBus()
|
||||
: ApplicationManager.getApplication().getMessageBus();
|
||||
PluginSettingChangeActionNotifier publisher =
|
||||
bus.syncPublisher(PluginSettingChangeActionNotifier.TOPIC);
|
||||
|
||||
// Select settings service: application or project
|
||||
CodyService apSettings = project != null ? CodyProjectService.getInstance(project) : CodyApplicationService.getInstance();
|
||||
// Select settings service: application or project
|
||||
CodyService apSettings =
|
||||
project != null
|
||||
? CodyProjectService.getInstance(project)
|
||||
: CodyApplicationService.getInstance();
|
||||
|
||||
// Get old and new settings
|
||||
String oldDotcomAccessToken = ConfigUtil.getDotcomAccessToken(project);
|
||||
String oldEnterpriseUrl = ConfigUtil.getSourcegraphUrl(project);
|
||||
String oldEnterpriseAccessToken = ConfigUtil.getEnterpriseAccessToken(project);
|
||||
String newDotcomAccessToken = settings.getDotcomAccessToken();
|
||||
String newEnterpriseUrl = settings.getEnterpriseUrl();
|
||||
String newEnterpriseAccessToken = settings.getEnterpriseAccessToken();
|
||||
String newCustomRequestHeaders = settings.getCustomRequestHeaders();
|
||||
// Get old and new settings
|
||||
String oldDotcomAccessToken = ConfigUtil.getDotcomAccessToken(project);
|
||||
String oldEnterpriseUrl = ConfigUtil.getSourcegraphUrl(project);
|
||||
String oldEnterpriseAccessToken = ConfigUtil.getEnterpriseAccessToken(project);
|
||||
String newDotcomAccessToken = settings.getDotcomAccessToken();
|
||||
String newEnterpriseUrl = settings.getEnterpriseUrl();
|
||||
String newEnterpriseAccessToken = settings.getEnterpriseAccessToken();
|
||||
String newCustomRequestHeaders = settings.getCustomRequestHeaders();
|
||||
|
||||
// Create context
|
||||
PluginSettingChangeContext context = new PluginSettingChangeContext(oldDotcomAccessToken, oldEnterpriseUrl, oldEnterpriseAccessToken,
|
||||
newEnterpriseUrl, newDotcomAccessToken, newEnterpriseAccessToken, newCustomRequestHeaders);
|
||||
// Create context
|
||||
PluginSettingChangeContext context =
|
||||
new PluginSettingChangeContext(
|
||||
oldDotcomAccessToken,
|
||||
oldEnterpriseUrl,
|
||||
oldEnterpriseAccessToken,
|
||||
newEnterpriseUrl,
|
||||
newDotcomAccessToken,
|
||||
newEnterpriseAccessToken,
|
||||
newCustomRequestHeaders);
|
||||
|
||||
// Notify listeners
|
||||
publisher.beforeAction(context);
|
||||
// Notify listeners
|
||||
publisher.beforeAction(context);
|
||||
|
||||
// Update settings
|
||||
String instanceTypeName = settings.getInstanceType().name();
|
||||
apSettings.setInstanceType(instanceTypeName.equals(SettingsComponent.InstanceType.ENTERPRISE.name()) || !newDotcomAccessToken.equals("")
|
||||
? instanceTypeName : null);
|
||||
apSettings.setDotcomAccessToken(!newDotcomAccessToken.equals("") ? newDotcomAccessToken : null);
|
||||
apSettings.setEnterpriseUrl(!newEnterpriseUrl.equals("") ? newEnterpriseUrl : null);
|
||||
apSettings.setEnterpriseAccessToken(!newEnterpriseAccessToken.equals("") ? newEnterpriseAccessToken : null);
|
||||
apSettings.setCustomRequestHeaders(settings.getCustomRequestHeaders());
|
||||
apSettings.setCodebase(!settings.getCodebase().equals("") ? settings.getCodebase() : null);
|
||||
apSettings.setChatPredictionsEnabled(settings.areChatPredictionsEnabled());
|
||||
// Update settings
|
||||
String instanceTypeName = settings.getInstanceType().name();
|
||||
apSettings.setInstanceType(
|
||||
instanceTypeName.equals(SettingsComponent.InstanceType.ENTERPRISE.name())
|
||||
|| !newDotcomAccessToken.equals("")
|
||||
? instanceTypeName
|
||||
: null);
|
||||
apSettings.setDotcomAccessToken(!newDotcomAccessToken.equals("") ? newDotcomAccessToken : null);
|
||||
apSettings.setEnterpriseUrl(!newEnterpriseUrl.equals("") ? newEnterpriseUrl : null);
|
||||
apSettings.setEnterpriseAccessToken(
|
||||
!newEnterpriseAccessToken.equals("") ? newEnterpriseAccessToken : null);
|
||||
apSettings.setCustomRequestHeaders(settings.getCustomRequestHeaders());
|
||||
apSettings.setCodebase(!settings.getCodebase().equals("") ? settings.getCodebase() : null);
|
||||
apSettings.setChatPredictionsEnabled(settings.areChatPredictionsEnabled());
|
||||
|
||||
// Notify listeners
|
||||
publisher.afterAction(context);
|
||||
}
|
||||
// Notify listeners
|
||||
publisher.afterAction(context);
|
||||
}
|
||||
|
||||
public static void reset(@Nullable Project project, @NotNull SettingsComponent mySettingsComponent) {
|
||||
CodyService settings = project != null ? CodyProjectService.getInstance(project) :CodyApplicationService.getInstance();
|
||||
String instanceType = settings.getInstanceType();
|
||||
mySettingsComponent.setInstanceType(instanceType != null ? instanceType.equals(SettingsComponent.InstanceType.ENTERPRISE.name())
|
||||
? SettingsComponent.InstanceType.ENTERPRISE : SettingsComponent.InstanceType.DOTCOM : SettingsComponent.InstanceType.DOTCOM);
|
||||
String dotcomAccessToken = settings.getDotcomAccessToken();
|
||||
mySettingsComponent.setDotcomAccessToken(dotcomAccessToken != null ? dotcomAccessToken : "");
|
||||
mySettingsComponent.setEnterpriseUrl(settings.getEnterpriseUrl());
|
||||
String enterpriseAccessToken = settings.getEnterpriseAccessToken();
|
||||
mySettingsComponent.setEnterpriseAccessToken(enterpriseAccessToken != null ? enterpriseAccessToken : "");
|
||||
mySettingsComponent.setCustomRequestHeaders(settings.getCustomRequestHeaders() != null ? settings.getCustomRequestHeaders() : "");
|
||||
String codebase = settings.getCodebase();
|
||||
mySettingsComponent.setCodebase(codebase != null ? codebase : "");
|
||||
mySettingsComponent.setAreChatPredictionsEnabled(settings.areChatPredictionsEnabled() != null && Boolean.TRUE.equals(settings.areChatPredictionsEnabled()));
|
||||
}
|
||||
public static void reset(
|
||||
@Nullable Project project, @NotNull SettingsComponent mySettingsComponent) {
|
||||
CodyService settings =
|
||||
project != null
|
||||
? CodyProjectService.getInstance(project)
|
||||
: CodyApplicationService.getInstance();
|
||||
String instanceType = settings.getInstanceType();
|
||||
mySettingsComponent.setInstanceType(
|
||||
instanceType != null
|
||||
? instanceType.equals(SettingsComponent.InstanceType.ENTERPRISE.name())
|
||||
? SettingsComponent.InstanceType.ENTERPRISE
|
||||
: SettingsComponent.InstanceType.DOTCOM
|
||||
: SettingsComponent.InstanceType.DOTCOM);
|
||||
String dotcomAccessToken = settings.getDotcomAccessToken();
|
||||
mySettingsComponent.setDotcomAccessToken(dotcomAccessToken != null ? dotcomAccessToken : "");
|
||||
mySettingsComponent.setEnterpriseUrl(settings.getEnterpriseUrl());
|
||||
String enterpriseAccessToken = settings.getEnterpriseAccessToken();
|
||||
mySettingsComponent.setEnterpriseAccessToken(
|
||||
enterpriseAccessToken != null ? enterpriseAccessToken : "");
|
||||
mySettingsComponent.setCustomRequestHeaders(
|
||||
settings.getCustomRequestHeaders() != null ? settings.getCustomRequestHeaders() : "");
|
||||
String codebase = settings.getCodebase();
|
||||
mySettingsComponent.setCodebase(codebase != null ? codebase : "");
|
||||
mySettingsComponent.setAreChatPredictionsEnabled(
|
||||
settings.areChatPredictionsEnabled() != null
|
||||
&& Boolean.TRUE.equals(settings.areChatPredictionsEnabled()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,25 +4,26 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ContextFile {
|
||||
private final @NotNull String fileName;
|
||||
private final @Nullable String repoName;
|
||||
private final @Nullable String revision;
|
||||
private final @NotNull String fileName;
|
||||
private final @Nullable String repoName;
|
||||
private final @Nullable String revision;
|
||||
|
||||
public ContextFile(@NotNull String fileName, @Nullable String repoName, @Nullable String revision) {
|
||||
this.fileName = fileName;
|
||||
this.repoName = repoName;
|
||||
this.revision = revision;
|
||||
}
|
||||
public ContextFile(
|
||||
@NotNull String fileName, @Nullable String repoName, @Nullable String revision) {
|
||||
this.fileName = fileName;
|
||||
this.repoName = repoName;
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
public @NotNull String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
public @NotNull String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public @Nullable String getRepoName() {
|
||||
return repoName;
|
||||
}
|
||||
public @Nullable String getRepoName() {
|
||||
return repoName;
|
||||
}
|
||||
|
||||
public @Nullable String getRevision() {
|
||||
return revision;
|
||||
}
|
||||
public @Nullable String getRevision() {
|
||||
return revision;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,27 +1,29 @@
|
||||
package com.sourcegraph.cody.context;
|
||||
|
||||
import com.sourcegraph.cody.context.embeddings.EmbeddingsSearcher;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
// TODO: Use this class to get context
|
||||
public class ContextGetter {
|
||||
private final @NotNull String codebase;
|
||||
private final @NotNull String codebase;
|
||||
|
||||
public ContextGetter(@NotNull String codebase) {
|
||||
this.codebase = codebase;
|
||||
}
|
||||
public ContextGetter(@NotNull String codebase) {
|
||||
this.codebase = codebase;
|
||||
}
|
||||
|
||||
public @NotNull List<ContextMessage> getContextMessages(@NotNull String query, int codeResultCount, int textResultCount, @NotNull String useContext) throws IOException {
|
||||
if (useContext.equals("embeddings")) {
|
||||
return EmbeddingsSearcher.getContextMessages(codebase, query, codeResultCount, textResultCount);
|
||||
} else {
|
||||
// TODO: Add keyword search if embeddings are not available
|
||||
//return KeywordSearcher.getContextMessages(query, codeResultCount, textResultCount);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
public @NotNull List<ContextMessage> getContextMessages(
|
||||
@NotNull String query, int codeResultCount, int textResultCount, @NotNull String useContext)
|
||||
throws IOException {
|
||||
if (useContext.equals("embeddings")) {
|
||||
return EmbeddingsSearcher.getContextMessages(
|
||||
codebase, query, codeResultCount, textResultCount);
|
||||
} else {
|
||||
// TODO: Add keyword search if embeddings are not available
|
||||
// return KeywordSearcher.getContextMessages(query, codeResultCount, textResultCount);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,23 +6,24 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ContextMessage extends Message {
|
||||
@Nullable
|
||||
private final ContextFile file;
|
||||
@Nullable private final ContextFile file;
|
||||
|
||||
public ContextMessage(@NotNull Speaker speaker, @NotNull String text, @Nullable ContextFile file) {
|
||||
super(speaker, text);
|
||||
this.file = file;
|
||||
}
|
||||
public ContextMessage(
|
||||
@NotNull Speaker speaker, @NotNull String text, @Nullable ContextFile file) {
|
||||
super(speaker, text);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public @Nullable ContextFile getFile() {
|
||||
return file;
|
||||
}
|
||||
public @Nullable ContextFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public static @NotNull ContextMessage createHumanMessage(@NotNull String text, @NotNull ContextFile file) {
|
||||
return new ContextMessage(Speaker.HUMAN, text, file);
|
||||
}
|
||||
public static @NotNull ContextMessage createHumanMessage(
|
||||
@NotNull String text, @NotNull ContextFile file) {
|
||||
return new ContextMessage(Speaker.HUMAN, text, file);
|
||||
}
|
||||
|
||||
public static @NotNull ContextMessage createDefaultAssistantMessage() {
|
||||
return new ContextMessage(Speaker.ASSISTANT, "Ok.", null);
|
||||
}
|
||||
public static @NotNull ContextMessage createDefaultAssistantMessage() {
|
||||
return new ContextMessage(Speaker.ASSISTANT, "Ok.", null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,43 +4,49 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class EmbeddingsSearchResult {
|
||||
private final @Nullable String repoName;
|
||||
private final @Nullable String revision;
|
||||
private final @NotNull String fileName;
|
||||
private final int startLine;
|
||||
private final int endLine;
|
||||
private final @NotNull String content;
|
||||
private final @Nullable String repoName;
|
||||
private final @Nullable String revision;
|
||||
private final @NotNull String fileName;
|
||||
private final int startLine;
|
||||
private final int endLine;
|
||||
private final @NotNull String content;
|
||||
|
||||
public EmbeddingsSearchResult(@Nullable String repoName, @Nullable String revision, @NotNull String fileName, int startLine, int endLine, @NotNull String content) {
|
||||
this.repoName = repoName;
|
||||
this.revision = revision;
|
||||
this.fileName = fileName;
|
||||
this.startLine = startLine;
|
||||
this.endLine = endLine;
|
||||
this.content = content;
|
||||
}
|
||||
public EmbeddingsSearchResult(
|
||||
@Nullable String repoName,
|
||||
@Nullable String revision,
|
||||
@NotNull String fileName,
|
||||
int startLine,
|
||||
int endLine,
|
||||
@NotNull String content) {
|
||||
this.repoName = repoName;
|
||||
this.revision = revision;
|
||||
this.fileName = fileName;
|
||||
this.startLine = startLine;
|
||||
this.endLine = endLine;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public @Nullable String getRepoName() {
|
||||
return repoName;
|
||||
}
|
||||
public @Nullable String getRepoName() {
|
||||
return repoName;
|
||||
}
|
||||
|
||||
public @Nullable String getRevision() {
|
||||
return revision;
|
||||
}
|
||||
public @Nullable String getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public @NotNull String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
public @NotNull String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public int getStartLine() {
|
||||
return startLine;
|
||||
}
|
||||
public int getStartLine() {
|
||||
return startLine;
|
||||
}
|
||||
|
||||
public int getEndLine() {
|
||||
return endLine;
|
||||
}
|
||||
public int getEndLine() {
|
||||
return endLine;
|
||||
}
|
||||
|
||||
public @NotNull String getContent() {
|
||||
return content;
|
||||
}
|
||||
public @NotNull String getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +1,33 @@
|
||||
package com.sourcegraph.cody.context.embeddings;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EmbeddingsSearchResults {
|
||||
private @NotNull List<EmbeddingsSearchResult> codeResults;
|
||||
private @NotNull List<EmbeddingsSearchResult> textResults;
|
||||
private @NotNull List<EmbeddingsSearchResult> codeResults;
|
||||
private @NotNull List<EmbeddingsSearchResult> textResults;
|
||||
|
||||
public EmbeddingsSearchResults(@NotNull List<EmbeddingsSearchResult> codeResults, @NotNull List<EmbeddingsSearchResult> textResults) {
|
||||
this.codeResults = codeResults;
|
||||
this.textResults = textResults;
|
||||
}
|
||||
public EmbeddingsSearchResults(
|
||||
@NotNull List<EmbeddingsSearchResult> codeResults,
|
||||
@NotNull List<EmbeddingsSearchResult> textResults) {
|
||||
this.codeResults = codeResults;
|
||||
this.textResults = textResults;
|
||||
}
|
||||
|
||||
// Getters and setters
|
||||
public @NotNull List<EmbeddingsSearchResult> getCodeResults() {
|
||||
return codeResults;
|
||||
}
|
||||
// Getters and setters
|
||||
public @NotNull List<EmbeddingsSearchResult> getCodeResults() {
|
||||
return codeResults;
|
||||
}
|
||||
|
||||
public void setCodeResults(@NotNull List<EmbeddingsSearchResult> codeResults) {
|
||||
this.codeResults = codeResults;
|
||||
}
|
||||
public void setCodeResults(@NotNull List<EmbeddingsSearchResult> codeResults) {
|
||||
this.codeResults = codeResults;
|
||||
}
|
||||
|
||||
public @NotNull List<EmbeddingsSearchResult> getTextResults() {
|
||||
return textResults;
|
||||
}
|
||||
public @NotNull List<EmbeddingsSearchResult> getTextResults() {
|
||||
return textResults;
|
||||
}
|
||||
|
||||
public void setTextResults(@NotNull List<EmbeddingsSearchResult> textResults) {
|
||||
this.textResults = textResults;
|
||||
}
|
||||
public void setTextResults(@NotNull List<EmbeddingsSearchResult> textResults) {
|
||||
this.textResults = textResults;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,162 +6,179 @@ import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.sourcegraph.api.GraphQlClient;
|
||||
import com.sourcegraph.api.GraphQlResponse;
|
||||
import com.sourcegraph.cody.prompts.Prompter;
|
||||
import com.sourcegraph.cody.context.ContextMessage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.sourcegraph.cody.prompts.Prompter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class EmbeddingsSearcher {
|
||||
public static @NotNull List<ContextMessage> getContextMessages(@NotNull String codebase, @NotNull String query, int codeResultCount, int textResultCount) throws IOException {
|
||||
// Get repo ID
|
||||
String repoId;
|
||||
repoId = EmbeddingsSearcher.getRepoIdIfEmbeddingExists(codebase);
|
||||
public static @NotNull List<ContextMessage> getContextMessages(
|
||||
@NotNull String codebase, @NotNull String query, int codeResultCount, int textResultCount)
|
||||
throws IOException {
|
||||
// Get repo ID
|
||||
String repoId;
|
||||
repoId = EmbeddingsSearcher.getRepoIdIfEmbeddingExists(codebase);
|
||||
|
||||
// Run embeddings search
|
||||
EmbeddingsSearchResults results = EmbeddingsSearcher.search(repoId, query, codeResultCount, textResultCount);
|
||||
// Run embeddings search
|
||||
EmbeddingsSearchResults results =
|
||||
EmbeddingsSearcher.search(repoId, query, codeResultCount, textResultCount);
|
||||
|
||||
// Concat results.getCodeResults() and results.getTextResults() into a single list
|
||||
List<EmbeddingsSearchResult> allResults = new ArrayList<>();
|
||||
allResults.addAll(results.getCodeResults());
|
||||
allResults.addAll(results.getTextResults());
|
||||
// Concat results.getCodeResults() and results.getTextResults() into a single list
|
||||
List<EmbeddingsSearchResult> allResults = new ArrayList<>();
|
||||
allResults.addAll(results.getCodeResults());
|
||||
allResults.addAll(results.getTextResults());
|
||||
|
||||
// Group results by file
|
||||
List<GroupedResults> groupedResults = ResultsGrouper.groupResultsByFile(allResults);
|
||||
// Group results by file
|
||||
List<GroupedResults> groupedResults = ResultsGrouper.groupResultsByFile(allResults);
|
||||
|
||||
// Reverse results so that they appear in ascending order of importance (least -> most)
|
||||
Collections.reverse(groupedResults);
|
||||
// Reverse results so that they appear in ascending order of importance (least -> most)
|
||||
Collections.reverse(groupedResults);
|
||||
|
||||
// Get context messages
|
||||
List<ContextMessage> messages = new ArrayList<>();
|
||||
for (GroupedResults group : groupedResults) {
|
||||
for (String snippet : group.getSnippets()) {
|
||||
String contextPrompt = Prompter.getContextPrompt(group.getFile().getFileName(), snippet);
|
||||
messages.add(ContextMessage.createHumanMessage(contextPrompt, group.getFile()));
|
||||
messages.add(ContextMessage.createDefaultAssistantMessage());
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
// Get context messages
|
||||
List<ContextMessage> messages = new ArrayList<>();
|
||||
for (GroupedResults group : groupedResults) {
|
||||
for (String snippet : group.getSnippets()) {
|
||||
String contextPrompt = Prompter.getContextPrompt(group.getFile().getFileName(), snippet);
|
||||
messages.add(ContextMessage.createHumanMessage(contextPrompt, group.getFile()));
|
||||
messages.add(ContextMessage.createDefaultAssistantMessage());
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
private static EmbeddingsSearchResults search(@NotNull String repoId, @NotNull String query, int codeResultsCount, int textResultsCount) throws IOException {
|
||||
// Prepare GraphQL query
|
||||
String graphQlQuery = "query LegacyEmbeddingsSearch($repo: ID!, $query: String!, $codeResultsCount: Int!, $textResultsCount: Int!) {\n" +
|
||||
" embeddingsSearch(repo: $repo, query: $query, codeResultsCount: $codeResultsCount, textResultsCount: $textResultsCount) {\n" +
|
||||
" codeResults {\n" +
|
||||
" fileName\n" +
|
||||
" startLine\n" +
|
||||
" endLine\n" +
|
||||
" content\n" +
|
||||
" }\n" +
|
||||
" textResults {\n" +
|
||||
" fileName\n" +
|
||||
" startLine\n" +
|
||||
" endLine\n" +
|
||||
" content\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
JsonObject variables = new JsonObject();
|
||||
variables.add("repo", new JsonPrimitive(repoId));
|
||||
variables.add("query", new JsonPrimitive(query));
|
||||
variables.add("codeResultsCount", new JsonPrimitive(codeResultsCount));
|
||||
variables.add("textResultsCount", new JsonPrimitive(textResultsCount));
|
||||
private static EmbeddingsSearchResults search(
|
||||
@NotNull String repoId, @NotNull String query, int codeResultsCount, int textResultsCount)
|
||||
throws IOException {
|
||||
// Prepare GraphQL query
|
||||
String graphQlQuery =
|
||||
"query LegacyEmbeddingsSearch($repo: ID!, $query: String!, $codeResultsCount: Int!, $textResultsCount: Int!) {\n"
|
||||
+ " embeddingsSearch(repo: $repo, query: $query, codeResultsCount: $codeResultsCount, textResultsCount: $textResultsCount) {\n"
|
||||
+ " codeResults {\n"
|
||||
+ " fileName\n"
|
||||
+ " startLine\n"
|
||||
+ " endLine\n"
|
||||
+ " content\n"
|
||||
+ " }\n"
|
||||
+ " textResults {\n"
|
||||
+ " fileName\n"
|
||||
+ " startLine\n"
|
||||
+ " endLine\n"
|
||||
+ " content\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}";
|
||||
JsonObject variables = new JsonObject();
|
||||
variables.add("repo", new JsonPrimitive(repoId));
|
||||
variables.add("query", new JsonPrimitive(query));
|
||||
variables.add("codeResultsCount", new JsonPrimitive(codeResultsCount));
|
||||
variables.add("textResultsCount", new JsonPrimitive(textResultsCount));
|
||||
|
||||
// Call GraphQL service
|
||||
GraphQlResponse response = GraphQlClient.callGraphQLService("TODO", "TODO", "TODO", graphQlQuery, variables); // TODO!
|
||||
// Call GraphQL service
|
||||
GraphQlResponse response =
|
||||
GraphQlClient.callGraphQLService("TODO", "TODO", "TODO", graphQlQuery, variables); // TODO!
|
||||
|
||||
// Parse response
|
||||
if (response.getStatusCode() != 200) {
|
||||
throw new IOException("GraphQL request failed with status code " + response.getStatusCode());
|
||||
// Parse response
|
||||
if (response.getStatusCode() != 200) {
|
||||
throw new IOException("GraphQL request failed with status code " + response.getStatusCode());
|
||||
} else {
|
||||
try {
|
||||
JsonObject body = response.getBodyAsJson();
|
||||
if (body.has("errors")) {
|
||||
throw new IOException("GraphQL request failed with errors: " + body.get("errors"));
|
||||
}
|
||||
JsonObject data = body.getAsJsonObject("data");
|
||||
if (data == null) {
|
||||
throw new IOException("GraphQL response is missing data field");
|
||||
}
|
||||
JsonObject embeddingsSearch = data.getAsJsonObject("embeddingsSearch");
|
||||
if (embeddingsSearch == null) {
|
||||
throw new IOException("GraphQL response is missing data.embeddingsSearch field");
|
||||
}
|
||||
|
||||
ArrayList<EmbeddingsSearchResult> codeResults =
|
||||
convertRawResultsToSearchResults(embeddingsSearch.getAsJsonObject("codeResults"));
|
||||
ArrayList<EmbeddingsSearchResult> textResults =
|
||||
convertRawResultsToSearchResults(embeddingsSearch.getAsJsonObject("textResults"));
|
||||
return new EmbeddingsSearchResults(codeResults, textResults);
|
||||
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new IOException("GraphQL response is not valid JSON", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts raw results from a GraphQL response to a list of EmbeddingsSearchResult objects. This
|
||||
* works for both code and text results.
|
||||
*/
|
||||
private static @NotNull ArrayList<EmbeddingsSearchResult> convertRawResultsToSearchResults(
|
||||
@Nullable JsonObject rawResults) {
|
||||
if (rawResults == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
ArrayList<EmbeddingsSearchResult> results = new ArrayList<>();
|
||||
for (JsonElement result : rawResults.getAsJsonArray()) {
|
||||
JsonPrimitive repoName = ((JsonObject) result).getAsJsonPrimitive("repoName");
|
||||
JsonPrimitive revision = ((JsonObject) result).getAsJsonPrimitive("revision");
|
||||
String fileName = ((JsonObject) result).getAsJsonPrimitive("fileName").getAsString();
|
||||
int startLine = ((JsonObject) result).getAsJsonPrimitive("startLine").getAsInt();
|
||||
int endLine = ((JsonObject) result).getAsJsonPrimitive("endLine").getAsInt();
|
||||
String content = ((JsonObject) result).getAsJsonPrimitive("content").getAsString();
|
||||
results.add(
|
||||
new EmbeddingsSearchResult(
|
||||
repoName != null ? repoName.toString() : null,
|
||||
revision != null ? revision.toString() : null,
|
||||
fileName,
|
||||
startLine,
|
||||
endLine,
|
||||
content));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repository ID if the repository exists and has an embedding, or null otherwise.
|
||||
*
|
||||
* @param repoName Like "github.com/sourcegraph/cody"
|
||||
* @return base64-encoded repoID like "UmVwb3NpdG9yeTozNjgwOTI1MA=="
|
||||
* @throws IOException Thrown if we can't reach the server.
|
||||
*/
|
||||
private static @NotNull String getRepoIdIfEmbeddingExists(String repoName) throws IOException {
|
||||
String query =
|
||||
"query Repository($name: String!) {\n"
|
||||
+ " repository(name: $name) {\n"
|
||||
+ " id\n"
|
||||
+ " embeddingExists\n"
|
||||
+ " }\n"
|
||||
+ "}";
|
||||
JsonObject variables = new JsonObject();
|
||||
variables.add("name", new JsonPrimitive(repoName));
|
||||
GraphQlResponse response =
|
||||
GraphQlClient.callGraphQLService("TODO", "TODO", "TODO", query, variables);
|
||||
if (response.getStatusCode() != 200) {
|
||||
throw new IOException("GraphQL request failed with status code " + response.getStatusCode());
|
||||
} else {
|
||||
try {
|
||||
JsonObject body = response.getBodyAsJson();
|
||||
JsonObject data = body.getAsJsonObject("data");
|
||||
JsonObject repository = data.getAsJsonObject("repository");
|
||||
if (repository == null) {
|
||||
throw new IOException("GraphQL response is missing data.repository field");
|
||||
} else {
|
||||
try {
|
||||
JsonObject body = response.getBodyAsJson();
|
||||
if (body.has("errors")) {
|
||||
throw new IOException("GraphQL request failed with errors: " + body.get("errors"));
|
||||
}
|
||||
JsonObject data = body.getAsJsonObject("data");
|
||||
if (data == null) {
|
||||
throw new IOException("GraphQL response is missing data field");
|
||||
}
|
||||
JsonObject embeddingsSearch = data.getAsJsonObject("embeddingsSearch");
|
||||
if (embeddingsSearch == null) {
|
||||
throw new IOException("GraphQL response is missing data.embeddingsSearch field");
|
||||
}
|
||||
|
||||
ArrayList<EmbeddingsSearchResult> codeResults = convertRawResultsToSearchResults(embeddingsSearch.getAsJsonObject("codeResults"));
|
||||
ArrayList<EmbeddingsSearchResult> textResults = convertRawResultsToSearchResults(embeddingsSearch.getAsJsonObject("textResults"));
|
||||
return new EmbeddingsSearchResults(codeResults, textResults);
|
||||
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new IOException("GraphQL response is not valid JSON", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts raw results from a GraphQL response to a list of EmbeddingsSearchResult objects.
|
||||
* This works for both code and text results.
|
||||
*/
|
||||
private static @NotNull ArrayList<EmbeddingsSearchResult> convertRawResultsToSearchResults(@Nullable JsonObject rawResults) {
|
||||
if (rawResults == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
ArrayList<EmbeddingsSearchResult> results = new ArrayList<>();
|
||||
for (JsonElement result : rawResults.getAsJsonArray()) {
|
||||
JsonPrimitive repoName = ((JsonObject) result).getAsJsonPrimitive("repoName");
|
||||
JsonPrimitive revision = ((JsonObject) result).getAsJsonPrimitive("revision");
|
||||
String fileName = ((JsonObject) result).getAsJsonPrimitive("fileName").getAsString();
|
||||
int startLine = ((JsonObject) result).getAsJsonPrimitive("startLine").getAsInt();
|
||||
int endLine = ((JsonObject) result).getAsJsonPrimitive("endLine").getAsInt();
|
||||
String content = ((JsonObject) result).getAsJsonPrimitive("content").getAsString();
|
||||
results.add(new EmbeddingsSearchResult(repoName != null ? repoName.toString() : null, revision != null ? revision.toString() : null,
|
||||
fileName, startLine, endLine, content));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repository ID if the repository exists and has an embedding, or null otherwise.
|
||||
*
|
||||
* @param repoName Like "github.com/sourcegraph/cody"
|
||||
* @return base64-encoded repoID like "UmVwb3NpdG9yeTozNjgwOTI1MA=="
|
||||
* @throws IOException Thrown if we can't reach the server.
|
||||
*/
|
||||
private static @NotNull String getRepoIdIfEmbeddingExists(String repoName) throws IOException {
|
||||
String query = "query Repository($name: String!) {\n" +
|
||||
" repository(name: $name) {\n" +
|
||||
" id\n" +
|
||||
" embeddingExists\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
JsonObject variables = new JsonObject();
|
||||
variables.add("name", new JsonPrimitive(repoName));
|
||||
GraphQlResponse response = GraphQlClient.callGraphQLService("TODO", "TODO", "TODO", query, variables);
|
||||
if (response.getStatusCode() != 200) {
|
||||
throw new IOException("GraphQL request failed with status code " + response.getStatusCode());
|
||||
} else {
|
||||
try {
|
||||
JsonObject body = response.getBodyAsJson();
|
||||
JsonObject data = body.getAsJsonObject("data");
|
||||
JsonObject repository = data.getAsJsonObject("repository");
|
||||
if (repository == null) {
|
||||
throw new IOException("GraphQL response is missing data.repository field");
|
||||
} else {
|
||||
boolean embeddingExists = repository.getAsJsonPrimitive("embeddingExists").getAsBoolean();
|
||||
if (embeddingExists) {
|
||||
return repository.getAsJsonPrimitive("id").getAsString();
|
||||
} else {
|
||||
throw new IOException("Repository does not have an embedding");
|
||||
}
|
||||
}
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new IOException("GraphQL response is not valid JSON", e);
|
||||
}
|
||||
boolean embeddingExists = repository.getAsJsonPrimitive("embeddingExists").getAsBoolean();
|
||||
if (embeddingExists) {
|
||||
return repository.getAsJsonPrimitive("id").getAsString();
|
||||
} else {
|
||||
throw new IOException("Repository does not have an embedding");
|
||||
}
|
||||
}
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new IOException("GraphQL response is not valid JSON", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,24 +1,23 @@
|
||||
package com.sourcegraph.cody.context.embeddings;
|
||||
|
||||
import com.sourcegraph.cody.context.ContextFile;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GroupedResults {
|
||||
private final @NotNull ContextFile file;
|
||||
private final @NotNull List<String> snippets;
|
||||
private final @NotNull ContextFile file;
|
||||
private final @NotNull List<String> snippets;
|
||||
|
||||
public GroupedResults(@NotNull ContextFile file, @NotNull List<String> snippets) {
|
||||
this.file = file;
|
||||
this.snippets = snippets;
|
||||
}
|
||||
public GroupedResults(@NotNull ContextFile file, @NotNull List<String> snippets) {
|
||||
this.file = file;
|
||||
this.snippets = snippets;
|
||||
}
|
||||
|
||||
public @NotNull ContextFile getFile() {
|
||||
return file;
|
||||
}
|
||||
public @NotNull ContextFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public @NotNull List<String> getSnippets() {
|
||||
return snippets;
|
||||
}
|
||||
public @NotNull List<String> getSnippets() {
|
||||
return snippets;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,64 +1,71 @@
|
||||
package com.sourcegraph.cody.context.embeddings;
|
||||
|
||||
import com.sourcegraph.cody.context.ContextFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ResultsGrouper {
|
||||
public static @NotNull List<GroupedResults> groupResultsByFile(@NotNull List<EmbeddingsSearchResult> results) {
|
||||
List<ContextFile> originalFileOrder = new ArrayList<>();
|
||||
for (EmbeddingsSearchResult result : results) {
|
||||
boolean found = false;
|
||||
for (ContextFile ogFile : originalFileOrder) {
|
||||
if (ogFile.getFileName().equals(result.getFileName())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
originalFileOrder.add(new ContextFile(result.getFileName(), result.getRepoName(), result.getRevision()));
|
||||
}
|
||||
public static @NotNull List<GroupedResults> groupResultsByFile(
|
||||
@NotNull List<EmbeddingsSearchResult> results) {
|
||||
List<ContextFile> originalFileOrder = new ArrayList<>();
|
||||
for (EmbeddingsSearchResult result : results) {
|
||||
boolean found = false;
|
||||
for (ContextFile ogFile : originalFileOrder) {
|
||||
if (ogFile.getFileName().equals(result.getFileName())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Map<String, List<EmbeddingsSearchResult>> resultsGroupedByFile = new HashMap<>();
|
||||
for (EmbeddingsSearchResult result : results) {
|
||||
List<EmbeddingsSearchResult> groupedResults = resultsGroupedByFile.get(result.getFileName());
|
||||
if (groupedResults == null) {
|
||||
groupedResults = new ArrayList<>();
|
||||
groupedResults.add(result);
|
||||
resultsGroupedByFile.put(result.getFileName(), groupedResults);
|
||||
} else {
|
||||
groupedResults.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
return originalFileOrder.stream()
|
||||
.map(file -> new GroupedResults(file, mergeConsecutiveResults(resultsGroupedByFile.get(file.getFileName()))))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
if (!found) {
|
||||
originalFileOrder.add(
|
||||
new ContextFile(result.getFileName(), result.getRepoName(), result.getRevision()));
|
||||
}
|
||||
}
|
||||
|
||||
private static @NotNull List<String> mergeConsecutiveResults(@NotNull List<EmbeddingsSearchResult> results) {
|
||||
List<EmbeddingsSearchResult> sortedResults = results.stream()
|
||||
Map<String, List<EmbeddingsSearchResult>> resultsGroupedByFile = new HashMap<>();
|
||||
for (EmbeddingsSearchResult result : results) {
|
||||
List<EmbeddingsSearchResult> groupedResults = resultsGroupedByFile.get(result.getFileName());
|
||||
if (groupedResults == null) {
|
||||
groupedResults = new ArrayList<>();
|
||||
groupedResults.add(result);
|
||||
resultsGroupedByFile.put(result.getFileName(), groupedResults);
|
||||
} else {
|
||||
groupedResults.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
return originalFileOrder.stream()
|
||||
.map(
|
||||
file ->
|
||||
new GroupedResults(
|
||||
file, mergeConsecutiveResults(resultsGroupedByFile.get(file.getFileName()))))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static @NotNull List<String> mergeConsecutiveResults(
|
||||
@NotNull List<EmbeddingsSearchResult> results) {
|
||||
List<EmbeddingsSearchResult> sortedResults =
|
||||
results.stream()
|
||||
.sorted(Comparator.comparingInt(EmbeddingsSearchResult::getStartLine))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<String> mergedResults = new ArrayList<>();
|
||||
mergedResults.add(sortedResults.get(0).getContent());
|
||||
List<String> mergedResults = new ArrayList<>();
|
||||
mergedResults.add(sortedResults.get(0).getContent());
|
||||
|
||||
for (int i = 1; i < sortedResults.size(); i++) {
|
||||
EmbeddingsSearchResult result = sortedResults.get(i);
|
||||
EmbeddingsSearchResult previousResult = sortedResults.get(i - 1);
|
||||
for (int i = 1; i < sortedResults.size(); i++) {
|
||||
EmbeddingsSearchResult result = sortedResults.get(i);
|
||||
EmbeddingsSearchResult previousResult = sortedResults.get(i - 1);
|
||||
|
||||
if (result.getStartLine() == previousResult.getEndLine()) {
|
||||
mergedResults.set(mergedResults.size() - 1, mergedResults.get(mergedResults.size() - 1) + result.getContent());
|
||||
} else {
|
||||
mergedResults.add(result.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
return mergedResults;
|
||||
if (result.getStartLine() == previousResult.getEndLine()) {
|
||||
mergedResults.set(
|
||||
mergedResults.size() - 1,
|
||||
mergedResults.get(mergedResults.size() - 1) + result.getContent());
|
||||
} else {
|
||||
mergedResults.add(result.getContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mergedResults;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,60 +1,63 @@
|
||||
package com.sourcegraph.cody.editor;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class EditorContext {
|
||||
@Nullable
|
||||
private final String currentFileName;
|
||||
@Nullable
|
||||
private final String currentFileContent;
|
||||
@Nullable
|
||||
private final String precedingText;
|
||||
@Nullable
|
||||
private final String selection;
|
||||
@Nullable
|
||||
private final String followingText;
|
||||
@Nullable private final String currentFileName;
|
||||
@Nullable private final String currentFileContent;
|
||||
@Nullable private final String precedingText;
|
||||
@Nullable private final String selection;
|
||||
@Nullable private final String followingText;
|
||||
|
||||
public EditorContext() {
|
||||
this(null, null, null, null,null);
|
||||
}
|
||||
public EditorContext() {
|
||||
this(null, null, null, null, null);
|
||||
}
|
||||
|
||||
public EditorContext(@Nullable String currentFileName, @Nullable String currentFileContent, @Nullable String precedingText, @Nullable String selection, @Nullable String followingText) {
|
||||
this.currentFileName = currentFileName;
|
||||
this.currentFileContent = currentFileContent;
|
||||
this.precedingText = precedingText;
|
||||
this.selection = selection;
|
||||
this.followingText = followingText;
|
||||
}
|
||||
public EditorContext(
|
||||
@Nullable String currentFileName,
|
||||
@Nullable String currentFileContent,
|
||||
@Nullable String precedingText,
|
||||
@Nullable String selection,
|
||||
@Nullable String followingText) {
|
||||
this.currentFileName = currentFileName;
|
||||
this.currentFileContent = currentFileContent;
|
||||
this.precedingText = precedingText;
|
||||
this.selection = selection;
|
||||
this.followingText = followingText;
|
||||
}
|
||||
|
||||
public @Nullable String getCurrentFileName() {
|
||||
return currentFileName;
|
||||
}
|
||||
public @Nullable String getCurrentFileName() {
|
||||
return currentFileName;
|
||||
}
|
||||
|
||||
public @Nullable String getCurrentFileExtension() {
|
||||
return currentFileName != null ? currentFileName.substring(currentFileName.lastIndexOf(".") + 1) : null;
|
||||
}
|
||||
public @Nullable String getCurrentFileExtension() {
|
||||
return currentFileName != null
|
||||
? currentFileName.substring(currentFileName.lastIndexOf(".") + 1)
|
||||
: null;
|
||||
}
|
||||
|
||||
public @Nullable String getCurrentFileContent() {
|
||||
return currentFileContent;
|
||||
}
|
||||
public @Nullable String getCurrentFileContent() {
|
||||
return currentFileContent;
|
||||
}
|
||||
|
||||
public @Nullable String getPrecedingText() {
|
||||
return precedingText;
|
||||
}
|
||||
public @Nullable String getPrecedingText() {
|
||||
return precedingText;
|
||||
}
|
||||
|
||||
public @Nullable String getSelection() {
|
||||
return selection;
|
||||
}
|
||||
public @Nullable String getSelection() {
|
||||
return selection;
|
||||
}
|
||||
|
||||
public @Nullable String getFollowingText() {
|
||||
return followingText;
|
||||
}
|
||||
public @Nullable String getFollowingText() {
|
||||
return followingText;
|
||||
}
|
||||
|
||||
public @NotNull ArrayList<String> getCurrentFileContentAsArrayList() {
|
||||
return currentFileContent != null ? new ArrayList<>(Collections.singletonList(this.getCurrentFileContent())) : new ArrayList<>();
|
||||
}
|
||||
public @NotNull ArrayList<String> getCurrentFileContentAsArrayList() {
|
||||
return currentFileContent != null
|
||||
? new ArrayList<>(Collections.singletonList(this.getCurrentFileContent()))
|
||||
: new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,38 +11,48 @@ import com.sourcegraph.cody.TruncationUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EditorContextGetter {
|
||||
@NotNull
|
||||
public static EditorContext getEditorContext(@NotNull Project project) {
|
||||
Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (editor == null) {
|
||||
return new EditorContext();
|
||||
}
|
||||
@NotNull Document currentDocument = editor.getDocument();
|
||||
VirtualFile currentFile = FileDocumentManager.getInstance().getFile(currentDocument);
|
||||
if (currentFile == null) {
|
||||
return new EditorContext();
|
||||
}
|
||||
|
||||
// Get preceding text
|
||||
int startLine = Math.max(0, currentDocument.getLineNumber(editor.getSelectionModel().getSelectionStart()) - TruncationUtils.SURROUNDING_LINES);
|
||||
int precedingTextStartOffset = currentDocument.getLineStartOffset(startLine);
|
||||
int precedingTextEndOffset = editor.getSelectionModel().getSelectionStart();
|
||||
String precedingText = currentDocument.getText(new TextRange(precedingTextStartOffset, precedingTextEndOffset));
|
||||
|
||||
// Get selection
|
||||
String selection = editor.getSelectionModel().getSelectedText();
|
||||
|
||||
// Get following text
|
||||
int endLine = Math.min(currentDocument.getLineCount() - 1, currentDocument.getLineNumber(editor.getSelectionModel().getSelectionEnd()) + TruncationUtils.SURROUNDING_LINES);
|
||||
int followingTextStartOffset = editor.getSelectionModel().getSelectionEnd();
|
||||
int followingTextEndOffset = currentDocument.getLineEndOffset(endLine);
|
||||
String followingText = currentDocument.getText(new TextRange(followingTextStartOffset, followingTextEndOffset));
|
||||
|
||||
return new EditorContext(
|
||||
currentFile.getName(),
|
||||
currentDocument.getText(),
|
||||
precedingText,
|
||||
selection == null || selection.isEmpty() ? null : selection,
|
||||
followingText);
|
||||
@NotNull
|
||||
public static EditorContext getEditorContext(@NotNull Project project) {
|
||||
Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (editor == null) {
|
||||
return new EditorContext();
|
||||
}
|
||||
@NotNull Document currentDocument = editor.getDocument();
|
||||
VirtualFile currentFile = FileDocumentManager.getInstance().getFile(currentDocument);
|
||||
if (currentFile == null) {
|
||||
return new EditorContext();
|
||||
}
|
||||
|
||||
// Get preceding text
|
||||
int startLine =
|
||||
Math.max(
|
||||
0,
|
||||
currentDocument.getLineNumber(editor.getSelectionModel().getSelectionStart())
|
||||
- TruncationUtils.SURROUNDING_LINES);
|
||||
int precedingTextStartOffset = currentDocument.getLineStartOffset(startLine);
|
||||
int precedingTextEndOffset = editor.getSelectionModel().getSelectionStart();
|
||||
String precedingText =
|
||||
currentDocument.getText(new TextRange(precedingTextStartOffset, precedingTextEndOffset));
|
||||
|
||||
// Get selection
|
||||
String selection = editor.getSelectionModel().getSelectedText();
|
||||
|
||||
// Get following text
|
||||
int endLine =
|
||||
Math.min(
|
||||
currentDocument.getLineCount() - 1,
|
||||
currentDocument.getLineNumber(editor.getSelectionModel().getSelectionEnd())
|
||||
+ TruncationUtils.SURROUNDING_LINES);
|
||||
int followingTextStartOffset = editor.getSelectionModel().getSelectionEnd();
|
||||
int followingTextEndOffset = currentDocument.getLineEndOffset(endLine);
|
||||
String followingText =
|
||||
currentDocument.getText(new TextRange(followingTextStartOffset, followingTextEndOffset));
|
||||
|
||||
return new EditorContext(
|
||||
currentFile.getName(),
|
||||
currentDocument.getText(),
|
||||
precedingText,
|
||||
selection == null || selection.isEmpty() ? null : selection,
|
||||
followingText);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,50 +1,49 @@
|
||||
package com.sourcegraph.cody.prompts;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class LanguageUtils {
|
||||
|
||||
private static final @NotNull Map<String, String> EXTENSION_TO_LANGUAGE;
|
||||
private static final @NotNull Map<String, String> EXTENSION_TO_LANGUAGE;
|
||||
|
||||
static {
|
||||
EXTENSION_TO_LANGUAGE = new HashMap<>();
|
||||
EXTENSION_TO_LANGUAGE.put("py", "Python");
|
||||
EXTENSION_TO_LANGUAGE.put("rb", "Ruby");
|
||||
EXTENSION_TO_LANGUAGE.put("md", "Markdown");
|
||||
EXTENSION_TO_LANGUAGE.put("php", "PHP");
|
||||
EXTENSION_TO_LANGUAGE.put("js", "Javascript");
|
||||
EXTENSION_TO_LANGUAGE.put("ts", "Typescript");
|
||||
EXTENSION_TO_LANGUAGE.put("jsx", "JSX");
|
||||
EXTENSION_TO_LANGUAGE.put("tsx", "TSX");
|
||||
static {
|
||||
EXTENSION_TO_LANGUAGE = new HashMap<>();
|
||||
EXTENSION_TO_LANGUAGE.put("py", "Python");
|
||||
EXTENSION_TO_LANGUAGE.put("rb", "Ruby");
|
||||
EXTENSION_TO_LANGUAGE.put("md", "Markdown");
|
||||
EXTENSION_TO_LANGUAGE.put("php", "PHP");
|
||||
EXTENSION_TO_LANGUAGE.put("js", "Javascript");
|
||||
EXTENSION_TO_LANGUAGE.put("ts", "Typescript");
|
||||
EXTENSION_TO_LANGUAGE.put("jsx", "JSX");
|
||||
EXTENSION_TO_LANGUAGE.put("tsx", "TSX");
|
||||
}
|
||||
|
||||
public static @NotNull String getNormalizedLanguageName(@Nullable String extension) {
|
||||
if (extension == null || extension.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
public static @NotNull String getNormalizedLanguageName(@Nullable String extension) {
|
||||
if (extension == null || extension.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String language = EXTENSION_TO_LANGUAGE.get(extension);
|
||||
if (language == null) {
|
||||
return extension.substring(0, 1).toUpperCase() + extension.substring(1);
|
||||
}
|
||||
|
||||
return language;
|
||||
String language = EXTENSION_TO_LANGUAGE.get(extension);
|
||||
if (language == null) {
|
||||
return extension.substring(0, 1).toUpperCase() + extension.substring(1);
|
||||
}
|
||||
|
||||
public static boolean isMarkdownFile(@NotNull String filePath) {
|
||||
String extension = getExtension(filePath);
|
||||
return extension.equals("md") || extension.equals("markdown");
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
public static @NotNull String getExtension(@NotNull String filePath) {
|
||||
int lastDotIndex = filePath.lastIndexOf('.');
|
||||
if (lastDotIndex == -1) {
|
||||
return "";
|
||||
}
|
||||
return filePath.substring(lastDotIndex + 1);
|
||||
public static boolean isMarkdownFile(@NotNull String filePath) {
|
||||
String extension = getExtension(filePath);
|
||||
return extension.equals("md") || extension.equals("markdown");
|
||||
}
|
||||
|
||||
public static @NotNull String getExtension(@NotNull String filePath) {
|
||||
int lastDotIndex = filePath.lastIndexOf('.');
|
||||
if (lastDotIndex == -1) {
|
||||
return "";
|
||||
}
|
||||
return filePath.substring(lastDotIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,52 +2,64 @@ package com.sourcegraph.cody.prompts;
|
||||
|
||||
import com.sourcegraph.cody.completions.Message;
|
||||
import com.sourcegraph.cody.completions.Speaker;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Preamble {
|
||||
|
||||
private static final String actions = "You are Cody, an AI-powered coding assistant created by Sourcegraph. You work inside a text editor. You have access to my currently open files. You perform the following actions:\n" +
|
||||
"- Answer general programming questions.\n" +
|
||||
"- Answer questions about the code that I have provided to you.\n" +
|
||||
"- Generate code that matches a written description.\n" +
|
||||
"- Explain what a section of code does.";
|
||||
private static final String actions =
|
||||
"You are Cody, an AI-powered coding assistant created by Sourcegraph. You work inside a text editor. You have access to my currently open files. You perform the following actions:\n"
|
||||
+ "- Answer general programming questions.\n"
|
||||
+ "- Answer questions about the code that I have provided to you.\n"
|
||||
+ "- Generate code that matches a written description.\n"
|
||||
+ "- Explain what a section of code does.";
|
||||
|
||||
private static final String rules = "In your responses, obey the following rules:\n" +
|
||||
"- Be as brief and concise as possible without losing clarity.\n" +
|
||||
"- All code snippets have to be markdown-formatted, and placed in-between triple backticks like this ```.\n" +
|
||||
"- Answer questions only if you know the answer or can make a well-informed guess. Otherwise, tell me you don't know and what context I need to provide you for you to answer the question.\n" +
|
||||
"- Only reference file names or URLs if you are sure they exist.";
|
||||
private static final String rules =
|
||||
"In your responses, obey the following rules:\n"
|
||||
+ "- Be as brief and concise as possible without losing clarity.\n"
|
||||
+ "- All code snippets have to be markdown-formatted, and placed in-between triple backticks like this ```.\n"
|
||||
+ "- Answer questions only if you know the answer or can make a well-informed guess. Otherwise, tell me you don't know and what context I need to provide you for you to answer the question.\n"
|
||||
+ "- Only reference file names or URLs if you are sure they exist.";
|
||||
|
||||
private static final String answer = "Understood. I am Cody, an AI assistant made by Sourcegraph to help with programming tasks.\n" +
|
||||
"I work inside a text editor. I have access to your currently open files in the editor.\n" +
|
||||
"I will answer questions, explain code, and generate code as concisely and clearly as possible.\n" +
|
||||
"My responses will be formatted using Markdown syntax for code blocks.\n" +
|
||||
"I will acknowledge when I don't know an answer or need more context.";
|
||||
private static final String answer =
|
||||
"Understood. I am Cody, an AI assistant made by Sourcegraph to help with programming tasks.\n"
|
||||
+ "I work inside a text editor. I have access to your currently open files in the editor.\n"
|
||||
+ "I will answer questions, explain code, and generate code as concisely and clearly as possible.\n"
|
||||
+ "My responses will be formatted using Markdown syntax for code blocks.\n"
|
||||
+ "I will acknowledge when I don't know an answer or need more context.";
|
||||
|
||||
public static List<Message> getPreamble(@Nullable String codebase) {
|
||||
List<String> preamble = new ArrayList<>();
|
||||
preamble.add(actions);
|
||||
preamble.add(rules);
|
||||
public static List<Message> getPreamble(@Nullable String codebase) {
|
||||
List<String> preamble = new ArrayList<>();
|
||||
preamble.add(actions);
|
||||
preamble.add(rules);
|
||||
|
||||
List<String> preambleResponse = new ArrayList<>();
|
||||
preambleResponse.add(answer);
|
||||
List<String> preambleResponse = new ArrayList<>();
|
||||
preambleResponse.add(answer);
|
||||
|
||||
// If we have a codebase, add a preamble about it
|
||||
if (codebase != null) {
|
||||
String codebasePreamble = "You have access to the `" + codebase + "` repository. You are able to answer questions about the `" + codebase + "` repository. " +
|
||||
"I will provide the relevant code snippets from the `" + codebase + "` repository when necessary to answer my questions.";
|
||||
// If we have a codebase, add a preamble about it
|
||||
if (codebase != null) {
|
||||
String codebasePreamble =
|
||||
"You have access to the `"
|
||||
+ codebase
|
||||
+ "` repository. You are able to answer questions about the `"
|
||||
+ codebase
|
||||
+ "` repository. "
|
||||
+ "I will provide the relevant code snippets from the `"
|
||||
+ codebase
|
||||
+ "` repository when necessary to answer my questions.";
|
||||
|
||||
preamble.add(codebasePreamble);
|
||||
preambleResponse.add("I have access to the `" + codebase + "` repository and can answer questions about its files.");
|
||||
}
|
||||
|
||||
// Return this as a list of two items
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message(Speaker.HUMAN, String.join("\n\n", preamble)));
|
||||
messages.add(new Message(Speaker.ASSISTANT, String.join("\n", preambleResponse)));
|
||||
return messages;
|
||||
preamble.add(codebasePreamble);
|
||||
preambleResponse.add(
|
||||
"I have access to the `"
|
||||
+ codebase
|
||||
+ "` repository and can answer questions about its files.");
|
||||
}
|
||||
|
||||
// Return this as a list of two items
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message(Speaker.HUMAN, String.join("\n\n", preamble)));
|
||||
messages.add(new Message(Speaker.ASSISTANT, String.join("\n", preambleResponse)));
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,32 +4,49 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class Prompter {
|
||||
|
||||
private static final @NotNull String CURRENT_EDITOR_CODE_TEMPLATE = "I have the `{filePath}` file opened in my editor. ";
|
||||
private static final @NotNull String CURRENT_EDITOR_SELECTED_CODE_TEMPLATE = "I am currently looking at this part of the code from `{filePath}`. ";
|
||||
private static final @NotNull String CODE_CONTEXT_TEMPLATE = "Use following code snippet from file `{filePath}`:\n```{language}\n{text}\n```";
|
||||
private static final @NotNull String TEXT_CONTEXT_TEMPLATE = "Use the following text from file `{filePath}`:\n{text}";
|
||||
private static final @NotNull String CURRENT_EDITOR_CODE_TEMPLATE =
|
||||
"I have the `{filePath}` file opened in my editor. ";
|
||||
private static final @NotNull String CURRENT_EDITOR_SELECTED_CODE_TEMPLATE =
|
||||
"I am currently looking at this part of the code from `{filePath}`. ";
|
||||
private static final @NotNull String CODE_CONTEXT_TEMPLATE =
|
||||
"Use following code snippet from file `{filePath}`:\n```{language}\n{text}\n```";
|
||||
private static final @NotNull String TEXT_CONTEXT_TEMPLATE =
|
||||
"Use the following text from file `{filePath}`:\n{text}";
|
||||
|
||||
public static @NotNull String getCurrentEditorCodePrompt(@NotNull String filePath, @NotNull String code) {
|
||||
String context = LanguageUtils.isMarkdownFile(filePath) ? getTextContextPrompt(filePath, code) : getCodeContextPrompt(filePath, code);
|
||||
return CURRENT_EDITOR_CODE_TEMPLATE.replace("{filePath}", filePath) + context;
|
||||
}
|
||||
public static @NotNull String getCurrentEditorCodePrompt(
|
||||
@NotNull String filePath, @NotNull String code) {
|
||||
String context =
|
||||
LanguageUtils.isMarkdownFile(filePath)
|
||||
? getTextContextPrompt(filePath, code)
|
||||
: getCodeContextPrompt(filePath, code);
|
||||
return CURRENT_EDITOR_CODE_TEMPLATE.replace("{filePath}", filePath) + context;
|
||||
}
|
||||
|
||||
public static @NotNull String getCurrentEditorSelectedCode(@NotNull String filePath, @NotNull String code) {
|
||||
String context = LanguageUtils.isMarkdownFile(filePath) ? getTextContextPrompt(filePath, code) : getCodeContextPrompt(filePath, code);
|
||||
return CURRENT_EDITOR_SELECTED_CODE_TEMPLATE.replace("{filePath}", filePath) + context;
|
||||
}
|
||||
public static @NotNull String getCurrentEditorSelectedCode(
|
||||
@NotNull String filePath, @NotNull String code) {
|
||||
String context =
|
||||
LanguageUtils.isMarkdownFile(filePath)
|
||||
? getTextContextPrompt(filePath, code)
|
||||
: getCodeContextPrompt(filePath, code);
|
||||
return CURRENT_EDITOR_SELECTED_CODE_TEMPLATE.replace("{filePath}", filePath) + context;
|
||||
}
|
||||
|
||||
public static @NotNull String getContextPrompt(@NotNull String filePath, @NotNull String code) {
|
||||
return LanguageUtils.isMarkdownFile(filePath) ? getTextContextPrompt(filePath, code) : getCodeContextPrompt(filePath, code);
|
||||
}
|
||||
public static @NotNull String getContextPrompt(@NotNull String filePath, @NotNull String code) {
|
||||
return LanguageUtils.isMarkdownFile(filePath)
|
||||
? getTextContextPrompt(filePath, code)
|
||||
: getCodeContextPrompt(filePath, code);
|
||||
}
|
||||
|
||||
public static @NotNull String getCodeContextPrompt(@NotNull String filePath, @NotNull String code) {
|
||||
return CODE_CONTEXT_TEMPLATE.replace("{filePath}", filePath)
|
||||
.replace("{language}", LanguageUtils.getNormalizedLanguageName(filePath))
|
||||
.replace("{text}", code);
|
||||
}
|
||||
public static @NotNull String getCodeContextPrompt(
|
||||
@NotNull String filePath, @NotNull String code) {
|
||||
return CODE_CONTEXT_TEMPLATE
|
||||
.replace("{filePath}", filePath)
|
||||
.replace("{language}", LanguageUtils.getNormalizedLanguageName(filePath))
|
||||
.replace("{text}", code);
|
||||
}
|
||||
|
||||
public static @NotNull String getTextContextPrompt(@NotNull String filePath, @NotNull String text) {
|
||||
return TEXT_CONTEXT_TEMPLATE.replace("{filePath}", filePath).replace("{text}", text);
|
||||
}
|
||||
public static @NotNull String getTextContextPrompt(
|
||||
@NotNull String filePath, @NotNull String text) {
|
||||
return TEXT_CONTEXT_TEMPLATE.replace("{filePath}", filePath).replace("{text}", text);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,106 +7,93 @@ import com.sourcegraph.cody.chat.ChatMessage;
|
||||
import com.sourcegraph.cody.editor.EditorContext;
|
||||
import com.sourcegraph.cody.editor.EditorContextGetter;
|
||||
import com.sourcegraph.cody.prompts.LanguageUtils;
|
||||
import java.util.ArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class RecipeRunner {
|
||||
private final @NotNull Project project;
|
||||
private final @NotNull UpdatableChat chat;
|
||||
private final @NotNull Project project;
|
||||
private final @NotNull UpdatableChat chat;
|
||||
|
||||
public RecipeRunner(@NotNull Project project, @NotNull UpdatableChat chat) {
|
||||
public RecipeRunner(@NotNull Project project, @NotNull UpdatableChat chat) {
|
||||
|
||||
this.project = project;
|
||||
this.chat = chat;
|
||||
this.project = project;
|
||||
this.chat = chat;
|
||||
}
|
||||
|
||||
private String getMarkdownFormatPrompt() {
|
||||
return "Enclose code snippets with three backticks like so: ```.";
|
||||
}
|
||||
|
||||
public void runExplainCodeDetailed() {
|
||||
EditorContext editorContext = EditorContextGetter.getEditorContext(project);
|
||||
if (editorContext.getSelection() == null) {
|
||||
chat.addMessage(
|
||||
ChatMessage.createAssistantMessage(
|
||||
"No code selected. Please select some code and try again."));
|
||||
return;
|
||||
}
|
||||
String languageName =
|
||||
LanguageUtils.getNormalizedLanguageName(editorContext.getCurrentFileExtension());
|
||||
|
||||
private String getMarkdownFormatPrompt() {
|
||||
return "Enclose code snippets with three backticks like so: ```.";
|
||||
}
|
||||
String truncatedSelectedText =
|
||||
TruncationUtils.truncateText(
|
||||
editorContext.getSelection(), TruncationUtils.MAX_RECIPE_INPUT_TOKENS);
|
||||
String truncatedPrecedingText =
|
||||
editorContext.getPrecedingText() != null
|
||||
? TruncationUtils.truncateTextStart(
|
||||
editorContext.getPrecedingText(), TruncationUtils.MAX_RECIPE_SURROUNDING_TOKENS)
|
||||
: "";
|
||||
String truncatedFollowingText =
|
||||
editorContext.getFollowingText() != null
|
||||
? TruncationUtils.truncateText(
|
||||
editorContext.getFollowingText(), TruncationUtils.MAX_RECIPE_SURROUNDING_TOKENS)
|
||||
: "";
|
||||
|
||||
public void runExplainCodeDetailed() {
|
||||
EditorContext editorContext = EditorContextGetter.getEditorContext(project);
|
||||
if (editorContext.getSelection() == null) {
|
||||
chat.addMessage(ChatMessage.createAssistantMessage("No code selected. Please select some code and try again."));
|
||||
return;
|
||||
}
|
||||
String languageName = LanguageUtils.getNormalizedLanguageName(editorContext.getCurrentFileExtension());
|
||||
|
||||
String truncatedSelectedText = TruncationUtils.truncateText(editorContext.getSelection(), TruncationUtils.MAX_RECIPE_INPUT_TOKENS);
|
||||
String truncatedPrecedingText = editorContext.getPrecedingText() != null ? TruncationUtils.truncateTextStart(editorContext.getPrecedingText(), TruncationUtils.MAX_RECIPE_SURROUNDING_TOKENS) : "";
|
||||
String truncatedFollowingText = editorContext.getFollowingText() != null ? TruncationUtils.truncateText(editorContext.getFollowingText(), TruncationUtils.MAX_RECIPE_SURROUNDING_TOKENS) : "";
|
||||
|
||||
String promptMessage = String.format(
|
||||
String promptMessage =
|
||||
String.format(
|
||||
"Please explain the following %s code. Be very detailed and specific, and indicate when it is not clear to you what is going on. Format your response as an ordered list.\n```\n%s\n```\n%s",
|
||||
languageName,
|
||||
truncatedSelectedText,
|
||||
getMarkdownFormatPrompt()
|
||||
);
|
||||
languageName, truncatedSelectedText, getMarkdownFormatPrompt());
|
||||
|
||||
String displayText = String.format(
|
||||
"Explain the following code:\n```\n%s\n```",
|
||||
editorContext.getSelection()
|
||||
);
|
||||
String displayText =
|
||||
String.format("Explain the following code:\n```\n%s\n```", editorContext.getSelection());
|
||||
|
||||
// return new Interaction(
|
||||
// { speaker: 'human', text: promptMessage, displayText },
|
||||
// { speaker: 'assistant' },
|
||||
// getContextMessagesFromSelection(
|
||||
// truncatedSelectedText,
|
||||
// truncatedPrecedingText,
|
||||
// truncatedFollowingText,
|
||||
// selection.fileName,
|
||||
// context.codebaseContext
|
||||
// )
|
||||
ChatMessage humanMessage = ChatMessage.createHumanMessage(promptMessage, new ArrayList<>());
|
||||
|
||||
chat.addMessage(humanMessage);
|
||||
}
|
||||
|
||||
// return new Interaction(
|
||||
// { speaker: 'human', text: promptMessage, displayText },
|
||||
// { speaker: 'assistant' },
|
||||
// getContextMessagesFromSelection(
|
||||
// truncatedSelectedText,
|
||||
// truncatedPrecedingText,
|
||||
// truncatedFollowingText,
|
||||
// selection.fileName,
|
||||
// context.codebaseContext
|
||||
// )
|
||||
ChatMessage humanMessage = ChatMessage.createHumanMessage(promptMessage, new ArrayList<>());
|
||||
// private ArrayList<ChatMessage> getContextMessagesFromSelection(EditorContext editorContext)
|
||||
// {
|
||||
// return ChatMessage.createHumanMessage(editorContext, new ArrayList<String>());
|
||||
// }
|
||||
|
||||
chat.addMessage(humanMessage);
|
||||
}
|
||||
public void runExplainCodeHighLevel() {}
|
||||
|
||||
// private ArrayList<ChatMessage> getContextMessagesFromSelection(EditorContext editorContext) {
|
||||
// return ChatMessage.createHumanMessage(editorContext, new ArrayList<String>());
|
||||
// }
|
||||
public void runGenerateUnitTest() {}
|
||||
|
||||
public void runExplainCodeHighLevel() {
|
||||
public void runGenerateDocstring() {}
|
||||
|
||||
}
|
||||
public void runImproveVariableNames() {}
|
||||
|
||||
public void runGenerateUnitTest() {
|
||||
public void runTranslateToLanguage() {}
|
||||
|
||||
}
|
||||
public void runGitHistory() {}
|
||||
|
||||
public void runGenerateDocstring() {
|
||||
public void runFindCodeSmells() {}
|
||||
|
||||
}
|
||||
public void runFixup() {}
|
||||
|
||||
public void runImproveVariableNames() {
|
||||
public void runContextSearch() {}
|
||||
|
||||
}
|
||||
|
||||
public void runTranslateToLanguage() {
|
||||
|
||||
}
|
||||
|
||||
public void runGitHistory() {
|
||||
|
||||
}
|
||||
|
||||
public void runFindCodeSmells() {
|
||||
|
||||
}
|
||||
|
||||
public void runFixup() {
|
||||
|
||||
}
|
||||
|
||||
public void runContextSearch() {
|
||||
|
||||
}
|
||||
|
||||
public void runReleaseNotes() {
|
||||
|
||||
}
|
||||
public void runReleaseNotes() {}
|
||||
}
|
||||
|
||||
@ -5,41 +5,44 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Event {
|
||||
final String eventName;
|
||||
final String anonymousUserId;
|
||||
final String url;
|
||||
final JsonObject eventProperties;
|
||||
/**
|
||||
* PRIVACY: Do NOT include any potentially private information, such as search queries or repository names.
|
||||
*/
|
||||
final JsonObject publicArgument;
|
||||
final String eventName;
|
||||
final String anonymousUserId;
|
||||
final String url;
|
||||
final JsonObject eventProperties;
|
||||
|
||||
public Event(@NotNull String eventName,
|
||||
@NotNull String anonymousUserId,
|
||||
@NotNull String url,
|
||||
@Nullable JsonObject eventProperties,
|
||||
@Nullable JsonObject publicArgument) {
|
||||
this.eventName = eventName;
|
||||
this.anonymousUserId = anonymousUserId;
|
||||
this.url = url;
|
||||
this.eventProperties = eventProperties;
|
||||
this.publicArgument = publicArgument;
|
||||
}
|
||||
/**
|
||||
* PRIVACY: Do NOT include any potentially private information, such as search queries or
|
||||
* repository names.
|
||||
*/
|
||||
final JsonObject publicArgument;
|
||||
|
||||
public JsonObject toJson() {
|
||||
JsonObject returnValue = new JsonObject();
|
||||
returnValue.addProperty("event", this.eventName);
|
||||
returnValue.addProperty("userCookieID", this.anonymousUserId);
|
||||
returnValue.addProperty("url", this.url);
|
||||
returnValue.addProperty("source", "IDEEXTENSION");
|
||||
returnValue.addProperty("referrer", "CODY-JETBRAINS");
|
||||
if (eventProperties != null) {
|
||||
returnValue.add("argument", eventProperties);
|
||||
}
|
||||
if (publicArgument != null) {
|
||||
returnValue.add("publicArgument", publicArgument);
|
||||
}
|
||||
returnValue.addProperty("deviceID", this.anonymousUserId);
|
||||
return returnValue;
|
||||
public Event(
|
||||
@NotNull String eventName,
|
||||
@NotNull String anonymousUserId,
|
||||
@NotNull String url,
|
||||
@Nullable JsonObject eventProperties,
|
||||
@Nullable JsonObject publicArgument) {
|
||||
this.eventName = eventName;
|
||||
this.anonymousUserId = anonymousUserId;
|
||||
this.url = url;
|
||||
this.eventProperties = eventProperties;
|
||||
this.publicArgument = publicArgument;
|
||||
}
|
||||
|
||||
public JsonObject toJson() {
|
||||
JsonObject returnValue = new JsonObject();
|
||||
returnValue.addProperty("event", this.eventName);
|
||||
returnValue.addProperty("userCookieID", this.anonymousUserId);
|
||||
returnValue.addProperty("url", this.url);
|
||||
returnValue.addProperty("source", "IDEEXTENSION");
|
||||
returnValue.addProperty("referrer", "CODY-JETBRAINS");
|
||||
if (eventProperties != null) {
|
||||
returnValue.add("argument", eventProperties);
|
||||
}
|
||||
if (publicArgument != null) {
|
||||
returnValue.add("publicArgument", publicArgument);
|
||||
}
|
||||
returnValue.addProperty("deviceID", this.anonymousUserId);
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,69 +7,91 @@ import com.intellij.openapi.project.Project;
|
||||
import com.sourcegraph.api.GraphQlClient;
|
||||
import com.sourcegraph.cody.config.ConfigUtil;
|
||||
import com.sourcegraph.cody.config.SettingsComponent;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class GraphQlLogger {
|
||||
private static final Logger logger = Logger.getInstance(GraphQlLogger.class);
|
||||
private static final Logger logger = Logger.getInstance(GraphQlLogger.class);
|
||||
|
||||
public static void logInstallEvent(@NotNull Project project, @NotNull Consumer<Boolean> callback) {
|
||||
String anonymousUserId = ConfigUtil.getAnonymousUserId();
|
||||
if (anonymousUserId != null) {
|
||||
Event event = new Event("CodyInstalled", anonymousUserId, ConfigUtil.getSourcegraphUrl(project), null, null);
|
||||
logEvent(project, event, (responseStatusCode) -> callback.accept(responseStatusCode == 200));
|
||||
}
|
||||
public static void logInstallEvent(
|
||||
@NotNull Project project, @NotNull Consumer<Boolean> callback) {
|
||||
String anonymousUserId = ConfigUtil.getAnonymousUserId();
|
||||
if (anonymousUserId != null) {
|
||||
Event event =
|
||||
new Event(
|
||||
"CodyInstalled", anonymousUserId, ConfigUtil.getSourcegraphUrl(project), null, null);
|
||||
logEvent(project, event, (responseStatusCode) -> callback.accept(responseStatusCode == 200));
|
||||
}
|
||||
}
|
||||
|
||||
public static void logUninstallEvent(@NotNull Project project) {
|
||||
String anonymousUserId = ConfigUtil.getAnonymousUserId();
|
||||
if (anonymousUserId != null) {
|
||||
Event event = new Event("CodyUninstalled", anonymousUserId, ConfigUtil.getSourcegraphUrl(project), null, null);
|
||||
logEvent(project, event, null);
|
||||
}
|
||||
public static void logUninstallEvent(@NotNull Project project) {
|
||||
String anonymousUserId = ConfigUtil.getAnonymousUserId();
|
||||
if (anonymousUserId != null) {
|
||||
Event event =
|
||||
new Event(
|
||||
"CodyUninstalled",
|
||||
anonymousUserId,
|
||||
ConfigUtil.getSourcegraphUrl(project),
|
||||
null,
|
||||
null);
|
||||
logEvent(project, event, null);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use this
|
||||
public static void logSearchDuration(@NotNull Project project, long duration) {
|
||||
String anonymousUserId = ConfigUtil.getAnonymousUserId();
|
||||
if (anonymousUserId != null) {
|
||||
JsonObject durationObject = new JsonObject();
|
||||
durationObject.addProperty("duration", duration);
|
||||
Event event = new Event("CodyJetBrainsExtension:keywordContext:searchDuration",
|
||||
anonymousUserId, ConfigUtil.getSourcegraphUrl(project), durationObject, durationObject);
|
||||
logEvent(project, event, null);
|
||||
}
|
||||
// TODO: Use this
|
||||
public static void logSearchDuration(@NotNull Project project, long duration) {
|
||||
String anonymousUserId = ConfigUtil.getAnonymousUserId();
|
||||
if (anonymousUserId != null) {
|
||||
JsonObject durationObject = new JsonObject();
|
||||
durationObject.addProperty("duration", duration);
|
||||
Event event =
|
||||
new Event(
|
||||
"CodyJetBrainsExtension:keywordContext:searchDuration",
|
||||
anonymousUserId,
|
||||
ConfigUtil.getSourcegraphUrl(project),
|
||||
durationObject,
|
||||
durationObject);
|
||||
logEvent(project, event, null);
|
||||
}
|
||||
}
|
||||
|
||||
// This could be exposed later (as public), but currently, we don't use it externally.
|
||||
private static void logEvent(@NotNull Project project, @NotNull Event event, @Nullable Consumer<Integer> callback) {
|
||||
String instanceUrl = ConfigUtil.getSourcegraphUrl(project);
|
||||
String accessToken = ConfigUtil.getInstanceType(project) == SettingsComponent.InstanceType.ENTERPRISE
|
||||
? ConfigUtil.getEnterpriseAccessToken(project) : ConfigUtil.getDotcomAccessToken(project);
|
||||
String customRequestHeaders = ConfigUtil.getCustomRequestHeaders(project);
|
||||
new Thread(() -> {
|
||||
String query = "mutation LogEvents($events: [Event!]) {" +
|
||||
" logEvents(events: $events) { " +
|
||||
" alwaysNil" +
|
||||
" }" +
|
||||
"}";
|
||||
// This could be exposed later (as public), but currently, we don't use it externally.
|
||||
private static void logEvent(
|
||||
@NotNull Project project, @NotNull Event event, @Nullable Consumer<Integer> callback) {
|
||||
String instanceUrl = ConfigUtil.getSourcegraphUrl(project);
|
||||
String accessToken =
|
||||
ConfigUtil.getInstanceType(project) == SettingsComponent.InstanceType.ENTERPRISE
|
||||
? ConfigUtil.getEnterpriseAccessToken(project)
|
||||
: ConfigUtil.getDotcomAccessToken(project);
|
||||
String customRequestHeaders = ConfigUtil.getCustomRequestHeaders(project);
|
||||
new Thread(
|
||||
() -> {
|
||||
String query =
|
||||
"mutation LogEvents($events: [Event!]) {"
|
||||
+ " logEvents(events: $events) { "
|
||||
+ " alwaysNil"
|
||||
+ " }"
|
||||
+ "}";
|
||||
|
||||
JsonArray events = new JsonArray();
|
||||
events.add(event.toJson());
|
||||
JsonObject variables = new JsonObject();
|
||||
variables.add("events", events);
|
||||
JsonArray events = new JsonArray();
|
||||
events.add(event.toJson());
|
||||
JsonObject variables = new JsonObject();
|
||||
variables.add("events", events);
|
||||
|
||||
try {
|
||||
int responseStatusCode = GraphQlClient.callGraphQLService(instanceUrl, accessToken, customRequestHeaders, query, variables).getStatusCode();
|
||||
try {
|
||||
int responseStatusCode =
|
||||
GraphQlClient.callGraphQLService(
|
||||
instanceUrl, accessToken, customRequestHeaders, query, variables)
|
||||
.getStatusCode();
|
||||
if (callback != null) {
|
||||
callback.accept(responseStatusCode);
|
||||
callback.accept(responseStatusCode);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (IOException e) {
|
||||
logger.info(e);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,46 +7,50 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.startup.StartupActivity;
|
||||
import com.sourcegraph.cody.config.ConfigUtil;
|
||||
import com.sourcegraph.cody.config.SettingsChangeListener;
|
||||
import java.util.UUID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PostStartupActivity implements StartupActivity.DumbAware {
|
||||
private static String generateAnonymousUserId() {
|
||||
return UUID.randomUUID().toString();
|
||||
private static String generateAnonymousUserId() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runActivity(@NotNull Project project) {
|
||||
// Make sure that SettingsChangeListener is loaded
|
||||
project.getService(SettingsChangeListener.class);
|
||||
|
||||
// When no anonymous user ID is set yet, we create a new one and treat this as an installation
|
||||
// event.
|
||||
// This likely means that the user has never started IntelliJ with our extension before
|
||||
if (ConfigUtil.getAnonymousUserId() == null) {
|
||||
ConfigUtil.setAnonymousUserId(generateAnonymousUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runActivity(@NotNull Project project) {
|
||||
// Make sure that SettingsChangeListener is loaded
|
||||
project.getService(SettingsChangeListener.class);
|
||||
|
||||
// When no anonymous user ID is set yet, we create a new one and treat this as an installation event.
|
||||
// This likely means that the user has never started IntelliJ with our extension before
|
||||
if (ConfigUtil.getAnonymousUserId() == null) {
|
||||
ConfigUtil.setAnonymousUserId(generateAnonymousUserId());
|
||||
}
|
||||
|
||||
PluginInstaller.addStateListener(new PluginStateListener() {
|
||||
public void install(@NotNull IdeaPluginDescriptor ideaPluginDescriptor) {
|
||||
GraphQlLogger.logInstallEvent(project, (wasSuccessful) -> {
|
||||
if (wasSuccessful) {
|
||||
ConfigUtil.setInstallEventLogged(true);
|
||||
}
|
||||
PluginInstaller.addStateListener(
|
||||
new PluginStateListener() {
|
||||
public void install(@NotNull IdeaPluginDescriptor ideaPluginDescriptor) {
|
||||
GraphQlLogger.logInstallEvent(
|
||||
project,
|
||||
(wasSuccessful) -> {
|
||||
if (wasSuccessful) {
|
||||
ConfigUtil.setInstallEventLogged(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstall(@NotNull IdeaPluginDescriptor ideaPluginDescriptor) {
|
||||
if (ideaPluginDescriptor.getPluginId().getIdString().equals("com.sourcegraph.cody")) {
|
||||
GraphQlLogger.logUninstallEvent(project);
|
||||
|
||||
// Clearing this so that we can detect a new installation if the user re-enables the
|
||||
// extension.
|
||||
ConfigUtil.setAnonymousUserId(null);
|
||||
|
||||
ConfigUtil.setInstallEventLogged(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstall(@NotNull IdeaPluginDescriptor ideaPluginDescriptor) {
|
||||
if (ideaPluginDescriptor.getPluginId().getIdString().equals("com.sourcegraph.cody")) {
|
||||
GraphQlLogger.logUninstallEvent(project);
|
||||
|
||||
// Clearing this so that we can detect a new installation if the user re-enables the extension.
|
||||
ConfigUtil.setAnonymousUserId(null);
|
||||
|
||||
ConfigUtil.setInstallEventLogged(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user